drivers: dac: add driver for AD568xx
Implement a driver for the DAC AD56xx series. Signed-off-by: Benedikt Schmidt <benedikt.schmidt@embedded-solutions.at>
This commit is contained in:
parent
54f8c2b98b
commit
536f8ae9cd
|
@ -17,4 +17,5 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCP4725 dac_mcp4725.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_DAC_MCP4728 dac_mcp4728.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DAC_GD32 dac_gd32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DAC_ESP32 dac_esp32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DAC_AD56XX dac_ad56xx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c)
|
||||
|
|
|
@ -52,4 +52,6 @@ source "drivers/dac/Kconfig.gd32"
|
|||
|
||||
source "drivers/dac/Kconfig.esp32"
|
||||
|
||||
source "drivers/dac/Kconfig.ad56xx"
|
||||
|
||||
endif # DAC
|
||||
|
|
33
drivers/dac/Kconfig.ad56xx
Normal file
33
drivers/dac/Kconfig.ad56xx
Normal file
|
@ -0,0 +1,33 @@
|
|||
# DAC configuration options
|
||||
|
||||
# Copyright (c) 2023 SILA Embedded Solutions GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config DAC_AD56XX
|
||||
bool "Analog Devices AD56xx DAC driver"
|
||||
default y
|
||||
select SPI
|
||||
depends on DT_HAS_ADI_AD5628_ENABLED \
|
||||
|| DT_HAS_ADI_AD5648_ENABLED \
|
||||
|| DT_HAS_ADI_AD5668_ENABLED \
|
||||
|| DT_HAS_ADI_AD5672_ENABLED \
|
||||
|| DT_HAS_ADI_AD5674_ENABLED \
|
||||
|| DT_HAS_ADI_AD5676_ENABLED \
|
||||
|| DT_HAS_ADI_AD5679_ENABLED \
|
||||
|| DT_HAS_ADI_AD5684_ENABLED \
|
||||
|| DT_HAS_ADI_AD5686_ENABLED \
|
||||
|| DT_HAS_ADI_AD5687_ENABLED \
|
||||
|| DT_HAS_ADI_AD5689_ENABLED
|
||||
help
|
||||
Enable the driver for the Analog Devices AD56xx DAC
|
||||
|
||||
if DAC_AD56XX
|
||||
|
||||
config DAC_AD56XX_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 80
|
||||
help
|
||||
Analog Devices AD56xx DAC device driver initialization priority.
|
||||
|
||||
endif # DAC_AD56XX
|
328
drivers/dac/dac_ad56xx.c
Normal file
328
drivers/dac/dac_ad56xx.c
Normal file
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Copyright (c) 2023 SILA Embedded Solutions GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/drivers/dac.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
LOG_MODULE_REGISTER(dac_ad56xx, CONFIG_DAC_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* These values are actually all way less than 1us, but we can only
|
||||
* wait with 1us precision.
|
||||
*
|
||||
* This should be checked when new types of this series are added to
|
||||
* this implementation.
|
||||
*/
|
||||
#define DAC_AD56XX_MINIMUM_PULSE_WIDTH_LOW_IN_US 1
|
||||
#define DAC_AD56XX_PULSE_ACTIVATION_TIME_IN_US 1
|
||||
|
||||
enum ad56xx_command {
|
||||
AD56XX_CMD_WRITE_UPDATE_CHANNEL = 3,
|
||||
AD56XX_CMD_SOFTWARE_RESET = 6,
|
||||
};
|
||||
|
||||
struct ad56xx_config {
|
||||
struct spi_dt_spec bus;
|
||||
const struct gpio_dt_spec gpio_reset;
|
||||
uint8_t resolution;
|
||||
|
||||
const uint8_t *channel_addresses;
|
||||
size_t channel_count;
|
||||
};
|
||||
|
||||
struct ad56xx_data {
|
||||
};
|
||||
|
||||
static int ad56xx_write_command(const struct device *dev, enum ad56xx_command command,
|
||||
uint8_t address, uint16_t value)
|
||||
{
|
||||
const struct ad56xx_config *config = dev->config;
|
||||
uint8_t buffer_tx[3];
|
||||
uint8_t buffer_rx[ARRAY_SIZE(buffer_tx)];
|
||||
const struct spi_buf tx_buf[] = {{
|
||||
.buf = buffer_tx,
|
||||
.len = ARRAY_SIZE(buffer_tx),
|
||||
}};
|
||||
const struct spi_buf rx_buf[] = {{
|
||||
.buf = buffer_rx,
|
||||
.len = ARRAY_SIZE(buffer_rx),
|
||||
}};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = tx_buf,
|
||||
.count = ARRAY_SIZE(tx_buf),
|
||||
};
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = ARRAY_SIZE(rx_buf),
|
||||
};
|
||||
|
||||
buffer_tx[0] = (command << 4) | address;
|
||||
value = value << (16 - config->resolution);
|
||||
sys_put_be16(value, buffer_tx + 1);
|
||||
|
||||
LOG_DBG("sending to DAC %s command 0x%02X, address 0x%02X and value 0x%04X", dev->name,
|
||||
command, address, value);
|
||||
int result = spi_transceive_dt(&config->bus, &tx, &rx);
|
||||
|
||||
if (result != 0) {
|
||||
LOG_ERR("spi_transceive failed with error %i", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad56xx_channel_setup(const struct device *dev, const struct dac_channel_cfg *channel_cfg)
|
||||
{
|
||||
const struct ad56xx_config *config = dev->config;
|
||||
|
||||
if (channel_cfg->channel_id >= config->channel_count) {
|
||||
LOG_ERR("invalid channel %i", channel_cfg->channel_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channel_cfg->resolution != config->resolution) {
|
||||
LOG_ERR("invalid resolution %i", channel_cfg->resolution);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad56xx_write_value(const struct device *dev, uint8_t channel, uint32_t value)
|
||||
{
|
||||
const struct ad56xx_config *config = dev->config;
|
||||
|
||||
if (value > BIT(config->resolution) - 1) {
|
||||
LOG_ERR("invalid value %i", value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channel > config->channel_count) {
|
||||
LOG_ERR("invalid channel %i", channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ad56xx_write_command(dev, AD56XX_CMD_WRITE_UPDATE_CHANNEL,
|
||||
config->channel_addresses[channel], value);
|
||||
}
|
||||
|
||||
static int ad56xx_init(const struct device *dev)
|
||||
{
|
||||
const struct ad56xx_config *config = dev->config;
|
||||
int result;
|
||||
|
||||
if (!spi_is_ready_dt(&config->bus)) {
|
||||
LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (config->gpio_reset.port != NULL) {
|
||||
LOG_DBG("reset %s with GPIO", dev->name);
|
||||
result = gpio_pin_configure_dt(&config->gpio_reset, GPIO_OUTPUT_ACTIVE);
|
||||
if (result != 0) {
|
||||
LOG_ERR("failed to initialize GPIO for reset");
|
||||
return result;
|
||||
}
|
||||
|
||||
k_busy_wait(DAC_AD56XX_MINIMUM_PULSE_WIDTH_LOW_IN_US);
|
||||
gpio_pin_set_dt(&config->gpio_reset, 0);
|
||||
} else {
|
||||
LOG_DBG("reset %s with command", dev->name);
|
||||
result = ad56xx_write_command(dev, AD56XX_CMD_SOFTWARE_RESET, 0, 0);
|
||||
if (result != 0) {
|
||||
LOG_ERR("failed to send reset command");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The pulse activation time is actually defined to start together
|
||||
* with the pulse start. To be on the safe side we add the wait time
|
||||
* on top of the actual pulse.
|
||||
*/
|
||||
k_busy_wait(DAC_AD56XX_PULSE_ACTIVATION_TIME_IN_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dac_driver_api ad56xx_driver_api = {
|
||||
.channel_setup = ad56xx_channel_setup,
|
||||
.write_value = ad56xx_write_value,
|
||||
};
|
||||
|
||||
BUILD_ASSERT(CONFIG_DAC_AD56XX_INIT_PRIORITY > CONFIG_SPI_INIT_PRIORITY,
|
||||
"CONFIG_DAC_AD56XX_INIT_PRIORITY must be higher than CONFIG_SPI_INIT_PRIORITY");
|
||||
|
||||
#define DAC_AD56XX_INST_DEFINE(index, name, res, channels, channels_count) \
|
||||
static struct ad56xx_data data_##name##_##index; \
|
||||
static const struct ad56xx_config config_##name##_##index = { \
|
||||
.bus = SPI_DT_SPEC_INST_GET( \
|
||||
index, SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
|
||||
.resolution = res, \
|
||||
.gpio_reset = GPIO_DT_SPEC_INST_GET_OR(index, reset_gpios, {0}), \
|
||||
.channel_addresses = channels, \
|
||||
.channel_count = channels_count, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(index, ad56xx_init, NULL, &data_##name##_##index, \
|
||||
&config_##name##_##index, POST_KERNEL, \
|
||||
CONFIG_DAC_AD56XX_INIT_PRIORITY, &ad56xx_driver_api);
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5628
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5628_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
};
|
||||
#define DAC_AD5628_RESOLUTION 12
|
||||
#define DAC_AD5628_CHANNELS ad5628_channels
|
||||
#define DAC_AD5628_CHANNEL_COUNT ARRAY_SIZE(ad5628_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5628_RESOLUTION,
|
||||
DAC_AD5628_CHANNELS, DAC_AD5628_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5648
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5648_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
};
|
||||
#define DAC_AD5648_RESOLUTION 14
|
||||
#define DAC_AD5648_CHANNELS ad5648_channels
|
||||
#define DAC_AD5648_CHANNEL_COUNT ARRAY_SIZE(ad5648_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5648_RESOLUTION,
|
||||
DAC_AD5648_CHANNELS, DAC_AD5648_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5668
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5668_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
};
|
||||
#define DAC_AD5668_RESOLUTION 16
|
||||
#define DAC_AD5668_CHANNELS ad5668_channels
|
||||
#define DAC_AD5668_CHANNEL_COUNT ARRAY_SIZE(ad5668_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5668_RESOLUTION,
|
||||
DAC_AD5668_CHANNELS, DAC_AD5668_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5672
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5672_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
};
|
||||
#define DAC_AD5672_RESOLUTION 12
|
||||
#define DAC_AD5672_CHANNELS ad5672_channels
|
||||
#define DAC_AD5672_CHANNEL_COUNT ARRAY_SIZE(ad5672_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5672_RESOLUTION,
|
||||
DAC_AD5672_CHANNELS, DAC_AD5672_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5674
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5674_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
#define DAC_AD5674_RESOLUTION 12
|
||||
#define DAC_AD5674_CHANNELS ad5674_channels
|
||||
#define DAC_AD5674_CHANNEL_COUNT ARRAY_SIZE(ad5674_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5674_RESOLUTION,
|
||||
DAC_AD5674_CHANNELS, DAC_AD5674_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5676
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5676_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
};
|
||||
#define DAC_AD5676_RESOLUTION 16
|
||||
#define DAC_AD5676_CHANNELS ad5676_channels
|
||||
#define DAC_AD5676_CHANNEL_COUNT ARRAY_SIZE(ad5676_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5676_RESOLUTION,
|
||||
DAC_AD5676_CHANNELS, DAC_AD5676_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5679
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5679_channels[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
#define DAC_AD5679_RESOLUTION 16
|
||||
#define DAC_AD5679_CHANNELS ad5679_channels
|
||||
#define DAC_AD5679_CHANNEL_COUNT ARRAY_SIZE(ad5679_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5679_RESOLUTION,
|
||||
DAC_AD5679_CHANNELS, DAC_AD5679_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5684
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5684_channels[] = {
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
};
|
||||
#define DAC_AD5684_RESOLUTION 12
|
||||
#define DAC_AD5684_CHANNELS ad5684_channels
|
||||
#define DAC_AD5684_CHANNEL_COUNT ARRAY_SIZE(ad5684_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5684_RESOLUTION,
|
||||
DAC_AD5684_CHANNELS, DAC_AD5684_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5686
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5686_channels[] = {
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
};
|
||||
#define DAC_AD5686_RESOLUTION 16
|
||||
#define DAC_AD5686_CHANNELS ad5686_channels
|
||||
#define DAC_AD5686_CHANNEL_COUNT ARRAY_SIZE(ad5686_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5686_RESOLUTION,
|
||||
DAC_AD5686_CHANNELS, DAC_AD5686_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5687
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5687_channels[] = {
|
||||
1,
|
||||
8,
|
||||
};
|
||||
#define DAC_AD5687_RESOLUTION 12
|
||||
#define DAC_AD5687_CHANNELS ad5687_channels
|
||||
#define DAC_AD5687_CHANNEL_COUNT ARRAY_SIZE(ad5687_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5687_RESOLUTION,
|
||||
DAC_AD5687_CHANNELS, DAC_AD5687_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
||||
|
||||
#define DT_DRV_COMPAT adi_ad5689
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
static const uint8_t ad5689_channels[] = {
|
||||
1,
|
||||
8,
|
||||
};
|
||||
#define DAC_AD5689_RESOLUTION 16
|
||||
#define DAC_AD5689_CHANNELS ad5689_channels
|
||||
#define DAC_AD5689_CHANNEL_COUNT ARRAY_SIZE(ad5689_channels)
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5689_RESOLUTION,
|
||||
DAC_AD5689_CHANNELS, DAC_AD5689_CHANNEL_COUNT)
|
||||
#endif
|
||||
#undef DT_DRV_COMPAT
|
Loading…
Reference in a new issue