序言
本次活动,收获是真的很大。跟着刘工及一众大佬初步学会了如何在实时开源操作系统Zephyr下开发单片机应用程序,以及开发蓝牙应用的一些知识。
一、项目介绍
使用恩智浦 FRDM-MCXW71 开发板在 Zephyr开发环境下实现即时读取温湿度传感器DHT11的数据,并通过板载蓝牙设备将数据发送出去,供上位机接收、显示。
二、系统构成
1、软件环境
·Win10
·Python 3.14.5
·CMake 4.3.2
·West 1.5.0
·Ninja 1.13.2
·Git 2.54.0
·VSCode 1.120.0(非必须,为了编程方便)
2、硬件构成
·恩智浦 FRDM-MCXW71 开发板

MCXW71是NXP推出的一款低功耗、高安全系数、面向无线连接的MCU,它的最大主频有96MHz,内核为Cortex-M33,片上集成1MB flash和128KB SRAM;FRDM-MCXW71是一款紧凑且可扩展的开发板,支持BLE、Zigbee、Thread和Matter多种无线协议。该板包括一个板载MCU-Link调试器、一个加速度传感器、一个光传感器和一个外接SPI闪存。
·DHT11温湿度传感器

DHT11是一款含有已校准数字信号输出的温湿度复合传感器,采用了集成式数字温湿度元件,应用专用的数字模块采集技术和温湿度传感技术,具有极高的可靠性与卓越的长期稳定性。DHT11传感器内包含一个温湿度测量元件和一个高性能MCU。
3、电路构成框图

三、功能设计
1、使用C语言以Zephyr方式建立工程
2、工作流程

