news 2026/10/14 19:04:55

基于STM32F429的智能家居系统源码:温湿度、密码锁与超声波测距实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32F429的智能家居系统源码:温湿度、密码锁与超声波测距实战

简介:本资源为基于STM32F429的智能家居系统完整设计资料,面向嵌入式开发学习者与电子设计竞赛参赛者,帮助掌握多模块协同的智能家居方案。系统集成温湿度检测与超限预警、红外识人安防、超声波倒车雷达测距以及板载按键密码锁等功能,通过蓝牙与手机端交互,实现远程监控与实时预警。压缩包共159个文件,约7.44MB,包含12个c源文件与15个h头文件承载核心逻辑,37个d依赖文件、37个o目标文件及35个crf编译中间文件记录构建过程,另有axf、hex可执行文件、uvprojx工程文件与map映射文件,便于直接编译烧录与调试。目前已有1496人学习下载。读者可获取完整工程源码、外设驱动模块(如timer、usart、key、light等)及可运行固件,适合作为课程设计、毕业设计或竞赛项目的参考模板,快速理解STM32F429在智能家居场景下的软硬件协同开发思路。

1. 从一块 F429 开发板说起:这套智能家居源码到底能跑出什么

前阵子帮一个做毕设的学弟看代码,他手里那块 STM32F429 开发板已经吃灰两个月,屏幕上还停在“请刷卡”的界面。他的诉求很典型:想用一块板子把温湿度检测、密码锁、超声波测距三件事串起来,做成一个能演示、能答辩、能写进简历的智能家居系统。这套基于 STM32F429 的智能家居系统设计源码包,解决的正是这个场景——它不是那种只点亮一颗 LED 的入门例程,而是把环境感知、身份验证、距离检测三条链路整合到同一块主控上,配合外设驱动和任务调度,形成一个可交互的嵌入式小系统。适合谁?正在做嵌入式课程设计、毕业设计,或者想从裸机点灯进阶到多外设协同的开发者。你拿到手之后,需要的不只是烧录,而是理解它怎么把 DHT11、矩阵键盘、HC-SR04 这些常见模块组织成一个有状态机的系统。下面我按实际拆包和复现的顺序,把关键环节讲透。

2. 硬件链路与选型逻辑:为什么是 F429 而不是 F103

2.1 主控资源与外设分配

STM32F429 属于 Cortex-M4 内核,主频能到 180MHz,自带 LCD 控制器、FMC 外部存储接口和足够多的定时器与串口。这套智能家居系统里,温湿度检测走的是单总线协议,密码锁依赖矩阵键盘扫描和 Flash 读写,超声波测距需要输入捕获定时器测回波高电平时间。三件事同时跑,F103 的 72MHz 和有限 RAM 会有点吃力,尤其是你要在本地做密码存储和 LCD 刷新的时候。F429 的 SRAM 有 256KB,Flash 有 2MB,足够放字库和用户密码表,不用外扩就能跑起来。

常见做法是把 DHT11 接在普通 GPIO 上,用微秒级延时读时序;矩阵键盘占 8 个 GPIO,行扫描列读取;HC-SR04 的 Trig 接普通输出,Echo 接带输入捕获的定时器通道。这样分配的好处是互不抢资源,中断优先级也好排。我一般会把超声波捕获中断设成最高,因为回波脉宽是微秒级,被别的中断打断就容易测出离谱的距离值。

2.2 三个功能模块的协同方式

系统上电后先初始化时钟树、GPIO、定时器和串口,然后进入主循环。主循环里做三件事:周期读温湿度、扫描键盘状态、触发超声波测距。密码锁不是一直占着 CPU,而是用状态机管理——等待输入、校验、开锁、超时锁定四个状态。温湿度每 2 秒读一次,因为 DHT11 本身采样率就低,读太快没意义。超声波测距可以按按键触发,也可以定时触发,看你的演示需求。

