当前位置:首页 > 工业控制 > 电路设计项目集锦
[导读]本项目是使用STM32F103C8“Blue Pill”芯片构建的房间监测装置,并在Wokwi中进行仿真。它可测量温度、湿度、环境光强和运动状态,通过OLED显示屏逐次显示一次读数,当房间温度过高或过低时会发出蜂鸣声,当房间无人时则自动关闭显示屏。

本项目是使用STM32F103C8“Blue Pill”芯片构建的房间监测装置,并在Wokwi中进行仿真。它可测量温度、湿度、环境光强和运动状态,通过OLED显示屏逐次显示一次读数,当房间温度过高或过低时会发出蜂鸣声,当房间无人时则自动关闭显示屏。

该项目由MSU-IIT的BCA182嵌入式系统编程课程(实验活动第1号)开发,采用PlatformIO、STM32Cube HAL以及原生FreeRTOS API实现。

动机

房间监测器需要以不同速度完成多项任务:每隔几秒采样一次传感器,当有人移动或转动旋钮时立即响应,并在温度超出范围时发出警报。将所有这些操作写成一个大循环,会导致时间安排难以理清。本项目的目标是学习实时操作系统(RTOS)如何解决这一问题:将任务拆分为多个子任务,根据各任务的响应速度设定优先级,并确保各任务之间安全通信。

功能特性

- 每2秒从DHT22读取一次温湿度

- 通过ADC从光敏电阻读取相对环境光照强度(0-100%)

- 使用PIR传感器检测运动

- SSD1306 OLED显示选定的测量数据

- 旋转编码器用于切换页面:温度、湿度、光照、运动(双向,带循环显示)

- 温度低于18°C或高于30°C时触发蜂鸣报警

- 活动/非活动状态机:无运动15秒后OLED关闭,再次有运动时恢复唤醒

- 6个FreeRTOS任务、3个队列、1个事件组、1个互斥锁,以及用于周期性采样的vTaskDelayUntil()

- 13个单元测试及使用cppcheck进行静态分析

电路

引脚连接:

•光敏电阻 AO 连接到 PA0(ADC1)

•DHT22 数据连接到 PA1

•蜂鸣器连接到 PA2(TIM2 PWM,1 kHz)

•PIR 输出连接到 PA3

•编码器时钟/数据信号连接到 PA4(中断)/ PA5

•串口发送/接收连接到 PA9 / PA10(USART1,115200)

•OLED SCL/SDA 连接到 PB6 / PB7(I2C1)

系统架构

四个输入(DHT22、光敏电阻、PIR和编码器)接入STM32,该芯片运行FreeRTOS系统,包含六个任务,并驱动三个输出:OLED显示屏、蜂鸣器以及串口日志。

代码被划分为多个模块:

•驱动模块与硬件通信(dht22.c、ldr.c、ssd1306.c、buzzer.c、log.c);

•各任务文件负责独立的任务(sensors.c、display.c、input.c、alarm.c、motion.c、system_state.c);

•logic.c中包含决策逻辑(如报警、页面导航、激活/非激活状态),不涉及硬件代码,因此可在PC上进行单元测试;

•main.c仅初始化硬件并调用app_main()函数,该函数创建所有任务。

FreeRTOS架构

任务及优先级(数值越高表示越紧急):

•MotionTask(优先级3,每100毫秒一次):读取PIR信号并触发运动检测;

•InputTask(优先级3,每50毫秒一次):将编码器的步数转换为页面切换;

•StateTask(优先级3,当检测到运动或每250毫秒一次):运行激活/非激活状态机;

•SensorTask(优先级2,每2秒一次,使用vTaskDelayUntil):读取DHT22和光敏电阻数据;

•AlarmTask(优先级2,每次新读取时触发):检查温度并控制蜂鸣器;

•DisplayTask(优先级1,每当有新数据或页面变化时执行):唯一一个绘制OLED内容的任务。

