尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32F103老人护理监测仪:多传感器接口融合与数据采集实战

STM32F103老人护理监测仪:多传感器接口融合与数据采集实战 简介面向STM32F103的老人病人护理监测仪完整工程适用于单片机与物联网项目开发者覆盖DHT11温湿度、微波生命雷达、红外体温、一氧化碳、液晶屏、WiFi等多种传感器模块的接入与联动。压缩包内共184个文件大小约3.8MB以C源文件、头文件、Keil工程配置.uvprojx/.uvoptx为主同时包含编译生成的.o/.d/.crf/.axf/.hex等中间与目标文件以及.sct、.bat等辅助内容便于直接打开工程查看、编译或烧录。代码采用KEIL标准库编写注释完整各传感器接线在代码中有明确定义若更换STM32F103其他型号只需调整芯片型号与Flash容量结构清晰可节省二次开发时间。当前已有46人学习浏览适合需快速掌握STM32多传感器集成或护理监测方案实现的开发者参考。通过此工程可了解多路传感器数据读取、LCD显示与无线传输的协作方式为实际项目提供可运行起点。1. 老人护理监测仪的核心不是传感器多而是四类接口的数据不打架老人房里同时摆一个体温计、一个煤气报警器、一个雷达跌倒检测器效果远不如一台把所有数据汇总到同一块屏幕上的设备。这个标题里的工程包做的就是这件事把 DHT11 温湿度、微波生命雷达、红外体温、一氧化碳、液晶屏和 WiFi 模块全部挂在 STM32F103 上组成一台能本地显示、远程上报的护理监测仪。真正考验开发者的不是某个传感器驱动怎么写而是单总线、模拟量、UART、I2C 这四类接口在同一颗芯片上如何规划引脚、分配中断、避免互相阻塞。适合用 F103 做医疗电子原型、嵌入式综合课设或产品预研的人阅读看完能直接照着一套可复用的方案把原型跑起来。2. 用STM32F103接入DHT11单总线和MQ-7一氧化碳传感器引脚规划与时序实现2.1 F103最小系统的引脚分配与外设冲突排查STM32F103 的资源在这个项目里属于“够用但不算宽裕”。六类外设分别需要DHT11 占用一个普通 GPIOMQ-7 占用一个 ADC 通道微波雷达占用一路 UART红外体温占用 I2C液晶屏可用 I2C 或 SPIWiFi 模块再占一路 UART。F103 有 3 路 USART、2 个 I2C、2 个 SPI 和多个 ADC 通道按下面这张表分配可以避开大多数外设冲突。模块接口类型推荐引脚关键注意点DHT11 温湿度单总线 GPIOPB8 开漏输出必须外接 4.7kΩ 上拉电阻MQ-7 一氧化碳ADC 模拟量PA1 / ADC1_IN1模块输出需在 0~3.3V 范围微波生命雷达USART3PB10 / PB11帧协议以模块手册为准MLX90614 红外体温I2C1PB6 / PB7与 OLED 共用总线需互斥0.96 寸 OLEDI2C1 复用PB6 / PB7地址 0x3C不可同时读写ESP8266 WiFiUSART2PA2 / PA3与 F103 共地独立供电拿到任何一个“传感器护理监测仪”的工程包第一件事就是对照这份引脚表核对头文件里的宏定义。F103 的 USART1/2/3 驱动逻辑几乎一致区别只在时钟使能、中断向量和复用引脚如果雷达接在 USART3 而代码里初始化的是 USART1现象就是完全收不到数据连“差一点点”的错觉都不会有。I2C 这里要留意OLED 和 MLX90614 共用一条总线没问题但 OLED 刷屏是连续 I2C 操作体温读取期间不能插入显示刷新否则总线时序互相打断后极难排查。2.2 DHT11单总线时序HAL库工程里为什么需要自己加微秒延时DHT11 的驱动在 HAL 库工程里最容易犯的错不是读数据而是用HAL_Delay去卡时序。HAL_Delay的粒度是毫秒级而 DHT11 的一位数据是通过 26~70 微秒的高电平宽度来区分 0 和 1 的。启动时主机先拉低总线至少 18ms然后释放DHT11 先拉低 80us 响应再拉高 80us随后输出 40 位数据每位以 50us 低电平开始后面高电平 26~28us 表示 0高电平 70us 表示 1。所以读位操作必须精确到微秒。常见做法是用 DWT 的 CYCCNT 寄存器实现微秒延时不占用定时器也不依赖 SysTick 优先级volatile uint32_t *DWT_CYCCNT (volatile uint32_t *)0xE0001004; volatile uint32_t *DWT_CTRL (volatile uint32_t *)0xE0001000; void delay_us(uint32_t us) { *DWT_CYCCNT 0; while (*DWT_CYCCNT us * (SystemCoreClock / 1000000U)); }使用前先使能 DWT 计数器CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; *DWT_CTRL | 1;。这个延时是阻塞式的耗时很短2us 到 80us 的调用不会影响系统实时性但需要注意在中断服务函数里不要调用长时间延时。读一位数据的核心代码static uint8_t dht11_read_bit(void) { while (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)); // 等待 50us 低电平结束 delay_us(30); // 在高电平时段中采样 uint8_t bit GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8); delay_us(30); // 等待高电平结束 return bit; }这段的逻辑是DHT11 发出每一位前都会拉低总线 50us代码先等到低电平结束再延时 30us。如果该位是 0高电平只剩大约 26us延时 30us 后总线已经回到低读到 0如果该位是 1高电平持续 70us延时 30us 后总线仍在高读到 1。读完 40 位后拆出湿度整数、湿度小数、温度整数、温度小数和校验和校验公式为湿度整数湿度小数温度整数温度小数的低 8 位等于校验字节。校验失败时不要重试太多次DHT11 两次读取间隔建议大于 1 秒连续读取会导致传感器内部测量未完成而始终无响应。这里还要明确一点DHT11 的精度是温度 ±2°C、湿度 ±5%做护理监测的趋势记录可以但不要把它当成医疗级温湿度计。如果项目需要更高的湿度一致性选 SHT30 这类 I2C 数字传感器会更合适但那就改变了标题里的器件选型驱动和时序都不再是这篇文章的范围。2.3 MQ-7一氧化碳传感器的ADC采集与浓度换算方法MQ-7 是模拟量输出气敏电阻随 CO 浓度变化模块上的负载电阻把电阻变化转换成电压。护理仪场景里MQ-7 的作用是识别缓慢的煤气泄漏或燃烧不充分产生的 CO不需要实验室级精度但需要稳定的趋势和合理的阈值判断。上电后 MQ-7 需要预热。加热电流会让传感器表面温度升高冷态下输出阻抗不稳常见做法是预热 60 秒后再启动采集任务。采集代码用多次采样取平均并丢弃第一次转换结果uint16_t mq7_read_adc(void) { uint16_t sum 0; uint16_t valid_count 0; for (int i 0; i 8; i) { HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 100) HAL_OK) { if (i 0) // 第一次转换结果不稳定丢弃 { sum HAL_ADC_GetValue(hadc1); valid_count; } } } return sum / valid_count; }hadc1需要在 CubeMX 里把 PA1 配置为 ADC1_IN1采样周期拉长到 55.5 周期以上可以减少信号源内阻带来的采样误差。得到 ADC 原始值后换算电压v_out (float)adc * 3.3f / 4095.0f。电压到 PPM 的换算是 MQ-7 驱动里最容易想当然的部分。传感器手册上的灵敏度曲线是双对数坐标工程上一般用log(PPM) A * log(v_out) B做拟合。系数 A、B 跟具体模块的负载电阻有关不同批次传感器差异明显。没有标气时先按经验系数做趋势级换算标定方法是用一瓶已知浓度的 CO 标准气记录电压点再反推系数。护理监测仪的报警逻辑建议直接基于电压阈值而不是基于换算后的 PPM因为电压是原始物理量重复性更可靠。3. 微波生命雷达的UART数据解析与MLX90614红外体温的I2C读取差异3.1 微波生命雷达的UART帧结构解析与环形缓冲区处理微波存在/生命雷达模块通过 UART 输出检测结果不同品牌的帧格式差异很大但共同结构是“帧头 状态 距离/强度 校验”。以常见的 4 字节精简帧为例AA AA 状态 校验状态位0x00表示无人0x01表示有人静止0x02表示有人移动校验为前三字节累加和取低 8 位。工程上 UART 接收不能放在主循环轮询否则传感器数据到达时 CPU 可能正忙于 LCD 刷新导致丢字节。标准做法是中断接收加一个环形缓冲区#define RING_SIZE 64 static uint8_t ring[RING_SIZE]; static volatile uint8_t wr 0; static volatile uint8_t rd 0; static uint8_t rx_byte; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART3) { ring[wr] rx_byte; wr % RING_SIZE; HAL_UART_Receive_IT(huart, rx_byte, 1); } }wr和rd分别记录写入和读取位置中断只往 ring 里写主循环读取并解析。单个字节的单字符接收中断在 9600 波特率下会每约 1ms 触发一次F103 完全扛得住。解析时把rd和wr比较逐字节做状态机匹配帧头AA AA匹配后读取状态和校验校验错误则丢弃整个帧。如果雷达一直收不到合法帧不要急着改代码。先把雷达模块的 TX 直接接到 USB 转 TTL 的 RX在 PC 串口助手里看原始数据。这一步能确认三件事波特率对不对、帧格式是不是自己想当然的那种、模块本身有没有输出。确定帧格式后再写解析能省一大半调试时间。另外 F103 的 USART1/2/3 中断回调函数名称相同但如果同时接了 ESP8266需要按huart-Instance区分来源两个模块不要共用一个接收缓冲区。3.2 MLX90614红外体温的I2C寄存器读取与温度换算MLX90614 是医疗电子里常用的非接触红外测温传感器I2C 地址0x5ARAM 地址0x07是红外物体温度0x06是环境温度。每个温度寄存器是 16 位补码分辨率 0.02°C传输时小端在前。HAL 库的读取写法float mlx90614_read_obj_temp(void) { uint8_t data[3] {0}; // 0x5A 1 是 8 位写地址0x07 是物体温度寄存器 HAL_I2C_Mem_Read(hi2c1, 0x5A 1, 0x07, I2C_MEMADD_SIZE_8BIT, data, 3, 100); uint16_t raw (uint16_t)(data[0] | (data[1] 8)); return (float)raw * 0.02f - 273.15f; }读到的 3 个字节里前两个是温度原始值第三个是 PEC 校验字节。上面这段代码没有校验 PEC原型阶段够用但如果设备要通过 CE 或医疗相关认证必须补 PEC 计算否则总线干扰时可能读到跳变的体温值。PEC 算法是 CRC-8多项式x^8x^2x1初始值 0计算范围覆盖从设备地址到数据最后一个字节。红外体温的校准是护理仪项目里最容易翻车的一块。MLX90614 测的是额头表面辐射温度不是人体核心体温。环境温度、测量距离、额头出汗都会造成偏差常见做法是在目标距离 3~5cm 处做单点偏置校正让受试者用电子体温计测一次腋下温度然后减去当前 MLX90614 读数得到偏置值存入 Flash。需要注意这个偏置只在相近环境温度下有效冬季和夏季最好分别标定一次。3.3 I2C总线上OLED与红外体温共存时的互斥与复位如果选 0.96 寸 OLED 作为液晶屏它和 MLX90614 大概率共用同一条 I2C1 总线。SSD1306 和 MLX90614 都支持 100kHz 标准模式跑 400kHz 也没问题真正的隐患是 F103 硬件 I2C 的 BUSY 状态卡死。F103 的硬件 I2C 在通信被干扰后可能进入 BUSY 状态HAL_I2C_IsDeviceReady一直返回超时。常见做法是在 I2C 错误回调里做总线复位把 SCL 引脚手动翻转 9 次让挂在总线上的从设备释放 SDA然后重新初始化 I2C 外设。更好的做法是 OLED 和 MLX90614 的访问通过一个互斥信号量串行化显示刷新任务里不直接调HAL_I2C_Mem_Write而是把屏幕数据打包成局部缓冲区后一次性发送缩短每次占用总线的时间。这里有个实际经验OLED 屏的 I2C 地址是0x3CMLX90614 的地址是0x5A两者地址不冲突但 OLED 初始化时如果执行了OLED_Clear整屏刷新I2C 总线上会产生几十字节的连续写操作。如果此时雷达中断触发了体温读取请求I2C 会被中途打断。用裸机轮询写代码时一定要安排成“同一时刻只有一个模块发起 I2C 通信”用 RTOS 时则要给 I2C 总线加互斥量后面第 4 章会详细讲这个调度模型。4. 液晶屏本地显示与ESP8266 WiFi远传裸机标志位到FreeRTOS任务模型4.1 0.96寸OLED与TFT液晶屏的选型以及显示刷新策略标题里的“液晶屏”在护理仪项目里通常指 0.96 寸 OLED 或 1.8 寸 TFT。两者驱动方式和资源占用差别很大选型直接决定 F103 剩余引脚够不够用。对比项0.96 寸 OLED (SSD1306)1.8 寸 TFT (ST7735)接口I2C 或 SPISPI DC/CS/RST 控制脚刷新方式整屏灰度点阵适合字符像素级写入可绘图刷新速度较慢数字刷新足够快适合波形绘制引脚占用2 根 (I2C)5~6 根 (SPI 控制)F103 资源影响与 MLX90614 共用 I2C占用 SPI1可能与 ADC/DMA 冲突做护理监测仪OLED 更合适原因是显示内容只有温度、湿度、CO 浓度、雷达状态和报警信息不需要图形界面。OLED 驱动只用到OLED_ShowString和OLED_ShowNum两个函数显示刷新时不要整屏清空重绘而是只更新变化字段OLED_ShowString(0, 0, TEMP:); OLED_ShowNum(40, 0, (int)temp, 2); OLED_ShowString(0, 16, HUMI:); OLED_ShowNum(40, 16, (int)humi, 2); if (co_ppm CO_ALARM_THRESHOLD) { OLED_ShowString(0, 32, CO ALARM!); }每次显示前先判断数值是否有变化变化的才调用OLED_ShowNum。整屏OLED_Clear会让 128×64 的缓冲区全部重写I2C 传输时间明显拉长主循环里的其他任务全部被阻塞。显示任务单独跑在 1s 周期里数值不变就不写屏这样 I2C 总线占用率极低。4.2 ESP8266 WiFi模块的AT指令对接与JSON数据上报ESP8266 接到 F103 的 USART2用 AT 指令固件时不需要在 F103 里写复杂的 WiFi 协议栈。接线时注意三点ESP8266 的 TX 接 F103 的 RXRX 接 F103 的 TX模块供电建议独立 3.3V 电源。ESP8266 峰值电流可达 300mA如果直接从 F103 开发板的 3.3V LDO 取电WiFi 连接瞬间电压跌落会导致模块重启表现为串口毫无响应。AT 指令以\r\n结尾建立 TCP 连接并上报数据的完整流程ATRST ATCWMODE1 ATCWJAPyour_ssid,your_password ATCIPSTARTTCP,192.168.1.100,8080 ATCIPSEND62 {temp:36.5,hum:60,co:12,radar:1,face:36.8,bat:3.9}每条 AT 指令都要等待返回OK或ERROR后再发下一条不能盲目用HAL_Delay硬等。ATCIPSEND62后面的 62 是 JSON 字符串的字节长度多一个字符少一个字符都会导致模块卡在发送状态直到超时。因此 MCU 端的发送代码不要手写固定长度而是先snprintf组包再用strlen拿到实际长度char payload[128]; snprintf(payload, sizeof(payload), {\temp\:%.1f,\hum\:%d,\co\:%d,\radar\:%d,\face\:%.1f,\bat\:%.1f}, temp, humi, co_ppm, radar_state, face_temp, battery_v); char cmd[64]; int len snprintf(cmd, sizeof(cmd), ATCIPSEND%d\r\n, strlen(payload)); // 先发 ATCIPSENDxx再等待 ESP8266 返回 提示符然后发 payload发送流程收到后立即发 payload之后等待SEND OK。收到SEND OK再发下一条不要连续调用ATCIPSEND。WiFi 断开时 ESP8266 会返回WIFI DISCONNECT此时要主动ATCIPCLOSE并重连 TCP而不是直接重启模块。重连逻辑里加一个计数器连续失败 5 次才执行软复位避免网络抖动导致整机周期性重启。4.3 多传感器并发时用FreeRTOS任务替换裸机标志位传感器越多裸机主循环就越难维护。DHT11 需要 1 秒间隔读取MQ-7 需要持续采集并滤波雷达要随时解析 UARTOLED 要周期性刷新WiFi 要 10 秒上报一次。所有这些周期叠加在一个while(1)里最直接的后果是某个耗时的传感器读取阻塞了其他所有任务。FreeRTOS 在这种场景下优势明显。任务划分和优先级如下任务周期优先级主要工作radar_parse事件触发3从环形缓冲解析雷达帧sensor_read2s2读 DHT11、MQ-7、MLX90614display_task1s1更新 OLED 变化字段wifi_report10s0JSON 组包、AT 发送、断线重连传感器数据用队列传递。sensor_read把结构体打包后xQueueSend到data_queuedisplay_task和wifi_report各自从队列接收数据避免多条任务直接读写全局变量。OLED 与 MLX90614 共用 I2C 总线给 I2C 操作加xSemaphoreTake/xSemaphoreGive显示任务读屏和体温任务读寄存器就不会互相穿插。F103 的内存只有 20KB SRAMFreeRTOS 堆配置 8KB 左右每个任务栈给 256 字1KB足够OLED 缓冲区和 WiFi 发送缓冲区单独用静态数组分配不要都挂在任务栈里。FreeRTOS 的移植可以用 CubeMX 直接生成也可以在标准外设库工程里手动添加FreeRTOS/Source和port/GCC/ARM_CM3。手动移植时注意PendSV_Handler和SysTick_Handler必须改为 FreeRTOS 提供的弱定义版本否则任务切换不生效现象是系统启动就死机或任务不调度。5. 整机联调技巧串口CSV日志回放验证六路传感器和时间戳传感器越多越不能靠肉眼判断“现在显示 36.5°C 对不对”。最可靠的整机验证方法是把所有传感器数据带时间戳输出到调试串口存成 CSV 后离线回放曲线。调试串口用 USART1USB 转 TTL 接 PA9/PA10每 2 秒打印一行格式固定为printf(%lu,%d,%d,%d,%d,%d\r\n, HAL_GetTick(), // 毫秒时间戳 dht11_humi, // 湿度百分比 dht11_temp, // 温度整数 (int)(face_temp * 10), // 红外体温乘10避免浮点打印 co_ppm, // CO 浓度 radar_state); // 雷达状态PC 端用一个串口工具把输出保存为 CSV 文件Excel 打开后按时间戳画折线图重点验证三件事。第一雷达状态从 0 变 1 时红外体温曲线是否有同步跳变。MLX90614 和雷达模块如果共用一个电源轨雷达发射瞬间会拉低电压导致 I2C 读取错误体温曲线出现毛刺。看到这种同步跳变优先处理共地和电源而不是在代码里加滤波。第二DHT11 的环境温度和 MLX90614 的红外体温应该保持一个固定差值。环境温度 27°C 时额头温度 36.5°C这个差值在 8~10°C 属于正常。如果两者始终接近说明红外传感器没对准人体或发射率配置成了默认的 1.0 而不是人体的 0.98。检查和调整发射率时要重新标定偏置。第三WiFi 断网期间日志不能断。断网时ATCWJAP重连要阻塞等待这段时间内传感器采集任务如果被阻塞日志行里会出现明显的时间戳间隔拉长。正确做法是把 WiFi 重连做成状态机放到低优先级任务里断网时本地显示和传感器读取保持正常日志依然每 2 秒一行。最后做 10 次整机断电上电循环重点观察 ESP8266 能否在每次上电后自动重连。如果依赖 TCP 长连接需要在服务端设置心跳超时MCU 端每 10 秒上报一次数据本身就是心跳不需要额外实现。整机连续运行 3 天第三天导出 CSV如果雷达事件、CO 超限、温度趋势都有完整时间戳记录这套护理监测仪原型才算真正达到可交付状态。本文还有配套的精品资源点击获取
返回列表