drivers: sensor: a01nyub: added driver

Added a driver for the DFRobot A01NYUB distance sensor. This sensor
sends its readings via UART at 9600 baud. This driver uses interrupts
to read the data from the sensor.

Signed-off-by: Oliver King <oliver.king@steadconnect.com>
This commit is contained in:
Oliver King 2023-05-20 18:43:50 -04:00 committed by Carles Cufí
commit fbc6a91a5a
8 changed files with 242 additions and 0 deletions

View file

@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_A01NYUB a01nyub)
add_subdirectory_ifdef(CONFIG_ADC_CMP_NPCX nuvoton_adc_cmp_npcx)
add_subdirectory_ifdef(CONFIG_ADT7310 adt7310)
add_subdirectory_ifdef(CONFIG_ADT7420 adt7420)

View file

@ -57,6 +57,7 @@ config SENSOR_INFO
comment "Device Drivers"
source "drivers/sensor/a01nyub/Kconfig"
source "drivers/sensor/adt7310/Kconfig"
source "drivers/sensor/adt7420/Kconfig"
source "drivers/sensor/adxl345/Kconfig"

View file

@ -0,0 +1,6 @@
# Copyright (c) 2023 SteadConnect
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(a01nyub.c)

View file

@ -0,0 +1,10 @@
# Copyright (c) 2023 SteadConnect
# SPDX-License-Identifier: Apache-2.0
config A01NYUB
bool "DFRobot A01NYUB distance sensor"
default y
depends on DT_HAS_DFROBOT_A01NYUB_ENABLED
depends on UART_INTERRUPT_DRIVEN
help
Enable driver for the DFRobot A01NYUB distance sensor.

View file

@ -0,0 +1,211 @@
/*
* 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)

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Steadconnect
# SPDX-License-Identifier: Apache-2.0
description: DFRobot A01NYUB Distance Sensor
compatible: "dfrobot,a01nyub"
include: [sensor-device.yaml, uart-device.yaml]

View file

@ -150,6 +150,7 @@ delta Delta Electronics, Inc.
denx Denx Software Engineering
devantech Devantech, Ltd.
dfi DFI Inc.
dfrobot DFRobot
dh DH electronics GmbH
difrnce Shenzhen Yagu Electronic Technology Co., Ltd.
digi Digi International Inc.

View file

@ -26,3 +26,7 @@ test_uart_grow_r502a {
int-gpios = <&test_gpio 0 0>;
};
};
test_uart_a01nyub {
compatible = "dfrobot,a01nyub";
};