Skip to content

Commit e58dfac

Browse files
erang20martinjaeger
authored andcommitted
drivers: dac: Add TI DACx0501 driver
Adds a DAC driver for Texas Instruments DACx0501 family of devices Signed-off-by: Eran Gal <[email protected]> Co-authored-by: Martin Jäger <[email protected]>
1 parent e9562b6 commit e58dfac

File tree

6 files changed

+255
-0
lines changed

6 files changed

+255
-0
lines changed

drivers/dac/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_DAC32 dac_mcux_dac32.c)
1010
zephyr_library_sources_ifdef(CONFIG_DAC_STM32 dac_stm32.c)
1111
zephyr_library_sources_ifdef(CONFIG_DAC_SAM dac_sam.c)
1212
zephyr_library_sources_ifdef(CONFIG_DAC_SAM0 dac_sam0.c)
13+
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0501 dac_dacx0501.c)
1314
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0508 dac_dacx0508.c)
1415
zephyr_library_sources_ifdef(CONFIG_DAC_DACX3608 dac_dacx3608.c)
1516
zephyr_library_sources_ifdef(CONFIG_DAC_LTC166X dac_ltc166x.c)

drivers/dac/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ source "drivers/dac/Kconfig.sam"
3737

3838
source "drivers/dac/Kconfig.sam0"
3939

40+
source "drivers/dac/Kconfig.dacx0501"
41+
4042
source "drivers/dac/Kconfig.dacx0508"
4143

4244
source "drivers/dac/Kconfig.dacx3608"

drivers/dac/Kconfig.dacx0501

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# DAC configuration options
2+
3+
# Copyright (c) 2023 Google, LLC.
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
config DAC_DACX0501
8+
bool "TI DACx0501 DAC driver"
9+
default y
10+
depends on DT_HAS_TI_DACX0501_ENABLED
11+
select I2C
12+
help
13+
Enable the driver for the TI DACx0501.
14+
15+
if DAC_DACX0501
16+
17+
config DAC_DACX0501_INIT_PRIORITY
18+
int "Init priority"
19+
default 80
20+
help
21+
TI DACx0501 DAC device driver initialization priority. Must be greater than I2C_INIT_PRIORITY.
22+
23+
endif # DAC_DACX0501