为何这些任务具有最高优先级:对人有反应的任务(如运动检测、编码器、状态变化)被赋予最高优先级,因为延迟的响应会明显察觉。传感器和报警任务以2秒为周期运行,因此几毫秒的延迟并不重要。OLED绘图是速度最慢且最不紧急的任务,因此其优先级最低,从不延迟用户输入。

任务之间的通信方式:

队列机制:SensorTask 通过两个队列将 SensorData 结构体(温度、湿度、光照、运动)发送给 DisplayTask 和 AlarmTask。InputTask 通过第三个队列将选定页面发送给 DisplayTask。每个队列仅存储最新的数据值。

事件组:用于表示 ACTIVE(激活)、MOTION(运动)、ALARM(报警)和 PIR 级别(PIR 指纹)。StateTask 设置 ACTIVE 状态,而显示、输入和报警任务均读取该状态。

互斥锁:保护串行端口,所有六个任务都使用该端口发送日志信息,因此两个任务永远不会互相覆盖打印。

工作原理

每2秒,SensorTask读取DHT22和光敏电阻的数据,并通过队列发送结果。它使用vTaskDelayUntil()函数,确保读取数据间隔精确为2秒,避免漂移。

DisplayTask等待新数据,并在OLED上显示所选页面。

转动编码器会触发一个短暂中断,仅用于计数步数。InputTask读取该计数值并更新页面。

AlarmTask检查每次新的温度值。当温度低于18°C或高于30°C时,蜂鸣器响起,OLED显示"! TEMP ALARM"。

MotionTask监控PIR传感器。若15秒内无运动,StateTask将状态切换为INACTIVE:OLED关闭,编码器被忽略,蜂鸣器静音。下一次检测到运动后,状态将恢复为ACTIVE。

测试与验证

单元测试:在PC上运行了13个测试,使用“pio test -e native”执行,全部通过。测试内容包括报警阈值(含精确18°C和30°C)、双向循环的页面导航,以及四种ACTIVE/INACTIVE状态的验证。

静态分析:“pio check”(cppcheck)未发现高风险或中等风险问题。低风险发现已审查并解释说明。大多数是误报,因为该工具无法识别通过FreeRTOS或中断表调用的函数。

在Wokwi中的功能测试:全部10项测试均通过。这些测试包括改变温度、湿度和光照,双向旋转编码器,当温度超过30°C时触发警报并再次关闭,以及系统在15秒后进入非活动状态,并在检测到运动时唤醒。

故障实验:我故意破坏了系统以观察会发生什么:

移除MotionTask中的延迟导致其占用CPU,其他所有任务停止运行。

为DisplayTask设置最高优先级在此模拟器构建中未出现明显问题,但意味着屏幕重绘可能会延迟用户输入。

移除互斥锁(mutex)在当前模拟器版本中没有导致输出混乱,但在真实硬件上使用标准端口时打印操作将不安全,因此保留了互斥锁。

演示

运行系统:OLED 显示温度并配有报警横幅,串口日志则显示任务活动情况。

遇到的挑战

最大的挑战是让 FreeRTOS 能够在 Wokwi 上正常运行。固件已构建并启动,但没有任何任务运行。通过在串口上打印 FreeRTOS 的断言信息,我发现标准的 Cortex-M 端口依赖了一些 Wokwi STM32F103 模型不完全支持的处理器特性:中断优先级位,以及 FreeRTOS 用于切换任务的 SVC/PendSV 异常。

最终版本使用了由同学项目改编的、兼容 Wokwi 的 FreeRTOS 版本(下文注明)。我在其中修复了另外两个问题:现在它以模拟器中有效的方式阻断中断,并且单独保存每个任务的临界区计数,从而避免了系统冻结。

其他问题:DHT22 在每次读取时都会失败,直到将数据引脚设置为带上拉电阻的输入模式;此外,蜂鸣器需要移至另一个引脚,因为其原始引脚被 RTOS 定时器占用了。

本文编译自hackster.io

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