From 610d8a8b961d97ff850765299b75459f05a9c2cd Mon Sep 17 00:00:00 2001 From: Victor Chen <victor.chen@lightelligence.ai> Date: Fri, 24 May 2024 19:05:23 +0800 Subject: [PATCH 1/2] Resolve #38-driver-support-for-spi-dac --- apps/demo/boards/nucleo_g071rb.conf | 2 +- apps/demo/boards/nucleo_g071rb.overlay | 20 +- core/DAC.cpp | 37 ++-- drivers/dac/CMakeLists.txt | 2 +- drivers/dac/Kconfig | 4 +- drivers/dac/dac_soc_stm32.c | 2 +- drivers/dac/dac_spi.c | 197 ++++++++++++++++++ drivers/dac/dac_spi_sc.c | 171 --------------- ...sis,dac-spi-sc.yaml => oasis,dac-spi.yaml} | 20 +- include/zoysia/core/DAC.h | 6 +- include/zoysia/drivers/dac_spi.h | 33 +++ include/zoysia/drivers/dac_spi_sc.h | 40 ---- 12 files changed, 283 insertions(+), 251 deletions(-) create mode 100644 drivers/dac/dac_spi.c delete mode 100644 drivers/dac/dac_spi_sc.c rename dts/bindings/{oasis,dac-spi-sc.yaml => oasis,dac-spi.yaml} (63%) create mode 100644 include/zoysia/drivers/dac_spi.h delete mode 100644 include/zoysia/drivers/dac_spi_sc.h diff --git a/apps/demo/boards/nucleo_g071rb.conf b/apps/demo/boards/nucleo_g071rb.conf index c10f749..4311000 100644 --- a/apps/demo/boards/nucleo_g071rb.conf +++ b/apps/demo/boards/nucleo_g071rb.conf @@ -23,7 +23,7 @@ CONFIG_SPI=y CONFIG_SPI_NOR=y CONFIG_ADC=y CONFIG_ADC_STM32=y -CONFIG_DAC_SPI_SC=y +CONFIG_DAC_SPI=y CONFIG_FISR_TIMER_STM32=y # Core dump diff --git a/apps/demo/boards/nucleo_g071rb.overlay b/apps/demo/boards/nucleo_g071rb.overlay index ad301a9..3ce96da 100644 --- a/apps/demo/boards/nucleo_g071rb.overlay +++ b/apps/demo/boards/nucleo_g071rb.overlay @@ -242,32 +242,38 @@ cs-gpios = <&gpioa 1 GPIO_ACTIVE_LOW>,<&gpioa 4 GPIO_ACTIVE_LOW>,<&gpioa 8 GPIO_ACTIVE_LOW>; - dac_spi_sc_1: dac_spi_sc@0 { - compatible = "oasis,dac-spi-sc"; + dac_spi_1: dac_spi@0 { + compatible = "oasis,dac-spi"; reg = <0>; spi-max-frequency = <10000000>; status = "okay"; cs = <0>; nbits = <12>; - shift = <2>; + command-nbits = <2>; + chan-commands = <0>; + frame-shift = <2>; }; - dac_spi_sc_2: dac_spi_sc@1 { - compatible = "oasis,dac-spi-sc"; + dac_spi_2: dac_spi@1 { + compatible = "oasis,dac-spi"; reg = <1>; spi-max-frequency = <10000000>; status = "okay"; cs = <1>; nbits = <14>; + command-nbits = <2>; + chan-commands = <0>; }; - dac_spi_sc_3: dac_spi_sc@2 { - compatible = "oasis,dac-spi-sc"; + dac_spi_3: dac_spi@2 { + compatible = "oasis,dac-spi"; reg = <2>; spi-max-frequency = <10000000>; status = "okay"; cs = <2>; nbits = <16>; + command-nbits = <2>; + chan-commands = <0>; twos-complement; }; }; diff --git a/core/DAC.cpp b/core/DAC.cpp index e3f1a20..0082c45 100644 --- a/core/DAC.cpp +++ b/core/DAC.cpp @@ -78,6 +78,11 @@ int DAC_::ChannelIndex(int chan) const return 0; } +/*virtual*/ int DAC_::RefVoltagemV() const +{ + return 0; +} + /*virtual*/ int DAC_::ReadValue(int idx) const { return INT_MIN; @@ -119,8 +124,8 @@ int DAC_::ChannelIndex(int chan) const #ifdef CONFIG_DAC_PWM DAC_PWM::Init(); #endif -#ifdef CONFIG_DAC_SPI_SC - DAC_SPI_SC::Init(); +#ifdef CONFIG_DAC_SPI + DAC_SPI::Init(); #endif for(const InitEntry *e = ms_initList; e != 0; e = e->next) @@ -285,32 +290,32 @@ int DAC_::ChannelIndex(int chan) const #endif -#ifdef CONFIG_DAC_SPI_SC +#ifdef CONFIG_DAC_SPI -#include "zoysia/drivers/dac_spi_sc.h" +#include "zoysia/drivers/dac_spi.h" -/*static*/ void DAC_SPI_SC::Init(void) +/*static*/ void DAC_SPI::Init(void) { - for (const struct device **pdev = devlist_dac_spi_sc; *pdev != 0; pdev++) + for (const struct device **pdev = devlist_dac_spi; *pdev != 0; pdev++) { - const struct dac_spi_sc_config *config = reinterpret_cast<const struct dac_spi_sc_config*>((*pdev)->config); - const int dac_spi_sc_cs = config->cs; - const int dac_spi_sc_nbits = config->nbits; - const int dac_spi_sc_nchan = config->nchan; - if (dac_spi_sc_cs < CONFIG_DAC_MAX && dac_spi_sc_nchan > 0) - new DAC_SPI_SC(dac_spi_sc_cs, dac_spi_sc_nbits, dac_spi_sc_nchan, *pdev); + const struct dac_spi_config *config = reinterpret_cast<const struct dac_spi_config*>((*pdev)->config); + const int dac_spi_cs = config->cs; + const int dac_spi_nbits = config->nbits; + const int dac_spi_nchan = config->nchan; + if (dac_spi_cs < CONFIG_DAC_MAX && dac_spi_nchan > 0) + new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, *pdev); } } -/*virtual*/ int DAC_SPI_SC::Command(int cmd, int value) +/*virtual*/ int DAC_SPI::Command(int cmd, int value) { - return dac_spi_sc_command(reinterpret_cast<const struct device*>(m_dev), cmd, value); + return dac_spi_command(reinterpret_cast<const struct device*>(m_dev), cmd, value); } -/*virtual*/ bool DAC_SPI_SC::IsTwosComplement() +/*virtual*/ bool DAC_SPI::IsTwosComplement() { const struct device *dev = reinterpret_cast<const struct device*>(m_dev); - const struct dac_spi_sc_config *config = reinterpret_cast<const struct dac_spi_sc_config*>(dev->config); + const struct dac_spi_config *config = reinterpret_cast<const struct dac_spi_config*>(dev->config); return config->twos_complement != 0; } diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt index 77d61c2..8d61892 100644 --- a/drivers/dac/CMakeLists.txt +++ b/drivers/dac/CMakeLists.txt @@ -5,4 +5,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_SOC dac_soc.c) zephyr_library_sources_ifdef(CONFIG_DAC_SOC_STM32 dac_soc_stm32.c) zephyr_library_sources_ifdef(CONFIG_DAC_PWM dac_pwm.c) zephyr_library_sources_ifdef(CONFIG_DAC_PWM_STM32 dac_pwm_stm32.c) -zephyr_library_sources_ifdef(CONFIG_DAC_SPI_SC dac_spi_sc.c) +zephyr_library_sources_ifdef(CONFIG_DAC_SPI dac_spi.c) diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig index 63c3e4d..68d0d30 100644 --- a/drivers/dac/Kconfig +++ b/drivers/dac/Kconfig @@ -50,8 +50,8 @@ config DAC_SPI_INIT_PRIORITY help SPI DAC driver device initialization priority. -config DAC_SPI_SC - bool "Single channel SPI DAC driver" +config DAC_SPI + bool "SPI DAC driver" select SPI select DAC help diff --git a/drivers/dac/dac_soc_stm32.c b/drivers/dac/dac_soc_stm32.c index 3eda9f0..2e9a2a8 100644 --- a/drivers/dac/dac_soc_stm32.c +++ b/drivers/dac/dac_soc_stm32.c @@ -165,7 +165,7 @@ int dac_soc_ref_internal(const struct device *dev) static const int dac_stm32_chan_##inst[] = { \ LISTIFY(DT_INST_PROP_LEN(inst, channels), DT_INST_DEV_CONFIG_GET, (,), inst) \ }; \ - static const int dac_stm32_nchan_##inst = sizeof(dac_stm32_chan_##inst) / sizeof(uint32_t); \ + static const int dac_stm32_nchan_##inst = sizeof(dac_stm32_chan_##inst) / sizeof(int); \ static const struct dac_stm32_cfg dac_stm32_cfg_##inst = { \ .config = { \ .cs = DT_INST_PROP(inst, cs), \ diff --git a/drivers/dac/dac_spi.c b/drivers/dac/dac_spi.c new file mode 100644 index 0000000..66ec7eb --- /dev/null +++ b/drivers/dac/dac_spi.c @@ -0,0 +1,197 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT oasis_dac_spi + +#include <zephyr/drivers/dac.h> +#include <zephyr/drivers/spi.h> +#include <zephyr/device.h> +#include <zephyr/kernel.h> +#include <zephyr/init.h> + +#include "zoysia/drivers/dac_spi.h" + +struct dac_spi_cfg { + const struct dac_spi_config config; + const struct spi_dt_spec spi; +}; + +struct dac_spi_data { + struct k_sem sem; + int command; + int value; +}; + +static void acquire_device(const struct device *dev) +{ + if (IS_ENABLED(CONFIG_MULTITHREADING)) + { + struct dac_spi_data *const data = dev->data; + k_sem_take(&data->sem, K_FOREVER); + } +} + +static void release_device(const struct device *dev) +{ + if (IS_ENABLED(CONFIG_MULTITHREADING)) + { + struct dac_spi_data *const data = dev->data; + k_sem_give(&data->sem); + } +} + +static int dac_spi_output(const struct device *dev) +{ + const struct dac_spi_cfg *cfg = dev->config; + struct dac_spi_data *const data = dev->data; + + const int nbits = cfg->config.nbits; + const int frame_shift = cfg->config.frame_shift; + const int command_nbits = cfg->config.command_nbits; + const int total_nbits = nbits + frame_shift + command_nbits; + + int output = ((data->command << nbits) | data->value) << frame_shift; + int length = 3; + + if (total_nbits <= 16) + { + output <<= 8; + length = 2; + } + + uint8_t buffer[3] = { + (output >> 16) & 0xFF, + (output >> 8) & 0xFF, + (output & 0xFF) + }; + + struct spi_buf spi_buf[1] = { + { + .buf = buffer, + .len = length + } + }; + const struct spi_buf_set tx_set = { + .buffers = spi_buf, + .count = 1, + }; + + int rc = spi_write_dt(&cfg->spi, &tx_set); + + return rc; +} + +static int dac_spi_write_value(const struct device *dev, uint8_t channel, uint32_t value) +{ + const struct dac_spi_cfg *cfg = dev->config; + struct dac_spi_data *const data = dev->data; + + int rc = -EIO; + + if (channel > 0 && channel <= cfg->config.nchan) + { + const int nbits = cfg->config.nbits; + acquire_device(dev); + data->command = cfg->config.chan_commands[channel-1]; + data->value = (int)(value & ((1 << nbits) - 1)); + rc = dac_spi_output(dev); + release_device(dev); + } + + return rc; +} + +static int dac_spi_init(const struct device *dev) +{ + const struct dac_spi_cfg *cfg = dev->config; + struct dac_spi_data *const data = dev->data; + + data->command = 0; + data->value = 0; + if (IS_ENABLED(CONFIG_MULTITHREADING)) + k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT); + + if (!spi_is_ready_dt(&cfg->spi)) + return -ENODEV; + + int pkt; + const int command_mask = (1 << cfg->config.command_nbits) - 1; + const int value_mask = (1 << cfg->config.nbits) - 1; + const int *cmd = cfg->config.init_commands; + while ((pkt = *cmd++) != 0) + { + data->command = (pkt >> (cfg->config.frame_shift + cfg->config.nbits)) & command_mask; + data->value = (pkt >> cfg->config.frame_shift) & value_mask; + int rc = dac_spi_output(dev); + if (rc != 0) + return rc; + } + + return 0; +} + +static const struct dac_driver_api dac_spi_driver_api = { + .write_value = dac_spi_write_value, +}; + +int dac_spi_command(const struct device *dev, int cmd, int value) +{ + const struct dac_spi_cfg *cfg = dev->config; + struct dac_spi_data *const data = dev->data; + + acquire_device(dev); + + int rc = EINVAL; + const int command_mask = (1 << cfg->config.command_nbits) - 1; + if ((cmd & ~command_mask) == 0) + { + data->command = (int)(cmd & command_mask); + const int value_mask = (1 << cfg->config.nbits) - 1; + if ((value & ~value_mask) == 0) + data->value = (int)(value & value_mask); + rc = dac_spi_output(dev); + } + + release_device(dev); + + return rc; +} + +#define DT_INST_DEV_CONFIG_INIT_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, init_commands, idx) +#define DT_INST_DEV_CONFIG_CHAN_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, chan_commands, idx) + +#define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)), +#define DAC_DEVICE_INIT(inst) \ + static const int dac_spi_init_cmd_##inst[] = { \ + LISTIFY(DT_INST_PROP_LEN(inst, init_commands), DT_INST_DEV_CONFIG_INIT_CMD_GET, (,), inst) \ + }; \ + static const int dac_spi_chan_cmd_##inst[] = { \ + LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_CHAN_CMD_GET, (,), inst) \ + }; \ + static const int dac_spi_nchan_cmd_##inst = sizeof(dac_spi_chan_cmd_##inst) / sizeof(int); \ + static const struct dac_spi_cfg dac_spi_cfg_##inst = { \ + .config = { \ + .cs = DT_INST_PROP(inst, cs), \ + .nbits = DT_INST_PROP(inst, nbits), \ + .nchan = dac_spi_nchan_cmd_##inst, \ + .init_commands = dac_spi_init_cmd_##inst, \ + .chan_commands = dac_spi_chan_cmd_##inst, \ + .command_nbits = DT_INST_PROP(inst, command_nbits), \ + .frame_shift = DT_INST_PROP(inst, frame_shift), \ + .twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \ + }, \ + .spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \ + }; \ + static struct dac_spi_data dac_spi_data_##inst; \ + DEVICE_DT_INST_DEFINE(inst, \ + &dac_spi_init, \ + NULL, \ + &dac_spi_data_##inst, \ + &dac_spi_cfg_##inst, POST_KERNEL, \ + CONFIG_DAC_SPI_INIT_PRIORITY, \ + &dac_spi_driver_api); \ + +DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_INIT) + +const struct device* devlist_dac_spi[] = { DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_ITEM) (0) }; diff --git a/drivers/dac/dac_spi_sc.c b/drivers/dac/dac_spi_sc.c deleted file mode 100644 index 8b585b9..0000000 --- a/drivers/dac/dac_spi_sc.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT oasis_dac_spi_sc - -#include <zephyr/drivers/dac.h> -#include <zephyr/drivers/spi.h> -#include <zephyr/device.h> -#include <zephyr/kernel.h> -#include <zephyr/init.h> - -#include "zoysia/drivers/dac_spi_sc.h" - -struct dac_spi_sc_cfg { - const struct dac_spi_sc_config config; - const struct spi_dt_spec spi; -}; - -struct dac_spi_sc_data { - struct k_sem sem; - int command; - int value; -}; - -static void acquire_device(const struct device *dev) -{ - if (IS_ENABLED(CONFIG_MULTITHREADING)) - { - struct dac_spi_sc_data *const data = dev->data; - k_sem_take(&data->sem, K_FOREVER); - } -} - -static void release_device(const struct device *dev) -{ - if (IS_ENABLED(CONFIG_MULTITHREADING)) - { - struct dac_spi_sc_data *const data = dev->data; - k_sem_give(&data->sem); - } -} - -static int dac_spi_sc_output(const struct device *dev) -{ - const struct dac_spi_sc_cfg *cfg = dev->config; - struct dac_spi_sc_data *const data = dev->data; - - const int nbits = cfg->config.nbits; - const int shift = cfg->config.shift; - const int command_nbits = cfg->config.command_nbits; - const int total_nbits = nbits + shift + command_nbits; - - int output = ((data->command << nbits) | data->value) << shift; - int length = 3; - - if (total_nbits <= 16) - { - output <<= 8; - length = 2; - } - - uint8_t buffer[3] = { - (output >> 16) & 0xFF, - (output >> 8) & 0xFF, - (output & 0xFF) - }; - - struct spi_buf spi_buf[1] = { - { - .buf = buffer, - .len = length - } - }; - const struct spi_buf_set tx_set = { - .buffers = spi_buf, - .count = 1, - }; - - int rc = spi_write_dt(&cfg->spi, &tx_set); - - return rc; -} - -static int dac_spi_sc_write_value(const struct device *dev, uint8_t channel, uint32_t value) -{ - const struct dac_spi_sc_cfg *cfg = dev->config; - struct dac_spi_sc_data *const data = dev->data; - - const int nbits = cfg->config.nbits; - - acquire_device(dev); - - data->command = cfg->config.write_command; - data->value = (int)(value & ((1 << nbits) - 1)); - int rc = dac_spi_sc_output(dev); - - release_device(dev); - - return rc; -} - -static int dac_spi_sc_init(const struct device *dev) -{ - const struct dac_spi_sc_cfg *cfg = dev->config; - struct dac_spi_sc_data *const data = dev->data; - - data->command = 0; - data->value = 0; - if (IS_ENABLED(CONFIG_MULTITHREADING)) - k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT); - - if (!spi_is_ready_dt(&cfg->spi)) - return -ENODEV; - - return 0; -} - -static const struct dac_driver_api dac_spi_sc_driver_api = { - .write_value = dac_spi_sc_write_value, -}; - -int dac_spi_sc_command(const struct device *dev, int cmd, int value) -{ - const struct dac_spi_sc_cfg *cfg = dev->config; - struct dac_spi_sc_data *const data = dev->data; - - acquire_device(dev); - - int rc = EINVAL; - int command_mask = (1 << cfg->config.command_nbits) - 1; - if ((cmd & ~command_mask) == 0) - { - data->command = (int)(cmd & command_mask); - int value_mask = (1 << cfg->config.nbits) - 1; - if ((value & ~value_mask) == 0) - data->value = (int)(value & value_mask); - rc = dac_spi_sc_output(dev); - } - - release_device(dev); - - return rc; -} - -#define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)), -#define DAC_DEVICE_INIT(inst) \ - static const struct dac_spi_sc_cfg dac_spi_sc_cfg_##inst = { \ - .config = { \ - .cs = DT_INST_PROP(inst, cs), \ - .nbits = DT_INST_PROP(inst, nbits), \ - .nchan = 1, \ - .shift = DT_INST_PROP(inst, shift), \ - .command_nbits = DT_INST_PROP(inst, command_nbits), \ - .write_command = DT_INST_PROP(inst, write_command), \ - .twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \ - }, \ - .spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \ - }; \ - static struct dac_spi_sc_data dac_spi_sc_data_##inst; \ - DEVICE_DT_INST_DEFINE(inst, \ - &dac_spi_sc_init, \ - NULL, \ - &dac_spi_sc_data_##inst, \ - &dac_spi_sc_cfg_##inst, POST_KERNEL, \ - CONFIG_DAC_SPI_INIT_PRIORITY, \ - &dac_spi_sc_driver_api); \ - -DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_INIT) - -const struct device* devlist_dac_spi_sc[] = { DT_INST_FOREACH_STATUS_OKAY(DAC_DEVICE_ITEM) (0) }; diff --git a/dts/bindings/oasis,dac-spi-sc.yaml b/dts/bindings/oasis,dac-spi.yaml similarity index 63% rename from dts/bindings/oasis,dac-spi-sc.yaml rename to dts/bindings/oasis,dac-spi.yaml index 23a32fb..adf173d 100644 --- a/dts/bindings/oasis,dac-spi-sc.yaml +++ b/dts/bindings/oasis,dac-spi.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 -description: Single channel SPI DAC node +description: SPI DAC node -compatible: "oasis,dac-spi-sc" +compatible: "oasis,dac-spi" include: [spi-device.yaml, pinctrl-device.yaml] @@ -15,21 +15,23 @@ properties: required: true type: int - shift: + init-commands: required: false - type: int - default: 0 + type: array + default: [0] + + chan-commands: + required: true + type: array command-nbits: - required: false + required: true type: int - default: 2 - write-command: + frame-shift: required: false type: int default: 0 twos-complement: - required: false type: boolean diff --git a/include/zoysia/core/DAC.h b/include/zoysia/core/DAC.h index ce6e403..bde0d07 100644 --- a/include/zoysia/core/DAC.h +++ b/include/zoysia/core/DAC.h @@ -103,15 +103,15 @@ private: }; #endif -#ifdef CONFIG_DAC_SPI_SC -class DAC_SPI_SC : public DAC_ +#ifdef CONFIG_DAC_SPI +class DAC_SPI : public DAC_ { public: static void Init(void); virtual int Command(int cmd, int value); virtual bool IsTwosComplement(); private: - DAC_SPI_SC(int cs, int nbits, int nchan, const void *dev) : + DAC_SPI(int cs, int nbits, int nchan, const void *dev) : DAC_(cs, nbits, nchan, 0, dev) { } }; #endif diff --git a/include/zoysia/drivers/dac_spi.h b/include/zoysia/drivers/dac_spi.h new file mode 100644 index 0000000..eff399b --- /dev/null +++ b/include/zoysia/drivers/dac_spi.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZOYSIA_DRIVERS_DAC_SPI_H +#define ZOYSIA_DRIVERS_DAC_SPI_H 1 + +#include <zephyr/device.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct dac_spi_config { + const int cs; + const int nbits; + const int nchan; + const int command_nbits; + const int *init_commands; + const int *chan_commands; + const int frame_shift; + const int twos_complement; +}; + +int dac_spi_command(const struct device*, int, int); + +extern const struct device* devlist_dac_spi[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/zoysia/drivers/dac_spi_sc.h b/include/zoysia/drivers/dac_spi_sc.h deleted file mode 100644 index b3882e9..0000000 --- a/include/zoysia/drivers/dac_spi_sc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZOYSIA_DRIVERS_DAC_SPI_SC_H -#define ZOYSIA_DRIVERS_DAC_SPI_SC_H 1 - -#include <zephyr/device.h> - -#ifdef __cplusplus -extern "C" { -#endif - -struct dac_spi_sc_config { - const int cs; - const int nbits; - const int nchan; - const int shift; - const int command_nbits; - const int write_command; - const int twos_complement; -}; - -enum -{ - DAC_SPI_SC_NORMAL = 0, - DAC_SPI_SC_PD1K = 1, - DAC_SPI_SC_PD100K = 2, - DAC_SPI_SC_PDHIZ = 3, -}; - -int dac_spi_sc_command(const struct device*, int, int); - -extern const struct device* devlist_dac_spi_sc[]; - -#ifdef __cplusplus -} -#endif - -#endif -- GitLab From cbc4b4633a02e043c5adade9f967c8625d4bc4d7 Mon Sep 17 00:00:00 2001 From: Victor Chen <victor.chen@lightelligence.ai> Date: Mon, 27 May 2024 00:38:41 +0800 Subject: [PATCH 2/2] Resolve #38-driver-support-for-spi-dac --- apps/demo/boards/nucleo_g071rb.overlay | 2 +- core/DAC.cpp | 206 +++++++++-------- drivers/dac/dac_pwm_stm32.c | 3 + drivers/dac/dac_soc.c | 7 +- drivers/dac/dac_soc_stm32.c | 2 +- drivers/dac/dac_spi.c | 301 +++++++++++++++++-------- dts/bindings/oasis,dac-pwm-stm32.yaml | 4 +- dts/bindings/oasis,dac-spi.yaml | 37 ++- include/zoysia/core/DAC.h | 60 ++--- include/zoysia/drivers/dac_mmap.h | 26 --- include/zoysia/drivers/dac_pwm.h | 1 + include/zoysia/drivers/dac_soc.h | 2 +- include/zoysia/drivers/dac_spi.h | 17 +- 13 files changed, 419 insertions(+), 249 deletions(-) delete mode 100644 include/zoysia/drivers/dac_mmap.h diff --git a/apps/demo/boards/nucleo_g071rb.overlay b/apps/demo/boards/nucleo_g071rb.overlay index 3ce96da..95e1f5b 100644 --- a/apps/demo/boards/nucleo_g071rb.overlay +++ b/apps/demo/boards/nucleo_g071rb.overlay @@ -248,10 +248,10 @@ spi-max-frequency = <10000000>; status = "okay"; cs = <0>; + shift = <2>; nbits = <12>; command-nbits = <2>; chan-commands = <0>; - frame-shift = <2>; }; dac_spi_2: dac_spi@1 { diff --git a/core/DAC.cpp b/core/DAC.cpp index 0082c45..883b33a 100644 --- a/core/DAC.cpp +++ b/core/DAC.cpp @@ -18,6 +18,7 @@ DAC_::DAC_(int cs, int nbits, int nchan, const int *chanlist, const void *dev) : m_nbits(nbits), m_nchan(nchan), m_chanlist(chanlist), + m_nchain(1), m_cache(0), m_dev(dev) { @@ -78,17 +79,12 @@ int DAC_::ChannelIndex(int chan) const return 0; } -/*virtual*/ int DAC_::RefVoltagemV() const +/*virtual*/ int DAC_::ReadValue(int idx, int chain) const { return 0; } -/*virtual*/ int DAC_::ReadValue(int idx) const -{ - return INT_MIN; -} - -/*virtual*/ int DAC_::WriteValue(int idx, int count) +/*virtual*/ int DAC_::WriteValue(int idx, int value) { return 0; } @@ -106,20 +102,7 @@ int DAC_::ChannelIndex(int chan) const /*static*/ int* DAC_::Init(int *persist) { #ifdef CONFIG_DAC_SOC - for (const struct device **pdev = devlist_dac_soc; *pdev != 0; pdev++) - { - const struct dac_soc_config *config = reinterpret_cast<const struct dac_soc_config*>((*pdev)->config); - const int dac_cs = config->cs; - const int dac_nbits = config->nbits; - const int dac_nchan = config->nchan; - const int *dac_chanlist = config->channels; - if (dac_cs < CONFIG_DAC_MAX && dac_nchan > 0) - new DAC_(dac_cs, dac_nbits, dac_nchan, dac_chanlist, *pdev); - } -#endif - -#ifdef CONFIG_DAC_MMAP - DAC_MMAP::Init(); + DAC_SOC::Init(); #endif #ifdef CONFIG_DAC_PWM DAC_PWM::Init(); @@ -132,66 +115,53 @@ int DAC_::ChannelIndex(int chan) const (*e->init)(); for (int cs = 0; cs < CONFIG_DAC_MAX; cs++) - { if (ms_inst[cs] != 0 && persist != 0) - { - ms_inst[cs]->AssignCache(persist); - const int nchan = ms_inst[cs]->nChan(); - persist += nchan; - } - } + persist = ms_inst[cs]->AssignCache(persist); return persist; } -/*virtual*/ int DAC_::RefVoltagemV() const +/*virtual*/ int DAC_::ReadValue(int idx, int chain) const { -#ifdef CONFIG_DAC_SOC - return dac_soc_ref_internal(reinterpret_cast<const struct device*>(m_dev)); -#else - return 0; -#endif + return (m_cache != 0) ? m_cache[chain * m_nchan + idx] : 0; } -/*virtual*/ int DAC_::ReadValue(int idx) const +/*virtual*/ int DAC_::WriteValue(int idx, int value) { -#ifdef CONFIG_DAC_SOC - int output = dac_soc_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]); -#else - int output = INT_MIN; -#endif - if (output == INT_MIN && m_cache != 0) - output = m_cache[idx]; - return output; -} -/*virtual*/ int DAC_::WriteValue(int idx, int count) -{ if (m_cache != 0) - m_cache[idx] = count; + for (int chain = 0; chain < m_nchain; chain++) + m_cache[chain * m_nchan + idx] = value; const int channel = (m_chanlist != 0) ? m_chanlist[idx] : idx; - return dac_write_value(reinterpret_cast<const struct device*>(m_dev), channel, count); + return dac_write_value(reinterpret_cast<const struct device*>(m_dev), channel, value); } -#ifdef CONFIG_DAC_MMAP - -#include "zoysia/drivers/dac_mmap.h" +#ifdef CONFIG_DAC_SOC -/*static*/ void DAC_MMAP::Init(void) +/*static*/ void DAC_SOC::Init(void) { - for (const struct device **pdev = devlist_dac_mmap; *pdev != 0; pdev++) + for (const struct device **pdev = devlist_dac_soc; *pdev != 0; pdev++) { - const struct dac_mmap_config *config = reinterpret_cast<const struct dac_mmap_config*>((*pdev)->config); - const int dac_mmap_cs = config->cs; - const int dac_mmap_nbits = config->nbits; - const int dac_mmap_nchan = config->nchan; - const int dac_mmap_width = config->width; - const int dac_mmap_addr = config->addr; - if (dac_mmap_cs < CONFIG_DAC_MAX && dac_mmap_nchan > 0) - new DAC_MMAP(dac_mmap_cs, dac_mmap_nbits, dac_mmap_nchan, dac_mmap_addr); + const struct dac_soc_config *config = reinterpret_cast<const struct dac_soc_config*>((*pdev)->config); + const int dac_cs = config->cs; + const int dac_nbits = config->nbits; + const int dac_nchan = config->nchan; + const int *dac_chanlist = config->channels; + if (dac_cs < CONFIG_DAC_MAX && dac_nchan > 0) + new DAC_SOC(dac_cs, dac_nbits, dac_nchan, dac_chanlist, *pdev); } } +/*virtual*/ int DAC_SOC::Reference(int idx) const +{ + return dac_soc_ref_internal(reinterpret_cast<const struct device*>(m_dev)); +} + +/*virtual*/ int DAC_SOC::ReadValue(int idx, int) const +{ + return dac_soc_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]); +} + #endif #ifdef CONFIG_DAC_PWM @@ -231,11 +201,11 @@ int DAC_::ChannelIndex(int chan) const } } -/*virtual*/ int DAC_PWM::WriteValue(int idx, int count) +/*virtual*/ int DAC_PWM::WriteValue(int idx, int value) { if (m_fsadj != 0) *m_fsadj = 0; - return dac_write_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx], count); + return dac_write_value(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx], value); } /*virtual*/ int DAC_PWM::WriteFraction(int idx, const Fraction& value) @@ -254,7 +224,7 @@ int DAC_::ChannelIndex(int chan) const return DAC_::WriteFraction(idx, value); } -/*virtual*/ int DAC_PWM::ReadValue(int idx) const +/*virtual*/ int DAC_PWM::ReadValue(int idx, int) const { return dac_pwm_cycles(reinterpret_cast<const struct device*>(m_dev), m_chanlist[idx]); } @@ -273,19 +243,14 @@ int DAC_::ChannelIndex(int chan) const return float(count) / float(period); } -/*virtual*/ void DAC_PWM::Lock() +/*virtual*/ int DAC_PWM::LDACTrigger() { - dac_pwm_lock(reinterpret_cast<const struct device*>(m_dev)); -} - -/*virtual*/ void DAC_PWM::UnLock() -{ - dac_pwm_unlock(reinterpret_cast<const struct device*>(m_dev)); -} - -/*virtual*/ bool DAC_PWM::IsLocked() const -{ - return dac_pwm_islocked(reinterpret_cast<const struct device*>(m_dev)); + if (dac_pwm_islocked(reinterpret_cast<const struct device*>(m_dev))) + { + dac_pwm_unlock(reinterpret_cast<const struct device*>(m_dev)); + dac_pwm_lock(reinterpret_cast<const struct device*>(m_dev)); + } + return 0; } #endif @@ -302,16 +267,44 @@ int DAC_::ChannelIndex(int chan) const const int dac_spi_cs = config->cs; const int dac_spi_nbits = config->nbits; const int dac_spi_nchan = config->nchan; + const int dac_spi_nchain = config->nchain; if (dac_spi_cs < CONFIG_DAC_MAX && dac_spi_nchan > 0) - new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, *pdev); + new DAC_SPI(dac_spi_cs, dac_spi_nbits, dac_spi_nchan, dac_spi_nchain, *pdev); } } +/*virtual*/ int DAC_SPI::Reference(int idx) const +{ + return dac_spi_ref_internal(reinterpret_cast<const struct device*>(m_dev), idx); +} + +/*virtual*/ int DAC_SPI::WriteChain(int idx, int *values) +{ + if (values == 0) + return -EINVAL; + + if (m_cache != 0) + for (int chain = 0; chain < m_nchain; chain++) + m_cache[chain * m_nchan + idx] = values[chain]; + + return dac_spi_write_chain(reinterpret_cast<const struct device*>(m_dev), idx, values); +} + /*virtual*/ int DAC_SPI::Command(int cmd, int value) { return dac_spi_command(reinterpret_cast<const struct device*>(m_dev), cmd, value); } +/*virtual*/ int DAC_SPI::Reset() +{ + return dac_spi_reset(reinterpret_cast<const struct device*>(m_dev)); +} + +/*virtual*/ int DAC_SPI::LDACTrigger() +{ + return dac_spi_ldac_trigger(reinterpret_cast<const struct device*>(m_dev)); +} + /*virtual*/ bool DAC_SPI::IsTwosComplement() { const struct device *dev = reinterpret_cast<const struct device*>(m_dev); @@ -391,20 +384,21 @@ private: } } - if (cmdproc->ArgParseKeyword("LOCK", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess()) + if (cmdproc->ArgParseKeyword("LDAC", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess()) { - dac->Lock(); + dac->LDACTrigger(); return; } - if (cmdproc->ArgParseKeyword("UNLOCK", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess()) + if (cmdproc->ArgParseKeyword("RESET", CommandProc::optional) == ArgParseResult(PARSE_OK) && cmdproc->check_excess()) { - dac->UnLock(); + dac->Reset(); return; } int setmode = 0; - int value; + int nvalues = 0; + int *values = new int[dac->nChain()]; float fvalue; int value_min = 0; int value_max = (1 << dac->nBits()) - 1; @@ -413,18 +407,33 @@ private: value_min -= 1 << (dac->nBits() - 1); value_max -= 1 << (dac->nBits() - 1); } - result = cmdproc->ArgParseInt(value, value_min, value_max, CommandProc::optional, 0); + result = cmdproc->ArgParseInt(values[nvalues++], value_min, value_max, CommandProc::optional, 0); if (result == ArgParseResult(PARSE_ERROR)) { return; } else if (result == ArgParseResult(PARSE_OK)) { + while (true) + { + int val; + if (cmdproc->ArgParseInt(val, value_min, value_max, CommandProc::optional, 0) == ArgParseResult(PARSE_OK)) + { + if (nvalues == dac->nChain()) + { + cmdproc->error("?Too many DAC values\n"); + return; + } + values[nvalues++] = val; + continue; + } + break; + } setmode = 1; } else { - result = cmdproc->ArgParseFloat(fvalue, 0.0f, float(dac->RefVoltagemV())/1000.0f, 4, CommandProc::optional); + result = cmdproc->ArgParseFloat(fvalue, CommandProc::optional); if (result == ArgParseResult(PARSE_ERROR)) { return; @@ -443,23 +452,38 @@ private: { if (setmode == 1) { - int ret = dac->WriteValue(i, value); + int ret = (nvalues > 1) ? dac->WriteChain(i, values) : dac->WriteValue(i, values[0]); if (ret != 0) cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: FAILED(%d)\n", cs, dac->Channel(i), ret); } else if (setmode == 2) { - int ret = dac->WriteVoltage(i, fvalue); + int ret = dac->WriteVoltageCurrent(i, fvalue); if (ret != 0) cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: FAILED(%d)\n", cs, dac->Channel(i), ret); } else { - const int value = dac->ReadValue(i); - cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: %d(%.3fmv)\n", cs, dac->Channel(i), value, dac->CalcVoltagemV(i, value)); + const int nchain = dac->nChain(); + const int val_buf_size = nchain * 16; + float *val_arr = new float[nchain]; + char *val_buf = new char[val_buf_size]; + int val, n = 0; + for (int chain = 0; chain < nchain; chain++) + { + val = dac->ReadValue(i, chain); + val_arr[chain] = dac->CalcVoltageCurrent(i, val); + n += snprintf(val_buf + n, val_buf_size - n, (chain == nchain - 1) ? "%d (" : "%d ", val); + } + for (int chain = 0; chain < nchain; chain++) + n += snprintf(val_buf + n, val_buf_size - n, (chain == nchain - 1) ? "%.3f)" : "%.3f ", val_arr[chain]); + cmdproc->GetComm().cprintf("DAC 0x%02X 0x%02X: %s %d\n", cs, dac->Channel(i), val_buf, dac->Reference(i)); + delete[] val_buf; + delete[] val_arr; } } } + delete[] values; } else if (result == ArgParseResult(PARSE_NONE)) { @@ -472,7 +496,7 @@ private: { int n = 0; int s = 32 * dac->nChan(); - char *buf = new char[s + 1]; + char *buf = new char[s]; for (int i = 0; i < dac->nChan(); i++) { char vbuf[32] = ""; @@ -485,9 +509,9 @@ private: } else snprintf(vbuf, sizeof(vbuf), "%d", val); - n += snprintf(buf + n, s + 1 - n, "%s%d:%s%s", i == 0 ? "(" : ",", dac->Channel(i), vbuf, i < dac->nChan() - 1 ? "" : ")"); + n += snprintf(buf + n, s - n, "%s%d:%s%s", i == 0 ? "(" : ",", dac->Channel(i), vbuf, i < dac->nChan() - 1 ? "" : ")"); } - cmdproc->GetComm().cprintf("%d/%db/%dmv,%s%s\n", cs + 1, dac->nBits(), dac->RefVoltagemV(), buf, dac->IsLocked() ? ",locked" : ""); + cmdproc->GetComm().cprintf("%d/%db,%s\n", cs + 1, dac->nBits(), buf); delete[] buf; } } diff --git a/drivers/dac/dac_pwm_stm32.c b/drivers/dac/dac_pwm_stm32.c index d907bc1..d816af7 100644 --- a/drivers/dac/dac_pwm_stm32.c +++ b/drivers/dac/dac_pwm_stm32.c @@ -147,6 +147,8 @@ static int dac_pwm_stm32_init(const struct device *dev) dac_pwm_stm32_channel_setup(dev, &chan_config); dac_pwm_stm32_write_value(dev, cfg->dac_config.channels[chan], cfg->dac_config.init_values[chan]); } + if (cfg->dac_config.enable_ldac) + dac_pwm_lock(dev); } const uint32_t period_cycles = LL_TIM_GetAutoReload(cfg->timer_config.timer); data->period_cycles_adjust = (int)(1 << cfg->dac_config.nbits) - (int)(period_cycles) - 1; @@ -215,6 +217,7 @@ int dac_pwm_cycles(const struct device *dev, int chan) .nchan = dac_stm32_timer_nchan_##inst, \ .channels = dac_stm32_timer_chan_##inst, \ .init_values = dac_stm32_timer_init_val_##inst, \ + .enable_ldac = DT_INST_PROP_OR(inst, enable_ldac, 0), \ .auto_period = (dac_stm32_timer_nchan_##inst != 1) ? 0 : DT_INST_PROP_OR(inst, auto_period, 0), \ }, \ .pinctrl_config = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ diff --git a/drivers/dac/dac_soc.c b/drivers/dac/dac_soc.c index 1e0ef47..f590287 100644 --- a/drivers/dac/dac_soc.c +++ b/drivers/dac/dac_soc.c @@ -7,7 +7,12 @@ __attribute__((weak)) const struct device* devlist_dac_soc[] = {0}; -__attribute__((weak)) int dac_soc_value(const struct device*, uint8_t) +__attribute__((weak)) int dac_soc_value(const struct device*, int) { return INT_MIN; } + +__attribute__((weak)) int dac_soc_ref_internal(const struct device*) +{ + return 0; +} diff --git a/drivers/dac/dac_soc_stm32.c b/drivers/dac/dac_soc_stm32.c index 2e9a2a8..47384e3 100644 --- a/drivers/dac/dac_soc_stm32.c +++ b/drivers/dac/dac_soc_stm32.c @@ -127,7 +127,7 @@ static const struct dac_driver_api dac_stm32_driver_api = { .write_value = dac_stm32_write_value }; -int dac_soc_value(const struct device *dev, uint8_t channel) +int dac_soc_value(const struct device *dev, int channel) { const struct dac_stm32_cfg *cfg = dev->config; diff --git a/drivers/dac/dac_spi.c b/drivers/dac/dac_spi.c index 66ec7eb..3cb7394 100644 --- a/drivers/dac/dac_spi.c +++ b/drivers/dac/dac_spi.c @@ -4,6 +4,8 @@ #define DT_DRV_COMPAT oasis_dac_spi +#include <stdlib.h> + #include <zephyr/drivers/dac.h> #include <zephyr/drivers/spi.h> #include <zephyr/device.h> @@ -17,149 +19,254 @@ struct dac_spi_cfg { const struct spi_dt_spec spi; }; -struct dac_spi_data { - struct k_sem sem; - int command; - int value; -}; - -static void acquire_device(const struct device *dev) +static int dac_spi_output(const struct device *dev, int output) { - if (IS_ENABLED(CONFIG_MULTITHREADING)) + const struct dac_spi_cfg *cfg = dev->config; + + int rc = -EINVAL; + + const int nbits = cfg->config.nbits; + const int shift = cfg->config.shift; + const int command_nbits = cfg->config.command_nbits; + const int total_nbits = nbits + shift + command_nbits; + const int bytes = (total_nbits <= 16) ? 2 : 3; + +#if (CONFIG_HEAP_MEM_POOL_SIZE > 0) + uint8_t *buffer = k_malloc(bytes * cfg->config.nchain); +#elif defined(CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE) + uint8_t *buffer = malloc(bytes * cfg->config.nchain); +#else + uint8_t *buffer = 0; +#endif + if (buffer) { - struct dac_spi_data *const data = dev->data; - k_sem_take(&data->sem, K_FOREVER); + for (int chain = 0; chain < cfg->config.nchain; chain++) + { + if (total_nbits <= 16) + { + buffer[2*chain+0] = (output >> 8) & 0xFF; + buffer[2*chain+1] = (output >> 0) & 0xFF; + } + else + { + buffer[3*chain+0] = (output >> 16) & 0xFF; + buffer[3*chain+1] = (output >> 8) & 0xFF; + buffer[3*chain+2] = (output >> 0) & 0xFF; + } + } + struct spi_buf spi_buf[1] = { + { + .buf = buffer, + .len = bytes * cfg->config.nchain, + } + }; + const struct spi_buf_set tx_set = { + .buffers = spi_buf, + .count = 1, + }; + rc = spi_write_dt(&cfg->spi, &tx_set); + free(buffer); } + + return rc; } -static void release_device(const struct device *dev) +static int dac_spi_write_value(const struct device *dev, uint8_t idx, uint32_t value) { - if (IS_ENABLED(CONFIG_MULTITHREADING)) + const struct dac_spi_cfg *cfg = dev->config; + + int rc = -EIO; + + if (idx >= 0 && idx < cfg->config.nchan) { - struct dac_spi_data *const data = dev->data; - k_sem_give(&data->sem); + const int command = (cfg->config.deferred_chan_commands[idx] != 0) ? + cfg->config.deferred_chan_commands[idx] : cfg->config.chan_commands[idx]; + const int val = (int)((value & ((1 << cfg->config.nbits) - 1)) << cfg->config.shift); + rc = dac_spi_output(dev, (command << (cfg->config.nbits + cfg->config.shift)) | val); } + + return rc; } -static int dac_spi_output(const struct device *dev) +static const struct dac_driver_api dac_spi_driver_api = { + .write_value = dac_spi_write_value, +}; + +int dac_spi_ref_internal(const struct device *dev, int idx) { const struct dac_spi_cfg *cfg = dev->config; - struct dac_spi_data *const data = dev->data; - const int nbits = cfg->config.nbits; - const int frame_shift = cfg->config.frame_shift; - const int command_nbits = cfg->config.command_nbits; - const int total_nbits = nbits + frame_shift + command_nbits; + int value = 0; - int output = ((data->command << nbits) | data->value) << frame_shift; - int length = 3; + if (idx >= 0 && idx < cfg->config.nchan) + value = cfg->config.references[idx]; - if (total_nbits <= 16) - { - output <<= 8; - length = 2; - } + return value; +} - uint8_t buffer[3] = { - (output >> 16) & 0xFF, - (output >> 8) & 0xFF, - (output & 0xFF) - }; +int dac_spi_ldac_trigger(const struct device *dev) +{ + const struct dac_spi_cfg *cfg = dev->config; - struct spi_buf spi_buf[1] = { - { - .buf = buffer, - .len = length - } - }; - const struct spi_buf_set tx_set = { - .buffers = spi_buf, - .count = 1, - }; + int rc; - int rc = spi_write_dt(&cfg->spi, &tx_set); + if (cfg->config.ldac_command != 0) + { + if ((rc = dac_spi_output(dev, cfg->config.ldac_command)) < 0) + return rc; + } + else if (cfg->config.ldac_gpio.port != 0 && device_is_ready(cfg->config.ldac_gpio.port)) + { + if ((rc = gpio_pin_set_dt(&cfg->config.ldac_gpio, 1)) < 0) + return rc; + if ((rc = gpio_pin_set_dt(&cfg->config.ldac_gpio, 0)) < 0) + return rc; + } - return rc; + return 0; } -static int dac_spi_write_value(const struct device *dev, uint8_t channel, uint32_t value) +int dac_spi_write_chain(const struct device *dev, int idx, int *values) { const struct dac_spi_cfg *cfg = dev->config; - struct dac_spi_data *const data = dev->data; int rc = -EIO; - if (channel > 0 && channel <= cfg->config.nchan) + if (idx >= 0 && idx < cfg->config.nchan) { const int nbits = cfg->config.nbits; - acquire_device(dev); - data->command = cfg->config.chan_commands[channel-1]; - data->value = (int)(value & ((1 << nbits) - 1)); - rc = dac_spi_output(dev); - release_device(dev); + const int shift = cfg->config.shift; + const int command_nbits = cfg->config.command_nbits; + const int total_nbits = nbits + shift + command_nbits; + + const int command = (cfg->config.deferred_chan_commands[idx] != 0) ? + cfg->config.deferred_chan_commands[idx] : cfg->config.chan_commands[idx]; + const int bytes = (total_nbits <= 16) ? 2 : 3; + +#if (CONFIG_HEAP_MEM_POOL_SIZE > 0) + uint8_t *buffer = k_malloc(bytes * cfg->config.nchain); +#elif defined(CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE) + uint8_t *buffer = malloc(bytes * cfg->config.nchain); +#else + uint8_t *buffer = 0; +#endif + if (buffer) + { + for (int chain = 0; chain < cfg->config.nchain; chain++) + { + const int value = (int)((values[cfg->config.nchain - chain - 1] & ((1 << nbits) - 1)) << shift); + const int output = (command << (nbits + shift)) | value; + if (total_nbits <= 16) + { + buffer[2*chain+0] = (output >> 8) & 0xFF; + buffer[2*chain+1] = (output >> 0) & 0xFF; + } + else + { + buffer[3*chain+0] = (output >> 16) & 0xFF; + buffer[3*chain+1] = (output >> 8) & 0xFF; + buffer[3*chain+2] = (output >> 0) & 0xFF; + } + } + struct spi_buf spi_buf[1] = { + { + .buf = buffer, + .len = bytes * cfg->config.nchain, + } + }; + const struct spi_buf_set tx_set = { + .buffers = spi_buf, + .count = 1, + }; + rc = spi_write_dt(&cfg->spi, &tx_set); + free(buffer); + } } return rc; } -static int dac_spi_init(const struct device *dev) +int dac_spi_command(const struct device *dev, int cmd, int value) { const struct dac_spi_cfg *cfg = dev->config; - struct dac_spi_data *const data = dev->data; - - data->command = 0; - data->value = 0; - if (IS_ENABLED(CONFIG_MULTITHREADING)) - k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT); - if (!spi_is_ready_dt(&cfg->spi)) - return -ENODEV; + int rc = -EINVAL; - int pkt; const int command_mask = (1 << cfg->config.command_nbits) - 1; - const int value_mask = (1 << cfg->config.nbits) - 1; + if ((cmd & ~command_mask) == 0) + { + const int command = (int)(cmd & command_mask); + value &= (1 << (cfg->config.nbits + cfg->config.shift)) - 1; + rc = dac_spi_output(dev, (command << (cfg->config.nbits + cfg->config.shift)) | value); + } + + return rc; +} + +static int dac_spi_configure(const struct device *dev) +{ + const struct dac_spi_cfg *cfg = dev->config; + + // Initial commands + int output; const int *cmd = cfg->config.init_commands; - while ((pkt = *cmd++) != 0) + while ((output = *cmd++) != 0) { - data->command = (pkt >> (cfg->config.frame_shift + cfg->config.nbits)) & command_mask; - data->value = (pkt >> cfg->config.frame_shift) & value_mask; - int rc = dac_spi_output(dev); + int rc = dac_spi_output(dev, output); if (rc != 0) return rc; } + // Initial DAC values + for (int i = 0; i < cfg->config.nchan; i++) + { + if (cfg->config.init_values[i] != 0) + { + int rc = dac_spi_write_value(dev, i, cfg->config.init_values[i]); + if (rc != 0) + return rc; + } + } return 0; } -static const struct dac_driver_api dac_spi_driver_api = { - .write_value = dac_spi_write_value, -}; +static int dac_spi_init(const struct device *dev) +{ + const struct dac_spi_cfg *cfg = dev->config; -int dac_spi_command(const struct device *dev, int cmd, int value) + if (!spi_is_ready_dt(&cfg->spi)) + return -ENODEV; + + const int rc = dac_spi_configure(dev); + if (rc != 0) + return rc; + + return 0; +} + +int dac_spi_reset(const struct device *dev) { const struct dac_spi_cfg *cfg = dev->config; - struct dac_spi_data *const data = dev->data; - acquire_device(dev); + int rc; - int rc = EINVAL; - const int command_mask = (1 << cfg->config.command_nbits) - 1; - if ((cmd & ~command_mask) == 0) + if (cfg->config.reset_command != 0) { - data->command = (int)(cmd & command_mask); - const int value_mask = (1 << cfg->config.nbits) - 1; - if ((value & ~value_mask) == 0) - data->value = (int)(value & value_mask); - rc = dac_spi_output(dev); + if ((rc = dac_spi_output(dev, cfg->config.reset_command)) != 0) + return rc; + k_busy_wait(cfg->config.reset_wait_us); + if ((rc = dac_spi_configure(dev)) != 0) + return rc; } - release_device(dev); - - return rc; + return 0; } #define DT_INST_DEV_CONFIG_INIT_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, init_commands, idx) #define DT_INST_DEV_CONFIG_CHAN_CMD_GET(idx, inst) DT_INST_PROP_BY_IDX(inst, chan_commands, idx) +#define DT_INST_DEV_CONFIG_DEFERRED_CHAN_CMD_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, deferred_chan_commands), (DT_INST_PROP_BY_IDX(inst, deferred_chan_commands, idx)), (0)) +#define DT_INST_DEV_CONFIG_INIT_VAL_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, init_values), (DT_INST_PROP_BY_IDX(inst, init_values, idx)), (0)) +#define DT_INST_DEV_CONFIG_REF_GET(idx, inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, references), (DT_INST_PROP_BY_IDX(inst, references, idx)), (0)) #define DAC_DEVICE_ITEM(inst) DEVICE_DT_GET(DT_DRV_INST(inst)), #define DAC_DEVICE_INIT(inst) \ @@ -169,25 +276,41 @@ int dac_spi_command(const struct device *dev, int cmd, int value) static const int dac_spi_chan_cmd_##inst[] = { \ LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_CHAN_CMD_GET, (,), inst) \ }; \ + static const int dac_spi_deferred_chan_cmd_##inst[] = { \ + LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_DEFERRED_CHAN_CMD_GET, (,), inst) \ + }; \ + static const int dac_spi_init_val_##inst[] = { \ + LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_INIT_VAL_GET, (,), inst) \ + }; \ + static const int dac_spi_chan_ref_##inst[] = { \ + LISTIFY(DT_INST_PROP_LEN(inst, chan_commands), DT_INST_DEV_CONFIG_REF_GET, (,), inst) \ + }; \ static const int dac_spi_nchan_cmd_##inst = sizeof(dac_spi_chan_cmd_##inst) / sizeof(int); \ static const struct dac_spi_cfg dac_spi_cfg_##inst = { \ .config = { \ .cs = DT_INST_PROP(inst, cs), \ + .shift = DT_INST_PROP_OR(inst, shift, 0), \ + .nchain = DT_INST_PROP_OR(inst, nchain, 1), \ .nbits = DT_INST_PROP(inst, nbits), \ .nchan = dac_spi_nchan_cmd_##inst, \ - .init_commands = dac_spi_init_cmd_##inst, \ - .chan_commands = dac_spi_chan_cmd_##inst, \ .command_nbits = DT_INST_PROP(inst, command_nbits), \ - .frame_shift = DT_INST_PROP(inst, frame_shift), \ .twos_complement = DT_INST_PROP_OR(inst, twos_complement, 0), \ + .init_commands = dac_spi_init_cmd_##inst, \ + .chan_commands = dac_spi_chan_cmd_##inst, \ + .deferred_chan_commands = dac_spi_deferred_chan_cmd_##inst, \ + .init_values = dac_spi_init_val_##inst, \ + .references = dac_spi_chan_ref_##inst, \ + .reset_wait_us = DT_INST_PROP_OR(inst, reset_wait_us, 1000), \ + .reset_command = DT_INST_PROP_OR(inst, reset_command, 0), \ + .ldac_command = DT_INST_PROP_OR(inst, ldac_command, 0), \ + .ldac_gpio = GPIO_DT_SPEC_GET_OR(DT_INST_PHANDLE(inst, ldac_gpio), gpios, {}), \ }, \ .spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \ }; \ - static struct dac_spi_data dac_spi_data_##inst; \ DEVICE_DT_INST_DEFINE(inst, \ &dac_spi_init, \ NULL, \ - &dac_spi_data_##inst, \ + NULL, \ &dac_spi_cfg_##inst, POST_KERNEL, \ CONFIG_DAC_SPI_INIT_PRIORITY, \ &dac_spi_driver_api); \ diff --git a/dts/bindings/oasis,dac-pwm-stm32.yaml b/dts/bindings/oasis,dac-pwm-stm32.yaml index f018d04..0980e79 100644 --- a/dts/bindings/oasis,dac-pwm-stm32.yaml +++ b/dts/bindings/oasis,dac-pwm-stm32.yaml @@ -30,12 +30,14 @@ properties: type: array auto-period: - required: false type: boolean init-values: type: array + enable-ldac: + type: boolean + "#dac-pwm-cells": type: int required: true diff --git a/dts/bindings/oasis,dac-spi.yaml b/dts/bindings/oasis,dac-spi.yaml index adf173d..dabb4ef 100644 --- a/dts/bindings/oasis,dac-spi.yaml +++ b/dts/bindings/oasis,dac-spi.yaml @@ -11,12 +11,24 @@ properties: required: true type: int + shift: + type: int + + nchain: + type: int + nbits: required: true type: int + command-nbits: + required: true + type: int + + twos-complement: + type: boolean + init-commands: - required: false type: array default: [0] @@ -24,14 +36,23 @@ properties: required: true type: array - command-nbits: - required: true + deferred-chan-commands: + type: array + + init-values: + type: array + + references: + type: array + + reset-wait-us: type: int - frame-shift: - required: false + reset-command: type: int - default: 0 - twos-complement: - type: boolean + ldac-command: + type: int + + ldac_gpio: + type: phandle diff --git a/include/zoysia/core/DAC.h b/include/zoysia/core/DAC.h index bde0d07..373f228 100644 --- a/include/zoysia/core/DAC.h +++ b/include/zoysia/core/DAC.h @@ -16,27 +16,27 @@ public: static DAC_* Instance(int cs) { return (cs >= 0 && cs < CONFIG_DAC_MAX) ? ms_inst[cs] : 0; } static void RegisterInit(void (*init)(void)); virtual bool IsTwosComplement() const { return false; } - virtual int RefVoltagemV() const; - virtual int WriteValue(int idx, int count); - virtual int ReadValue(int idx) const; + virtual int Reference(int idx) const { return 0; } + virtual int WriteChain(int idx, int *values) { return 0; } + virtual int WriteValue(int idx, int value); + virtual int ReadValue(int idx, int chain = 0) const; virtual int WriteFraction(int idx, const Fraction& value) { int raw = value.toInt(m_nbits); return WriteValue(idx, raw > 0 ? raw : 0); } virtual Fraction ReadFraction(int idx) const { return Fraction(ReadValue(idx), m_nbits); } virtual float ReadFloat(int idx) const { return float(ReadValue(idx)) / float(1 << m_nbits); } virtual int WriteFloat(int idx, float value) { return WriteFraction(idx, Fraction(value)); } virtual int Command(int cmd, int value) { return 0; } - virtual void Lock() { } - virtual void UnLock() { } - virtual bool IsLocked() const { return false; } + virtual int Reset() { return 0; } + virtual int LDACTrigger() { return 0; } virtual int ReadFullScaleAdjust() const { return 0; } - float CalcVoltagemV(int idx, int value) const { return float(value) / float(1 << m_nbits) * float(RefVoltagemV()); } - float ReadVoltagemV(int idx) const { return CalcVoltagemV(idx, ReadValue(idx)); } - float ReadVoltage(int idx) const { return ReadVoltagemV(idx) / 1000.0f; } - int WriteVoltage(int idx, float voltage) { return WriteFloat(idx, voltage * 1000.0f / float(RefVoltagemV())); } + float CalcVoltageCurrent(int idx, int value) const { return float(value) / float(1 << m_nbits) * float(Reference(idx)); } + float ReadVoltageCurrent(int idx) const { return CalcVoltageCurrent(idx, ReadValue(idx)); } + int WriteVoltageCurrent(int idx, float voltage) { return WriteFloat(idx, voltage / float(Reference(idx))); } int Channel(int idx) const; int ChannelIndex(int chan) const; + int nChain() const { return m_nchain; } int nChan() const { return m_nchan; } int nBits() const { return m_nbits; } - int* AssignCache(int *cache) { m_cache = cache; return cache != 0 ? cache + m_nchan : 0; } + int* AssignCache(int *cache) { m_cache = cache; return cache != 0 ? cache + m_nchan * m_nchain + 1 : 0; } static int ScaleFloat (float value, int steps); protected: @@ -45,6 +45,7 @@ protected: const int m_nbits; const int m_nchan; const int *m_chanlist; + int m_nchain; int *m_cache; const void *m_dev; @@ -58,27 +59,28 @@ class DAChan { public: DAChan(DAC_ &dac, int idx) : m_dac(dac), m_idx(idx) { } - int ReadValue() const { return m_dac.ReadValue(m_idx); } - int WriteValue(int count) { return m_dac.WriteValue(m_idx, count); } + int ReadValue(int chain = 0) const { return m_dac.ReadValue(m_idx, chain); } + int WriteValue(int value) { return m_dac.WriteValue(m_idx, value); } Fraction readFraction() const { return m_dac.ReadFraction(m_idx); } int WriteFraction(const Fraction& value) { return m_dac.WriteFraction(m_idx, value); } - float ReadVoltagemV() const { return m_dac.ReadVoltagemV(m_idx); } - float ReadVoltage() const { return m_dac.ReadVoltage(m_idx); } - int WriteVoltage(float value) { return m_dac.WriteVoltage(m_idx, value); } + float ReadVoltageCurrent() const { return m_dac.ReadVoltageCurrent(m_idx); } + int WriteVoltageCurrent(float value) { return m_dac.WriteVoltageCurrent(m_idx, value); } private: DAC_ &m_dac; const int m_idx; }; -#ifdef CONFIG_DAC_MMAP -class DAC_MMAP : public DAC_ +#ifdef CONFIG_DAC_SOC +class DAC_SOC : public DAC_ { public: - static void Init(void); + static void Init(void); + virtual int Reference(int idx) const; + virtual int ReadValue(int idx, int) const; private: - DAC_MMAP(int cs, int nbits, int nchan, const void *dev) : - DAC_(cs, nbits, nchan, 0, dev) { } + DAC_SOC(int cs, int nbits, int nchan, const int *chanlist, const void *dev) : + DAC_(cs, nbits, nchan, chanlist, dev) { } }; #endif @@ -87,14 +89,12 @@ class DAC_PWM : public DAC_ { public: static void Init(void); - virtual int WriteValue(int idx, int count); + virtual int WriteValue(int idx, int value); virtual int WriteFraction(int idx, const Fraction& value); - virtual int ReadValue(int idx) const; + virtual int ReadValue(int idx, int) const; virtual Fraction ReadFraction(int idx) const; virtual float ReadFloat(int idx) const; - virtual void Lock(); - virtual void UnLock(); - virtual bool IsLocked() const; + virtual int LDACTrigger(); virtual int ReadFullScaleAdjust() const { return (m_fsadj != 0) ? *m_fsadj : 0; } private: DAC_PWM(int auto_period, int cs, int nbits, int nchan, int *fsadj, const int *chanlist, const void *dev) : @@ -108,11 +108,15 @@ class DAC_SPI : public DAC_ { public: static void Init(void); + virtual int Reference(int idx) const; + virtual int WriteChain(int idx, int *values); virtual int Command(int cmd, int value); + virtual int Reset(); + virtual int LDACTrigger(); virtual bool IsTwosComplement(); private: - DAC_SPI(int cs, int nbits, int nchan, const void *dev) : - DAC_(cs, nbits, nchan, 0, dev) { } + DAC_SPI(int cs, int nbits, int nchan, int nchain, const void *dev) : + DAC_(cs, nbits, nchan, 0, dev) { m_nchain = nchain; } }; #endif diff --git a/include/zoysia/drivers/dac_mmap.h b/include/zoysia/drivers/dac_mmap.h deleted file mode 100644 index 0a7c1fc..0000000 --- a/include/zoysia/drivers/dac_mmap.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZOYSIA_DRIVERS_DAC_MMAP_H -#define ZOYSIA_DRIVERS_DAC_MMAP_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -struct dac_mmap_config { - const int cs; - const int nbits; - const int nchan; - const int width; - const int addr; -}; - -extern const struct device* devlist_dac_mmap[]; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/zoysia/drivers/dac_pwm.h b/include/zoysia/drivers/dac_pwm.h index 96be288..f257394 100644 --- a/include/zoysia/drivers/dac_pwm.h +++ b/include/zoysia/drivers/dac_pwm.h @@ -17,6 +17,7 @@ struct dac_pwm_config { const int nchan; const int *channels; const int *init_values; + const int enable_ldac; const int auto_period; }; diff --git a/include/zoysia/drivers/dac_soc.h b/include/zoysia/drivers/dac_soc.h index 8ae9378..92060ca 100644 --- a/include/zoysia/drivers/dac_soc.h +++ b/include/zoysia/drivers/dac_soc.h @@ -18,7 +18,7 @@ struct dac_soc_config { const int *channels; }; -int dac_soc_value(const struct device*, uint8_t); +int dac_soc_value(const struct device*, int); int dac_soc_ref_internal(const struct device*); extern const struct device* devlist_dac_soc[]; diff --git a/include/zoysia/drivers/dac_spi.h b/include/zoysia/drivers/dac_spi.h index eff399b..17008d3 100644 --- a/include/zoysia/drivers/dac_spi.h +++ b/include/zoysia/drivers/dac_spi.h @@ -6,6 +6,7 @@ #define ZOYSIA_DRIVERS_DAC_SPI_H 1 #include <zephyr/device.h> +#include <zephyr/drivers/gpio.h> #ifdef __cplusplus extern "C" { @@ -13,16 +14,28 @@ extern "C" { struct dac_spi_config { const int cs; + const int shift; const int nbits; const int nchan; + const int nchain; const int command_nbits; + const int twos_complement; const int *init_commands; const int *chan_commands; - const int frame_shift; - const int twos_complement; + const int *deferred_chan_commands; + const int *init_values; + const int *references; + const int reset_wait_us; + const int reset_command; + const int ldac_command; + struct gpio_dt_spec ldac_gpio; }; +int dac_spi_ref_internal(const struct device*, int); +int dac_spi_ldac_trigger(const struct device*); +int dac_spi_write_chain(const struct device *dev, int, int*); int dac_spi_command(const struct device*, int, int); +int dac_spi_reset(const struct device*); extern const struct device* devlist_dac_spi[]; -- GitLab