资讯详情

资讯详情

STM32F103智能风扇DIY:HC-SR501人体感应+PWM调速实战

简介这是一套面向嵌入式初学者与电子设计竞赛选手的STM32智能风扇完整工程资料以STM32F103为核心结合PWM调速、HCSR501人体红外感应与DHT11温湿度采集实现有人自动开启、无人自动关闭及按环境温湿度智能调节转速的风扇控制系统适合课程设计、毕业设计与DIY项目参考。压缩包共315个文件约16.96MB包含50个c源文件与52个h头文件构成的主程序框架、55个crf与56个o等编译中间文件、2个hex可执行文件及uvprojx、uvoptx等Keil工程配置另附2份doc说明、2份pdf资料与2张jpg实物图便于直接编译下载与对照理解。目前已有8891人学习下载热度较高。资料完整呈现了从传感器数据采集、PWM占空比调节到人机交互的软硬件实现思路读者可据此快速搭建实验环境、复现功能并在此基础上扩展改进。1. 从一把“会自己转头”的风扇说起STM32F103 智能风扇到底在做什么夏天最烦的不是热是半夜被冻醒还得摸黑找遥控器。我去年给卧室装了一台自己攒的 STM32 智能风扇核心就三块STM32F103 最小系统板、HC-SR501 人体红外模块、PWM 驱动的直流风扇。它干的事很朴素——检测到人进屋就转人走十分钟自动停温度高了自动加档全程不用动手。这套方案在宿舍、工位、小仓库都跑得通成本不到五十块比买成品风扇多了“感知”和“联动”两个能力。标题里几个词其实是一条完整链路STM32F103 是主控STM32PWM风扇 是执行方式hcsr501 是感知入口stm32风扇模块 是把这三者拼起来的硬件形态。热搜里 stm32f103最小系统、stm32f103 cubemx 定时器3、stm32f103 系列参考手册(rm0008) 这些词恰好对应了从选型到写代码的必经环节。这篇笔记就按我实际调试的顺序把选型、接线、PWM 配置、HC-SR501 触发逻辑、避坑和进阶调参讲透新手能照着复现熟手能直接拿去改参数。2. 硬件选型与接线STM32F103、HC-SR501 和风扇怎么接才不翻车2.1 为什么是 STM32F103 而不是别的选 STM32F103C8T6 最小系统板理由很实际72MHz 主频跑风扇控制绰绰有余4 个通用定时器里 TIM3 能出 4 路 PWM价格便宜到十块钱以内资料多到搜“stm32f103最小系统”能翻出几百篇。热搜里 stm32f103 pack包下载、stm32芯片包安装 这些词说明很多人卡在环境搭建我建议直接用 Keil MDK 加 ST-Link别一上来折腾 VSCode 配置 stm32 开发环境先把灯点亮再说。风扇这边分两种两线直流风扇只能调电压三线或四线 PWM 风扇才能直接吃 PWM 信号。我手上是四线风扇红线 12V、黑线 GND、黄线测速、蓝线 PWM。如果你只有两线风扇就得加一个 MOS 管模块用 PWM 控制栅极本质还是 STM32PWM风扇 的思路。HC-SR501 是经典的人体红外模块工作电压 4.5V 到 20V输出 3.3V 高电平和 STM32 的 IO 直连没问题。它有两个可调电位器一个调延时0.3 秒到 5 分钟一个调灵敏度3 米到 7 米。注意它上电后有 30 到 60 秒初始化时间这段时间输出会乱跳代码里必须加延时等待。2.2 接线表与供电注意模块引脚接到 STM32F103说明HC-SR501VCC5V模块内部有稳压5V 供电更稳HC-SR501GNDGND共地HC-SR501OUTPA0配置为浮空输入或下拉输入四线风扇PWMPA6 (TIM3_CH1)需要 3.3V 电平风扇 PWM 线一般兼容四线风扇GNDGND共地四线风扇12V外部 12V不要从 STM32 取电四线风扇测速PA1可选用定时器捕获测转速供电是第一个大坑。STM32 的 5V 引脚从 USB 取电带不动风扇必须外接 12V 电源而且 12V 和 3.3V 要共地。热搜里 stm32f103 5v转3.3v电路 这个词很热但这里不需要你转最小系统板自带 AMS1117你只要保证外部 12V 不灌进 3.3V 就行。提示HC-SR501 输出高电平是 3.3V但如果你买到的模块是 5V 供电输出 5V 电平直接接 PA0 会烧引脚。用万用表量一下 OUT 对地电压超过 3.6V 就加一个分压电阻。2.3 最小系统板的启动模式STM32F103 的 BOOT0 和 BOOT1 决定启动方式。正常跑程序时 BOOT0 接 GNDBOOT1 任意。如果你用 ST-Link 下载后程序不跑先查这两个跳线帽。热搜里 stm32芯片第一脚怎么确认 这种问题在最小系统板上看圆点或者丝印缺口就行别接反 VCC 和 GND反接必烧。3. 用 CubeMX 配 TIM3 PWM从 0 到风扇转起来的最小工程3.1 时钟树和 TIM3 参数怎么算打开 CubeMX选 STM32F103C8T6先配 RCC 为外部晶振时钟树里 HSE 选 8MHzPLL 倍频到 72MHz。TIM3 挂在 APB1 上APB1 预分频系数为 2 时定时器时钟是 72MHz。PWM 频率计算公式Fpwm 72MHz / ((Prescaler 1) * (Period 1))。四线风扇的 PWM 推荐频率是 25kHz人耳听不到啸叫。我取 Prescaler 71Period 39算下来 72MHz / (72 * 40) 25kHz。占空比用Pulse Period * duty来设duty 从 0 到 1。// tim.c 中 CubeMX 生成的配置片段 htim3.Instance TIM3; htim3.Init.Prescaler 71; // 72分频定时器时钟 1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 39; // 自动重装载值配合预分频得到 25kHz htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim3); // 配置通道1 TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 20; // 初始占空比 50% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1);这段代码里 Prescaler 和 Period 是核心参数改频率就动这两个。Pulse 决定占空比20 对应 50%因为 Period 是 3920/40 正好一半。注意 AutoReloadPreload 要开否则改占空比时会出现毛刺。3.2 主循环里怎么调占空比// 设置风扇转速duty 范围 0.0 ~ 1.0 void Fan_SetSpeed(float duty) { if (duty 0.0f) duty 0.0f; if (duty 1.0f) duty 1.0f; uint32_t pulse (uint32_t)(duty * (htim3.Init.Period 1)); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, pulse); }__HAL_TIM_SET_COMPARE是 HAL 库的宏直接改 CCR1 寄存器比重新调用 ConfigChannel 快得多。duty 为 0 时风扇停1 时全速。四线风扇在 duty 低于 20% 时可能不转这是正常现象不是代码问题。3.3 验证 PWM 输出的两种方法第一种用示波器看 PA6 波形频率 25kHz占空比随 duty 变化。第二种没有示波器就接 LED 加电阻看亮度变化。热搜里 keilc stm32查看io输出波形 这个词其实 Keil 的仿真模式也能看但需要配 Debug 里的 Logic Analyzer不如直接量来得快。注意如果你用的是两线风扇加 MOS 管PWM 频率要降到 1kHz 以下否则 MOS 管开关损耗大发热严重。四线风扇才适合 25kHz。4. HC-SR501 触发逻辑与状态机让人来灯亮、人走风停4.1 HC-SR501 的输出特性与去抖HC-SR501 检测到人体移动时输出高电平人不动了它还会保持一段时间这个时间由延时电位器决定。我一般调到最小大概 3 秒然后在代码里做软件延时。模块上电后有 30 到 60 秒初始化输出会随机跳所以main函数开头必须加HAL_Delay(60000)或者至少 30 秒。// 读取 HC-SR501 状态带简单去抖 uint8_t PIR_Read(void) { static uint8_t last_state 0; static uint32_t last_time 0; uint8_t current HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); uint32_t now HAL_GetTick(); if (current ! last_state (now - last_time) 200) { last_time now; last_state current; } return last_state; }去抖时间设 200ms因为 HC-SR501 输出变化不会太快但电源波动可能引起毛刺。HAL_GetTick()返回毫秒数依赖 SysTick 中断别在中断里关掉 SysTick。4.2 状态机设计待机、运行、延时关闭我一般用三个状态IDLE、RUNNING、COOLDOWN。IDLE 时风扇停检测到人进 RUNNING风扇按温度或固定档位转。人走后进 COOLDOWN风扇继续转 10 分钟期间如果又检测到人直接回 RUNNING。typedef enum { IDLE, RUNNING, COOLDOWN } FanState; FanState state IDLE; uint32_t cooldown_start 0; void Fan_Update(void) { uint8_t pir PIR_Read(); switch (state) { case IDLE: if (pir) { Fan_SetSpeed(0.6f); state RUNNING; } break; case RUNNING: if (!pir) { cooldown_start HAL_GetTick(); state COOLDOWN; } break; case COOLDOWN: if (pir) { state RUNNING; } else if (HAL_GetTick() - cooldown_start 600000) { Fan_SetSpeed(0.0f); state IDLE; } break; } }600000 是 10 分钟单位毫秒。这个值根据场景改卧室可以短一点仓库可以长一点。状态机放在主循环里每 50ms 调一次别放中断里否则 HAL_Delay 会卡死。4.3 和温度联动的扩展如果想更智能加一个 DS18B20 或 NTC 热敏电阻在 RUNNING 状态里根据温度调 duty。比如 26 度以下 40%26 到 30 度 60%30 度以上 100%。热搜里 stm32 adc切换通道 这个词用 NTC 时就需要 ADC 多通道采样注意采样时间要够长否则读数跳。5. 避坑与排查HC-SR501 乱触发、PWM 不转、程序跑飞5.1 现象HC-SR501 一直输出高电平风扇停不下来原因模块上电初始化没等够或者灵敏度电位器拧到最大检测范围覆盖了窗外或走廊。解决上电后等 60 秒再读灵敏度调到中间位置必要时在模块前面加一个遮光罩只留正前方探测窗口。5.2 现象PWM 输出正常但风扇不转原因四线风扇的 PWM 线电平不兼容或者 12V 电源没共地。解决用万用表量风扇 PWM 线对地电压STM32 输出 3.3V 方波如果风扇要求 5V 电平加一个电平转换模块。共地问题最常见12V 电源的 GND 必须和 STM32 的 GND 接在一起。5.3 现象程序下载后不跑或者跑一会儿卡死原因BOOT0 没接地或者中断优先级冲突导致 HardFault。解决检查 BOOT0 跳线用 ST-Link 调试模式看卡在哪。热搜里 stm32延时函数delay卡死 这个词多半是因为在中断里调用了 HAL_Delay而 HAL_Delay 依赖 SysTick 中断优先级低于当前中断就会死等。5.4 现象风扇转速忽高忽低有啸叫原因PWM 频率落在人耳敏感区间或者占空比更新时没有开 AutoReloadPreload。解决把频率提到 25kHz 以上CubeMX 里确认AutoReloadPreload设为 ENABLE。如果还有啸叫在风扇电源两端并一个 100uF 电解电容。5.5 现象HC-SR501 触发距离忽远忽近原因环境温度接近人体温度或者模块供电不稳。解决HC-SR501 在夏天高温环境灵敏度会下降这是物理限制。供电用独立的 5V LDO别和风扇共用一路风扇启动瞬间压降会让模块复位。6. 进阶调参用定时器捕获测转速和串口调 PID6.1 用 TIM2 捕获风扇测速线四线风扇的测速线输出方波频率和转速成正比。一般风扇规格是每转两个脉冲所以RPM 频率 * 60 / 2。用 TIM2 的通道 1 做输入捕获PA1 接测速线。// TIM2 输入捕获配置测频率 void TIM2_Capture_Init(void) { TIM_IC_InitTypeDef sConfigIC; htim2.Instance TIM2; htim2.Init.Prescaler 71; // 1MHz 计数 htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 0xFFFF; HAL_TIM_IC_Init(htim2); sConfigIC.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING; sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler TIM_ICPSC_DIV1; sConfigIC.ICFilter 0; HAL_TIM_IC_ConfigChannel(htim2, sConfigIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim2, TIM_CHANNEL_1); } // 捕获中断回调 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { static uint32_t last_capture 0; static float rpm 0; uint32_t capture HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); if (last_capture ! 0) { uint32_t diff capture - last_capture; if (diff 0) { float freq 1000000.0f / diff; // 1MHz 计数 rpm freq * 60.0f / 2.0f; // 每转2脉冲 } } last_capture capture; }这段代码里 Prescaler 设 71 让计数频率为 1MHzdiff 是两个上升沿之间的计数差倒数就是频率。rpm 算出来可以通过串口打印或者用来做闭环。注意捕获中断里不要做浮点运算太久我一般只存值主循环再算。6.2 串口调 PID 让转速更稳如果风扇负载变化大转速会掉可以用 PID 调。热搜里 stm32串口调试pid 这个词核心是把 PID 参数做成串口可改的变量。我用一个简单的结构体参数含义典型值Kp比例系数0.5Ki积分系数0.1Kd微分系数0.01Target目标转速1200 RPM串口收到P0.8就改 Kp收到T1500就改目标。PID 输出限幅到 0 到 1再喂给Fan_SetSpeed。调的时候先只加 P看到转速接近目标但有静差再加 I最后加 D 抑制超调。别一上来三个参数一起调那是玄学。6.3 一个我踩过的坑测速线电压风扇测速线是开漏输出需要上拉电阻到 3.3V否则 STM32 读不到高电平。我一开始没加上拉捕获中断死活不进查了半天以为是定时器配置错了。后来在 PA1 和 3.3V 之间焊了一个 10k 电阻立刻正常。这个坑在风扇手册里通常不写但血泪经验告诉我四线风扇的测速线必须上拉。6.4 验证方法用串口画曲线没有上位机就用串口助手每 100ms 打印一次rpm和duty复制到 Excel 画曲线。看转速能不能稳定在目标值上下 5% 以内。如果振荡先把 Kd 降到 0把 Ki 减半。我一般会留一个后悔药在 Flash 里存一组默认 PID 参数调崩了长按按键恢复。这套东西跑通之后你会发现 STM32F103 加 HC-SR501 加 PWM 风扇的组合能复用到鱼缸散热、机柜通风、宠物窝降温很多场景。我现在的习惯是每做一个新板子先把 PWM 和串口调通再往上叠传感器这样出问题能快速定位是硬件还是代码。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →