资讯动态

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

发布时间:2026/10/14 19:05:01 来源:尧图企业网站定制
简介本资源为基于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 而不是 F1032.1 主控资源与外设分配STM32F429 属于 Cortex-M4 内核主频能到 180MHz自带 LCD 控制器、FMC 外部存储接口和足够多的定时器与串口。这套智能家居系统里温湿度检测走的是单总线协议密码锁依赖矩阵键盘扫描和 Flash 读写超声波测距需要输入捕获定时器测回波高电平时间。三件事同时跑F103 的 72MHz 和有限 RAM 会有点吃力尤其是你要在本地做密码存储和 LCD 刷新的时候。F429 的 SRAM 有 256KBFlash 有 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-28us1 位持续 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 章对一遍基本能跑通。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