freertos开发空气检测仪之串口驱动与外部设备(PM2.5模块)交互实例
本篇文章带来的是freertos开发空气检测仪之串口驱动与外部设备(PM2.5模块)交互实例。
·
freertos开发空气检测仪之串口驱动与外部设备(PM2.5模块)交互实例
感谢粉丝的关注,昨天进行年会,加上马上就要放假了,这两天没更新文章。今天这个空气检测仪项目学习继续进行。本篇文章带来的是freertos开发空气检测仪之串口驱动与外部设备(PM2.5模块)交互实例。
PM2.5模块原理图

这个是本次项目中使用的原理图,可以看到pm2.5传感器使用串口的方式进行数据的交互,而且自带了一个私有的PA6的io引脚操作。
调试USART0串口
根据我的经验进行调试。
/*
* PM2.5传感器使用 USART0 (Tx: PA9, Rx: PA10)
* 私有控制引脚: PA6
*/
static CalUartConfig g_usart0_cfg = {
.usart_periph = USART0,
.usart_clk = RCU_USART0,
.tx_gpio_port = GPIOA,
.tx_gpio_pin = GPIO_PIN_9,
.tx_gpio_clk = RCU_GPIOA,
.rx_gpio_port = GPIOA,
.rx_gpio_pin = GPIO_PIN_10,
.rx_gpio_clk = RCU_GPIOA,
.irq_n = USART0_IRQn,
.irq_pre = 12,
.irq_sub = 0,
.use_dma_rx = 0
};
/* ------------------- 自定义 Init/Send 函数 ------------------- */
/* PM2.5 传感器私有初始化: 额外初始化 PA6 */
static int USART0_Dev_Init(struct UART_Device *pDev, uint32_t baudrate)
{
/* 1. 调用通用初始化 */
if (UART_Dev_Init(pDev, baudrate) != 0) return -1;
/* 2. 初始化私有引脚 PA6 */
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
/* 默认拉高 (Enable) */
gpio_bit_set(GPIOA, GPIO_PIN_6);
return 0;
}
/* 2. 定义设备实例 */
struct UART_Device g_usart0_dev = {
.name = "usart0",
.priv_cfg = &g_usart0_cfg,
.priv_data = NULL,
.Init = USART0_Dev_Init, /* 使用自定义 Init */
.Send = UART_Dev_Send, /* 使用通用 Send */
.Recv = UART_Dev_Recv
};
/* 3. 注册函数 */
void Driver_UART_Init(void)
{
UART_Device_Register(&g_wifi_uart_dev);
UART_Device_Register(&g_uart3_dev);
UART_Device_Register(&g_usart0_dev);
}
4.串口中断服务函数
void USART0_IRQHandler(void)
{
uint8_t rx_byte;
uint8_t status = CAL_UART_IRQHandler(&g_usart0_cfg, &rx_byte);
if (status == 1) {
UART_Dev_PushRxByte(&g_usart0_dev, rx_byte);
}
}
依据我调试的经验,经过一番折腾,串口0的驱动就设计好了。下一步调试模块相关代码。
PM2.5模块抽象结构体
/* PM2.5 数据结构 */
typedef struct
{
uint16_t pm1p0Conc; /* PM1.0 浓度 (ug/m3) */
uint16_t pm2p5Conc; /* PM2.5 浓度 (ug/m3) */
uint16_t pm10Conc; /* PM10 浓度 (ug/m3) */
uint16_t pm0p3Num; /* >0.3um 颗粒物个数 */
uint16_t pm0p5Num; /* >0.5um 颗粒物个数 */
uint16_t pm1p0Num; /* >1.0um 颗粒物个数 */
uint16_t pm2p5Num; /* >2.5um 颗粒物个数 */
uint16_t pm5p0Num; /* >5.0um 颗粒物个数 */
uint16_t pm10Num; /* >10um 颗粒物个数 */
} Pm25Data;
/* 空气质量等级 */
typedef enum
{
AQI_EXCELLENT, /* 优 */
AQI_GOOD, /* 良 */
AQI_MILD, /* 轻度污染 */
AQI_MODERATE, /* 中度污染 */
AQI_SEVERE, /* 重度污染 */
AQI_TERRIBLE, /* 严重污染 */
AQI_UNKNOWN
} AirQualityLevel;
/* PM2.5 设备结构体 */
typedef struct Pm25Device {
char *name;
struct UART_Device *uart_dev; /* 底层使用的串口设备 */
/* 接口函数 */
int (*Init)(struct Pm25Device *pDev);
int (*PowerControl)(struct Pm25Device *pDev, bool on);
int (*ReadData)(struct Pm25Device *pDev, Pm25Data *pData);
AirQualityLevel (*GetLevel)(uint16_t pm2p5Conc);
} Pm25Device;
/* 获取 PM2.5 设备实例 */
struct Pm25Device *GetPm25Device(void);
/* 静态实例 */
static struct Pm25Device g_pm25_dev = {
.name = "PM2.5 Sensor",
.uart_dev = NULL, /* Init时获取 */
.Init = Pm25_Init,
.PowerControl = Pm25_PowerControl,
.ReadData = Pm25_ReadData,
.GetLevel = Pm25_GetLevel
};
// 注册函数
struct Pm25Device *GetPm25Device(void)
{
return &g_pm25_dev;
}
然后在对应的文件中,填充好如下对应的函数代码片段即可。
/* 内部私有函数声明 */
static int Pm25_Init(struct Pm25Device *pDev);
static int Pm25_PowerControl(struct Pm25Device *pDev, bool on);
static int Pm25_ReadData(struct Pm25Device *pDev, Pm25Data *pData);
static AirQualityLevel Pm25_GetLevel(uint16_t pm2p5Conc);
使用AI与参考现成资料进行了开发,将上述的函数片段完成,进行整合了下,下一步进行测试。
pm2.5模块测试
static void pm25_test_task(void *pvParameters)
{
struct Pm25Device *pDev = GetPm25Device();
Pm25Data data;
if (pDev == NULL) {
DBG_log("[PM2.5 TEST] Failed to get device!\n");
vTaskDelete(NULL);
return;
}
/* 初始化设备 */
if (pDev->Init(pDev) != 0) {
DBG_log("[PM2.5 TEST] Failed to init device!\n");
vTaskDelete(NULL);
return;
}
DBG_log("[PM2.5 TEST] Device initialized. Waiting for data...\n");
while (1)
{
/* 尝试读取数据 */
/* ReadData 内部会尝试从串口队列读取数据并进行拼包解析 */
/* 返回 1 表示成功解析出一包数据 */
if (pDev->ReadData(pDev, &data) == 1)
{
AirQualityLevel level = pDev->GetLevel(data.pm2p5Conc);
char *levelStr;
switch(level) {
case AQI_EXCELLENT: levelStr = "Excellent"; break;
case AQI_GOOD: levelStr = "Good"; break;
case AQI_MILD: levelStr = "Mild"; break;
case AQI_MODERATE: levelStr = "Moderate"; break;
case AQI_SEVERE: levelStr = "Severe"; break;
case AQI_TERRIBLE: levelStr = "Terrible"; break;
default: levelStr = "Unknown"; break;
}
DBG_log("[PM2.5] PM2.5: %d ug/m3 (%s), PM1.0: %d, PM10: %d\n",
data.pm2p5Conc, levelStr, data.pm1p0Conc, data.pm10Conc);
}
/*
* 延时策略:
* PM2.5 传感器通常 1秒 发送一次数据。
* 如果这里延时太久,串口缓冲区可能会溢出。
* 如果延时太短,会空转浪费CPU。
* 建议 100ms 轮询一次,足以处理 32字节 的数据包。
*/
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void pm25_test_start(void)
{
/* 1. 初始化底层驱动 (Driver_UART_Init 已经被调用过吗?如果没有,需要调用) */
/* 在 main.c 中应该已经调用了 Driver_UART_Init,这里为了保险起见,
假设 main.c 会统一负责底层驱动初始化,或者在这里重复调用(如果支持幂等)*/
/* Driver_UART_Init(); */
/* 2. 创建测试任务 */
/* 优先级建议:中等,不需要像串口接收那样高,但要比后台任务高 */
xTaskCreate(pm25_test_task, "pm25_test", 512, NULL, 2, NULL);
}
实验现象

可以看到,本次项目使用的串口和外部设备就完成通讯交互的功能。
本文完!!
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)