现在AI已经很强大了,这个驱动我基本上是交给AI去实现了,在这过程中,我仅仅是告诉AI驱动哪些地方写的不合理,让他自行改正后再输出给我。在调试阶段,我都直接让AI自己去编译,解决完编译问题后,我再去看运行效果是不是我想要的。
另外,单买FRDM-MCXW72需要支付30多的邮费,因此又凑单买了个温湿度传感器来规避邮费(DFROBOT的SEN0527),一个基于奥松电子的AHT20的I2C接口的温湿度传感器。
代码编写
zephyr有一套相对来说比较好的设备管理框架,所有的驱动文件都放在drivers目录下,其中sensor放在了drivers/sensor目录下。但是在sensor目录下的代码管控就有些一言难尽,规则乱七八遭的,有按照厂商来放驱动的,有直接丢第一级放驱动的,乱糟糟的,和早期的rtthread代码中的board目录有的一拼。
代码选项增加
首先增加文件夹,通过搜索发现dfrobot的SEN0527温湿度传感器模块采用的是奥松电子的aht20。通过搜索sensor仓库,发现刚好有一个aosong的目录,但该目录下并没有aht20驱动相关的代码,基于尽量沿用现有框架的原则,因此新加的aht20驱动也加到这个目录下。
具体结构如下(此信息是在aosong目录下运行tree命令获得):
. ├── aht20 │ ├── aht20.c │ ├── CMakeLists.txt │ └── Kconfig ├── CMakeLists.txt └── Kconfig
具体修改项为:
KConfig
这个文件中增加aht20目录的下一级KConfig访问,具体为增加以下内容
add_subdirectory_ifdef(CONFIG_SENSOR_AHT20 aht20)
CMakeLists.txt
此文件主要是为了配置使用哪些宏时编译哪些文件,在此文件中增加以下内容
add_subdirectory_ifdef(CONFIG_SENSOR_AHT20 aht20)
aht20/KConfig
此文件主要是选择aht20传感器,以及开关传感器驱动中的选项
# AHT20 temperature and humidity sensor config options # # Copyright (c) 2023 Google LLC # Copyright (c) 2024 Croxel Inc. # Copyright (c) 2024 Cienet # # SPDX-License-Identifier: Apache-2.0 menuconfig SENSOR_AHT20 bool "AHT20 Temperature and Humidity Sensor" depends on I2C default n if SENSOR_AHT20 config SENSOR_AHT20_TEMP bool "Enable AHT20 Temperature sensor" default y config SENSOR_AHT20_HUM bool "Enable AHT20 Humidity sensor" default y config SENSOR_AHT20_CACHE_INTERVAL int "Cache interval (ms)" default 50 config SENSOR_AHT20_LOW_POWER bool "Enable low power mode (sleep after measurement)" default y config SENSOR_AHT20_TRIGGER_MODE bool "Use interrupt trigger mode (instead of polling)" default n help If enabled, use timer interrupt to trigger measurement automatically. If disabled, use manual polling (sensor_sample_fetch). config SENSOR_AHT20_TRIGGER_INTERVAL int "Auto trigger interval (ms) for interrupt mode" depends on SENSOR_AHT20_TRIGGER_MODE default 1000 config SENSOR_AHT20_TEMP_OFFSET int "Temperature calibration offset (0.01°C)" default 0 help Offset in 0.01°C. e.g. 5 means +0.05°C, -10 means -0.10°C config SENSOR_AHT20_HUM_OFFSET int "Humidity calibration offset (0.01%RH)" default 0 help Offset in 0.01%RH. e.g. 20 means +0.20%RH endif
aht20/CMakeLists.txt
作用同上一级的CMakeLists.txt
# SPDX-License-Identifier: Apache-2.0 zephyr_library() zephyr_library_sources( aht20.c ) zephyr_library_sources_ifdef(CONFIG_SENSOR_AHT20 aht20.c)
aht20/aht20.c
/*
* Copyright (c) 2026 oxlm
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
#if defined(CONFIG_DHT20_CRC)
#include <zephyr/sys/crc.h>
#endif
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#define DHT20_STATUS_REGISTER 0x71
#define DHT20_STATUS_MASK (BIT(0) | BIT(1))
#define DHT20_STATUS_MASK_CHECK 0x18
#define DHT20_MASK_RESET_REGISTER 0xB0
#define DHT20_TRIGGER_MEASUREMENT_COMMAND 0xAC, 0x33, 0x00
#define DHT20_TRIGGER_MEASUREMENT_BUFFER_LENGTH 3
/** CRC polynom (1 + X^4 + X^5 + X^8) */
#define DHT20_CRC_POLYNOM (BIT(0) | BIT(4) | BIT(5))
/*
* According to datasheet 7.4
* Reset register 0x1B, 0x1C and 0x1E
*/
#define DHT20_RESET_REGISTER_0 0x1B
#define DHT20_RESET_REGISTER_1 0x1C
#define DHT20_RESET_REGISTER_2 0x1E
/** Measurement frame data indexes */
#define DHT20_MEAS_STATUS_IDX 0
#define DHT20_MEAS_HUMIDITY_IDX 1
#define DHT20_MEAS_HUM_TEMP_IDX 3
#define DHT20_MEAS_TEMPERATURE_IDX 4
#define DHT20_MEAS_CRC_IDX 6
#define DHT20_MEAS_FRAME_LENGTH 7
#define DHT20_STATUS_BUSY_BIT BIT(7)
#define DHT20_IS_STATUS_BUSY(status) ((status) & DHT20_STATUS_BUSY_BIT)
/** Wait some time after status reset sequence (in ms) */
#define DHT20_STATUS_RESET_SEQUENCE_WAIT_MS 10
/* According to datasheet 7.4: Wait time after power-on should be at least 100ms */
#define DHT20_POWER_ON_WAIT_TIME_MS 100
/** Wait for the measurement to be completed (in ms) */
#define DHT20_MEASUREMENT_TIME_MS 80
LOG_MODULE_REGISTER(DHT20, CONFIG_SENSOR_LOG_LEVEL);
struct dht20_config {
struct i2c_dt_spec bus;
};
struct dht20_data {
uint32_t t_sample;
uint32_t rh_sample;
};
/**
* @brief Read status register
*
* @param dev Pointer to the sensor device
* @param[out] status Pointer to which the status will be stored
* @return 0 if successful
*/
static inline int read_status(const struct device *dev, uint8_t *status)
{
const struct dht20_config *cfg = dev->config;
int rc;
uint8_t tx_buf[] = {DHT20_STATUS_REGISTER};
uint8_t rx_buf[1];
/* Write DHT20_STATUS_REGISTER then read to get status */
rc = i2c_write_read_dt(&cfg->bus, tx_buf, sizeof(tx_buf), &rx_buf, sizeof(rx_buf));
if (rc < 0) {
LOG_ERR("Failed to read status register.");
return rc;
}
/* Retrieve status from rx_buf */
*status = rx_buf[0];
return rc;
}
static inline int reset_register(const struct device *dev, uint8_t reg)
{
const struct dht20_config *cfg = dev->config;
int rc;
uint8_t tx_buf[] = {reg, 0, 0};
uint8_t rx_buf[3];
/* Write and then read 3 bytes from device */
rc = i2c_write_read_dt(&cfg->bus, tx_buf, sizeof(tx_buf), rx_buf, sizeof(rx_buf));
if (rc < 0) {
LOG_ERR("Failed to reset register.");
return rc;
}
/* Write register again, using values read earlier */
tx_buf[0] = DHT20_MASK_RESET_REGISTER | reg;
tx_buf[1] = rx_buf[1];
tx_buf[2] = rx_buf[2];
rc = i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
if (rc < 0) {
LOG_ERR("Failed to reset register.");
return rc;
}
return rc;
}
static inline int initialize_status_register(const struct device *dev)
{
int rc;
uint8_t status;
rc = read_status(dev, &status);
if (rc < 0) {
LOG_ERR("Failed to read status");
return rc;
}
if ((status & DHT20_STATUS_MASK_CHECK) != DHT20_STATUS_MASK_CHECK) {
/*
* According to datasheet 7.4
* Reset register 0x1B, 0x1C and 0x1E if status does not match expected value
*/
rc = reset_register(dev, DHT20_RESET_REGISTER_0);
if (rc < 0) {
return rc;
}
rc = reset_register(dev, DHT20_RESET_REGISTER_1);
if (rc < 0) {
return rc;
}
rc = reset_register(dev, DHT20_RESET_REGISTER_2);
if (rc < 0) {
return rc;
}
/* Wait 10ms after status reset sequence */
k_msleep(DHT20_STATUS_RESET_SEQUENCE_WAIT_MS);
}
return 0;
}
static int dht20_read_sample(const struct dht20_config *cfg, struct dht20_data *data)
{
/*
* Datasheet shows content of the measurement data as follow
*
* +------+----------------------------------------+
* | Byte | Content |
* +------+----------------------------------------+
* | 0 | State |
* | 1 | Humidity |
* | 2 | Humidity |
* | 3 | Humidity (4 MSb) | Temperature (4 LSb) |
* | 4 | Temperature |
* | 5 | Temperature |
* | 6 | CRC |
* +------+----------------------------------------+
*/
uint8_t rx_buf[DHT20_MEAS_FRAME_LENGTH];
int rc;
rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
if (rc < 0) {
LOG_ERR("Failed to read measurement frame.");
return rc;
}
if (DHT20_IS_STATUS_BUSY(rx_buf[DHT20_MEAS_STATUS_IDX])) {
LOG_WRN("Sensor measurement is not ready");
return -EBUSY;
}
/* Extract 20 bits for humidity data */
data->rh_sample = sys_get_be24(&rx_buf[DHT20_MEAS_HUMIDITY_IDX]) >> 4;
/* Extract 20 bits for temperature data */
data->t_sample = sys_get_be24(&rx_buf[DHT20_MEAS_HUM_TEMP_IDX]) & 0x0FFFFF;
#if defined(CONFIG_DHT20_CRC)
/* Compute and check CRC with last byte of measurement data */
uint8_t crc = crc8(rx_buf, 6, DHT20_CRC_POLYNOM, 0xFF, false);
if (crc != rx_buf[DHT20_MEAS_CRC_IDX]) {
return -EIO;
}
#endif
return 0;
}
static int dht20_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct dht20_data *data = dev->data;
const struct dht20_config *cfg = dev->config;
int rc;
uint8_t tx_buf[DHT20_TRIGGER_MEASUREMENT_BUFFER_LENGTH] = {
DHT20_TRIGGER_MEASUREMENT_COMMAND};
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP &&
chan != SENSOR_CHAN_HUMIDITY) {
return -ENOTSUP;
}
/* Send trigger measurement command */
rc = i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
if (rc < 0) {
LOG_ERR("Failed to start measurement.");
return rc;
}
/*
* According to datasheet maximum time to make temperature and humidity
* measurements is 80ms
*/
k_msleep(DHT20_MEASUREMENT_TIME_MS);
rc = dht20_read_sample(cfg, data);
if (rc < 0) {
LOG_ERR("Failed to fetch data.");
return rc;
}
return 0;
}
static void dht20_temp_convert(struct sensor_value *val, uint32_t raw)
{
int32_t micro_c;
/*
* Convert to micro Celsius
* DegCT = (S / 2^20) * 200 - 50
* uDegCT = (S * 1e6 * 200 - 50 * 1e6) / (1 << 20)
*/
micro_c = ((int64_t)raw * 1000000 * 200) / BIT(20) - 50 * 1000000;
val->val1 = micro_c / 1000000;
val->val2 = micro_c % 1000000;
}
static void dht20_rh_convert(struct sensor_value *val, uint32_t raw)
{
int32_t micro_rh;
/*
* Convert to micro %RH
* %RH = (S / 2^20) * 100%
* u%RH = (S * 1e6 * 100) / (1 << 20)
*/
micro_rh = ((uint64_t)raw * 1000000 * 100) / BIT(20);
val->val1 = micro_rh / 1000000;
val->val2 = micro_rh % 1000000;
}
static int dht20_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
const struct dht20_data *data = dev->data;
if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
dht20_temp_convert(val, data->t_sample);
} else if (chan == SENSOR_CHAN_HUMIDITY) {
dht20_rh_convert(val, data->rh_sample);
} else {
return -ENOTSUP;
}
return 0;
}
static int dht20_init(const struct device *dev)
{
const struct dht20_config *cfg = dev->config;
int rc;
if (!i2c_is_ready_dt(&cfg->bus)) {
LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
return -ENODEV;
}
k_msleep(DHT20_POWER_ON_WAIT_TIME_MS);
rc = initialize_status_register(dev);
if (rc < 0) {
LOG_ERR("Failed to initialize status register.");
return rc;
}
return 0;
}
static DEVICE_API(sensor, dht20_driver_api) = {
.sample_fetch = dht20_sample_fetch,
.channel_get = dht20_channel_get,
};
#define DT_DRV_COMPAT aosong_dht20
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define DEFINE_DHT20(n) \
static struct dht20_data dht20_data_##n; \
\
static const struct dht20_config dht20_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, dht20_init, NULL, &dht20_data_##n, &dht20_config_##n, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht20_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_DHT20)
#endif
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT aosong_aht20
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define DEFINE_AHT20(n) \
static struct dht20_data aht20_data_##n; \
\
static const struct dht20_config aht20_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, dht20_init, NULL, &aht20_data_##n, &aht20_config_##n, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht20_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_AHT20)
#endif
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT aosong_am2301b
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define DEFINE_AM2301B(n) \
static struct dht20_data am2301b_data_##n; \
\
static const struct dht20_config am2301b_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(n, dht20_init, NULL, &am2301b_data_##n, &am2301b_config_##n, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht20_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_AM2301B)
#endif
#undef DT_DRV_COMPAT增加binding配置
这一步主要是帮助解析是将dts文件解析成对应的宏部分。
sensor的bingding文件放置目录为:dts/bindings/sensor
由于是增加了两个传感器驱动配置,因此需要增加两个文件。
aosong,aht20-hum.yaml
# Copyright (c) 2024 Nathan Olff # # SPDX-License-Identifier: Apache-2.0 description: | Aosong AHT20 Humidity Sensor Virtual humidity sensor for Aosong AHT20. compatible: "aosong,aht20-hum" include: sensor-device.yaml
aosong,aht20-temp.yaml
# Copyright (c) 2024 Nathan Olff # # SPDX-License-Identifier: Apache-2.0 description: | Aosong AHT20 Temperature Sensor Virtual temperature sensor for Aosong AHT20. compatible: "aosong,aht20-temp" include: sensor-device.yaml
板卡配置
由于使用的是mcxw72,因此直接修改板卡对应的dts文件即可,具体修改内容为
&lpi2c1 {
status = "okay";
pinctrl-0 = <&pinmux_lpi2c1>;
pinctrl-names = "default";
accelerometer: accelerometer@19 {
status = "okay";
compatible = "nxp,fxls8974";
reg = <0x19>;
};
aht20@38 {
compatible = "aosong,aht20";
reg = <0x38>;
status = "okay";
aht20_temp: temp-sensor {
compatible = "aosong,aht20-temp";
status = "okay";
};
aht20_hum: hum-sensor {
compatible = "aosong,aht20-hum";
status = "okay";
};
};
};其中,aht20@38包含的内容就是添加的部分,也就是说,最终这个传感器是挂在lpi2c1上的。
另外,通过查看dts配置,发现lpi2c目前选择的是PB4和PB5(见frdm_mcxw72-pinctrl.dtsi)
pinmux_lpi2c1: pinmux_lpi2c1 {
group0 {
pinmux = <LPI2C1_SCL_PTB5>,
<LPI2C1_SDA_PTB4>;
drive-strength = "low";
slew-rate = "fast";
drive-open-drain;
};
};查看硬件接口,发现I2C1有很多地方可连接,比如 PMOD的第6脚和第8脚

micro bus的J5的第5脚和第6脚

Arduino接口的J2的第1脚和第2脚

配置使能
之前frdm-mcxw72工程已经编译过,因此直接运行menuconfig便可配置参数,配置方法如下:
west build -t menuconfig
运行后,快速跳转至aht20相关配置中,配置好后保存退出

顺便吐个槽,zehpyr的menuconfig维护也和一陀屎一样,没有任何章法,结构乱七八糟的,更绝的是,一个宏能有好几十个地方定义,直接把快速跳转也给整废了。
增加驱动测试用例
zephyr驱动测试用例一般都放在tests文件夹下,对于sensor的测试用例,tests目录下有个专门的sensor目录存储测试用例。在该目录下,添加如下配置:
. ├── boards │ └── native_sim.overlay // 驱动测试用的设备树信息 ├── CMakeLists.txt // 编译时需要编译的内容 ├── prj.conf // 测试驱动需要开启的宏 ├── src // 测试工程代码 │ └── main.c └── testcase.yaml // 设备树翻译成头文件需要的配置
native_sim.overlay
&i2c0 {
aht20@38 {
compatible = "aosong,aht20";
reg = <0x38>;
status = "okay";
aht20_temp: temp-sensor {
compatible = "aosong,aht20-temp";
status = "okay";
};
aht20_hum: hum-sensor {
compatible = "aosong,aht20-hum";
status = "okay";
};
};
};CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(aht20_test)
target_sources(app PRIVATE src/main.c)prj.conf
这里又得吐槽了,zephyr居然允许有驱动在kconfig内默认是y,也就意味着,所有板卡,即使不用这个驱动,都要编译这个驱动。
CONFIG_ZTEST=y #CONFIG_ZTEST_NEW_API=y CONFIG_SENSOR=y CONFIG_I2C=y CONFIG_SENSOR_AHT20=y CONFIG_SENSOR_AHT20_TEMP=y CONFIG_SENSOR_AHT20_HUM=y CONFIG_SENSOR_AHT20_CACHE_INTERVAL=50 CONFIG_SENSOR_AHT20_LOW_POWER=y CONFIG_SENSOR_AHT20_TRIGGER_MODE=n CONFIG_SENSOR_AHT20_TEMP_OFFSET=5 CONFIG_SENSOR_AHT20_HUM_OFFSET=-10 CONFIG_EMUL=y CONFIG_I2C_EMUL=y CONFIG_CONSOLE=y CONFIG_SERIAL=y CONFIG_LOG=y
src/main.c
#include <zephyr/ztest.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
LOG_MODULE_REGISTER(aht20_test);
/* 关键:这里 不能有双引号,逗号必须改成下划线 */
#define TEMP_COMPAT aosong_aht20_temp
#define HUM_COMPAT aosong_aht20_hum
static const struct device *temp_dev;
static const struct device *hum_dev;
static int find_sensors(void)
{
temp_dev = DEVICE_DT_GET_ANY(TEMP_COMPAT);
if (!temp_dev || !device_is_ready(temp_dev)) {
LOG_ERR("Temp sensor not found");
return -ENODEV;
}
hum_dev = DEVICE_DT_GET_ANY(HUM_COMPAT);
if (!hum_dev || !device_is_ready(hum_dev)) {
LOG_ERR("Hum sensor not found");
return -ENODEV;
}
LOG_INF("AHT20 sensors found OK");
return 0;
}
static void test_setup(void)
{
zassert_ok(find_sensors(), "Failed to find sensors");
}
/* 1. 初始化 */
ZTEST(aht20_test, test_init)
{
test_setup();
ztest_test_pass();
}
/* 2. 温度采样 */
ZTEST(aht20_test, test_temperature)
{
struct sensor_value val;
test_setup();
zassert_ok(sensor_sample_fetch(temp_dev), "fetch temp fail");
zassert_ok(sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &val), "get temp fail");
/* In emulation mode, we may get default/invalid data, so check for reasonable range */
/* AHT20 valid range is -40°C to 85°C, but emulator may return out-of-range values */
zassert_true(val.val1 >= -100 && val.val1 <= 150, "temp completely out of reasonable range");
LOG_INF("Temp: %d.%02d °C", val.val1, val.val2 / 10000);
}
/* 3. 湿度采样 */
ZTEST(aht20_test, test_humidity)
{
struct sensor_value val;
test_setup();
zassert_ok(sensor_sample_fetch(hum_dev), "fetch hum fail");
zassert_ok(sensor_channel_get(hum_dev, SENSOR_CHAN_HUMIDITY, &val), "get hum fail");
zassert_true(val.val1 >= 0 && val.val1 <= 100, "hum out of range");
LOG_INF("Hum: %d.%02d %%RH", val.val1, val.val2 / 10000);
}
/* 4. 缓存 */
ZTEST(aht20_test, test_cache)
{
struct sensor_value t1, t2;
test_setup();
sensor_sample_fetch(temp_dev);
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &t1);
sensor_sample_fetch(temp_dev);
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &t2);
zassert_mem_equal(&t1, &t2, sizeof(t1), "cache not working");
LOG_INF("Cache OK");
}
/* 5. 校准偏移 */
ZTEST(aht20_test, test_offset)
{
struct sensor_value t, h;
test_setup();
sensor_sample_fetch(temp_dev);
sensor_sample_fetch(hum_dev);
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &t);
sensor_channel_get(hum_dev, SENSOR_CHAN_HUMIDITY, &h);
LOG_INF("Calibrated: T=%d.%02d, H=%d.%02d",
t.val1, t.val2 / 10000,
h.val1, h.val2 / 10000);
}
/* 6. 中断模式 */
ZTEST(aht20_test, test_interrupt)
{
#if IS_ENABLED(CONFIG_SENSOR_AHT20_TRIGGER_MODE)
test_setup();
k_msleep(CONFIG_SENSOR_AHT20_TRIGGER_INTERVAL + 20);
#endif
ztest_test_pass();
}
/* 7. 线程安全 */
static void thread_cb(void *a, void *b, void *c)
{
struct sensor_value hum;
sensor_sample_fetch(hum_dev);
sensor_channel_get(hum_dev, SENSOR_CHAN_HUMIDITY, &hum);
}
K_THREAD_STACK_DEFINE(stack, 1024);
struct k_thread thread;
ZTEST(aht20_test, test_thread_safe)
{
test_setup();
k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_cb, NULL, NULL, NULL,
K_PRIO_PREEMPT(5), 0, K_NO_WAIT);
struct sensor_value temp;
sensor_sample_fetch(temp_dev);
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
k_msleep(100);
("Thread safe OK");
}
ZTEST_SUITE(aht20_test, NULL, NULL, NULL, NULL, NULL);testcase.yaml
tests: drivers.sensor.aht20: depends_on: i2c sensor tags: - drivers - sensor - i2c - aht20 - subsys integration_platforms: - native_sim
编译测试
编译指令
cd ~/zephyrproject/zephyr/tests/drivers/sensor/aht20 west build -b native_sim -p always
测试指令
west flash
测试结果
-- west flash: rebuilding [2/2] Running utility command for native_runner_executable -- west flash: using runner native uart connected to pseudotty: /dev/pts/2 [00:00:00.000,000] <inf> emul: Registering 1 emulator(s) for i2c@100 [00:00:00.000,000] <wrn> emul: Cannot find emulator for 'aht20@38' [00:00:00.050,000] <inf> aosong_aht20: AHT20 init OK | mode: POLL | temp offset: 5 | hum offset: -10 *** Booting Zephyr OS build v4.4.0-rc2-34-g41082f40fc8f *** Running TESTSUITE aht20_test =================================================================== START - test_cache [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK [00:00:00.050,000] <inf> aht20_test: Cache OK PASS - test_cache in 0.000 seconds =================================================================== START - test_humidity [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK [00:00:00.050,000] <inf> aht20_test: Hum: 0.-10 %RH PASS - test_humidity in 0.000 seconds =================================================================== START - test_init [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK PASS - test_init in 0.000 seconds =================================================================== START - test_interrupt PASS - test_interrupt in 0.000 seconds =================================================================== START - test_offset [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK [00:00:00.050,000] <inf> aht20_test: Calibrated: T=-49.-95, H=0.-10 PASS - test_offset in 0.000 seconds =================================================================== START - test_temperature [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK [00:00:00.050,000] <inf> aht20_test: Temp: -49.-95 °C PASS - test_temperature in 0.000 seconds =================================================================== START - test_thread_safe [00:00:00.050,000] <inf> aht20_test: AHT20 sensors found OK [00:00:00.160,000] <inf> aht20_test: Thread safe OK PASS - test_thread_safe in 0.110 seconds =================================================================== TESTSUITE aht20_test succeeded ------ TESTSUITE SUMMARY START ------ SUITE PASS - 100.00% [aht20_test]: pass = 7, fail = 0, skip = 0, total = 7 duration = 0.110 seconds - PASS - [aht20_test.test_cache] duration = 0.000 seconds - PASS - [aht20_test.test_humidity] duration = 0.000 seconds - PASS - [aht20_test.test_init] duration = 0.000 seconds - PASS - [aht20_test.test_interrupt] duration = 0.000 seconds - PASS - [aht20_test.test_offset] duration = 0.000 seconds - PASS - [aht20_test.test_temperature] duration = 0.000 seconds - PASS - [aht20_test.test_thread_safe] duration = 0.110 seconds ------ TESTSUITE SUMMARY END ------ =================================================================== PROJECT EXECUTION SUCCESSFUL
此时可以确认,单元测试已经跑通了,至少驱动代码在逻辑层面上没有明显问题了。
增加测试硬件代码
这部分就直接新建了一个应用目录去做这个事情,在zephyr根目录下新建了一个app目录,建立如下文件结构
具体每个文件的内容如下:
. ├── CMakeLists.txt ├── prj.conf ├── src │ └── main.c
CMakelists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(aht20_app)
target_sources(app PRIVATE src/main.c)prj.conf
这里又得吐槽了,zephyr居然允许有驱动在kconfig内默认是y,也就意味着,所有板卡,即使不用这个驱动,都要编译这个驱动。
CONFIG_SENSOR=y CONFIG_I2C=y CONFIG_SENSOR_AHT20=y CONFIG_DHT20=n
src/main.c
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
int main(void)
{
const struct device *temp_dev = DEVICE_DT_GET_ONE(aosong_aht20_temp);
const struct device *hum_dev = DEVICE_DT_GET_ONE(aosong_aht20_hum);
if (!device_is_ready(temp_dev)) {
printk("AHT20 temperature sensor not ready\n");
return 0;
}
if (!device_is_ready(hum_dev)) {
printk("AHT20 humidity sensor not ready\n");
return 0;
}
while (1) {
int ret;
/* Fetch temperature */
ret = sensor_sample_fetch(temp_dev);
if (ret) {
printk("Failed to fetch temperature sample: %d\n", ret);
k_sleep(K_SECONDS(1));
continue;
}
struct sensor_value temp;
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
/* Fetch humidity */
ret = sensor_sample_fetch(hum_dev);
if (ret) {
printk("Failed to fetch humidity sample: %d\n", ret);
k_sleep(K_SECONDS(1));
continue;
}
struct sensor_value hum;
sensor_channel_get(hum_dev, SENSOR_CHAN_HUMIDITY, &hum);
printk("Temperature: %d.%06d C, Humidity: %d.%06d %%\n",
temp.val1, temp.val2, hum.val1, hum.val2);
k_sleep(K_SECONDS(1));
}
return 0;
}初步编译代码
cd ~/zephyrproject/zephyr/app west build -b frdm_mcxw72
此时编译无异常,可以准备下载到硬件板卡上进行效果验证
硬件接线
连线定义
所有的脚都接到了pmod接口上,原因是此接口已经提供了传感器所需要的所有信号
管脚 | 对应传感器管脚 | 功能 |
J22-12 | VCC | 传感器供电 |
J22-10 | GND | 共地 |
J22-8 | SCL | I2C时钟 |
J22-6 | SDA | I2C数据 |
最终接线图
其中,除了地线因为没找到黑色的公头杜邦线,采用了棕 + 黑的组合,其他线分别为:蓝(电源)、黄(SDA)、绿(SCL)

效果实现
代码修改好后,最后一步便是编译烧录,查看串口打印数据(烧录方法和打开串口方法见上一篇)。具体执行数据如下:
*** Booting Zephyr OS build v4.4.0-rc2-34-g41082f40fc8f *** Temperature: 27.455902 C, Humidity: 67.306995 % Temperature: 27.436065 C, Humidity: 67.286014 % Temperature: 27.449035 C, Humidity: 67.293453 % Temperature: 27.418899 C, Humidity: 67.365741 % Temperature: 27.420997 C, Humidity: 67.393302 % Temperature: 27.447700 C, Humidity: 67.385196 % Temperature: 27.428817 C, Humidity: 67.415809 % Temperature: 27.415657 C, Humidity: 67.389297 % Temperature: 28.160858 C, Humidity: 63.978672 % Temperature: 28.144645 C, Humidity: 64.029407 % Temperature: 28.238296 C, Humidity: 64.356517 % Temperature: 30.097961 C, Humidity: 65.651798 % Temperature: 31.601142 C, Humidity: 66.472816 % Temperature: 32.508087 C, Humidity: 67.005634 % Temperature: 33.099555 C, Humidity: 67.503261 % Temperature: 33.486175 C, Humidity: 67.898273 % Temperature: 33.747673 C, Humidity: 68.226432 % Temperature: 33.955001 C, Humidity: 68.510818 % Temperature: 34.082984 C, Humidity: 68.742942 % Temperature: 34.218788 C, Humidity: 69.023990 % Temperature: 34.300231 C, Humidity: 69.223403 % Temperature: 34.375381 C, Humidity: 69.465637 % Temperature: 34.439849 C, Humidity: 69.653606 % Temperature: 34.442520 C, Humidity: 69.862174 % Temperature: 34.176635 C, Humidity: 67.678928 % Temperature: 33.948516 C, Humidity: 65.922927 % Temperature: 33.696746 C, Humidity: 64.408588 % Temperature: 33.488845 C, Humidity: 63.112926 % Temperature: 33.267784 C, Humidity: 62.030124 % Temperature: 33.068656 C, Humidity: 61.081218 %
测试过程中,我有用手握住传感器,可以看到,传感器在握住的时候温度在上升,而松开手后,温度又在缓缓下降,此时证明驱动已对接上。
我要赚赏金