这里有个设计取舍:要不要上 RTOS。这套源码是裸机前后台架构,主循环加中断。对于三个功能来说够用,代码量小,调试直观。如果你后面要加 WiFi 模块或者 OLED 多级菜单,再考虑移植 FreeRTOS 也不迟。选型理由很简单——先跑通,再优化。

2.3 关键引脚分配表

功能模块引脚外设资源备注
DHT11 数据PG9普通 GPIO需 4.7K 上拉
矩阵键盘行PD0-PD3GPIO 输出行扫描
矩阵键盘列PD4-PD7GPIO 输入内部上拉
HC-SR04 TrigPB6GPIO 输出10us 高电平触发
HC-SR04 EchoPA0TIM2_CH1输入捕获
串口调试PA9/PA10USART1115200 波特率
状态 LEDPB0/PB1GPIO 输出指示运行与开锁

这张表是我实际接线时核对过的,不同开发板丝印可能不一样,以你手头原理图为准。注意 DHT11 的上拉电阻不能省,否则读出来的全是 0。

3. 温湿度与超声波驱动:时序、捕获和滤波怎么写

3.1 DHT11 单总线时序的代码实现

DHT11 用一根线完成双向通信,靠的是拉低拉高的持续时间区分 0 和 1。F429 主频高,用 HAL 库的 HAL_Delay 做微秒延时精度不够,得用 DWT 或者定时器做忙等。下面这段是常见的读一次温湿度的流程:

// DHT11 读一个字节,返回 8 位数据 uint8_t DHT11_ReadByte(void) { uint8_t i, byte = 0; for (i = 0; i < 8; i++) { // 等待 50us 低电平结束 while (DHT11_DATA_IN() == 0); DHT11_DelayUs(40); // 延时 40us 后采样 byte <<= 1; if (DHT11_DATA_IN() == 1) { byte |= 1; // 等待高电平结束 while (DHT11_DATA_IN() == 1); } } return byte; }

逻辑说明:每 bit 先等低电平过去,然后延时 40us 再读线电平。如果还是高,说明这一位是 1,否则是 0。参数上,40us 是个经验值,DHT11 的 0 位高电平持续 26-28us,1 位持续 70us,取中间偏后的时间采样最稳。读完之后要校验和,前四个字节相加取低 8 位等于第五字节才算有效。失败时别急着报错,连续读三次取有效值,这是血泪经验——DHT11 偶尔抽风很正常。

3.2 超声波输入捕获与距离换算

HC-SR04 的 Echo 高电平持续时间就是声波往返时间。用 TIM2 通道 1 做输入捕获,上升沿记下 CCR1,下降沿再记一次,差值乘以定时器周期就是时间。距离 = 时间 × 声速 / 2。声速取 340m/s,换算成 cm/us 就是 0.034。

// 输入捕获中断回调,处理上升沿和下降沿 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { if (capture_edge == 0) { // 上升沿,记录起始值 capture_start = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); capture_edge = 1; // 切换为下降沿捕获 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); } else { // 下降沿,计算差值 capture_end = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); capture_edge = 0; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); if (capture_end > capture_start) { pulse_width = capture_end - capture_start; } else { // 定时器溢出处理 pulse_width = (0xFFFF - capture_start) + capture_end; } distance_cm = pulse_width * 0.034 / 2; } } }

参数说明:TIM2 预分频设成 84-1,这样计数频率是 1MHz,每个计数 1us,算距离直接乘 0.034 就行。注意溢出情况,如果回波时间超过定时器周期,得加上溢出次数。实际测距范围 2cm 到 400cm,超出范围读数是无效的,代码里加个判断过滤掉。

3.3 中值滤波去抖动

超声波测距有个玄学问题:同一个位置连续测,偶尔跳出一个离谱值。常见做法是连测五次,去掉最大最小,剩下三个取平均。这个滤波放在应用层做,不占中断时间。

