基于STM32毕业设计题目的新手入门实战指南:从选题到固件开发的完整路径
摘要:很多电子/自动化专业的同学第一次做 STM32 毕设时,都会卡在“点灯”和“跑通串口”之间——代码能编译,板子没反应,仿真器连不上,网上例程又各说各话。本文把我自己踩过的坑、帮学弟调过的板子、还有实验室老师反复提醒的细节,打包成一份“新手通关地图”。读完你可以直接上手一个“DHT11 温湿度采集 + UART 上传”的毕设原型,并知道以后往哪个方向继续加功能。
1. 新手常见痛点:90% 的“跑不通”都绕不开这三点
- 时钟树配错:外部晶振 8 M 却按 25 M 算,结果 UART 波特率 115200 实测 9600,上位机全是乱码。
- 中断优先级乱设:SysTick 优先级比外部中断低,导致采样周期被按键扫描打乱,数据飘得怀疑人生。
- 调试工具链不熟:ST-Link 固件没升级,CubeProgrammer 提示“连接超时”,折腾一下午才发现是 USB 线太长。
一句话总结:先让板子“喘口气”——时钟、电源、SWD 三线正常,再谈功能。
2. 三种开发方式对比:CubeMX+HAL、标准外设库、LL 库
| 维度 | CubeMX+HAL | 标准外设库 (SPL) | LL 库 |
|---|---|---|---|
| 上手速度 | 最快,图形化配引脚 | 中等,寄存器封装 | 偏慢,接近寄存器 |
| 代码体积 | 最大,易超 64 KB | 中等 | 最小,可 < 32 KB |
| 移植性 | 跨 STM家族最好 | F1 专用,已停止维护 | 与 HAL 并存,需手动 |
| 实时性 | 一般,嵌套抽象层 | 较好 | 最好,可单周期操作 |
| 社区资料 | 最新,官方主推 | 老旧,但例程极多 | 少,需要看手册 |
毕设场景建议:
- 想快速出原型,用 CubeMX+HAL;
- 若导师要求“裸机”或 Flash <64 KB,可 LL 库手写;
- SPL 不建议新课题再用,2025 年后基本找不到官方例程更新。
3. 典型毕设实战:DHT11 温湿度采集 + UART 上传
3.1 硬件准备
- 核心板:STM32F103C8T6 最小系统(某宝 20 元)
- 传感器:DHT11 单总线温湿度模块(3.3 V 兼容)
- 下载器:ST-Link V2(山寨版即可)
- 串口模块:CH340G USB-TTL,波特率 115200
3.2 CubeMX 配置步骤
- 新建工程,选 MCU
STM32F103C8Tx - 配置高速外部时钟
HSE = 8 MHz,PLL 倍频到72 MHz - 启用
USART1全局中断,波特率 115200,长度 8N1 - 选
PA0为GPIO_Output驱动 DHT11,重命名为DHT11_PIN - 打开
SWD调试口,保持SysTick1 ms 中断 - 生成代码,IDE 选
STM32CubeIDE(自带 Makefile,后期 CI 方便)
3.3 代码框架(Clean Code 版)
/* main.c 节选 */ #include "dht11.h" #include "uart_app.h" static void SystemClock_Config(void); static void MX_GPIO_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); DHT11_t sensor = {.port = GPIOA, .pin = GPIO_PIN_0}; char txbuf[64]; while (1) { if (DHT11_Read(&sensor) == DHT11_OK) { int len = snprintf(txbuf, sizeof(txbuf), "{\"T\":%d,\"H\":%d}\r\n", sensor.temp, sensor.rh); UART_Send(txbuf, len); } HAL_Delay(2000); /* 2 s 采样,留足 1 s 稳定 */ } }/* dht11.c 核心时序 */ #define DHT11_DELAY_US(x) delay_us(x) /* 用 TIM2 做 us 定时 */ DHT11_Status DHT11_Read(DHT11_t* d) { uint8_t bits[5] = {0}; /* 主机拉低 18 ms > 启动信号 */ HAL_GPIO_WritePin(d->port, d->pin, GPIO_PIN_RESET); HAL_Delay(18); HAL_GPIO_WritePin(d->port, d->pin, GPIO_PIN_SET); DHT11_DELAY_US(30); /* 切换到输入模式,等待从机响应 80 us 低电平 */ GPIO_InputMode(d->port, d->pin); if (!WAIT_LOW(80)) return DHT11_TIMEOUT; if (!WAIT_HIGH(80)) return DHT11_TIMEOUT; /* 读 40 bit 数据 */ for (int i = 0; i < 40; i++) { if (!WAIT_LOW(50)) return DHT11_TIMEOUT; uint32_t h_time = 0; while (HAL_GPIO_ReadPin(d->port, d->pin) == GPIO_PIN_SET) { h_time++; DHT11_DELAY_US(1); if (h_time > 100) return DHT11_TIMEOUT; } bits[i / 8] |= (h_time > 40) ? (1 << (7 - i % 8)) : 0; } /* 校验 */ if (bits[4] != (bits[0] + bits[1] + bits[2] + bits[3])) return DHT11_CRC_ERR; d->rh = bits[0]; d->temp = bits[2]; return DHT11_OK; }要点注释:
- 单总线对时序敏感,关中断读数最稳;
- 用
snprintf拼 JSON,方便后期直接接 Python 上位机;- 把底层时序与业务逻辑分层,后面换传感器只改
dht11.c。
4. 实际约束:低功耗 & Flash 占用
- 低功耗:毕设若要求电池跑 3 个月,把
SysTick关掉,用STOP模式 + RTC 唤醒,电流从 12 mA 降到 120 μA。 - Flash:HAL 版工程
-Og优化后 38 KB,若导师限定 64 KB 以内,可开-Os并关掉没用到的HAL_XXX_MODULE。 - 看门狗:IWDG 一旦开就关不掉,喂狗间隔要 > 最长单总线读取时间,否则 DHT11 读数到一半就复位。
5. 生产环境避坑指南
- SWD 调试失效:BOOT0 下拉 10 kΩ 到地,复位电路加 100 nF 电容,山寨 ST-Link 换新固件。
- 看门狗误触发:中断里别喂狗,主循环设置“心跳标志位”,确保只在业务空闲时喂。
- 电源噪声:DHT11 数据线离 8 MHz 晶振远点,走地孔包一圈,采样前 UART 先暂停发送,减少突发电流。
- 复位脚悬空:部分最小系统板把 NRST 引到排针,手一碰就复位,直接焊 100 kΩ 上拉到 3.3 V。
6. 下一步:把最小系统变成“可扩展平台”
- 把 PA9/PA10 留给蓝牙模块(JDY-31),手机直接看温湿度曲线。
- 多路 ADC 采集电池电压,低于 3.2 V 自动进入深度休眠。
- 上 FreeRTOS:把传感器、OLED 显示、数据上传拆成三个线程,毕业答辩时“实时多任务”一亮,老师秒懂。
结尾碎碎念:
第一次做 STM32 毕设,别急着堆功能,先让板子“稳定呼吸”——时钟对、串口通、调试口随时能打断点。
今晚就找个最小系统,把上面的 DHT11 例程跑一遍,明天你会突然发现:选题报告里那些“高大上”功能,其实就是在今晚的代码里多写几个if和多接几根线。祝你调试顺利,早点睡觉,毕业不熬夜!