3、关键代码
1)系统配置
A. DHT11用接口PA16(文件:frdm_mcxw71.overlay)
{
aliases {
dht11 = &dht11_node;
ledg = &ledg_node;
};
dht11_node: dht11 {
compatible = "aosong,dht";
dio-gpios = <&gpioa 16 GPIO_ACTIVE_HIGH>;
status = "okay";
};
ledg_node: ledg {
compatible = "gpio-leds";
gpios = <&gpioa 19 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
/* Enable gpioa for DHT11 (PTA16) */
&gpioa {
status = "okay";
};B. 蓝牙模块(文件:prj.conf)
# Bluetooth configuration CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_SMP=y CONFIG_BT_MAX_CONN=3 CONFIG_BT_DEVICE_NAME="DHT11_MCXW71" CONFIG_BT_DIS=n CONFIG_BT_BAS=n CONFIG_BT_HRS=n
2) DHT11-初始化及读取温湿度数据
int dht11_init(const struct device *gpio_dev, int pin)
{
if (!gpio_dev || !device_is_ready(gpio_dev)) {
return -ENODEV;
}
int ret = gpio_pin_configure(gpio_dev, pin, GPIO_INPUT | GPIO_PULL_UP);
if (ret < 0) {
return ret;
}
return 0;
}
int dht11_read(const struct device *gpio_dev, int pin, uint8_t *humidity, uint8_t *temperature) {
uint8_t data[5] = {0};
int ret;
if (!gpio_dev || !humidity || !temperature) {
return -EINVAL;
}
/* Send start signal: output low for 18ms */
ret = gpio_pin_configure(gpio_dev, pin, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
return ret;
}
dwt_delay_us(18000); /* 18ms */
/* Release bus: input with pull-up */
ret = gpio_pin_configure(gpio_dev, pin, GPIO_INPUT | GPIO_PULL_UP);
if (ret < 0) {
return ret;
}
/* Wait for DHT ACK low (~80us low) */
int ack_low = wait_and_measure_us(gpio_dev, pin, 0);
if (ack_low >= 1000) {
return -ETIMEDOUT; /* No response */
}
/* Wait for DHT ACK high (~80us high) */
int ack_high = wait_and_measure_us(gpio_dev, pin, 1);
if (ack_high >= 1000) {
return -ETIMEDOUT;
}
/* Read 40 bits */
for (int i = 0; i < 40; i++) {
/* Wait for bit start (low) */
int low_dur = wait_and_measure_us(gpio_dev, pin, 0);
if (low_dur >= 1000) {
return -EIO;
}
/* Measure high duration to determine bit value */
int high_dur = wait_and_measure_us(gpio_dev, pin, 1);
/* Store bit: 0 bit ~30us, 1 bit ~70us */
int byte_idx = i / 8;
int bit_idx = 7 - (i % 8);
if (high_dur > 40) {
data[byte_idx] |= (1 << bit_idx);
}
}
/* Verify checksum */
uint8_t sum = data[0] + data[1] + data[2] + data[3];
if (sum != data[4]) {
return -EIO; /* Checksum error */
}
*humidity = data[0];
*temperature = data[2];
return 0;
}
3)蓝牙初始化及发送数据
int btDevice_init(void) {
int err;
err = bt_enable(NULL);
if (err) {
printf("BLE: bt_enable failed (err %d)\n", err);
return err;
}
printf("BLE: Bluetooth initialized\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
printf("BLE: Advertising failed (err %d)\n", err);
return err;
}
printf("BLE: Advertising started\n");
return 0;
}
void btDevice_notify(uint8_t temp, uint8_t hum) {
int err;
unsigned char info[32];
/* Convert sensor_value to uint16_t */
sensor_data.temp = (uint16_t)temp;
sensor_data.hum = (uint16_t)hum;
int len = snprintf(info, sizeof(info), "T=%d°C, H=%d%%\r\n", temp, hum);
err = bt_gatt_notify(NULL, &dht11_svc.attrs[5], info, len);
if (err) {
printf("send info ng(%d)!\r\n", err);
}
}3) 主程序
int main(void) {
int ret;
uint8_t humidity, temperature;
printk("DHT11 Test starting...\n");
const struct device *gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpioa));
ret = dht11_init(gpio_dev, DHT11_PIN);
if (ret < 0) {
printk("DHT11 init failed: %d\n", ret);
return 0;
}
printk("DHT11 initialized\n");
ret = gpio_pin_configure(gpio_dev, LED_PIN, GPIO_OUTPUT_INACTIVE );
if (ret < 0) {
printk("LED init failed: %d\n", ret);
return ret;
}
/* Initialize BLE and start advertising */
ret = btDevice_init();
if (ret != 0) {
printf("BLE init failed: %d\n", ret);
}
while (1) {
led_state = !led_state;
gpio_pin_configure(gpio_dev, LED_PIN, led_state ?GPIO_OUTPUT_ACTIVE: GPIO_OUTPUT_INACTIVE );
ret = dht11_read(gpio_dev, DHT11_PIN, &humidity, &temperature);
if (ret == 0) {
printk("H=%d%% T=%dC OK\n", humidity, temperature);
} else {
printk("Read failed: %d\n", ret);
}
/* Send sensor data via BLE notifications */
btDevice_notify(temperature, humidity);
k_sleep(K_SECONDS(3));
}
return 0;
}四、运行效果
1、串口接收端

注:手机端蓝牙没有连接场合,出现的“send info ng(-128)!”信息是正常的。连接成功后,不会再出现。
2、蓝牙接收端(使用微信小程序:蓝牙串口)

五、演示视频
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=116644735160806&bvid=BV1cDGk6pELJ&cid=38643368343&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
六、总结
这是首次使用Zephyr开发,在配置开发环境时遇到麻烦。使用软件自动安装的方式,花了两天时间也没有成功。不得已手动下载用到的软件,解压到对应的环境中。这个过程如果没有刘工的帖子的指导,根本不可能成功。在建立点灯工程、蓝牙工程时,依旧是早刘工及其他网友的帖子的帮助下,完成了最终的成果。
从整个开发过程来看,Zephyr有其方便之处。只需要做一些简单的配置,就可以省去底层代码的处理。这一点与Arduino的第三方库支持有些相似,虽然在使用便捷度上不如Arduino,但比Keil下的开发方便多了。另一面,Zephyr的学习成本相对要高一些,比如在如何制作配置文件上,且底层函数不能像STM32的HAL库那样可见,有一定难度。这些都需要花时间去学习。
通过这个活动,能了解、学习Zephyr开发是最大的收获。再次特别感谢刘工无私的付出!
我要赚赏金