drivers/dac/dac_dacx0501.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* Copyright (c) 2024 Google LLC.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @brief Driver for Texas Instruments DACx0501 series
9+
*
10+
* Device driver for the Texas Instruments DACx0501 series of devices: DAC60501, DAC70501 and
11+
* DAC80501: Digital to Analog Converters with a single channel output and with 12, 14 and 16
12+
* bits resolution respectively. Data sheet can be found here:
13+
* https://www.ti.com/lit/ds/symlink/dac80501.pdf
14+
*/
15+
16+
#include <zephyr/kernel.h>
17+
#include <zephyr/drivers/i2c.h>
18+
#include <zephyr/drivers/dac.h>
19+
#include <zephyr/logging/log.h>
20+
#include <zephyr/sys/byteorder.h>
21+
22+
LOG_MODULE_REGISTER(dac_dacx0501, CONFIG_DAC_LOG_LEVEL);
23+
24+
#define DACX0501_REG_DEVICE_ID 0x01U
25+
#define DACX0501_REG_SYNC 0x02U
26+
#define DACX0501_REG_CONFIG 0x03U
27+
#define DACX0501_REG_GAIN 0x04U
28+
#define DACX0501_REG_TRIGGER 0x05U
29+
#define DACX0501_REG_STATUS 0x07U
30+
#define DACX0501_REG_DAC 0x08U
31+
32+
#define DACX0501_MASK_DEVICE_ID_RES GENMASK(14, 12)
33+
#define DACX0501_MASK_CONFIG_REF_PWDWN BIT(8)
34+
#define DACX0501_MASK_CONFIG_DAC_PWDWN BIT(0)
35+
#define DACX0501_MASK_GAIN_BUFF_GAIN BIT(0)
36+
#define DACX0501_MASK_GAIN_REFDIV_EN BIT(8)
37+
#define DACX0501_MASK_TRIGGER_SOFT_RESET (BIT(1) | BIT(3))
38+
#define DACX0501_MASK_STATUS_REF_ALM BIT(0)
39+
40+
/* Specifies the source of the reference voltage. */
41+
enum voltage_reference_source {
42+
REF_INTERNAL, /* Internal 2.5V voltage reference. */
43+
REF_EXTERNAL, /* External pin voltage reference. */
44+
};
45+
46+
/* Specifies the reference voltage multiplier. */
47+
enum output_gain {
48+
VM_MUL2, /* Multiplies by 2. */
49+
VM_MUL1, /* Multiplies by 1. */
50+
VM_DIV2, /* Multiplies by 0.5 */
51+
};
52+
53+
struct dacx0501_config {
54+
struct i2c_dt_spec i2c_spec;
55+
enum voltage_reference_source voltage_reference;
56+
enum output_gain output_gain;
57+
};
58+
59+
struct dacx0501_data {
60+
/* Number of bits in the DAC register: Either 12, 14 or 16. */
61+
uint8_t resolution;
62+
};
63+
64+
static int dacx0501_reg_read(const struct device *dev, const uint8_t addr, uint16_t *data)
65+
{
66+
const struct dacx0501_config *config = dev->config;
67+
uint8_t raw_data[2];
68+
int status;
69+
70+
status = i2c_write_read_dt(&config->i2c_spec, &addr, sizeof(addr), raw_data,
71+
sizeof(raw_data));
72+
if (status != 0) {
73+
return status;
74+
}
75+
76+
/* DAC is big endian. */
77+
*data = sys_get_be16(raw_data);
78+
return 0;
79+
}
80+
81+
static int dacx0501_reg_write(const struct device *dev, uint8_t addr, uint16_t data)
82+
{
83+
const struct dacx0501_config *config = dev->config;
84+
uint8_t write_cmd[3] = {addr};
85+
86+
/* DAC is big endian. */
87+
sys_put_be16(data, write_cmd + 1);
88+
89+
return i2c_write_dt(&config->i2c_spec, write_cmd, sizeof(write_cmd));
90+
}
91+
92+
static int dacx0501_channel_setup(const struct device *dev,
93+
const struct dac_channel_cfg *channel_cfg)
94+
{
95+
struct dacx0501_data *data = dev->data;
96+
97+
/* DACx0501 series only has a single output channel. */
98+
if (channel_cfg->channel_id != 0) {
99+
LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
100+
return -ENOTSUP;
101+
}
102+
103+
if (channel_cfg->resolution != data->resolution) {
104+
LOG_ERR("Unsupported resolution %d. Actual: %d", channel_cfg->resolution,
105+
data->resolution);
106+
return -ENOTSUP;
107+
}
108+
109+
return 0;
110+
}
111+
112+
static int dacx0501_write_value(const struct device *dev, uint8_t channel, uint32_t value)
113+
{
114+
struct dacx0501_data *data = dev->data;
115+
116+
if (channel != 0) {
117+
LOG_ERR("dacx0501: Unsupported channel %d", channel);
118+
return -ENOTSUP;
119+
}
120+
121+
if (value >= (1 << data->resolution)) {
122+
LOG_ERR("dacx0501: Value %d out of range", value);
123+
return -EINVAL;
124+
}
125+
126+
value <<= (16 - data->resolution);
127+
128+
return dacx0501_reg_write(dev, DACX0501_REG_DAC, value);
129+
}
130+
131+
static int dacx0501_init(const struct device *dev)
132+
{
133+
const struct dacx0501_config *config = dev->config;
134+
struct dacx0501_data *data = dev->data;
135+
uint16_t device_id;
136+
int status;
137+
138+
if (!i2c_is_ready_dt(&config->i2c_spec)) {
139+
LOG_ERR("I2C bus %s not ready", config->i2c_spec.bus->name);
140+
return -ENODEV;
141+
}
142+
143+
status = dacx0501_reg_read(dev, DACX0501_REG_DEVICE_ID, &device_id);
144+
if (status != 0) {
145+
LOG_ERR("read DEVICE_ID register failed");
146+
return status;
147+
}
148+
149+
/* See DEVICE_ID register RES field in the data sheet. */
150+
data->resolution = 16 - 2 * FIELD_GET(DACX0501_MASK_DEVICE_ID_RES, device_id);
151+
152+
status = dacx0501_reg_write(dev, DACX0501_REG_CONFIG,
153+
FIELD_PREP(DACX0501_MASK_CONFIG_REF_PWDWN,
154+
config->voltage_reference == REF_EXTERNAL));
155+
if (status != 0) {
156+
LOG_ERR("write CONFIG register failed");
157+
return status;
158+
}
159+
160+
status = dacx0501_reg_write(
161+
dev, DACX0501_REG_GAIN,
162+
FIELD_PREP(DACX0501_MASK_GAIN_REFDIV_EN, config->output_gain == VM_DIV2) |
163+
FIELD_PREP(DACX0501_MASK_GAIN_BUFF_GAIN, config->output_gain == VM_MUL2));
164+
if (status != 0) {
165+
LOG_ERR("GAIN Register update failed");
166+
return status;
167+
}
168+
169+
return 0;
170+
}
171+
172+
static const struct dac_driver_api dacx0501_driver_api = {
173+
.channel_setup = dacx0501_channel_setup,
174+
.write_value = dacx0501_write_value,
175+
};
176+
177+
#define DT_DRV_COMPAT ti_dacx0501
178+
179+
#define DACX0501_DEFINE(n) \
180+
static struct dacx0501_data dacx0501_data_##n = {}; \
181+
static const struct dacx0501_config dacx0501_config_##n = { \
182+
.i2c_spec = I2C_DT_SPEC_INST_GET(n), \
183+
.voltage_reference = \
184+
_CONCAT(REF_, DT_STRING_UPPER_TOKEN(DT_DRV_INST(n), voltage_reference)), \
185+
.output_gain = _CONCAT(VM_, DT_STRING_UPPER_TOKEN(DT_DRV_INST(n), output_gain)), \
186+
}; \
187+
DEVICE_DT_INST_DEFINE(n, &dacx0501_init, NULL, &dacx0501_data_##n, &dacx0501_config_##n, \
188+
POST_KERNEL, CONFIG_DAC_DACX0501_INIT_PRIORITY, \
189+
&dacx0501_driver_api);
190+
191+
DT_INST_FOREACH_STATUS_OKAY(DACX0501_DEFINE)

