1 /*
2 * Copyright (c) 2023 SteadConnect
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Datasheet:
7 * https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313
8 *
9 */
10
11 #define DT_DRV_COMPAT dfrobot_a01nyub
12
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/uart.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/drivers/sensor.h>
19
20 LOG_MODULE_REGISTER(a01nyub_sensor, CONFIG_SENSOR_LOG_LEVEL);
21
22 #define A01NYUB_BUF_LEN 4
23 #define A01NYUB_CHECKSUM_IDX 3
24 #define A01NYUB_HEADER 0xff
25
26 const struct uart_config uart_cfg_a01nyub = {
27 .baudrate = 9600,
28 .parity = UART_CFG_PARITY_NONE,
29 .stop_bits = UART_CFG_STOP_BITS_1,
30 .data_bits = UART_CFG_DATA_BITS_8,
31 .flow_ctrl = UART_CFG_FLOW_CTRL_NONE
32 };
33
34 struct a01nyub_data {
35 /* Max data length is 16 bits */
36 uint16_t data;
37 uint8_t xfer_bytes;
38 uint8_t rd_data[A01NYUB_BUF_LEN];
39 };
40
41 struct a01nyub_cfg {
42 const struct device *uart_dev;
43 uart_irq_callback_user_data_t cb;
44 };
45
a01nyub_uart_flush(const struct device * uart_dev)46 static void a01nyub_uart_flush(const struct device *uart_dev)
47 {
48 uint8_t c;
49
50 while (uart_fifo_read(uart_dev, &c, 1) > 0) {
51 continue;
52 }
53 }
54
a01nyub_checksum(const uint8_t * data)55 static uint8_t a01nyub_checksum(const uint8_t *data)
56 {
57 uint16_t cs = 0;
58
59 for (uint8_t i = 0; i < A01NYUB_BUF_LEN - 1; i++) {
60 cs += data[i];
61 }
62
63 return (uint8_t) (cs & 0x00FF);
64 }
65
a01nyub_poll_data(const struct device * dev)66 static inline int a01nyub_poll_data(const struct device *dev)
67 {
68 struct a01nyub_data *data = dev->data;
69 uint8_t checksum;
70
71 checksum = a01nyub_checksum(data->rd_data);
72 if (checksum != data->rd_data[A01NYUB_CHECKSUM_IDX]) {
73 LOG_DBG("Checksum mismatch: calculated 0x%x != data checksum 0x%x",
74 checksum,
75 data->rd_data[A01NYUB_CHECKSUM_IDX]);
76 LOG_DBG("Data bytes: (%x,%x,%x,%x)",
77 data->rd_data[0],
78 data->rd_data[1],
79 data->rd_data[2],
80 data->rd_data[3]);
81
82 return -EBADMSG;
83 }
84
85 data->data = (data->rd_data[1]<<8) + data->rd_data[2];
86
87 return 0;
88 }
89
a01nyub_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)90 static int a01nyub_channel_get(const struct device *dev, enum sensor_channel chan,
91 struct sensor_value *val)
92 {
93 struct a01nyub_data *data = dev->data;
94
95 if (chan != SENSOR_CHAN_DISTANCE) {
96 return -ENOTSUP;
97 }
98 /* val1 is meters, val2 is microns. Both are int32_t
99 * data->data is in mm and units of uint16_t
100 */
101 val->val1 = (uint32_t) (data->data / (uint16_t) 1000);
102 val->val2 = (uint32_t) ((data->data % 1000) * 1000);
103 return 0;
104 }
105
a01nyub_sample_fetch(const struct device * dev,enum sensor_channel chan)106 static int a01nyub_sample_fetch(const struct device *dev, enum sensor_channel chan)
107 {
108 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
109
110 if (chan == SENSOR_CHAN_DISTANCE || chan == SENSOR_CHAN_ALL) {
111 return a01nyub_poll_data(dev);
112 }
113
114 return -ENOTSUP;
115 }
116
117 static DEVICE_API(sensor, a01nyub_api_funcs) = {
118 .sample_fetch = a01nyub_sample_fetch,
119 .channel_get = a01nyub_channel_get,
120 };
121
a01nyub_uart_isr(const struct device * uart_dev,void * user_data)122 static void a01nyub_uart_isr(const struct device *uart_dev, void *user_data)
123 {
124 const struct device *dev = user_data;
125 struct a01nyub_data *data = dev->data;
126
127 if (uart_dev == NULL) {
128 LOG_DBG("UART device is NULL");
129 return;
130 }
131
132 if (!uart_irq_update(uart_dev)) {
133 LOG_DBG("Unable to start processing interrupts");
134 return;
135 }
136
137 if (uart_irq_rx_ready(uart_dev)) {
138 data->xfer_bytes += uart_fifo_read(uart_dev, &data->rd_data[data->xfer_bytes],
139 A01NYUB_BUF_LEN - data->xfer_bytes);
140
141 /* The first byte should be A01NYUB_HEADER for a valid read.
142 * If we do not read A01NYUB_HEADER on what we think is the
143 * first byte, then reset the number of bytes read until we do
144 */
145 if ((data->rd_data[0] != A01NYUB_HEADER) & (data->xfer_bytes == 1)) {
146 LOG_DBG("First byte not header! Resetting # of bytes read.");
147 data->xfer_bytes = 0;
148 }
149
150 if (data->xfer_bytes == A01NYUB_BUF_LEN) {
151 LOG_DBG("Read (0x%x,0x%x,0x%x,0x%x)",
152 data->rd_data[0],
153 data->rd_data[1],
154 data->rd_data[2],
155 data->rd_data[3]);
156 a01nyub_uart_flush(uart_dev);
157 data->xfer_bytes = 0;
158 }
159 }
160 }
161
a01nyub_init(const struct device * dev)162 static int a01nyub_init(const struct device *dev)
163 {
164 const struct a01nyub_cfg *cfg = dev->config;
165 int ret = 0;
166
167 uart_irq_rx_disable(cfg->uart_dev);
168 uart_irq_tx_disable(cfg->uart_dev);
169
170 a01nyub_uart_flush(cfg->uart_dev);
171
172 LOG_DBG("Initializing A01NYUB driver");
173
174 ret = uart_configure(cfg->uart_dev, &uart_cfg_a01nyub);
175 if (ret == -ENOSYS) {
176 LOG_ERR("Unable to configure UART port");
177 return -ENOSYS;
178 }
179
180 ret = uart_irq_callback_user_data_set(cfg->uart_dev, cfg->cb, (void *)dev);
181
182 if (ret < 0) {
183 if (ret == -ENOTSUP) {
184 LOG_ERR("Interrupt-driven UART API support not enabled");
185 } else if (ret == -ENOSYS) {
186 LOG_ERR("UART device does not support interrupt-driven API");
187 } else {
188 LOG_ERR("Error setting UART callback: %d", ret);
189 }
190 return ret;
191 }
192
193 uart_irq_rx_enable(cfg->uart_dev);
194
195 return ret;
196 }
197
198 #define A01NYUB_INIT(inst) \
199 \
200 static struct a01nyub_data a01nyub_data_##inst; \
201 \
202 static const struct a01nyub_cfg a01nyub_cfg_##inst = { \
203 .uart_dev = DEVICE_DT_GET(DT_INST_BUS(inst)), \
204 .cb = a01nyub_uart_isr, \
205 }; \
206 \
207 SENSOR_DEVICE_DT_INST_DEFINE(inst, a01nyub_init, NULL, \
208 &a01nyub_data_##inst, &a01nyub_cfg_##inst, \
209 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &a01nyub_api_funcs);
210
211 DT_INST_FOREACH_STATUS_OKAY(A01NYUB_INIT)
212