拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于STM32的智能绿色风扇毕业设计:从硬件选型到代码实现详解

许多单片机毕业设计题目看起来相似但真正拉开差距的往往是系统设计的完整性和“绿色智能”这两个词的落地程度。市面上很多风扇控制方案只是用手动按键调速离“智能”还有一段距离而如果只做一个温度控制风扇又缺少人体感应、自动/手动模式切换等工程化设计难以体现完整的系统思维。本文围绕“基于 STM32 的智能绿色风扇”展开梳理一套可直接用于毕业设计答辩和实物演示的实现方案。内容覆盖需求分析、器件选型、电路设计、STM32 核心代码、传感器驱动、PWM 调速逻辑、调试方法以及常见坑点。无论你是刚接触 STM32 的本科生还是想快速搭建一个单片机综合项目的开发者都可以照着本文一步步完成。1. 智能绿色风扇的需求背景与设计思路1.1 什么是智能绿色风扇智能绿色风扇并不是一个标准名词而是单片机系统设计中常见的“节能 自动控制”类项目。它通常具备以下能力根据环境温度自动调整风扇转速温度高时转速升高温度低时转速降低或停止。通过人体红外感应判断是否有人存在无人在场时自动进入待机/停转状态。提供自动模式和手动模式既能全自动运行也能由用户手动调速。能够显示当前温度、工作模式、风速档位等信息方便演示和答辩。从“绿色”角度看核心目标不是“风扇本身有多节能”而是通过合理控制策略避免风扇长期满速空转从而节省电能、延长电机寿命。从“智能”角度看系统至少需要两路输入一路是环境温度一路是人体存在信号。单片机作为控制核心根据这两路输入决定执行器直流电机/风扇模块的输出状态。如果再增加光照检测、定时、无线控制就属于进阶扩展。1.2 为什么选择 STM32 作为主控这套设计选择 STM32 而不是传统的 51 单片机主要考虑以下几点STM32 主频高、外设丰富除了普通 GPIO还内置多路定时器、ADC、I2C、USART 等外设扩展能力强。项目演示时STM32 能够直接驱动 OLED、读取传感器数据、产生 PWM 波形整体可展示性强。毕业生在黑板上/论文中能写的内容更多比如中断优先级、定时器配置、PWM 频率计算、ADC 采样这些都是答辩时可以深入展开的知识点。STM32 开发资料非常成熟网上可以找到大量参考工程遇到问题容易排查。当然如果项目要求比较简单用 STM32F103C8T6 最小系统板作为核心即可成本较低、资料多、引脚足够。1.3 系统整体框图在正式开发前先画出系统框图。典型的智能绿色风扇系统结构如下[温度传感器 DS18B20 / DHT11] --- STM32 GPIO 读取 [人体红外模块 HC-SR501] --- STM32 GPIO 电平检测 [按键模式切换/手动调速] --- STM32 GPIO 输入 [OLED 显示屏] --- STM32 I2C 接口 [直流电机/风扇模块] --- STM32 定时器 PWM 输出 \\--- 电机驱动电路系统的信息流是STM32 周期性读取温度传感器数据。STM32 检测人体红外模块输出电平。根据当前模式自动/手动计算目标风速。通过 PWM 占空比调节电机转速。OLED 显示温度值、模式和档位信息。按键可切换模式、手动调节档位。2. 硬件选型与开发环境准备2.1 主控芯片与开发板推荐使用 STM32F103C8T6 最小系统板也就是常说的“STM32 蓝板”或“核心板”。它成本低、引脚引出方便非常适合毕设实物焊接和面包板搭建。关键技术参数内核ARM Cortex-M3主频最高 72 MHzFlash64 KBSRAM20 KB定时器多个包含高级定时器 TIM1 和通用定时器 TIM2/TIM3/TIM4ADC12 位逐次逼近型 ADC多通道通信接口I2C、SPI、USART、CAN 等对于风扇控制项目Flash 和 SRAM 完全够用。2.2 电机与驱动模块智能风扇项目通常有两种方式小功率直流风机直接使用 5V 直流风扇模块配合三极管/MOS 管或者 L298N/ULN2003 驱动。这种方式适合桌面小风扇演示。大功率交流风扇需要继电器或可控硅调压涉及 220V 强电安全性要求高一般不推荐毕设实物随意操作。如果没有特殊说明建议选择 5V 直流电机或电脑散热风扇模块由 USB/充电宝/电源适配器供电即可。驱动方式可以使用 NPN 三极管如 S8050 续流二极管或者使用 MOSFET如 AO3400或者使用现成的 DRV8833 / L298N 模块。从学习角度出发使用三极管或 MOS 管自己搭一个 PWM 驱动电路更有技术含量但直接使用 L298N 模块更稳定。在 PWM 驱动电机时要注意频率不能太低否则电机会发出明显的噪声也不能太高否则开关损耗增加。常见习惯选择 10kHz~20kHz 之间的 PWM 频率。2.3 温度传感器选型智能风扇需要感知环境温度。常见选择有两种DS18B20单总线数字温度传感器测量范围 -55℃~125℃精度 ±0.5℃-10℃~85℃可通过 GPIO 口直接读取无需 ADC。DHT11/DHT22温度和湿度传感器DHT11 精度相对较低适合要求不高的项目。毕业设计建议优先使用 DS18B20。理由是单总线协议简单直观能够展示底层时序编程能力。数据是数字量不需要校准。演示时可以用手捂住传感器看到温度变化再观察风扇转速变化演示效果直观。2.4 人体感应模块常用模块是 HC-SR501 人体红外感应模块基于热释电红外传感器PIR。模块特性工作电压 5V~20V通常用 5V 供电。输出高电平信号默认无入时输出低电平检测到人体活动时输出高电平。可以通过模块上的两个电位器调节灵敏度和延时时间。使用时要特别注意HC-SR501 在刚上电时有约 30s~60s 的预热/初始化时间此时输出不稳定容易被误触发。人体传感器检测的是人体移动静止的人长时间待在原地可能会因红外变化不明显而恢复低电平。所以软件控制逻辑要设计合理不能完全依赖它判断“房间是否始终有人”。2.5 显示模块推荐使用 0.96 寸 OLED 显示屏I2C 接口SSD1306 驱动芯片。OLED 显示清晰、占用引脚少、刷新简单非常适合在毕设实物中展示当前状态。如果不想用 OLED也可以用 LCD1602 液晶屏。但 LCD1602 需要占用 6 个甚至更多 IO并且显示内容有限。对于能显示中文和图形的需求OLED 是更好的选择。如果题目明确要求更复杂的界面可以考虑带中文字库的 LCD12864 或 TFT 彩屏。2.6 按键与电源按键用于切换工作模式和手动调速。常见做法是接 4 个独立按键模式切换键自动 / 手动风速加风速减开关机/确认按键电路采用“按键一端接 GPIO另一端接 GND”内部启用上拉电阻或外部接 10kΩ 上拉电阻。按下时引脚读到低电平实现低电平有效。电源部分如果所有模块都是 5V 供电可以直接用 USB 5V 电源如果个别模块需要 3.3V则用 AMS1117-3.3 稳压芯片转换或者使用开发板自带的 3.3V 输出。2.7 开发软件与版本说明本文示例使用以下开发环境Keil MDK 5ARMCC 编译器也可以使用 STM32CubeMX Keil/GCC 组合或者 VSCode EIDE/PlatformIO核心思路一致。STM32 标准外设库Standard Peripheral Library对于 STM32F1 系列资料较多示例代码易读性好。STM32CubeMX 用于生成初始化代码时可以选择 HAL 库版本结构会更清晰。版本方面不同系列 Pack 会导致函数名略有差异。实际环境中的芯片型号、器件库版本可能不同所以示范代码需要按自己的工程情况调整。最关键的是理解配置原理而不是死记某个函数名。3. 核心模块原理与配置方法3.1 定时器 PWM 输出原理PWMPulse Width Modulation脉宽调制通过调节高电平在一个周期内的占比也就是占空比来改变输出到电机的平均电压。STM32 通用定时器输出 PWM 的核心逻辑是定时器计数器 CNT 从 0 增加到自动重装载值 ARR。比较寄存器 CCR 存储一个比较值。当 CNT 小于 CCR 时输出有效电平当 CNT 大于等于 CCR 时输出另一个电平。改变 CCR 即可改变占空比。例如 ARR 999 时PWM 周期为 1000 个计数单位。若 CCR 250占空比为 25%CCR 500 时占空比为 50%。PWM 频率计算公式PWM频率 定时器时钟频率 / ((ARR 1) * (PSC 1))例如 STM32F103 内部定时器挂在 APB1 上APB1 经过倍频后通常为 72MHz。如果 PSC 71ARR 99则PWM频率 72MHz / ((99 1) * (71 1)) 72000000 / 7200 10000 Hz也就是说配置定时器分频系数为 71自动重装载值为 99即可得到 10kHz 的 PWM 波。3.2 温度传感器 DS18B20 读取流程DS18B20 使用单总线协议。一次完整温度转换需要以下步骤发送复位脉冲主机将总线拉低至少 480μs然后释放并等待存在脉冲。检测存在脉冲DS18B20 收到复位脉冲后会拉低总线 60μs~240μs。发送 ROM 命令如果总线上只有一个传感器可以发送跳过 ROM 命令 0xCC。发送功能命令启动温度转换命令 0x44。等待转换完成通常需要 750ms12 位分辨率。再次复位并跳过 ROM发送读取暂存器命令 0xBE。读取低字节和高字节合成为 16 位温度数据。温度值计算方法符号位为 0 时温度为正值。温度 高字节 8 | 低字节 4精确到 0.0625℃。也可以直接乘 0.0625 得到实际小数。在编写底层代码时最重要的是严格控制延时。DS18B20 对时序要求比较高如果延时不准读取会失败。可以用 STM32 的微秒级延时函数实现。3.3 人体红外检测逻辑HC-SR501 输出的是数字电平信号单片机只需要读 GPIO 电平即可。但需要注意逻辑设计不能简单认为“检测到高电平就一直开着风扇”。推荐的控制逻辑是若人体传感器输出高电平表示当前检测到有人活动系统进入工作状态。若人体传感器输出低电平不代表一定没人因为模块有延时设置通常默认延时约 2s~3s。为避免风扇因传感器短暂失效而频繁启停软件上应加入“关断延时”。例如检测到无人后继续运行 30s 或 60s 再停止或者当温度较低且无人才逐渐关闭。3.4 系统状态机设计为了让程序结构清晰推荐使用简单的状态机。状态说明 [0] 关机/待机状态不输出 PWMOLED 显示待机。 [1] 自动模式运行状态温度决定风速无检测到人则等待关闭。 [2] 手动模式运行状态用户通过按键加减风速。主循环每次扫描按键、传感器然后根据状态机决定输出。这种方式比让空 while 循环里的代码堆在一起更清晰也方便答辩时讲“系统采用状态机编程”。4. 完整代码实战下面以一个简化但功能完整的工程为例展开代码。重点展示初始化流程DS18B20 底层读取PWM 调速主循环状态控制OLED 显示逻辑为了便于阅读以下代码使用“标准外设库”思路不过为了工程通用性也会注意函数命名友好。4.1 工程结构建议建议按照以下结构组织文件SmartFan/ ├── User/ │ ├── main.c │ ├── stm32f10x_it.c ├── Hardware/ │ ├── ds18b20.c │ ├── ds18b20.h │ ├── pwm.c │ ├── pwm.h │ ├── key.c │ ├── key.h │ ├── oled.c │ ├── oled.h │ ├── infrared.c │ └── infrared.h ├── System/ │ └── STM32 启动文件和库文件 └── Project/ └── MDK-ARM 工程文件这样功能拆分明确不同模块之间的耦合度低后续将 OLED 换成 LCD 或增加蓝牙模块只需要新增对应硬件文件即可。4.2 主函数框架// 文件路径User/main.c #include stm32f10x.h #include delay.h #include ds18b20.h #include pwm.h #include key.h #include oled.h #include infrared.h typedef enum { SYS_POWER_OFF 0, SYS_AUTO_MODE, SYS_MANUAL_MODE } SysMode; volatile SysMode sysMode SYS_AUTO_MODE; volatile uint8_t currentLevel 1; // 当前风速档 0~4 volatile uint8_t manualLevel 2; // 手动档位 volatile uint8_t personDetect 0; // 是否检测到人 volatile float temperature 25.0f; // 当前温度 static void System_Init(void); static void Task_KeyScan(void); static void Task_TemperatureRead(void); static void Task_MotorControl(void); static void Task_Display(void); int main(void) { System_Init(); while (1) { Task_KeyScan(); Task_TemperatureRead(); Task_MotorControl(); Task_Display(); delay_ms(100); } } static void System_Init(void) { delay_init(); OLED_Init(); DS18B20_Init(); PWM_Init(); KEY_Init(); Infrared_Init(); OLED_Clear(); OLED_ShowString(0, 0, Smart Fan System); }主循环采用轮询方式每 100ms 执行一次。对于这个项目来说轮询足够满足实时性要求。如果后续要加入无线模块、语音识别等耗时任务再考虑使用定时器中断调度或 FreeRTOS。4.3 DS18B20 驱动代码下面是 DS18B20 初始化与读取温度的核心代码。// 文件路径Hardware/ds18b20.c #include ds18b20.h #include delay.h // GPIO 定义这里以 PA0 为例 #define DS18B20_PORT GPIOA #define DS18B20_PIN GPIO_Pin_0 #define DS18B20_RCC RCC_APB2Periph_GPIOA static void DS18B20_Mode_Input(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin DS18B20_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); } static void DS18B20_Mode_Output(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin DS18B20_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); } static void DS18B20_Reset(void) { DS18B20_Mode_Output(); GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(500); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); delay_us(60); DS18B20_Mode_Input(); } static uint8_t DS18B20_CheckPresence(void) { uint8_t presence 1; // Reset 后读取存在脉冲 if (GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_PIN) 0) { presence 0; // 拉低说明存在 } delay_us(200); return presence; } static void DS18B20_WriteByte(uint8_t data) { uint8_t i; for (i 0; i 8; i) { DS18B20_Mode_Output(); GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(2); if (data 0x01) { GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); } else { GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); } delay_us(60); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); data 1; } } static uint8_t DS18B20_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { data 1; DS18B20_Mode_Output(); GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(2); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); DS18B20_Mode_Input(); if (GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_PIN)) { data | 0x80; } delay_us(60); } return data; }然后封装一个获取温度的函数float DS18B20_GetTemperature(void) { uint8_t tempL 0, tempH 0; int16_t raw 0; float temperature 0.0f; DS18B20_Reset(); DS18B20_CheckPresence(); DS18B20_WriteByte(0xCC); // 跳过 ROM DS18B20_WriteByte(0x44); // 启动温度转换 delay_ms(750); // 等待转换完成 DS18B20_Reset(); DS18B20_CheckPresence(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读取暂存器 tempL DS18B20_ReadByte(); tempH DS18B20_ReadByte(); raw (int16_t)((tempH 8) | tempL); temperature raw * 0.0625f; return temperature; }DS18B20 比较耗时的一点是温度转换等待时间较长。如果每 100ms 主循环都调用一次 DS18B20_GetTemperature会卡住系统。因此更合理的做法是定义转换状态每 750ms 才发起一次完整读取。温度读取放在后台或低频任务中主控制逻辑不依赖每次实时温度。4.4 PWM 电机驱动代码以 TIM2 的 CH1PA0 在某些板子上作为 TIM2_CH1但如果 PA0 被 DS18B20 占用需要调整引脚这里只作演示逻辑为例。实际使用时DS18B20 和数据手册引脚需要根据你的核心板分配一般 PA0 是 TIM2_CH1 和 ADC 等多功能引脚需要认真查看原理图。如果使用 PA1 作为 TIM2_CH2配置代码如下// 文件路径Hardware/pwm.c #include pwm.h #include stm32f10x.h void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; // TIM2_CH2 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler 71; // 72MHz / 72 1MHz TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 999; // PWM 频率 1kHz TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE); } void PWM_SetDuty(uint8_t duty_percent) { uint16_t ccr 0; if (duty_percent 100) { duty_percent 100; } ccr (uint16_t)(duty_percent * 10); // ARR999占空比调整最大不超过999 TIM_SetCompare2(TIM2, ccr); }注意这个配置里 PWM 频率是 1kHz适合小风扇如果想要 10kHzTIM_Prescaler 不变把 TIM_Period 改为 99那么占空比调整公式也需要变。实际频率选择请根据电机噪声与驱动效果调试。4.5 自动控制策略代码下面是最关键的“智能控制逻辑”。自动模式下可以设定一个简单的温度区间温度低于 20℃风扇停转。温度在 20℃~25℃风扇低速30% PWM。温度在 25℃~30℃风扇中速60% PWM。温度高于 30℃风扇高速100% PWM。如果无人检测信号超过设定时间则不管温度多高都逐渐停止体现节能。具体代码如下static void Task_MotorControl(void) { uint8_t targetDuty 0; if (sysMode SYS_POWER_OFF) { PWM_SetDuty(0); return; } // 检测人体传感器电平 personDetect Infrared_ReadLevel(); if (sysMode SYS_AUTO_MODE) { if (personDetect 0) { // 无人状态为节电可降低风速如果使用延时关机逻辑需要在这里维护时长变量 PWM_SetDuty(0); currentLevel 0; } else { if (temperature 20.0f) targetDuty 0; else if (temperature 25.0f) targetDuty 30; else if (temperature 30.0f) targetDuty 60; else targetDuty 100; if (targetDuty 0) { currentLevel (uint8_t)(targetDuty / 25); // 1~4挡备查 } PWM_SetDuty(targetDuty); } } else if (sysMode SYS_MANUAL_MODE) { switch (manualLevel) { case 0: PWM_SetDuty(0); break; case 1: PWM_SetDuty(25); break; case 2: PWM_SetDuty(50); break; case 3: PWM_SetDuty(75); break; case 4: PWM_SetDuty(100); break; default: PWM_SetDuty(0); break; } } }上面的 targetDuty 直接代表 PWM 占空比。实际项目还会增加“无人延时关闭计时”检测到无人后并不立即停转而是持续运行比如 30s 再停。要完成这个功能需要在主循环中维护一个递减计数变量代码结构如下若 personDetect 为 1 清零 noPersonCounter 正常运行自动模式逻辑 若 personDetect 为 0 noPersonCounter; 若 noPersonCounter 300也就是30秒 停转否则维持当前转速4.6 OLED 显示逻辑OLED 显示逻辑一般比较简单调用字符显示接口即可。以 SSD1306 的 0.96 英寸 OLED 为例常用 API 包括OLED_Clear(); OLED_ShowString(uint8_t x, uint8_t y, char *str); OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len);显示主界面时第一行显示当前模式第二行显示温度第三行显示档位/转速百分比第四行显示人体检测状态。static void Task_Display(void) { char buf[32]; OLED_Clear(); OLED_ShowString(0, 0, Mode:); if (sysMode SYS_AUTO_MODE) { OLED_ShowString(40, 0, Auto ); } else if (sysMode SYS_MANUAL_MODE) { OLED_ShowString(40, 0, Manual); } else { OLED_ShowString(40, 0, Off ); } // 温度显示单位 ℃ OLED_ShowString(0, 2, Temp:); OLED_ShowNum(36, 2, (uint32_t)temperature, 2); OLED_ShowString(56, 2, .); uint16_t tempDec (uint16_t)((temperature - (int)temperature) * 10); OLED_ShowNum(64, 2, tempDec, 1); OLED_ShowString(76, 2, C ); OLED_ShowString(0, 4, Level:); if (currentLevel 0) { OLED_ShowString(48, 4, Stop ); } else { OLED_ShowNum(48, 4, currentLevel, 1); OLED_ShowString(56, 4, /4 ); } OLED_ShowString(0, 6, Human:); if (personDetect) { OLED_ShowString(48, 6, YES ); } else { OLED_ShowString(48, 6, NO ); } OLED_Refresh(); }OLED 刷新频率不能太高否则会造成闪烁和占用 CPU。建议 1~5Hz 刷新即可例如每 200ms 刷新一次。4.7 按键处理按键处理要处理机械抖动。一个简单的扫描函数如下// 返回按键值1 模式切换2 档位加3 档位减4 开关机 uint8_t Key_Scan(void) { static uint8_t keyState 0; uint8_t keyValue 0; if (KEY_MODE_PIN 0 || KEY_PLUS_PIN 0 || KEY_MINUS_PIN 0 || KEY_POWER_PIN 0) { delay_ms(10); if (KEY_MODE_PIN 0) { keyValue 1; } else if (KEY_PLUS_PIN 0) { keyValue 2; } else if (KEY_MINUS_PIN 0) { keyValue 3; } else if (KEY_POWER_PIN 0) { keyValue 4; } } // 等待按键释放避免一次长按多次触发的副作用 while (KEY_MODE_PIN 0 || KEY_PLUS_PIN 0 || KEY_MINUS_PIN 0 || KEY_POWER_PIN 0) { delay_ms(10); } return keyValue; }注意如果在主循环中直接写死等待按键释放的 while那么长按会阻塞整个主循环有可能会卡住传感器读取或 PWM 控制。对于简单演示项目影响不大但如果是工程开发建议使用“检测下降沿 防抖标志再触发一次”的方式或把按键扫描放入定时器中断。在主函数中按键动作对应的处理static void Task_KeyScan(void) { uint8_t key Key_Scan(); if (key 1) // 模式切换自动/手动 { if (sysMode SYS_AUTO_MODE) { sysMode SYS_MANUAL_MODE; } else if (sysMode SYS_MANUAL_MODE) { sysMode SYS_AUTO_MODE; } } else if (key 2) // 档位加 { if (manualLevel 4) { manualLevel; } } else if (key 3) // 档位减 { if (manualLevel 0) { manualLevel--; } } else if (key 4) // 开关机进入待机 { sysMode SYS_POWER_OFF; PWM_SetDuty(0); } }5. 系统联调与演示验证5.1 基本功能验证完成代码后建议按以下顺序进行硬件联调。第一步单独验证最小系统只连接 STM32 最小系统板和下载器确认程序能下载并且 LED 闪烁程序运行正常。第二步验证 OLED 显示连接 OLED下载 OLED 测试程序观察能否正常显示字符。常见问题是 I2C 地址不对、接线反了、模块供电不足。第三步验证 DS18B20 温度读取单独调试 DS18B20 驱动用串口或 OLED 输出当前温度。可以用手捏住传感器看温度是否上升或者用冰水测试降温。第四步验证 PWM 输出与电机转动将 PWM 代码占空比固定为 50%用示波器或万用表测量输出波形再连接电机驱动电路观察风扇转速是否正常。第五步验证人体感应模块在代码中临时写一个读 GPIO 的逻辑把人体感应输出接上人走过时观察电平变化。如果一直有输出或一直无输出要调节 HC-SR501 上的两个电位器。全部模块单独正常工作后再整合完整程序。5.2 演示建议毕业设计现场演示时建议提前准备几个有说服力的场景用手握 DS18B20风扇转速会提高。这个过程只需要几秒钟体感明显。让手掌在 HC-SR501 前晃动风扇会立刻启动然后人离开后几十秒自动停止。按键切换到手动模式手动加减档位时 OLED 上档位和风速同步变化。将 OLED 放到显著位置让评委能直观看到温度、模式等数据。如果答辩现场环境温度已经高于 30℃可以让温度一直处于高速状态演示“降温到阈值后自动恢复低速”可能比较困难。这时可以在代码临时把阈值改高例如 35℃方便现场演示。6. 常见问题与排查思路在 STM32 开发过程中最容易出问题的地方通常是硬件接线和底层延时下面列举高频问题。6.1 程序可以下载但 OLED 不显示可能原因OLED 的 SCL/SDA 引脚接错。I2C 地址不对。核心板供电不足OLED 屏幕异常。焊接虚焊或杜邦线接触不良。解决措施使用 I2C 扫描程序扫描地址。检查核心板原理图确认复用引脚。替换一根杜邦线或直接焊接。6.2 DS18B20 读取温度固定为 85℃ 或 0℃DS18B20 上电后的默认温度为 85℃如果读到 85℃ 且不变化大概率数据转换没成功或程序读到了“上电默认值”。如果温度持续显示 85℃重点检查GPIO 引脚模式是否配置为开漏/上拉输入。单总线是否需要外部 4.7kΩ 上拉电阻。复位脉冲和时序的延时是否准确。DS18B20 数据线通常需要接一个 4.7kΩ 上拉电阻到 VCC虽然部分开发板已经内置但使用杜邦线连接时建议自行加上。6.3 风扇转速无法调节只高速或只停可能原因PWM 频率太低或太高导致驱动异常。电机驱动部分没有真正接 PWM 信号而是接了普通高电平。Timer 通道复用冲突GPIO 没有配置为复用推挽输出。解决办法用示波器或简单用 LED 亮度观察 PWM 输出是否变化。在代码中先写死不同的占空比验证是否逐级变化。检查是否用了正确的定时器通道函数例如 TIM2_CH2 要调用 TIM_SetCompare2。6.4 HC-SR501 误触发或一直触发HC-SR501 模块上有两个电位器分别调节灵敏度和延时。上电前需要先等待 30~60 秒预热。如果输出一直为高可以逆时针减小灵敏度。如果输出常低可以顺时针调高灵敏度。6.5 系统频繁复位或 OLED 花屏常见原因是电源纹波过大或者电机启动瞬间电流拉低电压。解决方式电机电源与控制逻辑电源分开电机使用单独电源。在电机电源两端并联 100μF 电解电容和 104 陶瓷电容。电机是感性负载时续流二极管必须接好否则驱动管容易被击穿。问题现象常见原因解决思路OLED 无显示接线错误/地址不对检查 I2C 地址与接线温度不变 85℃DS18B20 上拉电阻或时序错误检查上拉电阻与延时函数风扇转速不均PWM 频率不匹配调整定时器 PSC/ARR电机启动导致复位供电不足或被拉低分离电源并加去耦电容人体传感器乱触发模块调试参数不合适调节灵敏度和延时旋钮7. 最佳实践与工程化建议7.1 代码规范与可维护性毕业设计如果是多人配合或需要后续扩展建议代码上注意分层驱动层只负责硬件寄存器操作和底层协议例如 DS18B20 驱动只提供 GetTemperature。逻辑层只处理模式切换、档位计算、温度阈值判断。应用层负责把结果显示到 OLED 或响应按键。这样做的最大好处是以后把 DS18B20 换成 DHT11或者把 OLED 换成 LCD只需要重写驱动层核心控制逻辑不用大改。7.2 异常处理与边界条件不要假设传感器永远正常。例如 DS18B20 拔掉时读取温度可能返回极值容易导致风扇误动作。建议在代码里增加温度合法范围判断if ((temperature -20.0f) || (temperature 80.0f)) { // 传感器异常进入安全模式停止风扇并提示报警 PWM_SetDuty(0); return; }如果人体传感器故障一直输出低电平建议设计“故障超时”或“手动模式兜底”避免整个系统无法使用。7.3 低功耗与节能策略绿色智能风扇的“绿色”体现在使用 PWM而不是电阻降压调速能显著降低损耗。无人自动延时停机。温度不高的场景尽量让风扇低速运转而不是开关频繁切换。睡眠模式下OLED 可以关闭背光或降低刷新率。如果项目要求更严格的功耗指标可以让 STM32 进入 STOP 或 STANDBY 模式通过 RTC 定时唤醒或人体传感器外部中断唤醒。当然这会增加代码复杂度答辩时可以作为“改进方向”来写。7.4 硬件安全提醒如果最终方案使用 220V 交流风扇务必注意以下几点强电与弱电必须隔离。控制板不要裸露接触高压。继电器或可控硅需要满足额定电流。实际操作和演示时必须有指导教师在场注意人身安全。如果只是毕设展示通常建议以 5V 直流风扇作为执行机构安全、方便、演示效果一样良好。7.5 扩展方向如果一个基础自动控制风扇不足以体现工作量可以从以下方向扩展增加手机 App 控制通过 ESP8266/蓝牙模块与 STM32 通信实现远程开关和调档。增加 WIFI 温湿度云上报通过 ESP8266 MQTT 上传数据到云平台。增加语音控制用 LD3320 或离线语音模块识别“打开风扇”“提高风速”等指令。增加太阳能充电供电模块进一步体现“绿色”主题。增加学习记忆功能记录用户的使用习惯自动在常用时段提前调速。扩展时需要注意 STM32F103C8T6 的引脚资源如果外设过多可以把显示部分换成串口屏或者升级到 STM32F407 等更多引脚/更高性能的型号。8. 总结与后续学习建议本文以智能绿色风扇为例完整梳理了一个基于 STM32 的自动化控制系统从需求分析到硬件设计、代码实现、联调排错的闭环流程。核心知识点包括温度传感器 DS18B20 的单总线读取方法。STM32 定时器输出 PWM 的原理与配置。人体红外传感器在节能控制中的使用方式。自动/手动模式切换的状态机编程思路。OLED 信息显示与按键交互设计。如果你正在准备单片机毕业设计下一步建议先不要急着写大量代码而是把上面第 2 节中的硬件连接图画清楚然后按照 5.1 的顺序逐模块验证。每完成一个模块就拍照留档这些都是论文和答辩 PPT 中可以使用的素材。在此基础上如果想让方案更有竞争力可以继续研究 FreeRTOS 在 STM32 上的移植把温度采集、按键扫描、屏幕刷新放到不同任务里这会让代码结构上一个台阶。也可以结合 WIFI 模块做 App 联动增加项目的现代感。学习时建议多阅读官方参考手册和数据手册尤其是定时器与 GPIO 章节这是理解所有外设的基础。对代码中的每一处延时、每一个寄存器配置都尝试回答“如果删除这行会怎样”能够帮助你快速建立系统级认识。实物调试时不要怕出问题把每个现象记录下来多试几组参数你会逐渐积累出一套属于自己的排查方法。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门