dts/bindings/dac/ti,dacx0501.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (c) 2024 Google, LLC.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
include: base.yaml
5+
6+
description: TI DACx0501 12 to 16 bit DAC series for DAC60501, DAC70501 and DAC80501 devices.
7+
8+
compatible: "ti,dacx0501"
9+
10+
properties:
11+
voltage-reference:
12+
type: string
13+
required: true
14+
enum:
15+
- "internal"
16+
- "external"
17+
description: |
18+
DAC voltage reference select: either internal (2.5 V) or external
19+
20+
output-gain:
21+
type: string
22+
required: true
23+
enum:
24+
- "mul2"
25+
- "mul1"
26+
- "div2"
27+
description: |
28+
This setting can be used to control the output voltage range within the supported bit
29+
resolution. mul2 will double the output range but lower the resolution, while div2 will
30+
lower the range but double the resolution.

tests/drivers/build_all/dac/app.overlay

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@
6868
voltage_reference = <0>;
6969
power_down_mode = <0>;
7070
};
71+
72+
test_i2c_dacx0501:dacx0501@62 {
73+
compatible = "ti,dacx0501";
74+
status = "okay";
75+
reg = <0x62>;
76+
voltage-reference = "internal";
77+
output-gain = "mul2";
78+
};
7179
};
7280

7381
test_spi: spi@33334444 {

0 commit comments

Comments
 (0)