1 /*
2 * Copyright (c) 2024, Vitrolife A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Datasheet:
7 * https://www.festo.com/media/pim/620/D15000100140620.PDF
8 *
9 */
10
11 #define DT_DRV_COMPAT festo_veaa_x_3
12
13 #include <stdio.h>
14
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/adc.h>
17 #include <zephyr/drivers/dac.h>
18 #include <zephyr/drivers/sensor.h>
19 #include <zephyr/drivers/sensor/veaa_x_3.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/sys/math_extras.h>
22 #include <zephyr/sys/util_macro.h>
23
24 LOG_MODULE_REGISTER(veaa_x_3_sensor, CONFIG_SENSOR_LOG_LEVEL);
25
26 struct veaa_x_3_data {
27 uint16_t adc_buf;
28 };
29
30 struct veaa_x_3_cfg {
31 const struct adc_dt_spec adc;
32 const struct device *dac;
33 const uint8_t dac_channel;
34 const uint8_t dac_resolution;
35 const uint16_t kpa_max;
36 const uint8_t kpa_min;
37 };
38
veaa_x_3_kpa_range(const struct veaa_x_3_cfg * cfg)39 static uint16_t veaa_x_3_kpa_range(const struct veaa_x_3_cfg *cfg)
40 {
41 return cfg->kpa_max - cfg->kpa_min;
42 }
43
veaa_x_3_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)44 static int veaa_x_3_attr_set(const struct device *dev, enum sensor_channel chan,
45 enum sensor_attribute attr, const struct sensor_value *val)
46 {
47 const struct veaa_x_3_cfg *cfg = dev->config;
48 uint32_t tmp;
49
50 if (chan != SENSOR_CHAN_PRESS) {
51 return -ENOTSUP;
52 }
53
54 switch ((enum sensor_attribute_veaa_x_3)attr) {
55 case SENSOR_ATTR_VEAA_X_3_SETPOINT:
56 if (val->val1 > cfg->kpa_max || val->val1 < cfg->kpa_min) {
57 LOG_ERR("%d kPa outside range", val->val1);
58 return -EINVAL;
59 }
60
61 /* Convert from kPa to DAC value */
62 tmp = val->val1 - cfg->kpa_min;
63 if (u32_mul_overflow(tmp, BIT(cfg->dac_resolution) - 1, &tmp)) {
64 LOG_ERR("kPa to DAC overflow");
65 return -ERANGE;
66 }
67 tmp /= veaa_x_3_kpa_range(cfg);
68
69 return dac_write_value(cfg->dac, cfg->dac_channel, tmp);
70 default:
71 return -ENOTSUP;
72 }
73 }
74
veaa_x_3_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)75 static int veaa_x_3_attr_get(const struct device *dev, enum sensor_channel chan,
76 enum sensor_attribute attr, struct sensor_value *val)
77 {
78 const struct veaa_x_3_cfg *cfg = dev->config;
79
80 if (chan != SENSOR_CHAN_PRESS) {
81 return -ENOTSUP;
82 }
83
84 switch ((enum sensor_attribute_veaa_x_3)attr) {
85 case SENSOR_ATTR_VEAA_X_3_RANGE:
86 val->val1 = cfg->kpa_min;
87 val->val2 = cfg->kpa_max;
88 return 0;
89 default:
90 return -ENOTSUP;
91 }
92 }
93
veaa_x_3_sample_fetch(const struct device * dev,enum sensor_channel chan)94 static int veaa_x_3_sample_fetch(const struct device *dev, enum sensor_channel chan)
95 {
96 int rc;
97 const struct veaa_x_3_cfg *cfg = dev->config;
98 struct veaa_x_3_data *data = dev->data;
99 struct adc_sequence sequence = {
100 .buffer = &data->adc_buf,
101 .buffer_size = sizeof(data->adc_buf),
102 };
103
104 if (chan != SENSOR_CHAN_PRESS && chan != SENSOR_CHAN_ALL) {
105 return -ENOTSUP;
106 }
107
108 rc = adc_sequence_init_dt(&cfg->adc, &sequence);
109 if (rc != 0) {
110 return rc;
111 }
112 sequence.options = NULL;
113 sequence.buffer = &data->adc_buf;
114 sequence.buffer_size = sizeof(data->adc_buf);
115 sequence.calibrate = false;
116
117 rc = adc_read_dt(&cfg->adc, &sequence);
118 if (rc != 0) {
119 return rc;
120 }
121
122 return 0;
123 }
124
veaa_x_3_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)125 static int veaa_x_3_channel_get(const struct device *dev, enum sensor_channel chan,
126 struct sensor_value *val)
127 {
128 const struct veaa_x_3_cfg *cfg = dev->config;
129 struct veaa_x_3_data *data = dev->data;
130 const uint32_t max_adc_val = BIT(cfg->adc.resolution) - 1;
131
132 if (chan != SENSOR_CHAN_PRESS) {
133 return -ENOTSUP;
134 }
135
136 /* Convert from ADC value to kPa */
137 if (u32_mul_overflow(data->adc_buf, veaa_x_3_kpa_range(cfg), &val->val1)) {
138 LOG_ERR("ADC to kPa overflow");
139 return -ERANGE;
140 }
141 val->val2 = (val->val1 % max_adc_val) * 1000000 / max_adc_val;
142 val->val1 = (val->val1 / max_adc_val) + cfg->kpa_min;
143
144 return 0;
145 }
146
147 static DEVICE_API(sensor, veaa_x_3_api_funcs) = {
148 .attr_set = veaa_x_3_attr_set,
149 .attr_get = veaa_x_3_attr_get,
150 .sample_fetch = veaa_x_3_sample_fetch,
151 .channel_get = veaa_x_3_channel_get,
152 };
153
veaa_x_3_init(const struct device * dev)154 static int veaa_x_3_init(const struct device *dev)
155 {
156 int rc;
157 const struct veaa_x_3_cfg *cfg = dev->config;
158 const struct dac_channel_cfg dac_cfg = {
159 .channel_id = cfg->dac_channel,
160 .resolution = cfg->dac_resolution,
161 .buffered = false,
162 };
163
164 LOG_DBG("Initializing %s with range %u-%u kPa", dev->name, cfg->kpa_min, cfg->kpa_max);
165
166 if (!adc_is_ready_dt(&cfg->adc)) {
167 LOG_ERR("ADC not ready");
168 return -ENODEV;
169 }
170
171 rc = adc_channel_setup_dt(&cfg->adc);
172 if (rc != 0) {
173 LOG_ERR("%s setup failed: %d", cfg->adc.dev->name, rc);
174 return -ENODEV;
175 }
176
177 if (!device_is_ready(cfg->dac)) {
178 LOG_ERR("DAC not ready");
179 return -ENODEV;
180 }
181
182 rc = dac_channel_setup(cfg->dac, &dac_cfg);
183 if (rc != 0) {
184 LOG_ERR("%s setup failed: %d", cfg->dac->name, rc);
185 return -ENODEV;
186 }
187
188 return 0;
189 }
190
191 #define VEAA_X_3_RANGE_KPA_INIT(n) \
192 COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11), ({.max = 1000, min = 5}), \
193 (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9), \
194 ({.max = 600, min = 3}), ({.max = 200, .min = 1}))))
195
196 #define VEAA_X_3_TYPE_INIT(n) \
197 COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11), \
198 (.kpa_max = 1000, .kpa_min = 5), \
199 (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9), \
200 (.kpa_max = 600, kpa_min = 3), (.kpa_max = 200, .kpa_min = 1))))
201
202 #define VEAA_X_3_INIT(n) \
203 \
204 static struct veaa_x_3_data veaa_x_3_data_##n; \
205 \
206 static const struct veaa_x_3_cfg veaa_x_3_cfg_##n = { \
207 .adc = ADC_DT_SPEC_INST_GET(n), \
208 .dac = DEVICE_DT_GET(DT_INST_PHANDLE(n, dac)), \
209 .dac_channel = DT_INST_PROP(n, dac_channel_id), \
210 .dac_resolution = DT_INST_PROP(n, dac_resolution), \
211 VEAA_X_3_TYPE_INIT(n)}; \
212 \
213 SENSOR_DEVICE_DT_INST_DEFINE(n, veaa_x_3_init, NULL, &veaa_x_3_data_##n, \
214 &veaa_x_3_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
215 &veaa_x_3_api_funcs);
216
217 DT_INST_FOREACH_STATUS_OKAY(VEAA_X_3_INIT)
218