这个基于STM32的自动调速风扇系统,本质上是一个典型的嵌入式温控应用。它通过温度传感器实时监测环境温度,结合人体红外感应模块的状态,自动调节风扇转速。系统采用模块化设计,主要包含五个核心部分:STM32F103C8T6最小系统板作为主控、L298N电机驱动模块、HC-SR501红外传感器、DS18B20温度传感器以及0.96寸OLED显示屏。
为什么选择STM32F103C8T6?这款Cortex-M3内核的MCU具有72MHz主频、64KB Flash和20KB RAM,完全满足实时控制需求,且价格低廉。更重要的是它内置高级定时器,可直接生成PWM波控制电机转速。
系统工作流程分为自动和手动两种模式:
这种设计既保证了日常使用的便利性,又保留了手动控制的灵活性。特别值得注意的是红外传感器的引入——只有当检测到人体存在时才会启动风扇,这个设计显著降低了能耗。
STM32F103C8T6最小系统板包含:
实测中发现:使用内部RC振荡器虽然节省成本,但会导致PWM频率漂移。建议始终使用外部晶振,特别是需要精确控制电机转速时。
L298N是双H桥驱动芯片,关键参数:
接线示意图:
code复制L298N STM32 DC Motor
IN1 PB5 Motor+
IN2 PB6 Motor-
ENA PB4(PWM) N/C
12V 12V电源
GND GND
常见问题:电机不转时先检查ENA使能端是否接PWM信号。很多初学者会忘记这个关键点。
DS18B20采用单总线协议,具有以下特点:
典型电路连接:
code复制DS18B20 STM32
VDD 3.3V
DQ PA0(需4.7K上拉)
GND GND
调试技巧:单总线对时序要求严格,建议将GPIO配置为开漏输出模式,并精确控制延时。遇到读取失败时,可用逻辑分析仪抓取波形。
HC-SR501工作特性:
接线方式:
code复制HC-SR501 STM32
VCC 5V
OUT PA1
GND GND
注意事项:模块需要2-3分钟初始化时间,期间可能误触发。实际应用中应添加软件滤波,如连续5次检测到信号才判定为有人。
SSD1306驱动的OLED屏优势:
I2C配置:
code复制OLED STM32
VCC 3.3V
GND GND
SCL PB8
SDA PB9
显示优化:频繁刷新会导致屏幕闪烁。建议使用双缓冲机制,先在内存中构建完整帧再一次性写入。
完整的系统初始化包含以下步骤:
c复制void RCC_Configuration(void) {
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
}
c复制void Peripheral_Init(void) {
GPIO_Config();
TIM3_PWM_Init(999, 71); // 10kHz PWM
I2C_Config();
DS18B20_Init();
OLED_Init();
}
c复制SystemState sys = {
.mode = AUTO_MODE,
.gear = 0,
.temp = 0.0,
.human_detected = 0
};
定时器3配置为PWM模式:
c复制void TIM3_PWM_Init(u16 arr, u16 psc) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 时基配置
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
占空比设置函数:
c复制void Set_Motor_Speed(uint8_t gear) {
static const uint16_t duty_table[] = {0, 300, 500, 800, 1000};
if(gear <= 4) {
TIM_SetCompare1(TIM3, duty_table[gear]);
}
}
电机控制经验:直流电机在低速时转矩较小,建议设置最低30%的占空比以保证可靠启动。PWM频率选择10kHz可避免可闻噪声。
DS18B20温度读取流程:
c复制float Read_Temperature(void) {
uint8_t tempL, tempH;
int16_t temp;
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 启动转换
Delay_ms(750); // 等待转换完成
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读取暂存器
tempL = DS18B20_ReadByte();
tempH = DS18B20_ReadByte();
temp = (tempH << 8) | tempL;
return temp * 0.0625f; // 转换为摄氏度
}
温度滤波算法:原始数据可能存在波动,建议采用滑动平均滤波:
c复制#define FILTER_LEN 5
float temp_history[FILTER_LEN];
uint8_t filter_idx = 0;
float Filter_Temperature(float new_temp) {
temp_history[filter_idx++] = new_temp;
if(filter_idx >= FILTER_LEN) filter_idx = 0;
float sum = 0;
for(uint8_t i=0; i<FILTER_LEN; i++) {
sum += temp_history[i];
}
return sum / FILTER_LEN;
}
系统主状态机实现:
c复制typedef enum {AUTO_MODE, MANUAL_MODE} SystemMode;
void System_Update(SystemState *sys) {
// 读取传感器数据
sys->temp = Read_Temperature();
sys->human_detected = HC_SR501_Read();
// 自动模式逻辑
if(sys->mode == AUTO_MODE) {
if(sys->human_detected) {
if(sys->temp < 25.0f) sys->gear = 0;
else if(sys->temp < 28.0f) sys->gear = 1;
else if(sys->temp < 32.0f) sys->gear = 2;
else if(sys->temp < 36.0f) sys->gear = 3;
else sys->gear = 4;
} else {
sys->gear = 0; // 无人时停止
}
}
// 更新电机状态
Set_Motor_Speed(sys->gear);
// 更新显示
OLED_Display_Update(sys);
}
按键处理函数:
c复制void Key_Handler(SystemState *sys) {
static uint8_t menu_level = 0;
if(KEY1_Pressed()) { // 模式选择
menu_level = !menu_level;
OLED_Clear_Menu();
}
if(KEY2_Pressed()) { // 确认
sys->mode = menu_level ? MANUAL_MODE : AUTO_MODE;
}
if(sys->mode == MANUAL_MODE) {
if(KEY3_Pressed()) sys->gear = (sys->gear < 4) ? sys->gear + 1 : 4;
if(KEY4_Pressed()) sys->gear = (sys->gear > 1) ? sys->gear - 1 : 1;
}
}
实测中发现的问题:
改进方案:
HC-SR501常见误触发原因:
软件滤波方案:
c复制#define DETECTION_THRESHOLD 3
uint8_t human_detect_count = 0;
uint8_t Reliable_Human_Detect(void) {
if(HC_SR501_Read()) {
if(++human_detect_count >= DETECTION_THRESHOLD) {
return 1;
}
} else {
human_detect_count = 0;
}
return 0;
}
DS18B20长距离布线时注意:
不同PWM频率的对比:
| 频率范围 | 优点 | 缺点 |
|---|---|---|
| 1kHz-5kHz | 驱动简单 | 可能有可闻噪声 |
| 10kHz-20kHz | 无噪声,效率高 | 需要更高性能MCU |
| >20kHz | 完全静音 | 驱动电路成本高 |
本项目选择10kHz的折中方案,既保证静音又不会对STM32造成过大负担。
可添加ESP8266模块实现WiFi控制:
典型接线:
code复制ESP8266 STM32
VCC 3.3V
GND GND
TX PA3
RX PA2
当前的分段控制可改进为PID连续调速:
c复制typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
} PID_Controller;
float PID_Update(PID_Controller *pid, float setpoint, float input) {
float error = setpoint - input;
pid->integral += error;
if(pid->integral > 1000) pid->integral = 1000;
if(pid->integral < -1000) pid->integral = -1000;
float derivative = error - pid->prev_error;
pid->prev_error = error;
return pid->Kp*error + pid->Ki*pid->integral + pid->Kd*derivative;
}
添加电流检测电路(如INA219)实现:
机械设计建议:
这个项目最让我惊喜的是STM32的PWM控制精度——通过简单的配置就能实现精确的电机转速控制。在实际调试中发现,给L298N的使能端加上100nF的去耦电容,能显著改善电机低速运行时的稳定性。