代码仓库:https://github.com/Atropine33333/HRTTest
HRT 硬件组 2026 秋季招新试题的双题实现(两题共用一块外设扩展板):
- 题目 A:智能状态机小夜灯(三色 LED + 双按键 + 状态机 + 三击呼吸灯加分项)
- 题目 B:串口调速风扇 + NTC 温度上报(HEX)协议 + 图形化上位机)
| 按键 | 操作 | 功能 |
|---|---|---|
| KEY1(PA0) | 单击(按住 < 300ms) | 颜色循环:红 → 蓝 → 黄 |
| 双击(抬起间隔 < 300ms) | 进入/退出 0.5s 亮 + 0.5s 灭 闪烁 | |
| 三击(加分项) | 进入/退出呼吸灯:红 → 蓝 → 黄 → 三灯一起,每段一个完整呼吸 2s | |
| 四击 | 进入/退出跑马灯:亮度波在红/蓝/黄之间错开 1/3 周期流动 | |
| 长按(≥ 1s) | 点亮 / 关闭(任何模式直接生效) | |
| KEY1+KEY2 | 同时长按 ≥ 1s | 进入/退出"温度显示":<25℃ 黄灯、25~40℃ 蓝灯、≥40℃ 红灯(阈值可由上位机修改) |
| KEY2(PA1) | 单击 | 进入/退出报警:三灯全亮并屏蔽 KEY1,退出后恢复原灯效 |
- 按键识别:软件状态机(消抖 20ms)+ 连续点击计数窗口,无
delay死等; - 按压时长 300ms ~ 1s 之间不产生任何事件,长按触发后不再产生单击/双击;
- 呼吸亮度查 100 点升余弦表(20ms 步进),硬件 PWM(TIM2_CH2 / TIM3_CH1 / TIM3_CH2),运行时不做三角函数运算。
- 协议:
AA 55 | 命令 | 数据 | XOR 校验(校验 = 命令字与数据区异或,不收帧头),大端; - 命令:
0x01设占空比、0x02查温度、0x03查占空比、0x04查灯效状态(扩展); - 上报:每 500ms 自动上报
0x81(温度 0.1℃ + 占空比);灯效状态变化时主动上报0x84(扩展); - 占空比:16 位
0~65535线性映射到定时器 ARR,PWM 96kHz(96MHz / 1000); - 温度:NTC 10k/B=3950 + 10k 分压,ADC 8 点滑动平均,-20 ~ 80℃ 限幅;
- 风扇启动/升速带 100% 冲速(默认 300ms),越过低压起转死区;
- 温度闭环(板载):
0x07切换手动/自动,自动时 30℃ 以下停转、30~60℃ 按 30%~100% 线性映射、60℃ 以上满速; - 摩尔斯电码灯:
0x06帧携带"长度 + 文本",蓝灯按点/划闪出(单位 120ms,支持 A-Z / 0-9 / 空格)。
- 核心板:STM32F411CEU6 Black Pill(25MHz 晶振,HSE→PLL→96MHz);
- 外设板:自绘 PCB(嘉立创 EDA),原理图 / PCB / Gerber 见
../HRT_Test_HDWare。
| 功能 | 引脚 | 说明 |
|---|---|---|
| LED 红/蓝/黄 | PB3 / PB4 / PB5 | 串 470Ω,共地 |
| KEY1 / KEY2 | PA0 / PA1 | 10k 上拉 + 100nF 硬件消抖 |
| 风扇 PWM | PA8(TIM1_CH1) | 串 100Ω → AO3400 栅极(原理图含栅极 10k 下拉,实物板现状见下方说明) |
| NTC 采样 | PA2(ADC1_IN2) | 10k 分压,对地 100nF |
| 串口 | PA9 / PA10(USART1) | 115200 8N1,接 USB-TTL(3.3V) |
硬件现状与改动说明(重要):
打样版本(V1.0)的 R2 接在了 MOSFET 源极与 PGND 之间,源极没有直接接地,风扇电流被 10 kΩ 挡住而无法转动(完整排查过程见 docs/排错报告.md 案例 3)。最终处理与现状:
- 实物板:把 R2 短接为 0 Ω(两端堆锡或焊细铜丝),源极直接落地,风扇恢复正常(100% 时端电压 4.96 V,满转);
- 代价:实物板没有栅极下拉电阻(R2 被当成了接地跳线)。本项目里 PA8 在上电后很快被配置为推挽输出,实测功能一切正常;仅在"芯片复位、GPIO 尚未初始化"的窗口内栅极悬空,属于理论隐患,不影响交付功能;
- 不再重新打板:原理图已按修正方案更新(R2 移到栅极做下拉、源极直接接地),PCB / Gerber 保留打样版本 V1.0 作为记录,与实物板"R2 短接"的状态对应。后续若再打板,直接按修正后的原理图即可(另:实物 PCB 上存在而原理图尚未画入的 R9(GND–PGND 单点连接),下次打板一并补齐);
- 风扇 5V 供电与板子共地;
VIN排针不要接 12V(它与核心板 5V 相连)。
Core/Inc, Core/Src 应用代码
main.c 主循环调度 + 灯效模式状态机
key_task.c/.h 按键状态机(消抖/单击/双击/三击/长按)
led.c/.h 常亮/闪烁的 GPIO 点灯
breath.c/.h 呼吸灯:TIM2/TIM3 硬件 PWM + 亮度查表
temp_sensor.c/.h ADC + NTC 温度换算
fan_control.c/.h PWM 占空比 + 冲速启动
protocol.c/.h HEX 帧解析/发送(含错误恢复)
Drivers/ STM32F4 HAL + CMSIS
cmake/, CMakeLists.txt CMake + Ninja 构建
SerialGUI.py 上位机:实时曲线 + 自动映射 + 日志(Tkinter + pyserial + matplotlib)
SerialProbe.py 原始串口探针:绕过 GUI 直接看 HEX / 发指令
ProtocolTest.py 协议健壮性黑盒测试(坏校验/粘包/半包/压力)
docs/ 排错报告、人机协作报告、提交记录
package.py 一键打包提交物
工程用 CMake + Ninja + arm-none-eabi-gcc(STM32CubeCLT 1.22.0)构建,工具链路径在 CMakePresets.json / CMakeLists.txt 里(Windows):
cmake --preset default
cmake --build build
产物:build/HRT_Test.elf,用 ST-Link / STM32CubeProgrammer 烧录即可。
python SerialGUI.py # 图形界面:串口连接、曲线、自动映射、日志保存
python SerialProbe.py COM3 # 只看原始帧(排查链路)
python SerialProbe.py COM3 --send # 每秒查询温度
python SerialProbe.py COM3 --duty 50 # 直接设 50% 占空比
python ProtocolTest.py COM3 # 协议健壮性测试(输出 PASS/FAIL)
GUI 说明:
- 温度 / 占空比 30s 实时曲线;
- Auto Mapping:设定温度区间与占空比区间(如 25℃→30%、60℃→100%),勾选后按上报温度自动下发
SET_DUTY,第三张图实时显示映射线与工作点; - Night Lamp 面板:实时显示灯效状态文字(solid/blink/breathe/temp/marquee、ON/OFF、ALARM),旁边可直接修改温度显示阈值(Apply 下发
0x08,Query 读回0x86); - Morse Text:输入文字点 "Send Morse",板子会用蓝灯把电码闪出来;
- 风扇控制默认用 GUI 的 Auto Mapping;板载温度闭环(
0x07)在固件里仍然可用,GUI 上的开关默认隐藏(SHOW_MCU_AUTO改True可显示); - 日志区可保存为
serial_log_*.txt; - 若 MCU 开启了文本日志(见下),日志区不受影响(非帧字节会被自动跳过)。
| 方向 | 命令 | 数据 | 说明 |
|---|---|---|---|
| 上位机 → MCU | 0x01 |
2B 占空比(大端) | 设 PWM,0x7FFF=50% |
0x02 |
— | 查询温度 → 0x82 |
|
0x03 |
— | 查询占空比 → 0x83 |
|
0x04 |
— | 查询灯效状态 → 0x84 |
|
0x05 |
— | 查询风扇模式 → 0x85 |
|
0x06 |
1B 长度 + N 字符 | 摩尔斯电码(N ≤ 16) | |
0x07 |
1B 模式 | 风扇 0=手动 / 1=温度闭环 → 0x85 |
|
0x08 |
2B + 2B(0.1℃) | 温度显示阈值:黄灯上限、蓝灯上限 → 0x86 |
|
0x09 |
— | 查询温度显示阈值 → 0x86 |
|
| MCU → 上位机 | 0x81 |
2B 温度 + 2B 占空比 | 每 500ms 主动上报 |
0x82 |
2B 温度(0.1℃) | 温度查询回复 | |
0x83 |
2B 占空比 | 占空比查询回复 | |
0x84 |
1B 模式 + 1B 颜色 + 1B 标志 + 1B 呼吸相位 | 灯效状态:模式 0/1/2/3/4=常亮/闪烁/呼吸/温度显示/跑马灯,颜色 0/1/2=红/蓝/黄,bit0=亮灭 bit1=报警,相位 0/1/2/3=红/蓝/黄/三灯一起(非呼吸时 0xFF),呼吸每换相位都会上报 | |
0x85 |
1B 模式 + 2B 目标占空比 | 风扇模式与目标值 | |
0x86 |
2B + 2B(0.1℃) | 温度显示阈值(修改成功后回读) |
文本状态日志:app_config.h 中 LED_STATE_TEXT_LOG = 1 时,灯效每次变化会额外发一行
[LED] mode=solid color=red on=1 alarm=0;呼吸模式下为 [LED] mode=breathe phase=yellow on=1 alarm=0,
便于串口助手直接观察状态机(非帧数据,不影响协议解析)。
| 现象 | 结论 |
|---|---|
| 复位/烧录后蓝灯微亮,LED 两端约 2.5V | PB4 是 JTAG NJTRST,芯片复位默认带内部上拉(~40kΩ),经 470Ω 注入约 20µA 电流。程序运行后 MX_GPIO_Init 配成推挽输出即消失,非故障 |
| 5V 风扇在 50% 占空比下不转 | 该风扇欠压保护阈值接近 5V,只能满速或停(详见排错报告第 3 条);固件仍按标准映射输出 PWM,可用万用表在 PA8 观察平均电压 |
| 双击响应有约 300ms 延迟 | 为了让"单击/双击/三击"判定可靠,统一在 300ms 连续点击窗口结束后发事件(KEY_MULTI_CLICK_MS 可调) |
python package.py # 生成 dist/HRT_Test_提交包_YYYYMMDD/
# ├─ 源码/HRT_Test_src.zip
# ├─ 上位机/SerialGUI.py, SerialProbe.py, ProtocolTest.py
# ├─ 硬件资料/原理图.pdf, PCB.png, Gerber.zip
# └─ 报告/README.md, 排错报告.md, 人机协作报告.md
排错报告、人机协作报告与完整提交记录见 docs/。