STM32 智能交互风扇(按键控制+电机调速+LED状态+OLED显示)

张开发
2026/4/16 5:37:59 15 分钟阅读

分享文章

STM32 智能交互风扇(按键控制+电机调速+LED状态+OLED显示)
1. 项目背景与核心功能最近在工作室捣鼓STM32的时候突然想到做个能实时显示状态的智能风扇应该挺有意思。这个项目最吸引我的地方在于它把嵌入式开发中最常用的几种外设控制都串起来了——GPIO中断处理按键、定时器PWM控制电机、I2C驱动OLED屏还有LED状态指示。实际做下来发现这种多外设联动的项目特别适合用来练手。这个智能风扇有三个核心功能一是通过物理按键切换三档风速低速/中速/高速二是电机转速会随档位实时变化三是OLED屏会同步显示当前风速和PWM占空比。我特意加了LED流水灯效果作为视觉反馈不同档位对应不同的灯光模式。比如低速时只有两个LED交替闪烁高速时四个LED会跑马灯式循环操作起来特别有仪式感。2. 硬件选型与电路设计2.1 主控与外围器件选择主控用的STM32F103ZET6这款Cortex-M3内核的芯片性价比超高72MHz主频完全够用。电机驱动选的L9110S这种双H桥芯片特别适合驱动小功率直流电机最大输出电流能达到800mA。OLED屏是0.96寸的SSD1306I2C接口省引脚显示效果也够清晰。有个细节要注意L9110S的工作电压是2.5-12V而电机额定电压是5V。我在PCB上单独放了两个电源接口电机用5V/2A的电源适配器供电STM32和OLED屏用3.3V供电。这样既避免电机启动电流干扰MCU又能保证显示稳定。2.2 关键电路连接电机驱动部分的接线要特别注意L9110S的OA接PA15PWM输出OB接PC13固定低电平这样通过单路PWM就能实现调速。四个按键接PE2-PE4和PA0都配置成上升沿触发的外部中断。LED灯接PC0-PC3推挽输出模式。OLED的SCL/SDA分别接PB10/PB11记得要加上拉电阻。实测中发现个坑如果直接用杜邦线连接电机运行时可能会因为接触不良导致MCU复位。后来改用了带锁紧功能的接线端子问题就解决了。建议大家在PCB设计时大电流线路最好预留焊盘方便后期飞线加固。3. 软件开发环境搭建3.1 工具链配置我用的是STM32CubeMXKeil MDK的组合。CubeMX版本是6.5.0配置外设特别方便。安装时记得勾选STM32F1系列的HAL库支持。Keil要安装Device Family Pack for STM32F1xx建议用最新版的ARM Compiler 6。有个小技巧在CubeMX里配置工程时把Toolchain/IDE选为Makefile这样生成的代码可以直接用VSCodePlatformIO开发。我后来调试OLED显示时就切到VSCode了因为它的串口绘图功能超好用。3.2 关键外设初始化定时器2的PWM配置是核心时钟源选内部时钟Channel1设成PWM Generation1。预分频系数PSC设为7199自动重载值ARR99这样得到的PWM频率就是72MHz/(71991)/(991)100Hz。这个频率对电机调速来说刚刚好既不会听到明显的啸叫声控制精度也足够。I2C配置为标准模式100kHz要开启I2C中断。OLED的初始化代码要放在主循环前记得先执行清屏操作。按键中断优先级保持默认就行但要在NVIC里把所有EXTI线都使能。4. 核心功能实现细节4.1 多档位PWM调速在HAL_TIM_PWM_Start()之后电机并不会立即转动因为CCR寄存器初始值为0。我在中断回调函数里用__HAL_TIM_SET_COMPARE()动态修改占空比30%对应低速档CCR3060%中速档90%高速档。实测发现占空比低于20%时电机可能无法启动所以最低档设在了30%。有个优化点直接切换占空比会导致转速突变电机可能打齿。后来我改成每次增减10%的渐变方式用for循环实现软启动。代码里加了限制条件防止占空比超过100%for(int icurrent_duty; i!target_duty; istep){ __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, i); HAL_Delay(50); }4.2 状态同步机制定义了一个全局变量sys_state来同步所有外设状态typedef enum { OFF_MODE, LOW_SPEED, MEDIUM_SPEED, HIGH_SPEED } FanState;按键中断里修改这个状态量主循环根据当前状态控制LED和OLED。比如中速档时OLED显示风速中速60%同时LD1-LD3三个灯开始流水效果。这种状态机设计让代码逻辑特别清晰后期加新功能也方便。5. 显示界面优化技巧5.1 汉字显示实现用Pctolcd2002生成字模时建议选阴码逐列式顺向模式字体大小16x16。一个实用技巧把常用汉字如风速、低速等做成数组再用指针数组索引const uint8_t CHS_风速[] {...}; const uint8_t *chs_table[] {CHS_风速,...}; void ShowChinese(uint8_t x, uint8_t y, uint8_t idx){ OLED_ShowCHinese(x, y, chs_table[idx]); }5.2 动态效果设计除了静态文字我还加了动态进度条显示占空比。在OLED_DrawBMP()基础上封装了个函数void ShowProgressBar(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t percent){ OLED_DrawRectangle(x, y, xwidth, yheight); uint16_t fill_width (width-2)*percent/100; OLED_Fill(x1, y1, x1fill_width, yheight-1); }每次按键操作后屏幕会先全白闪烁三次再刷新内容。这个视觉反馈特别重要能明确告知用户操作已生效。实现起来就几行代码for(int i0; i3; i){ OLED_Fill(0,0,128,64,1); HAL_Delay(100); OLED_Fill(0,0,128,64,0); HAL_Delay(100); }6. 常见问题排查6.1 电机异常抖动调试时遇到过电机间歇性抖动的问题后来发现是电源功率不足。用万用表测量发现电机启动瞬间电压会被拉到4V以下。解决方法有两个一是给电机电源并联大电容我加了2个1000μF的电解电容二是采用分时上电策略先让MCU和OLED启动完成再使能电机。6.2 OLED显示花屏I2C通信不稳定会导致花屏。首先要检查上拉电阻我用的是4.7kΩ其次可以降低I2C时钟速度。如果还不行试试在每次传输前加I2C总线恢复序列void I2C_Recovery(){ GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_OD; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); for(int i0; i9; i){ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); HAL_Delay(1); } }7. 功能扩展方向现在已经实现了基础功能但还有不少升级空间。比如可以加个温湿度传感器实现自动调速。我用DHT11试过读取到温度超过30度就自动切到高速档。代码里加个条件判断就行if(temp 30 sys_state ! HIGH_SPEED){ SetFanSpeed(HIGH_SPEED); OLED_ShowString(0,5,Auto Mode); }另一个有意思的改进是加入语音控制。我用LD3320模块实现了简单的开机、加速等指令识别。需要额外开个定时器做超时检测防止误触发。还可以通过串口连接上位机用Python写个控制界面实时显示转速曲线。

更多文章