尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于STM32的智能风扇系统实战:温度控制、PWM调速与模式切换

基于STM32的智能风扇系统实战:温度控制、PWM调速与模式切换 最近在做一个嵌入式课程设计需要实现一个智能风扇系统要求能根据温度自动调节风速还要支持手动和自动双模式切换。网上找了一圈发现很多教程要么代码不完整要么缺少关键配置说明调试起来特别费劲。正好手头有江科大的STM32开发套件于是决定自己从头实现一个完整的智能风扇项目把温度采集、PWM控制、模式切换、报警功能都整合进去。本文就是这次项目的完整实战记录从硬件选型、电路设计到代码编写、调试优化每一步都有详细说明和完整代码。无论你是正在做课程设计的学生还是想学习STM32实际应用的开发者都能跟着一步步做出可用的智能风扇系统。1. 项目背景与核心功能设计智能风扇是一个典型的嵌入式物联网应用它融合了传感器数据采集、执行器控制和用户交互。使用STM32单片机作为核心控制器成本低、性能强、生态完善非常适合作为学习和项目开发平台。本项目基于江科大STM32开发套件通常指正点原子或野火等常见开发板核心为STM32F103系列实现一个具备以下核心功能的智能风扇系统温度监测与显示通过DS18B20数字温度传感器实时采集环境温度并在OLED屏幕上显示。自动温控模式系统根据设定的温度阈值自动控制风扇转速。例如温度低于25°C时风扇停止温度在25°C~30°C时风扇低速运行温度高于30°C时风扇全速运行。手动控制模式用户可以通过按键手动设置风扇的PWM占空比即风速等级实现无级或有级调速。双模式切换通过另一个按键在“自动模式”和“手动模式”之间自由切换当前模式在OLED上有明确标识。温度报警功能当温度超过设定的安全阈值如35°C时系统通过蜂鸣器鸣叫和LED闪烁进行声光报警提醒用户注意。系统整体架构 传感器DS18B20将温度数据发送给STM32STM32核心根据当前模式自动/手动和算法计算出应对的PWM信号输出给风扇驱动电路如MOS管从而控制风扇转速。同时STM32驱动OLED显示状态并监听按键输入。整个逻辑清晰涵盖了输入、处理、输出、人机交互等嵌入式系统核心环节。2. 硬件准备与电路设计“江科大STM32套件”通常包含STM32最小系统板、下载器、杜邦线、常用传感器和模块。以下是本项目所需的核心硬件清单主控芯片STM32F103C8T6最小系统板核心板。温度传感器DS18B20单总线数字温度传感器精度±0.5°C。显示模块0.96寸OLED显示屏I2C接口SSD1306驱动。风扇模块5V直流风扇注意电流开发板IO口不能直接驱动。驱动电路N沟道MOS管如IRF540N或电机驱动模块如L298N用于驱动风扇。输入设备两个轻触按键。报警设备有源蜂鸣器、LED灯。其他电阻、面包板、杜邦线若干。2.1 核心电路连接原理STM32的IO口驱动能力有限通常8-20mA无法直接驱动风扇电流可能超过100mA。因此必须使用MOS管或电机驱动芯片作为“开关”和“放大器”。1. 风扇驱动电路关键这是最容易出问题的地方。推荐使用N沟道MOS管方案成本低、电路简单。STM32 PWM输出引脚如PA8连接到MOS管的栅极(G)。MOS管的漏极(D)连接风扇的负极。风扇的正极接5V电源正极。MOS管的源极(S)接地GND。在栅极(G)和地(GND)之间连接一个10kΩ的下拉电阻确保STM32复位期间MOS管关闭。在栅极(G)和STM32引脚之间串联一个100-220Ω的电阻用于限流保护STM32的IO口。电源注意开发板的3.3V或5V引脚可能无法提供风扇所需的大电流建议使用外部5V电源单独为风扇供电但务必确保外部电源的地GND和开发板的地GND连接在一起即“共地”。2. 温度传感器DS18B20连接DS18B20采用单总线协议仅需一根数据线。VDD接3.3V或5V。DQ数据线接STM32任一GPIO口如PA1同时通过一个4.7kΩ上拉电阻接VDD。GND接地。3. OLED显示屏连接I2CVCC接3.3V。GND接地。SCL接STM32的PB6I2C1_SCL。SDA接STM32的PB7I2C1_SDA。4. 按键连接两个按键分别接STM32的GPIO口如PA0 PA1另一端接地。GPIO配置为上拉输入模式按键未按下时读到的为高电平按下时为低电平。5. 蜂鸣器与LED有源蜂鸣器正极通过一个三极管或MOS管小功率可直接串联电阻接STM32 GPIO如PB8负极接地。LED阳极串联一个220Ω-1kΩ的限流电阻后接STM32 GPIO如PB9阴极接地。2.2 软件环境准备IDEKeil uVision 5 (MDK-ARM)。确保已安装STM32F1系列的Device Family Pack。固件库使用标准外设库Standard Peripheral Library或HAL库均可。本文示例基于标准外设库因为它更贴近硬件便于理解原理。江科大的教学资料也多以此为基础。串口调试助手用于调试时打印数据如SecureCRT、Putty或野火/正点原子自带的调试助手。程序下载器ST-Link V2 或 J-Link用于将编译好的程序烧录到STM32芯片中。3. 软件架构与关键驱动实现在开始写主程序之前我们需要先为各个外设编写可靠的底层驱动程序。一个好的驱动应该封装好初始化、读、写等基本操作为上层的应用逻辑提供清晰的接口。3.1 DS18B20温度传感器驱动DS18B20的时序要求严格需要微秒级延时。我们首先实现底层的延时、复位、读写位函数。// ds18b20.c #include ds18b20.h #include delay.h // 需要一个精准的微秒延时函数需自己实现或使用SysTick // 引脚定义根据实际连接修改 #define DS18B20_PORT GPIOA #define DS18B20_PIN GPIO_Pin_1 #define DS18B20_DQ_OUT PAout(1) // 输出宏来自GPIO位带操作 #define DS18B20_DQ_IN PAin(1) // 输入宏 // 初始化将引脚设置为推挽输出 void DS18B20_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 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); DS18B20_DQ_OUT 1; // 先拉高 } // 写一个字节 void DS18B20_WriteByte(u8 dat) { u8 i; for(i0; i8; i) { DS18B20_DQ_OUT 0; // 拉低开始写时序 delay_us(5); // 保持低电平至少1us我们延时5us DS18B20_DQ_OUT dat 0x01; // 输出数据位 delay_us(60); // 保持60us DS18B20_DQ_OUT 1; // 释放总线 dat 1; delay_us(5); // 恢复时间 } } // 读一个字节 u8 DS18B20_ReadByte(void) { u8 i, value 0; for(i0; i8; i) { value 1; DS18B20_DQ_OUT 0; // 拉低开始读时序 delay_us(2); // 拉低2us后释放 DS18B20_DQ_OUT 1; delay_us(10); // 等待10us后采样 if(DS18B20_DQ_IN) value | 0x80; delay_us(50); // 等待该时隙结束 } return value; } // 开始温度转换 void DS18B20_Start(void) { DS18B20_Init(); DS18B20_Reset(); // 复位函数需实现 DS18B20_WriteByte(0xCC); // 跳过ROM命令 DS18B20_WriteByte(0x44); // 开始转换命令 } // 读取温度值整数部分 float DS18B20_GetTemp(void) { u8 tempL, tempH; u16 temp; float temperature; DS18B20_Init(); DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器命令 tempL DS18B20_ReadByte(); tempH DS18B20_ReadByte(); temp (tempH 8) | tempL; if(temp 0x8000) { // 负温度 temperature -(float)((temp ^ 0xFFFF) 1) * 0.0625; } else { // 正温度 temperature (float)temp * 0.0625; // 精度0.0625°C/LSB } return temperature; }3.2 OLED显示屏驱动I2COLED驱动通常使用现成的ssd1306或oled.c库。这里展示关键初始化与显示函数接口。// oled.h #ifndef __OLED_H #define __OLED_H #include stm32f10x.h void OLED_Init(void); void OLED_Clear(void); void OLED_ShowString(u8 x, u8 y, const u8 *p); void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size); void OLED_ShowFloat(u8 x, u8 y, float num, u8 int_len, u8 dec_len, u8 size); void OLED_Refresh(void); // 更新显存到屏幕 #endif// oled.c (部分关键函数) // 假设已移植好底层I2C读写和字库 void OLED_ShowTempAndMode(float temp, u8 mode, u8 fan_speed) { OLED_Clear(); OLED_ShowString(0, 0, Temp:); OLED_ShowFloat(40, 0, temp, 2, 2, 16); // 显示温度如“25.50” OLED_ShowString(90, 0, C); OLED_ShowString(0, 2, Mode:); if(mode 0) { OLED_ShowString(40, 2, AUTO); } else { OLED_ShowString(40, 2, MANUAL); } OLED_ShowString(0, 4, Speed:); OLED_ShowNum(40, 4, fan_speed, 3, 16); // 显示速度百分比 OLED_ShowString(70, 4, %); OLED_Refresh(); }3.3 PWM输出配置用于控制风扇速度我们使用STM32的定时器如TIM1的PWM输出功能来产生不同占空比的方波控制MOS管的通断时间从而调节风扇两端的平均电压。// pwm.c #include pwm.h #include stm32f10x.h // 使用TIM1的通道1PA8输出PWM void PWM_Init(u16 arr, u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // 配置GPIO GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 配置定时器基础 TIM_TimeBaseStructure.TIM_Period arr; // 自动重装载值决定PWM频率 TIM_TimeBaseStructure.TIM_Prescaler psc; // 预分频系数 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 配置PWM模式 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; // 输出极性高 TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比为0风扇不转 TIM_OC1Init(TIM1, TIM_OCInitStructure); // 使能预装载寄存器 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); // 使能定时器 TIM_Cmd(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE); // 高级定时器必须使能主输出 } // 设置PWM占空比 // compare: 比较值范围0~arr。占空比 compare / (arr1) void PWM_SetCompare1(u32 compare) { TIM_SetCompare1(TIM1, compare); }参数计算PWM频率 定时器时钟 / ((arr1)(psc1))。例如定时器时钟72MHz设置arr999psc71则PWM频率 72,000,000 / (100072) 1000Hz (1kHz)。这是一个适合电机控制的频率。3.4 按键扫描与模式切换我们需要两个按键KEY_MODE用于切换自动/手动模式KEY_UP在手动模式下增加风速。// key.c #include key.h #include delay.h // 引脚定义 #define KEY_MODE_GPIO_PORT GPIOA #define KEY_MODE_GPIO_PIN GPIO_Pin_0 #define KEY_UP_GPIO_PORT GPIOA #define KEY_UP_GPIO_PIN GPIO_Pin_1 // 初始化上拉输入 void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin KEY_MODE_GPIO_PIN | KEY_UP_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); } // 扫描函数返回按键值带消抖 // 0: 无按键1: KEY_MODE按下2: KEY_UP按下 u8 KEY_Scan(void) { static u8 key_up 1; // 按键松开标志 if(key_up (KEY_MODE0 || KEY_UP0)) { delay_ms(10); // 消抖 key_up 0; if(KEY_MODE 0) return 1; else if(KEY_UP 0) return 2; } else if(KEY_MODE1 KEY_UP1) { key_up 1; } return 0; }4. 核心逻辑与主程序实现有了底层驱动我们就可以编写主程序的业务逻辑了。核心是一个状态机根据当前模式决定如何响应传感器数据和按键输入。4.1 全局变量与宏定义// main.c 部分全局变量 #include stm32f10x.h #include delay.h #include pwm.h #include ds18b20.h #include oled.h #include key.h #include beep.h // 蜂鸣器驱动 #include led.h // LED驱动 // 系统工作模式 typedef enum { MODE_AUTO 0, MODE_MANUAL } FanMode_t; FanMode_t g_fan_mode MODE_AUTO; // 默认自动模式 u8 g_fan_speed_percent 0; // 风扇速度百分比 0-100 float g_current_temp 0.0; // 当前温度 float g_temp_high_alarm 35.0; // 高温报警阈值 // 温度控制阈值自动模式用 #define TEMP_THRESHOLD_LOW 25.0 #define TEMP_THRESHOLD_MID 30.0 #define TEMP_THRESHOLD_HIGH 35.0 // PWM周期值ARR与初始化一致 #define PWM_MAX_ARR 9994.2 自动模式控制算法在自动模式下系统根据采集到的温度按照预设的规则映射到PWM占空比。// 根据温度计算自动模式下的PWM占空比 u8 AutoMode_CalcSpeed(float temp) { u8 speed 0; if(temp TEMP_THRESHOLD_LOW) { speed 0; // 低于25度关闭 } else if(temp TEMP_THRESHOLD_LOW temp TEMP_THRESHOLD_MID) { // 25-30度线性增速从0%到50% speed (u8)((temp - TEMP_THRESHOLD_LOW) / (TEMP_THRESHOLD_MID - TEMP_THRESHOLD_LOW) * 50); } else if(temp TEMP_THRESHOLD_MID temp TEMP_THRESHOLD_HIGH) { // 30-35度线性增速从50%到100% speed 50 (u8)((temp - TEMP_THRESHOLD_MID) / (TEMP_THRESHOLD_HIGH - TEMP_THRESHOLD_MID) * 50); } else { speed 100; // 高于35度全速 } // 限制在0-100 if(speed 100) speed 100; return speed; }4.3 手动模式控制逻辑手动模式下风速由用户通过KEY_UP按键控制。每次按下风速增加一个步进如10%达到100%后循环回0%。// 手动模式风速调整 void ManualMode_AdjustSpeed(void) { g_fan_speed_percent 10; // 每次增加10% if(g_fan_speed_percent 100) { g_fan_speed_percent 0; // 超过100%则归零 } }4.4 温度报警处理当温度超过报警阈值时触发声光报警。这里采用非阻塞方式在循环中检查并控制报警器。// 报警控制函数 void Alarm_Control(float temp) { static u32 alarm_tick 0; if(temp g_temp_high_alarm) { // 超过阈值触发报警例如LED闪烁蜂鸣器间歇鸣叫 if((SysTick-VAL % 1000) 500) { // 简易闪烁约1Hz LED_ON(); BEEP_ON(); } else { LED_OFF(); BEEP_OFF(); } } else { // 温度正常关闭报警 LED_OFF(); BEEP_OFF(); } }4.5 主函数与主循环将以上所有模块整合到主函数中。int main(void) { u8 key_value 0; u32 last_temp_read_time 0; u32 last_display_update_time 0; // 系统初始化 delay_init(); // 延时函数初始化使用SysTick PWM_Init(999, 71); // PWM频率1kHz DS18B20_Init(); OLED_Init(); KEY_Init(); LED_Init(); BEEP_Init(); OLED_ShowString(0, 0, Smart Fan Init...); delay_ms(1000); OLED_Clear(); while(1) { u32 current_time SysTick-VAL; // 获取系统tick需根据实际情况调整 // 1. 按键扫描与处理约每10ms扫描一次 key_value KEY_Scan(); if(key_value 1) { // 模式切换键 g_fan_mode (g_fan_mode MODE_AUTO) ? MODE_MANUAL : MODE_AUTO; // 切换模式时可以有些提示比如蜂鸣器响一声 BEEP_ON(); delay_ms(100); BEEP_OFF(); } else if(key_value 2 g_fan_mode MODE_MANUAL) { // 手动模式下调速 ManualMode_AdjustSpeed(); } // 2. 温度采集约每500ms采集一次避免DS18B20频繁操作 if(current_time - last_temp_read_time 500) { DS18B20_Start(); // 开始转换 delay_ms(750); // 等待转换完成12位精度最大需750ms g_current_temp DS18B20_GetTemp(); last_temp_read_time current_time; } // 3. 根据模式计算目标风速 u8 target_speed 0; if(g_fan_mode MODE_AUTO) { target_speed AutoMode_CalcSpeed(g_current_temp); // 自动模式下更新全局速度变量用于显示 g_fan_speed_percent target_speed; } else { target_speed g_fan_speed_percent; // 手动模式直接用设定的速度 } // 4. 设置PWM输出将百分比转换为比较值 u32 compare (target_speed * PWM_MAX_ARR) / 100; PWM_SetCompare1(compare); // 5. 报警判断与控制 Alarm_Control(g_current_temp); // 6. 更新显示约每200ms更新一次避免OLED刷新过快 if(current_time - last_display_update_time 200) { OLED_ShowTempAndMode(g_current_temp, g_fan_mode, g_fan_speed_percent); last_display_update_time current_time; } delay_ms(10); // 主循环延时降低CPU占用 } }5. 项目调试与常见问题排查将代码编译下载后可能遇到各种问题。以下是基于经验的排查清单。问题现象可能原因排查步骤与解决方案风扇不转1. 电源问题2. PWM无输出3. MOS管接线错误或损坏4. 风扇损坏1. 用万用表测量风扇两端电压检查5V电源和共地。2. 用示波器或逻辑分析仪检查STM32的PWM引脚如PA8是否有波形输出。如果没有检查PWM初始化代码和引脚配置。3. 检查MOS管的G、D、S极是否接反栅极下拉电阻和限流电阻是否焊接良好。可以用导线短暂连接D和S看风扇是否转动判断MOS管好坏。4. 直接给风扇加5V看是否转动。温度显示为0或85°C固定值1. DS18B20初始化或时序错误2. 上拉电阻未接或接触不良3. 传感器损坏1. 检查DS18B20的DQ线是否通过4.7kΩ电阻上拉到3.3V/5V。2. 用逻辑分析仪抓取单总线时序与DS18B20数据手册对比。重点检查复位脉冲、应答脉冲的宽度。3. 检查delay_us()函数的准确性DS18B20对微秒延时非常敏感。4. 更换一个DS18B20传感器试试。OLED白屏或不显示1. I2C地址错误2. I2C引脚配置错误3. 初始化序列错误4. 电源问题1. 确认OLED模块的I2C地址通常是0x78或0x7A。2. 用示波器检查SCL和SDA线上是否有波形。检查GPIO是否配置为开漏输出并上拉。3. 检查OLED初始化代码确保发送了正确的命令序列。4. 测量OLED模块的VCC是否为3.3V。按键无反应或连按1. GPIO模式配置错误应为上拉输入2. 按键消抖处理不当3. 按键硬件接触不良1. 确认按键GPIO初始化为GPIO_Mode_IPU上拉输入。2. 在按键扫描函数中增加合适的延时消抖10-20ms。3. 用万用表测量按键按下时IO口是否被拉低到0V。模式切换后风扇行为不对1. 全局变量g_fan_mode未正确更新2. 自动/手动速度计算逻辑有误3. PWM设置未在模式切换后立即更新1. 在OLED上显示当前模式确认按键是否成功切换了g_fan_mode。2. 在自动模式下用手捂住DS18B20加热观察OLED显示的速度百分比是否按预期变化。3. 确保每次循环都根据当前模式重新计算并设置PWM。蜂鸣器不响或常响1. 蜂鸣器是有源还是无源接错2. 驱动三极管/MOS管电路错误3. GPIO控制逻辑反了1.有源蜂鸣器给高电平就响给低电平就停。无源蜂鸣器需要给一定频率的PWM信号才会响。本项目用有源蜂鸣器做报警提示更简单。2. 检查驱动管的基极/栅极是否通过电阻连接到STM32 IO口集电极/漏极是否接蜂鸣器正极。3. 检查代码中BEEP_ON()是置高还是置低。调试建议分模块调试不要一次性写完所有代码。先单独测试DS18B20能否读出温度并串口打印再单独测试PWM能否驱动风扇最后测试OLED显示。善用串口打印在关键逻辑处使用printf通过串口输出变量值如温度、模式、速度这是最直接的调试手段。需要初始化串口并重定向fputc函数。使用调试器利用ST-Link和Keil的在线调试功能设置断点观察变量实时值单步执行能快速定位逻辑错误。6. 功能优化与扩展建议完成基本功能后可以考虑以下优化和扩展让项目更完善、更实用。6.1 增加更多控制模式自然风模式让PWM占空比随机或有规律地变化模拟自然风。睡眠模式在夜间根据时间自动降低风速或关闭风扇。定时关闭增加定时功能设置风扇运行一段时间后自动停止。6.2 改善用户交互增加更多按键添加“减速”、“设置”等按键。使用旋转编码器替代加减速按键实现更流畅的速度调节。添加声音反馈不同操作模式切换、报警使用不同频率的提示音。3. 使用更高级的控制器PID控制在自动模式下使用PID算法让风扇转速更平滑地跟随温度变化避免阶跃变化。// 简易PID思路伪代码 error target_temp - current_temp; integral error; derivative error - last_error; output Kp*error Ki*integral Kd*derivative; // 将output映射为PWM占空比状态机框架使用QP等状态机框架来管理“关机”、“自动”、“手动”、“报警”等系统状态使程序结构更清晰。6.4 增加通信与联网功能蓝牙/Wi-Fi控制接入ESP8266或HC-05模块通过手机APP或小程序控制风扇、设置参数、查看温度历史。上位机监控通过STM32的串口将温度、风速数据发送到电脑上位机绘制实时曲线。6.5 提升可靠性传感器数据滤波对DS18B20读取的温度值进行滑动平均滤波或中值滤波避免显示值跳动。#define FILTER_LEN 5 float temp_buffer[FILTER_LEN]; float Get_Filtered_Temp(float new_temp) { static u8 index 0; temp_buffer[index] new_temp; index (index 1) % FILTER_LEN; float sum 0; for(u8 i0; iFILTER_LEN; i) sum temp_buffer[i]; return sum / FILTER_LEN; }参数存储将用户设定的报警阈值、手动模式下的偏好速度等参数保存到STM32的Flash中掉电不丢失。看门狗启用独立看门狗IWDG防止程序跑飞导致风扇失控。这个基于江科大STM32套件的智能风扇项目从硬件连接到软件编程完整地走了一遍嵌入式系统开发流程。它不仅是一个课程设计更是一个理解传感器、执行器、PWM、人机交互和状态控制的绝佳范例。代码虽然不复杂但涵盖了嵌入式开发的核心思想感知、决策、执行。在实际动手时最关键的是耐心调试硬件电路和底层驱动。遇到问题按照第5部分的排查清单一步步来大部分都能解决。希望这份详细的教程和代码能帮你少走弯路顺利做出属于自己的智能风扇。
返回列表