blob: f1d57becd04d057d5da46daf65ae0ca84b2fdae2 [file] [log] [blame]
/*
* Copyright (c) 2023 SteadConnect
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313
*
*/
#define DT_DRV_COMPAT dfrobot_a01nyub
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/drivers/sensor.h>
LOG_MODULE_REGISTER(a01nyub_sensor, CONFIG_SENSOR_LOG_LEVEL);
#define A01NYUB_BUF_LEN 4
#define A01NYUB_CHECKSUM_IDX 3
#define A01NYUB_HEADER 0xff
const struct uart_config uart_cfg_a01nyub = {
.baudrate = 9600,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE
};
struct a01nyub_data {
/* Max data length is 16 bits */
uint16_t data;
uint8_t xfer_bytes;
uint8_t rd_data[A01NYUB_BUF_LEN];
};
struct a01nyub_cfg {
const struct device *uart_dev;
uart_irq_callback_user_data_t cb;
};
static void a01nyub_uart_flush(const struct device *uart_dev)
{
uint8_t c;
while (uart_fifo_read(uart_dev, &c, 1) > 0) {
continue;
}
}
static uint8_t a01nyub_checksum(const uint8_t *data)
{
uint16_t cs = 0;
for (uint8_t i = 0; i < A01NYUB_BUF_LEN - 1; i++) {
cs += data[i];
}
return (uint8_t) (cs & 0x00FF);
}
static inline int a01nyub_poll_data(const struct device *dev)
{
struct a01nyub_data *data = dev->data;
uint8_t checksum;
checksum = a01nyub_checksum(data->rd_data);
if (checksum != data->rd_data[A01NYUB_CHECKSUM_IDX]) {
LOG_DBG("Checksum mismatch: calculated 0x%x != data checksum 0x%x",
checksum,
data->rd_data[A01NYUB_CHECKSUM_IDX]);
LOG_DBG("Data bytes: (%x,%x,%x,%x)",
data->rd_data[0],
data->rd_data[1],
data->rd_data[2],
data->rd_data[3]);
return -EBADMSG;
}
data->data = (data->rd_data[1]<<8) + data->rd_data[2];
return 0;
}
static int a01nyub_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct a01nyub_data *data = dev->data;
if (chan != SENSOR_CHAN_DISTANCE) {
return -ENOTSUP;
}
/* val1 is meters, val2 is microns. Both are int32_t
* data->data is in mm and units of uint16_t
*/
val->val1 = (uint32_t) (data->data / (uint16_t) 1000);
val->val2 = (uint32_t) ((data->data % 1000) * 1000);
return 0;
}
static int a01nyub_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
if (chan == SENSOR_CHAN_DISTANCE || chan == SENSOR_CHAN_ALL) {
return a01nyub_poll_data(dev);
}
return -ENOTSUP;
}
static const struct sensor_driver_api a01nyub_api_funcs = {
.sample_fetch = a01nyub_sample_fetch,
.channel_get = a01nyub_channel_get,
};
static void a01nyub_uart_isr(const struct device *uart_dev, void *user_data)
{
const struct device *dev = user_data;
struct a01nyub_data *data = dev->data;
if (uart_dev == NULL) {
LOG_DBG("UART device is NULL");
return;
}
if (!uart_irq_update(uart_dev)) {
LOG_DBG("Unable to start processing interrupts");
return;
}
if (uart_irq_rx_ready(uart_dev)) {
data->xfer_bytes += uart_fifo_read(uart_dev, &data->rd_data[data->xfer_bytes],
A01NYUB_BUF_LEN - data->xfer_bytes);
/* The first byte should be A01NYUB_HEADER for a valid read.
* If we do not read A01NYUB_HEADER on what we think is the
* first byte, then reset the number of bytes read until we do
*/
if ((data->rd_data[0] != A01NYUB_HEADER) & (data->xfer_bytes == 1)) {
LOG_DBG("First byte not header! Resetting # of bytes read.");
data->xfer_bytes = 0;
}
if (data->xfer_bytes == A01NYUB_BUF_LEN) {
LOG_DBG("Read (0x%x,0x%x,0x%x,0x%x)",
data->rd_data[0],
data->rd_data[1],
data->rd_data[2],
data->rd_data[3]);
a01nyub_uart_flush(uart_dev);
data->xfer_bytes = 0;
}
}
}
static int a01nyub_init(const struct device *dev)
{
const struct a01nyub_cfg *cfg = dev->config;
int ret = 0;
uart_irq_rx_disable(cfg->uart_dev);
uart_irq_tx_disable(cfg->uart_dev);
a01nyub_uart_flush(cfg->uart_dev);
LOG_DBG("Initializing A01NYUB driver");
ret = uart_configure(cfg->uart_dev, &uart_cfg_a01nyub);
if (ret == -ENOSYS) {
LOG_ERR("Unable to configure UART port");
return -ENOSYS;
}
ret = uart_irq_callback_user_data_set(cfg->uart_dev, cfg->cb, (void *)dev);
if (ret < 0) {
if (ret == -ENOTSUP) {
LOG_ERR("Interrupt-driven UART API support not enabled");
} else if (ret == -ENOSYS) {
LOG_ERR("UART device does not support interrupt-driven API");
} else {
LOG_ERR("Error setting UART callback: %d", ret);
}
return ret;
}
uart_irq_rx_enable(cfg->uart_dev);
return ret;
}
#define A01NYUB_INIT(inst) \
\
static struct a01nyub_data a01nyub_data_##inst; \
\
static const struct a01nyub_cfg a01nyub_cfg_##inst = { \
.uart_dev = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.cb = a01nyub_uart_isr, \
}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, a01nyub_init, NULL, \
&a01nyub_data_##inst, &a01nyub_cfg_##inst, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &a01nyub_api_funcs);
DT_INST_FOREACH_STATUS_OKAY(A01NYUB_INIT)