// 中值滤波,输入五次测量值,返回有效距离 float Ultrasonic_Filter(float *buf, uint8_t len) { float temp; // 冒泡排序 for (uint8_t i = 0; i < len - 1; i++) { for (uint8_t j = 0; j < len - 1 - i; j++) { if (buf[j] > buf[j + 1]) { temp = buf[j]; buf[j] = buf[j + 1]; buf[j + 1] = temp; } } } // 取中间值 return buf[len / 2]; }

这段代码简单但有效,五次采样排序后取中位数,能滤掉大部分脉冲干扰。如果你要更平滑,可以再加一阶低通滤波,但响应会变慢,看演示需求取舍。

4. 密码锁状态机与数据存储:别把密码写死在代码里

4.1 矩阵键盘扫描与键值映射

4x4 矩阵键盘用行扫描法:每行依次输出低电平,读列线状态,哪一列为低就说明哪个键按下。扫描周期 10ms 左右,加 20ms 消抖。键值映射成 0-9、确认、取消、修改等功能键。

// 扫描一次键盘,返回键值,无按键返回 0xFF uint8_t Keypad_Scan(void) { uint8_t row, col, key = 0xFF; for (row = 0; row < 4; row++) { // 当前行输出低,其余行高 KEYPAD_ROW_PORT->ODR = ~(1 << row) & 0x0F; for (col = 0; col < 4; col++) { if ((KEYPAD_COL_PORT->IDR & (1 << col)) == 0) { HAL_Delay(20); // 消抖 if ((KEYPAD_COL_PORT->IDR & (1 << col)) == 0) { key = key_map[row][col]; // 等待按键释放 while ((KEYPAD_COL_PORT->IDR & (1 << col)) == 0); return key; } } } } return key; }

逻辑说明:行线依次拉低,列线读输入。key_map 是个二维数组,把行列坐标映射成实际键值。注意等待释放那一步不能省,否则一次按下会触发多次。

4.2 密码存储与校验流程

密码不要写死在代码里,否则改密码就得重新编译烧录。常见做法是存在 F429 内部 Flash 的某个扇区,上电读出来,修改时擦除再写。F429 的 Flash 扇区比较大,注意别和程序区冲突,选最后一个扇区。

// 从 Flash 读取密码,地址为自定义存储区 void Password_Read(uint8_t *pwd) { uint32_t addr = PWD_FLASH_ADDR; for (uint8_t i = 0; i < 6; i++) { pwd[i] = *(uint8_t *)(addr + i); } } // 校验输入密码 uint8_t Password_Verify(uint8_t *input) { uint8_t stored[6]; Password_Read(stored); for (uint8_t i = 0; i < 6; i++) { if (input[i] != stored[i]) { return 0; // 密码错误 } } return 1; // 密码正确 }

参数说明:PWD_FLASH_ADDR 要按 F429 的扇区边界对齐,比如 0x080E0000 这种。写入前先解锁 Flash,擦除扇区,再按半字写入。校验时逐位比较,别用 strcmp,因为密码可能包含 0x00。

4.3 状态机管理开锁与锁定

密码锁不是一次校验就完事,要有状态管理。等待输入、校验中、开锁、错误计数、锁定这几个状态用 switch-case 切换。连续错三次锁定 30 秒,这个逻辑写在主循环里,用系统滴答计时。

// 密码锁状态机,在主循环中调用 void Lock_StateMachine(void) { static uint8_t state = STATE_IDLE; static uint8_t error_count = 0; static uint32_t lock_timer = 0; uint8_t key = Keypad_Scan(); switch (state) { case STATE_IDLE: if (key != 0xFF) { input_buffer[input_index++] = key; if (input_index >= 6) { state = STATE_VERIFY; } } break; case STATE_VERIFY: if (Password_Verify(input_buffer)) { state = STATE_UNLOCK; error_count = 0; } else { error_count++; if (error_count >= 3) { state = STATE_LOCKED; lock_timer = HAL_GetTick(); } else { state = STATE_IDLE; } } input_index = 0; break; case STATE_UNLOCK: // 开锁动作,延时后自动回 IDLE HAL_GPIO_WritePin(LOCK_GPIO_Port, LOCK_Pin, GPIO_PIN_SET); HAL_Delay(3000); HAL_GPIO_WritePin(LOCK_GPIO_Port, LOCK_Pin, GPIO_PIN_RESET); state = STATE_IDLE; break; case STATE_LOCKED: if (HAL_GetTick() - lock_timer > 30000) { state = STATE_IDLE; error_count = 0; } break; } }

