简介:这是一份基于STM32单片机的智能风扇系统设计文档,适合电子、嵌入式方向的本科生用于课程设计或毕业设计参考。方案以STM32为核心,结合DS18B20温度传感器、LCD1602液晶屏、人体红外感应模块和PWM风扇,实现温度自动调速与人体感应启停。文档从系统总体设计、各硬件模块工作原理到软件编程实现逐步展开,包含方案论证、硬件选型与连接、温度控制算法、人体红外感应逻辑及LCD显示流程等完整章节,并附有中英文摘要和目录结构。压缩包内仅1个doc文档,大小908KB,内容集中便于阅读。目前已有264人学习下载,可作为智能家电类课题的完整设计范例。读者可从方案中获取硬件模块选型依据、STM32外设使用思路、Keil5编程框架,以及LCD显示、红外检测与温度控制算法的整合方法,对完成类似单片机应用设计有直接参考价值。
1. 项目概述与整体设计思路
1.1 为什么选择用 STM32 做智能风扇
把 STM32 和智能风扇放在一起,是很多电子爱好者、自动化专业学生和嵌入式入门开发者都会接触到的经典组合。这个项目之所以经典,是因为它在不算太复杂的硬件电路上,几乎覆盖了嵌入式开发的核心技能:GPIO 控制、定时器 PWM 输出、ADC 模拟量采集、按键输入检测、外设通信(如 I2C/串口)、中断系统,以及低功耗和异常处理等。可以说,做完这个项目,你对 STM32 的基础开发框架就有一个完整的认知了。
从实际需求角度看,传统风扇只有几个固定档位,要么靠手拧旋钮,要么靠遥控器,但温度变化时不会自己调节。智能风扇的核心诉求就是“自动感知环境温度,自动调节风速”,同时保留手动控制能力。用 STM32 来做主控,一方面是因为它资源丰富、外设齐全,另一方面是它上手资料多、调试工具成熟,遇到问题基本都能找到参考解决方案,对初学者非常友好。
我见过不少人一上来就想着堆功能——加 WIFI、加 APP 控制、加语音识别——结果项目做了两个月还在调网络协议栈。如果你是这个方向的新手,我的建议是:第一版先把“温度检测 + 自动调速 + 手动控制 + 状态显示”这四件事做扎实,跑通了再去考虑联网和云端的事情。后面我会详细说为什么这个顺序更重要。
1.2 系统功能拆解与预期目标
把“智能风扇”这个标题拆开,我们需要明确它到底要完成哪些功能。一个功能清晰、边界明确的系统设计方案,比盲目堆料要重要得多:
| 功能模块 | 具体描述 | 实现方式 |
|---|---|---|
| 温度采集 | 实时检测环境温度,作为调速依据 | DS18B20 单总线传感器 / NTC 热敏电阻 + ADC |
| 自动调速 | 根据温度区间自动切换风速档位 | 定时器 PWM 输出,控制电机驱动功率 |
| 手动控制 | 用户可切换自动/手动模式,手动调节档位 | 独立按键 + 状态机逻辑 |
| 状态显示 | 显示当前温度、模式、风速档位 | OLED 屏(I2C)或数码管/LED 指示 |
| 电机驱动 | 驱动直流风扇电机,实现无级调速 | 三极管/MOS 管 + PWM 占空比调节,或 L298N 模块 |
这套预期目标里,我特别想强调的是“无级调速”和“档位调速”的区别。很多智能风扇项目只做三档固定风速——低速、中速、高速——其实是把 PWM 输出简化成了几个离散值。真正的智能风扇应该能做无级调速,也就是 PWM 占空比随温度连续变化,这样体验会好很多。当然,从项目答辩或展示角度,设置 3-5 个温度区间对应不同 PWM 占空比区间,更容易说清楚逻辑,也更容易调试。这两者不冲突,你完全可以做成“区间分段 + 区间内线性插值”,既有逻辑清晰度,又有平滑体验。
我在初期写需求文档时,会给每个功能模块加上“验收标准”。比如对温度采集模块,验收标准是“温度读数误差不超过 ±0.5℃,刷新频率不低于 1Hz”。对自动调速模块,验收标准是“温度从 25℃ 升到 35℃ 时,PWM 占空比能从 20% 平滑上升到 90%,响应时间不超过 5 秒”。有这些量化指标,后面调试代码时你才知道自己做得对不对,而不是“感觉差不多就行”。
2. 硬件选型与电路设计要点
2.1 主控选型:从 F103 到 F407 的取舍
智能风扇这个项目对主控的要求其实不高——几个 GPIO、一个定时器、一个 ADC、一个 I2C 就足够了。市面上最常见的选择是 STM32F103C8T6,也就是大家常说的“蓝丸”核心板。这颗芯片的主频是 72MHz,Flash 64KB,RAM 20KB,在智能风扇这种场景下绰绰有余。
也有人会用 STM32F407 来做,理由是“以后扩展功能方便”。但我的实际体会是:F407 的开发复杂度比 F103 高不少,尤其是时钟树配置和外设初始化,对新手来说容易在多出的功能里迷路。如果你只是做智能风扇,F103C8T6 完全够用,而且资料极多,几乎每一个外设都有现成的例程可以参考。等以后做更复杂的项目(比如摄像头识别、音频处理)再上 F407 或 F429 更合理。
另一个需要考虑的点是开发板还是最小系统板。我的建议是:第一版直接用核心板(比如正点原子或野火的 Mini 板),把精力放在外设电路和逻辑代码上。等你想把它做成一个真正能用的产品,再自己画 PCB、做最小系统板。自己做板子时要特别注意晶振电路、复位电路、BOOT 引脚配置和电源去耦,这些小细节直接决定了板子能不能稳定跑起来。我见过不少自己画板失败的情况,十有八九是电源纹波太大或者晶振负载电容选得不对。
2.2 温度传感器的选择:DS18B20 到底好在哪
温度采集是智能风扇的核心环节,传感器选择直接影响整个系统的精度和复杂度。目前项目里最常见的方案有两种:DS18B20 数字温度传感器和 NTC 热敏电阻加 ADC 采样。
DS18B20 的优点是直接输出数字信号,不需要校准,测量范围 -55℃ 到 +125℃,精度在 ±0.5℃(-10℃ 到 +85℃ 范围内),和 STM32 之间只需要一根数据线通信(单总线协议)。缺点是单总线协议时序要求比较严格,代码里需要用延时精确控制时序,对新手来说第一次调通可能需要一点耐心。不过好消息是,绝大多数 STM32 例程包里都有成熟的 DS18B20 驱动代码,你不需要从零开始写时序,理解它怎么工作、怎么改延时就行。
NTC 热敏电阻的方案更“嵌入式”——用 ADC 采集电阻分压后的电压,再通过查表或公式(如 Steinhart-Hart 方程)换算成温度。它的优点是电路简单、成本更低、响应速度快,而且能锻炼你 ADC 采集和数据处理的能力。缺点是精度受限于电阻精度、参考电压稳定性和 ADC 分辨率,通常需要做一点软件校准才能达到实用水平。
我个人在智能风扇项目里更推荐 DS18B20,理由是省心、稳定。温度采集这个环节不应该成为项目的主要难点,把精力留给 PWM 调速和整体逻辑更值。如果你想挑战一下自己,或者手上正好有 NTC 传感器,用 ADC 方案也不是不行,只要做好软件滤波和校准,效果一样能看。
2.3 电机驱动:PWM 调速的硬件基础
风扇电机最常用的是直流有刷电机(两线),驱动方式是用 MOSFET 或专用电机驱动芯片控制电机的供电电压或电流。通过 PWM 调节平均电压,就能实现转速控制。
对新手来说,最简单的方案是用一个 NPN 三极管(比如 S8050)或 N-MOSFET(比如 AO3400)做低端驱动,单片机 GPIO 输出 PWM 信号到三极管基极或 MOSFET 栅极,控制电机通断。电路结构是:电源正极 → 电机 → 三极管/MOSFET → GND。注意电机两端一定要并联一个续流二极管(比如 1N4007 或 SS34),否则关闭瞬间的感应电动势会击穿驱动管——这个错误我见过新手犯过很多次,烧管是小事,崩到单片机 IO 口才是大麻烦。
如果电机功率稍大(比如 12V 的大风扇),或者你想做正反转控制,建议直接用 L298N 或 TB6612 驱动模块。这些模块内部集成了 H 桥电路和续流保护,用起来省心很多,缺点是多花几块钱,而且要注意共地问题——驱动模块和单片机必须共地,否则控制信号电平参考不一致,电机根本转不起来。
还有一个容易忽略的点是电源设计。风扇电机启动瞬间电流是额定电流的好几倍,如果和单片机共用同一个电源,电机启动瞬间会把电压拉低,可能导致单片机复位。稳妥的做法是电机单独供电(比如 12V 适配器),通过驱动模块给电机供能,单片机和传感器用 USB 5V 或独立 3.3V LDO 供电。不一致的电源轨之间只需要共地,不需要共电压。
3. 软件架构与核心代码实现
3.1 代码框架:裸机轮询还是定时器状态机
智能风扇这种复杂度适中的项目,代码架构不需要上实时操作系统(RTOS),用裸机 + 定时器状态机就能写得非常清晰。核心思路是:主循环里不做任何延时阻塞操作,所有时间敏感的任务(比如 DS18B20 时序、按键消抖)都交给定时器中断或定时器轮询标志位处理。
我推荐的软件模块划分是这样的:
- 主循环:检查标志位,执行对应的任务(温度采集完成则处理温度数据;按键事件产生则更新模式状态;显示刷新定时到则更新屏幕内容)。
- 定时器中断(比如 TIM2,1ms 周期):维护一个毫秒计数器和各种软件定时标志位(比如“每 500ms 采集一次温度”“每 200ms 扫描一次按键”“每 100ms 刷新一次显示”)。
- DS18B20 驱动:单总线时序初始化、温度读取,注意读温度时需要短暂关闭中断或者使用临界区保护,避免时序错乱。
- PWM 输出:用 TIM3 的通道 1 产生 PWM 信号,通过修改捕获比较寄存器(CCR)的值来控制占空比。
- 按键驱动:使用 GPIO 外部中断或轮询加消抖,处理单击、长按或组合按键逻辑。
- 状态机:定义风扇的工作模式(自动模式 / 手动模式),在模式切换时执行相应的初始化动作。
我在实际写代码时,会先在纸上画出状态迁移图:上电默认进入自动模式,读取温度并设置对应风速;按下按键 A 切换自动/手动模式;手动模式下按键 B 增加档位、按键 C 降低档位。状态图理清楚了,代码就是按状态图填内容的事情。
3.2 代码片段示例:PWM 与 ADC 核心逻辑
下面给出几个最核心的代码片段。环境以 STM32F103C8T6 + HAL 库为例,因为目前 HAL 库已经是主流,标准库的使用者虽然还很多,但新项目还是建议直接用 HAL 库起步。
PWM 输出初始化(TIM3, 通道1, PA6):
// 使能 TIM3 时钟和 GPIOA 时钟 __HAL_RCC_TIM3_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // 配置 PA6 为复用推挽输出 GPIO_InitTypeDef gpio_init = {0}; gpio_init.Pin = GPIO_PIN_6; gpio_init.Mode = GPIO_MODE_AF_PP; gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &gpio_init); // TIM3 时基配置,72MHz / 72 = 1MHz,计数到 1000,即 PWM 频率 = 1kHz TIM_OC_InitTypeDef oc_config = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 72 - 1; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 1000 - 1; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim3); oc_config.OCMode = TIM_OCMODE_PWM1; oc_config.Pulse = 500; // 初始占空比 50% oc_config.OCPolarity = TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(&htim3, &oc_config, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);设置占空比直接用:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, duty); // duty 范围 0~999这里的参数选择逻辑很简单:PWM 频率设为 1kHz,足够驱动直流电机且听不到明显的啸叫声。占空比范围 0~999,对应分辨率约 0.1%,对于风速调节来说是足够的。如果你想更平滑,可以把 Period 加大到 2000 甚至 5000,但要注意频率不能太低(低于 500Hz 电机会有明显顿挫感),也不能太高(超过 20kHz 可能出现音频啸叫或驱动管开关损耗增大)。
DS18B20 温度读取的关键步骤:
// 跳过 ROM(只有单设备接在总线上) ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0x44); // 启动温度转换 // 等待转换完成,典型转换时间 750ms HAL_Delay(750); // 读取温度 ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 读取暂存器 uint8_t temp_l = ds18b20_read_byte(); uint8_t temp_h = ds18b20_read_byte(); int16_t raw_temp = (temp_h << 8) | temp_l; float temperature = raw_temp * 0.0625f; // 12位分辨率每位代表0.0625℃这里有个细节很多人不注意:DS18B20 的上电默认分辨率是 12 位,对应温度分辨率 0.0625℃。如果你在配置寄存器里改成了 9 位分辨率,那么转换时间会短很多(约 94ms),但 raw_value 每位代表的温度值就不一样了。建议保持默认 12 位,代码里的乘法系数就不用改。
自动调速的 PID 风格的逻辑(简化版):
#define TEMP_MIN 20.0f // 最低温度:低于此温度风扇停转或最低速 #define TEMP_MAX 35.0f // 最高温度:高于此温度风扇全速 float map_temperature_to_duty(float temp) { if (temp <= TEMP_MIN) return 0; if (temp >= TEMP_MAX) return 999; // 线性映射,也可以用分段函数或查表 return (temp - TEMP_MIN) / (TEMP_MAX - TEMP_MIN) * 999.0f; }这个线性映射是最简单的方案,但实际体验中我建议加入一个“启动阈值”:温度低于 22℃ 时风扇保持停止,温度超过 25℃ 时才开始转动,转速从 20% 起步。原因是直流电机和 PWM 驱动中存在死区——占空比太低时电机转不动,但会嗡嗡响。实测中很多风扇套件在占空比低于 15%-20% 时电机不会转,白白浪费了能量还有噪音。所以在做调速映射时,要把可用的占空比区间限制在 20%-100%,低于 20% 直接给 0 关闭输出。
3.3 显示模块与用户交互设计
显示部分我用的是 0.96 英寸 OLED(SSD1306 驱动芯片,I2C 接口),四根线接好就能用:VCC、GND、SCL、SDA。STM32 的硬件 I2C 在某些库版本里可能有兼容性问题,但 HAL 库的 I2C 已经做得比较稳定了,直接调用HAL_I2C_Mem_Write往 SSD1306 的显存地址写数据就行。
OLED 界面设计建议分三层:
- 第一行显示当前温度,比如
Temp: 28.5C。 - 第二行显示工作模式,比如
Mode: AUTO或Mode: MANUAL。 - 第三行显示当前风速或 PWM 占空比,比如
Duty: 65%。
显示刷新频率不要太高,100ms 刷新一次足够,OLED 本身刷新也快不了多少,写显存的频率太高反而浪费 CPU 时间。
用户交互方面,我设计了三颗按键:
- SW1:模式切换(自动 ↔ 手动)。
- SW2:手动模式下风速加一档(或占空比 +10%)。
- SW3:手动模式下风速减一档(或占空比 -10%)。
按键处理的关键是消抖和防连按。消抖最稳妥的方法不是硬件电容滤波,而是在代码里做 20ms 延时确认——检测到按键按下后延时 20ms 再读一次,确认还是低电平才判定为有效按下。长按和短按的逻辑可以后续再加,第一版先做短按就行了。
4. 调试流程与常见问题排查
4.1 调试环境搭建:从 Keil 到 VSCode 的选择
STM32 开发最常见的是 Keil MDK,资料多、上手快、破解也方便(当然学习版也够用)。但如果你经常写代码、对编辑器有要求,可以试试 VSCode + EIDE 插件或者 STM32CubeIDE。我个人建议:如果你是第一次做 STM32 项目,直接用 Keil 就好,别在工具上折腾太多时间。Keil 搭配 ST-Link 调试器,可以看寄存器值、变量值、打断点,这些功能对排查问题非常关键。
接线调试时,ST-Link 的四根线(SWDIO、SWCLK、GND、3.3V)连到板子上,注意 SWDIO 和 SWCLK 不要接反。如果出现“No STM32 Target Found”的报错,先检查这几根线,再看看板子是否上了电,BOOT0 引脚是否拉到了低电平(VBAT 模式选择问题)——这些都是最常犯的低级错误。
4.2 我踩过的坑:这些错误你可能也会遇到
这个项目看起来不复杂,但实际调试过程中有一堆问题等着你。我把自己踩过的、身边朋友踩过的坑整理成一个速查表,按出现频率排序:
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| DS18B20 一直读不到温度 | 单总线时序不对或上拉电阻缺失 | 在 DQ 数据线上接 4.7kΩ 上拉电阻;检查 GPIO 是否配置为开漏输出 |
| 电机嗡嗡响但不转 | PWM 占空比过低,处于死区 | 调大最小占空比阈值,低于阈值直接输出 0 |
| OLED 屏幕花屏或不亮 | I2C 地址错误或 SCL/SDA 接反 | 检查屏背面丝印,常见地址是 0x3C 或 0x3D;确认接线顺序 |
| 风扇全速运转但不受控 | MOSFET/三极管被击穿,或 PWM 引脚配置错误 | 检查驱动电路续流二极管是否接反;用示波器/万用表测 PWM 引脚是否有波形 |
| 按键按下无反应 | 按键接法和 GPIO 内部上下拉配置不匹配 | 确认按键一端接 GND、另一端接 GPIO,GPIO 配置为内部上拉输入 |
| 系统频繁复位 | 电机启动瞬间拉低电源电压 | 电机和单片机分开供电,或加大电源电容(1000μF 电解电容) |
| 温度读数跳动大 | 电源干扰或采样时间不够 | 在温度采样值上做中值滤波或滑动平均滤波;适当延长 DS18B20 转换等待时间 |
| ST-Link 连接失败 | BOOT0 引脚状态不对或接线松动 | BOOT0 接 GND;重新插拔 ST-Link;检查驱动是否正常 |
每个人遇到的问题各不相同,但排查思路是一样的:先把系统拆成模块,用最简单的方式测试每个模块是否正常。比如怀疑电机驱动有问题,就先写一个固定占空比 50% 的测试代码,看电机能不能转。如果转,说明硬件没问题,问题在调速逻辑;如果不转,就用万用表测驱动管栅极电压、测电机两端电压,一步步缩小范围。
4.3 调试技巧:善用串口打印与逻辑分析仪
嵌入式开发中最实用的调试手段,永远是串口打印。在代码里加一个 UART 初始化,重定向printf到串口(记得在 Keil 里勾选 Use MicroLIB),然后用 USB 转 TTL 模块接在 PA9(TX)和 PA10(RX)上,就能在电脑串口助手里看到系统内部的状态信息。
调试智能风扇时,我建议每隔 500ms 打印一行:
Temp: 28.50C | Mode: AUTO | Duty: 650/1000 | Speed: 2600rpm这样运行时你可以直观地看到温度变化对占空比的影响,也能发现逻辑上的问题,比如温度升高了占空比反而下降,那多半是线性映射公式写反了或者传感器数据接到了反方向。
逻辑分析仪是另一个利器,尤其是调试 DS18B20 的单总线时序和 PWM 波形时。市面上几十块钱的 8 通道逻辑分析仪配上 PulseView 软件,就能直观看到时序波形。把探针接到 DQ 数据线上抓一段波形,你能立刻看出复位脉冲、存在脉冲、数据位的时序是否正常。新手很容易遇到“代码照着写但就是不通”的情况,拿逻辑分析仪一看,往往是延时精度不够或者 GPIO 输出模式配错。
4.4 掉电备份与异常保护经验
智能风扇在真实使用场景里有一个容易被忽略的问题:突然断电再来电,系统应该恢复到什么状态。大部分第一版代码的做法是上电默认自动模式,这没问题,但如果你想做得更“智能”一点,可以在 EEPROM(比如 STM32 内部 Flash 模拟 EEPROM,或者外挂 AT24C02)里保存用户的模式和档位设置,上电时先读取保存的配置再启动。
另一个值得考虑的保护逻辑是“无传感器堵转检测”。直流电机堵转时电流会显著上升,可以通过采样电机回路电流(例如串联一个采样电阻接 ADC)来判断风扇是否被异物卡住。如果检测到堵转,立即关断 PWM 输出并报警(比如 LED 快闪或 OLED 显示错误码),避免电机长时间堵转过热烧毁。这个功能虽然不属于“标题必需”,但加上之后项目的完整度会明显提升,在答辩或展示时也是一个很好的亮点。
5. 项目扩展方向与最终体会
5.1 从智能风扇到智能家居节点
如果你不满足于“风扇能根据温度自动变速”,这个项目可以往多个方向扩展。最容易的是加一个红外接收头,用家用电器的遥控器来控制风扇——从“温度自动”升级成“遥控 + 自动”双模式。更进一步的方案是加 ESP8266 或 ESP32 模块,通过 MQTT 协议连接局域网,实现手机 APP 或语音助手控制。这个方向会涉及网络协议栈、JSON 数据解析、嵌入式 GUI 等更复杂的内容,但整个系统的技术上限会高很多。
还有一种扩展方向是“多传感器融合”,在温度基础上增加湿度传感器(DHT11/DHT22)和人体红外传感器(HC-SR501)。有人在场时根据温度湿度综合调速,无人时自动进入低速节能模式甚至关闭风扇。这才是真正的“智能”——不是炫技式的联网,而是让设备学会根据环境和使用者状态做更合理的决策。
5.2 我在做完整个项目后的几点实在体会
做了这么多年嵌入式项目,智能风扇是我认为最适合作为“入门关底”的项目之一。它的硬件设计不复杂,代码逻辑不难写,但要把所有模块整合起来稳定运行,需要你对单片机的核心外设都有实实在在的理解。
第一点体会是:别一开始就想着把所有功能做全。先把最小可行版本跑通——“能测温度、能调速、能显示、能按键控制”——然后再逐步加功能。每加一个功能之前,先明确它的验收标准和测试方法,否则你会花大量时间在排查“不知道哪里出的问题”。
第二点体会是:调试日志真的能救命。不要嫌串口打印麻烦,也不要觉得“反正逻辑简单不用打印”。等到风扇转起来但温度不准、或者偶尔不听话的时候,你就知道有日志和没日志的差别有多大。我现在的习惯是:每写一个功能模块,先把对应的调试打印加上,跑通了再精简。
第三点体会是:多看数据手册和参考手册,少在网上“猜答案”。STM32 的很多问题(比如 GPIO 复用配置、定时器计数模式、DMA 请求映射)在参考手册里都有明确说明,一些网上答案反而会误导你。养成查手册的习惯,对你的嵌入式开发能力提升帮助极大。
最后建议你把整个项目的过程记录下来,包括设计文档、引脚分配表、调试笔记、遇到的问题和解决办法。毕业设计答辩或面试展示时,这些记录能让你把项目讲得有条有理,也帮你把零散的实践经验变成系统的项目经验。毕竟,任何一个项目的价值,都不仅在于它跑起来了,更在于你从中真正学会了什么。
本文还有配套的精品资源,点击获取