这段状态机把输入、校验、开锁、锁定串起来,逻辑清晰,也方便加新状态。注意开锁延时用 HAL_Delay 会阻塞主循环,如果温湿度读取要求实时性,可以改成非阻塞计时。

5. 避坑与排查:那些让你怀疑板子坏了的瞬间

5.1 DHT11 一直返回 0 或 255

现象:串口打印温湿度全是 0 或者 255,偶尔正常。原因:上拉电阻没接,或者微秒延时不准,或者采样时机偏了。解决:先确认数据线有 4.7K 到 10K 上拉,然后用示波器看时序,没有示波器就调整延时参数,把 40us 改成 30us 或 50us 试。另外 DHT11 上电后要等 1 秒再读,刚上电就读容易失败。

5.2 超声波测距跳变严重

现象:静止物体前,距离值在 10cm 到 200cm 之间乱跳。原因:回波信号被中断打断,或者定时器溢出没处理,或者电源纹波大。解决:把输入捕获中断优先级设最高,检查溢出计数逻辑,给 HC-SR04 的 VCC 加 100uF 电容。软件上加中值滤波,五次采样取中间值,基本能稳住。

5.3 矩阵键盘按键不灵敏或连击

现象:按一下出好几个字符,或者某些键没反应。原因:消抖时间不够,或者等待释放的循环被其他中断打断。解决:消抖延时加到 20-30ms,等待释放时加超时退出,避免死等。另外检查行线输出和列线输入的 GPIO 模式,列线要设成上拉输入。

5.4 密码写入 Flash 后读出来不对

现象:修改密码后重启,密码没变或者变成乱码。原因:Flash 擦除不完整,或者写入地址没对齐,或者写之前没解锁。解决:确认擦除的是整个扇区,写入按半字或字对齐,解锁和上锁成对出现。F429 的 Flash 编程手册里对电压和时序有要求,别在低电压下写。

5.5 LCD 刷新导致系统卡顿

现象:屏幕刷新时按键响应变慢,超声波测距也不准了。原因:LCD 刷屏占用大量 CPU 时间,阻塞了主循环。解决:用 DMA 刷屏,或者降低刷新频率,只刷新变化区域。如果用的是 FSMC 接口,检查时序配置,读操作比写操作慢很多,尽量少读。

6. 进阶技巧:把三个功能串成可演示的完整流程

6.1 用串口命令统一调度

调试阶段最烦的是改一个参数就要重新烧录。我一般会在串口上做一个简单命令解析,输入th读温湿度,dist测距离,pwd改密码,lock手动开锁。这样演示的时候不用碰板子,串口助手就能操作。

// 串口命令解析,在主循环中轮询 void UART_Command_Handler(void) { if (uart_rx_flag) { uart_rx_flag = 0; if (strcmp((char *)uart_rx_buf, "th") == 0) { printf("Temp: %d C, Humi: %d %%\r\n", temperature, humidity); } else if (strcmp((char *)uart_rx_buf, "dist") == 0) { printf("Distance: %.1f cm\r\n", distance_cm); } else if (strncmp((char *)uart_rx_buf, "pwd ", 4) == 0) { Password_Write((uint8_t *)&uart_rx_buf[4]); printf("Password updated\r\n"); } memset(uart_rx_buf, 0, sizeof(uart_rx_buf)); } }

参数说明:串口接收用中断,收到回车换行认为一帧结束。命令字符串比较用 strcmp,注意缓冲区要清零。这个命令集可以根据你的演示脚本扩展,比如加个auto进入自动循环模式。

6.2 自动演示模式与状态指示

答辩或演示时,手动操作容易出错。加一个自动模式:上电后 LED 慢闪表示待机,按键触发后依次读温湿度、测距、等待密码输入,每一步用不同 LED 状态指示。这样即使紧张也不会乱。

LED 状态含义触发条件
慢闪 1Hz系统待机上电初始化完成
快闪 5Hz等待密码输入按键触发
常亮开锁成功密码校验通过
双闪测距中超声波触发
熄灭锁定状态连续错误三次

这张表是我实际演示时用的,LED 状态一目了然,评委不用看串口就知道系统在干什么。

6.3 从裸机到 RTOS 的迁移边界

如果你后面要加 WiFi、OLED 菜单、语音模块,裸机前后台会越来越吃力。这时候可以考虑移植 FreeRTOS,把温湿度读取、键盘扫描、超声波测距各放一个任务,用队列通信。但注意,任务优先级和栈大小要调,超声波捕获中断里不要调用 RTOS API,用信号量通知任务处理。迁移不是必须的,但知道边界在哪,心里有底。

从那以后我每次拿到新的外设模块,都强制先写一个最小测试工程,把时序和电气特性摸清楚再往系统里集成。这套 F429 智能家居源码的价值不在于代码多复杂,而在于它把三个典型模块的驱动和状态管理串成了一条可复现的链路。你照着接线、烧录、调参,遇到问题翻回第 5 章对一遍,基本能跑通。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 19:03:43

VTOL 仿真避坑指南:深入解析 AirSim AirLib 的 vtol 分支

简介&#xff1a;VTOL-AirLib是针对AirSim中AirLib库的扩展实现&#xff0c;专为垂直起降&#xff08;VTOL&#xff09;与倾转旋翼机仿真而设计&#xff0c;适合具备C基础、希望深入研究无人机飞控或eVTOL空中出租车控制算法的开发者与研究人员&#xff0c;可用于控制策略设计、…

作者头像 李华
网站建设 2026/10/14 19:03:38

STM32F407实现1Hz分辨率FFT:频率、谐波、THD与相位差测量

简介&#xff1a;基于STM32F407的FFT频谱分析工程&#xff0c;实现1Hz频率分辨率&#xff0c;适用于信号频率测量、谐波分析、失真度评估与双波形相位差测量&#xff0c;面向嵌入式开发、音频处理与电力监测等方向的工程师和进阶学习者。工程利用Cortex-M4硬件浮点单元完成数字…

作者头像 李华
网站建设 2026/10/14 19:03:21

航迹数据转换实践:CSV转GPX/KML/GeoJSON及坐标纠偏指南

简介&#xff1a;这是一款面向航空航海、GIS分析及户外运动等场景的航迹数据转换工具&#xff0c;主要用于解决GPS轨迹在GPX、KML、TCX等格式间的互转与兼容问题&#xff0c;帮助用户在不同设备、地图服务或分析软件之间顺畅迁移轨迹数据。资源包共4个文件&#xff0c;整体仅84…

作者头像 李华
网站建设 2026/10/14 19:02:17

OpenClaw技能开发全链路指南:从概念设计到生产部署的实战方法论

做 OpenClaw 技能开发这摊事&#xff0c;前前后后也有大半年时间了。从最开始拿示例技能改着玩&#xff0c;到后来在团队内部把一个技能真正跑上生产环境、接进核心业务链路&#xff0c;中间踩过的坑、趟出来的流程&#xff0c;我觉得值得整理成一篇完整的东西。这篇文章想跟你…

作者头像 李华
网站建设 2026/10/14 19:02:10

从机器人原型到量产:研发团队分享ZED视觉系统在实际项目中的使用心得

一、人形机器人真正难的不是“看见”&#xff0c;而是让感知跟得上全身运动​二维画面并不等于可执行的空间信息。​人形机器人需要在三维空间里完成移动、避障、抓取、放置和人与环境交互&#xff0c;仅知道“目标在哪里”往往不够&#xff0c;还要同时获得距离、尺度、自由空…

作者头像 李华