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

资讯详情

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

基于STM32的温控风扇系统:从PWM调速到转速闭环的嵌入式实践

基于STM32的温控风扇系统:从PWM调速到转速闭环的嵌入式实践 1. 项目概况为什么我做了这个温控风扇而不是直接买一个做这个项目的原因挺直接的。我手头有一台常年吃灰的旧工控机风扇策略只有高中低三档温度上来得慢风扇却先全速转起来了夜里听着像飞机起飞。我本来想刷个BIOS或者干脆换台机器后来一看这块板子的EC逻辑还是决定自己动手做一套外置温控系统直接把风扇控制权从主板手里夺过来。市面上成品温控板不是没有几十块钱的都在卖但问题在于你没法改它的控温曲线也没法接自己的传感器更别提把数据通过串口发出来看曲线了。对普通用户来说够用对喜欢折腾的人来说完全不够。所以我干脆从零做了一套基于STM32的多功能智能温控风扇系统把代码、原理图、仿真工程全放出来了。这套系统具体能干什么用一句话概括实时采集温度按照自定义策略控制风扇转速同时把状态显示在屏幕上支持按键调节参数、串口上报数据、报警输出。它不绑定任何特定场景——你可以拿它给机箱散热、给功放机柜控温、给3D打印机仓体恒温甚至改一改传感器类型就能当孵化器控制器用。硬件主控选的是STM32F103C8T6最低配的Cortex-M3168MHz是没有的72MHz足够干这点活了。温度传感器用DS18B20PWM输出直接驱动一个12V的四线风扇OLED屏显示当前温度、目标温度、转速百分比和PID参数。整个系统还有一个非常关键的设计——所有控制逻辑全部从PID迁移到了分段PWM映射策略上这样既保证响应速度又避免了PID参数调不好的尴尬。我觉得这个项目对三类人最有价值正在学STM32但苦于没有完整项目练手的同学需要一套可靠温控方案但有修改需求的电子爱好者和创客还有想搞明白电机PWM控制、传感器滤波、串口协议这些底层细节的嵌入式入门者。文章后面我会把硬件设计、代码框架、仿真过程、实测数据全部展开该踩的坑一个不落全写出来。2. 硬件选型与原理图设计一堆元件的取舍逻辑2.1 STM32之外我为什么没选Arduino和ESP32先聊聊主控的选择这是整套系统的地基。很多教程为了降低门槛会用Arduino Uno配置一样能写甚至代码量还少一大截。但Arduino的GPIO翻转速度、定时器资源的灵活性、ADC精度在需要精度控温的场景下都差点意思。比如我要生成25kHz的PWM波Arduino Uno只有一个定时器能输出这个频率而且占空比分辨率会降到8位以下STM32的TIM1和TIM3都是16位定时器随便一配就是高分辨率PWM还不影响其他外设的工作。ESP32我也考虑过选项确实诱人——能连WiFi温度曲线能直接推到手机上看着挺炫。但问题也在这ESP32的ADC线性度一般官方文档里写得很清楚全量程精度约±6%在单片校准前这个误差做温度采集是不够看的。而且ESP32风扇控制这种低频任务杀鸡用牛刀功耗还高如果场景是无电池供电的机箱散热一个STM32F103的量级就够了。还有个现实因素STM32的器件生态太成熟了。Keil、STM32CubeMX、HAL库、标准外设库随便一搜都是资料。我用的是标准外设库SPL写的别觉得老SPL在F103上的稳定性经过十几年验证逻辑透明不像HAL库封装得你都不知道底层干了什么。2.2 DS18B20的接线、上拉电阻和信号完整性DS18B20是Dallas出的单总线数字温度传感器精度是12位模式下的±0.5°C。它的通讯协议很有意思一根线既传数据又传时钟时序要求微妙。有人觉得这个东西太老、速度太慢——确实一次完整转换需要750ms但在温控场景里这个实时性一点问题都没有你并不需要每毫秒都拿到新温度。接线要注意一个老生常谈但总有人栽跟头的点数据线上必须接上拉电阻。DS18B20靠总线上的寄生电容供电如果不上拉你会发现读回来的数据是乱码或者干脆找不到设备。上拉电阻我选择4.7kΩ这是官方手册的标准值。如果线缆超过1米改成2.2kΩ更稳妥。我的原理图上画的是3.3V上拉因为STM32的GPIO容忍5V输入但为了保险起见用3.3V上拉这样电平匹配更安全。画原理图时还有人喜欢在DS18B20的VCC和GND之间加一个0.1μF的退耦电容我强烈建议留这个位置。虽然单总线器件电流很小但走的线如果靠近PWM驱动端电源噪声会被引入信号线导致时序判断出错。PCB上这个电容离传感器本体越近越好。2.3 PWM驱动电路直接从GPIO到风扇是大忌这是整个项目里最核心的硬件设计点也是最多初学者会炸的地方。STM32的GPIO输出能力有限推挽输出时最大也就20mA左右3.3V电平也拉不动大多数12V风扇的满速运行。如果你直接把风扇接在GPIO上轻则风扇转速上不去重则STM32的引脚直接烧掉。所以中间必须加一级驱动。我的驱动方案是NPN三极管逻辑电平MOSFET的组合实际工作中其实一个S8050就能带动小风扇但为了给大电流风扇留余地我用了AO3400A这颗N沟道MOSFET。原理图上是这样接的STM32的PA1输出PWM信号经过1kΩ电阻接到AO3400A的栅极源极接地漏极接风扇的负极风扇正极接12V。当PWM为高电平时MOSFET导通风扇得电低电平时关闭风扇失电。这样靠PWM占空比控制风扇的平均电压从而控制转速。为什么栅极要串一个1kΩ电阻因为MOSFET的栅极有结电容在PWM切换的瞬间会产生很大的电流尖峰如果没有限流电阻这个尖峰会把STM32的GPIO打坏。另外在栅极和源极之间还需要一个10kΩ的下拉电阻防止MCU在复位的瞬间引脚悬空导致MOSFET误导通。四线风扇的测速线FG也很关键。我直接从风扇的FG引脚拉信号到STM32的PA8通过TIM1的输入捕获模式测转速。但注意这个FG输出是开漏结构必须要外部上拉到3.3V才能出波形否则你捕获到的永远是低电平。原理图上我用了10kΩ上拉实测波形干净利落每转一圈输出两个脉冲。2.4 显示、按键和供电别让简单的东西拖后腿显示我用的是0.96寸的OLEDI2C接口SSD1306控制器。选它不选LCD1602原因是1602需要背光控制、对比度调节引脚占得多而且显示内容太单一。OLED直接I2C挂上去就完事SCL接PB6SDA接PB7功耗还低。按键部分我设计了3个模式键切换显示模式、加键、减键。做了软件消抖硬件上就只接了上拉电阻到3.3V按下接地。注意STM32的GPIO内部有上拉我一直建议在原理图上还是老老实实画出外部上拉电阻因为内部上拉阻值约30-50kΩ在潮湿环境下不稳定而且你想接外部设备时排查问题会非常麻烦。供电是整个板子最容易忽视的环节。系统里同时存在3.3V的数字电路、12V的风扇电源如果一起从同一个LDO接出来风扇启动瞬间的电流跌落会把屏幕搞闪烁严重时直接复位MCU。我的设计是12V电源进来后先过一颗LM2596降压模块输出稳定的5V5V再通过AMS1117降到3.3V给MCU和OLED供电。风扇单独挂在12V上不受降压模块后级影响。这个两级降压设计思路以后做任何系统都可以沿用——数字部分和负载部分的电源一定要在物理上分开。2.5 完整的引脚分配表画原理图之前先把引脚规划好否则到了布线阶段会后悔。我最终的引脚分配如下功能引脚配置说明DS18B20数据PA0开漏输出外部上拉单总线协议PWM输出PA1TIM2_CH225kHz推挽输出OLED SCLPB6I2C1_SCL复用开漏OLED SDAPB7I2C1_SDA复用开漏风扇测速FGPA8TIM1_CH1输入捕获模式键PB0输入上拉EXTI外部中断加键PB1输入上拉轮询检测减键PB10输入上拉轮询检测报警输出PB5推挽输出接LED或蜂鸣器3. 软件框架那些教科书里不会写清楚的设计思路3.1 状态机架构温控系统不适合裸奔的while(1)如果你去看很多入门教程的代码主循环里常常是一个while(1)里塞了延时函数温度读取延时要750msOLED刷新延时要几十毫秒按键消抖延时要20ms——堆在一起整个系统就是一个串行阻塞的灾难现场。按键按下去要等温度读完才有反应这种体验放在温控系统里是绝对不合格的。我从一开始就确定用状态机加定时器中断的结构。所谓状态机就是把系统拆成几个互斥的状态初始化状态、正常运行状态、参数设置状态、报警状态。每个状态下面有对应的处理函数每次主循环扫描一遍当前状态该做的事然后快速回到循环继续扫描。这样任何时刻都不会因为一个操作卡死其他功能。关键是在后台把时间分片。我用SysTick做了一个1ms的时基在这个时基里维护几个标志位温度采集标志每1秒置一次、OLED刷新标志每200ms置一次、按键扫描标志每10ms置一次。主循环通过判断这些标志位去执行对应的任务。这样温度转换的750ms延时放在后台不用在代码里阻塞等待OLED的刷新也不会跟温度采集抢时间。3.2 DS18B20的驱动从时序图到手写驱动的全过程DS18B20的驱动是很多人第一次接触软件时序这个概念的地方。它不需要任何外设全靠GPIO的翻转延时来模拟时序所以写起来要格外注意。初始化时序是整个通信的开始主机把总线拉低至少480μs然后释放DS18B20会在15~60μs内把总线拉低响应存在脉冲。这个检测是必须的如果读不到这个脉冲就得检查接线和上拉电阻。读时序和写时序都是通过控制总线拉低的时间长短实现的。写0是拉低总线60~120μs写1是拉低1~15μs后释放。读数据则是主机拉低总线至少1μs后释放然后在15μs内采样电平。我用的延时函数是基于SysTick做的微秒级延时这在72MHz主频下没有任何问题。但提醒一句如果你把系统时钟改成了其他频率延时基准必须跟着改否则DS18B20的时序会漂移症状表现就是时好时坏非常难排查。驱动里还有一个很多人会忽略的细节——ROM匹配和搜索。如果总线上只挂一个DS18B20可以直接发0xCC跳过ROM指令省掉64位序列号的读取和匹配。但如果以后想多挂几个传感器求平均温度必须在初始化时读出每个传感器的ROM码然后按地址操作。我的代码里两种模式都留了接口读完序列号会打印到串口。3.3 温控策略从PID到分段PWM映射的选择温控策略是整个系统的灵魂。一开始我确实是用位置式PID实现的温度误差作为输入PWM占空比作为输出调参调到怀疑人生P大了振荡I大了超调D对噪声敏感而且不同风扇的响应特性还不一样。后来我把策略改成了分段线性映射。简单来说就是把温度区间切成几段每段用一条直线公式把温度映射到占空比。举例来说温度低于25°C占空比固定为0%风扇停转温度在25~40°C线性从0%升到60%温度在40~55°C线性从60%升到100%温度高于55°C占空比固定为100%全速这个映射用代码实现就是查表加插值不存在调参问题行为完全可预期。有人会说这样风扇转速会有阶梯感我加了滞回控制升温时用一条曲线降温时用另一条偏移2°C的曲线这样风扇不会在临界点来回抖。至于PID我保留了一个PID控制模式但只作为高级玩法藏在了设置菜单深处。默认出厂用的是分段PWM映射对绝大多数应用场景来说这个方案最简单、最稳、最实用。3.4 转速闭环用输入捕获把指令转速变成实际转速开环控制有个问题风扇在低温下容易启动不了。很多四线风扇的启动电压不是0%大概在20%~30%占空比左右。如果温度刚过阈值系统给了10%的PWM占空比风扇可能纹丝不动但你测电流会发现它在持续发热。为了彻底解决这个问题我加入了转速闭环。思路是这样的用TIM1的输入捕获通道测量FG引脚上的脉冲频率换算成实际转速目标转速由温控映射表给出再把目标转速和实际转速做差用一个小环路的PI调节器校正PWM占空比。风扇不转误差为正占空比自动往上加直到转起来为止。转速换算公式也很直接。四线风扇的FG信号每转一圈输出两个脉冲所以实际转速RPM 60 / 脉冲周期 × 2脉冲周期用输入捕获的两个上升沿时间差算出来。这个数据我不仅在内部用于闭环还通过串口实时上报你在电脑上看串口数据时能直接看到风扇的实际转速曲线用来验证温控策略的响应是否正确。3.5 按键省心处理状态机消抖与长按逻辑按键看着简单处理不好就是玄学。我在这个项目里做了两级处理10ms定时扫描加状态机消抖。所谓状态机消抖不是简单地延时20ms再读一次而是维护一个按键状态的序列检测到按下状态切到确认按下连续扫描N次都为按下才真正执行动作任何一次读到释放就重置回初始态。这样做的好处是不会因为一次的抖动误触发也不会因为长按产生拖尾。另外我写了一个长按加速逻辑按住加键或减键超过500ms后每100ms自动重复执行一次调整方便快速设置参数。这类小细节看起来不起眼但实际用起来会舒服很多。4. 仿真验证Proteus里先把逻辑跑通少烧十块板子4.1 仿真文件的搭建过程我是在Proteus 8.6里搭的仿真工程。BOM清单里选STM32F103C8、DS18B20的模型Proteus内置、一个虚拟示波器、一个七段数码管或者虚拟终端再加一个电位器模拟温度变化。等等Proteus里DS18B20是仿真模型它的温度值是实时调节的。我在仿真里用一个滑动变阻器接到DS18B20的信号脚上模拟温度的连续变化这样就能观察风扇转速是否随温度正确调整。但是有个坑Proteus的STM32模型默认不能直接加载Keil生成的hex文件需要在Project Options里设置好芯片型号并且保证Keil生成的hex是针对F103C8的。另外记得在Proteus里把时钟频率设置成72MHz如果设成默认的8MHz所有跟延时相关的逻辑都会跑慢好几倍PWM频率、串口波特率全部不对。4.2 仿真阶段最值得调的三个bug第一个是OLED刷新异常。仿真阶段I2C时序可能会因为虚拟示波器的探针干扰变得不稳定表现是屏幕花屏。这时候把OLED的刷新周期从200ms拉长到1000ms再看看基本能定位到是不是时序竞争问题。第二个是DS18B20读不到温度。仿真里这个问题十有八九是初始化时序不对而不是接线问题。你在代码里加一个调试输出把初始化阶段的每个步骤都打出来看看是哪一步超时。我记得有一次是我把微秒级延时的循环参数写错了导致初始化信号的时间变成5msDS18B20当然不响应。第三个是风扇转速反馈信号在仿真里没有。Proteus里的直流电机模型没有FG输出引脚我仿真的时候用一个PULSE信号源模拟风扇的FG信号频率手动设定为对应转速的值。这样转速闭环的逻辑可以在仿真里完整验证虽然信号源是人为的但至少能证明PI调节器的方向对不对。4.3 从仿真到实物差距在哪仿真解决的是逻辑问题不解决物理问题。仿真里通过的东西到实物上大概率要再调一遍但调的方向会明确很多。最典型的例子是PWM频率。仿真里不管PWM是1kHz还是25kHz风扇都是转的但在实物上低频率的PWM会让风扇产生可听见的啸叫声甚至某些风扇驱动电路在低频下会过热。我最终定在25kHz已经超过人耳可听范围也避开了风扇驱动板LC滤波器的谐振点。还有一点仿真里的DS18B20读取永远是稳定的但实物上如果你用的杜邦线太长寄生电容会改变单总线的时序尤其是1μs级别的高速读时序特别容易挂。我调试时把DS18B20和板子之间的杜邦线缩短到20cm以内就很少出现读不到设备的情况了。5. 实测数据篇这套系统在真实场景里表现如何5.1 测试环境和方法室内温度26°C把DS18B20绑在CPU散热器的鳍片上风扇用的是一款12V/0.25A的四线散热风扇额定转速3200RPM。给CPU施加不同的负载等级稳定5分钟后记录系统读数。测试时串口设置为115200-8-N-1每秒输出一帧数据格式温度、目标转速、实际转速、PWM占空比、当前工作状态。分别测了静音模式风扇转速上限限制在60%、标准模式上限100%和高温压力测试三个场景。5.2 温升与转速响应曲线空载时CPU表面温度稳定在31°C风扇停转整机几乎没有声音。跑满负载5分钟后温度升到47°C风扇稳定在100%转速约3150RPM跟标称值一致。从26°C升温到47°C的过程中风扇转速从0起步在35°C左右开始有启动动作。这里就能看到转速闭环的好处了开环控制时如果目标占空比是8%风扇就卡在0不转闭环控制下系统检测到实际转速为0会自动加大占空比直到风扇启动随后回调稳定在目标值附近。降温曲线同样平滑。负载撤掉后我把滞回区间设成2°C升温时45°C对应80%占空比降温时45°C对应60%有效避免了风扇在临界点附近的周期性抖动。5.3 最高温差与系统稳定性标准模式下满载温度稳定在47°C环境温度26°C温升为21°C。这个成绩对风冷散热来说不算优秀但要注意我的测试条件属意于考验控制策略而不是散热效能。压力测试时我特意把PWM输出波形用示波器看了一下25kHz的方波边沿干净没有明显的过冲和振铃占空比从0~100%单调变化没有跳变。这说明MOSFET驱动电路的栅极电阻和下拉电阻取值合理没有出现高频振铃导致的发热失控。还有一个小细节长时间满载运行后STM32芯片温度用手摸只有温热远没到烫手的程度。F103本身的功耗控制很好72MHz跑这点任务绰绰有余。5.4 串口数据的实际截图分析我挑了三组典型的串口数据帧还原成表格时间(s)温度(°C)目标转速(RPM)实际转速(RPM)PWM占空比(%)状态532.5000待机3038.21600158742升温12047.132003145100满速从数据能看出三件事目标转速和实际转速的偏差在2%以内闭环控制有效占空比在42%时实际转速能从0跳到1587RPM说明这颗风扇在42%占空比附近已经过了启动阈值温度到47°C时占空比已经到100%跟预设的55°C全速策略相比我的曲线在40~55°C区间内确实做了提前量。6. 代码结构与核心代码讲解让开源代码不再是天书6.1 工程文件布局很多人下载了开源代码第一反应是打开main.c从头读到尾结果越读越晕。我的工程文件布局是下面这样的同类的项目可以照抄这个套路/User/ main.c stm32f10x_it.c system_stm32f10x.c /Hardware/ ds18b20.c/h pwm.c/h oled.c/h key.c/h fan.c/h /System/ delay.c/h usart.c/h /App/ control.c/h display.c/h核心逻辑都按模块拆开Hardware文件夹下是底层驱动App文件夹下是上层控制逻辑。这样别人拿到代码想改温度传感器就只动ds18b20.c想调策略就只碰control.c不需要碰跟外设相关的任何底层代码。6.2 主循环的运行骨架主循环的骨架大概是这样的思路int main(void) { SystemInit(); delay_init(72); USART1_Config(115200); DS18B20_Init(); PWM_Init(); OLED_Init(); while(1) { if (flag_1s) { flag_1s 0; temp DS18B20_GetTemp(); target_pwm Temp_To_PWM(temp); fan_set_speed(target_pwm); } if (flag_200ms) { flag_200ms 0; OLED_Update(temp, duty, rpm); } if (flag_10ms) { flag_10ms 0; Key_Scan(); } USART_SendStatus(); } }这个结构看着简单但实际运行非常稳定。三个标志位在SysTick中断里置位主循环只做判断和任务执行不阻塞等待任何设备。6.3 分段PWM映射的具体实现让我把温控策略的代码贴出来这段是整个项目里最值得看的uint8_t Temp_To_PWM(float temperature) { if (temperature TEMP_STOP) return 0; if (temperature TEMP_FULL) return 100; // 25~40°C: 0%~60% if (temperature TEMP_STOP temperature TEMP_MID) { return (uint8_t)((temperature - TEMP_STOP) / (TEMP_MID - TEMP_STOP) * 60); } // 40~55°C: 60%~100% if (temperature TEMP_MID temperature TEMP_FULL) { return 60 (uint8_t)((temperature - TEMP_MID) / (TEMP_FULL - TEMP_MID) * 40); } return 100; }这里宏定义 TEMP_STOP25TEMP_MID40TEMP_FULL55。改成你想要的阈值只需要动这三行宏代码其他地方一行都不用碰。滞回控制的实现是在这个函数外面做的如果当前是升温阶段本次温度高于上次直接计算映射值如果是降温阶段计算出来的目标占空比再减去一个固定偏移量本质上是改变了触发点温度。6.4 转速闭环的PI计算转速闭环是核心创新点在fan.c里的实现逻辑是这样的int16_t speed_error target_rpm - actual_rpm; static float integral 0; float Kp 0.8f, Ki 0.05f; float output; integral speed_error; if (integral 50) integral 50; if (integral -50) integral -50; output Kp * speed_error Ki * integral; if (output 50) output 50; if (output -50) output -50; current_pwm (int8_t)output; if (current_pwm 100) current_pwm 100; if (current_pwm 0) current_pwm 0;因为PI输出的是占空比的修正量而不是绝对值所以积分项幅度限幅很关键不然启动时会超调到100%导致风扇猛地窜一下。实测Kp0.8Ki0.05时转速从0上升到目标值的90%大概需要1.5秒左右没有过冲响应也算够快。6.5 关于I2C OLED驱动的一个补充SSD1306的驱动网上到处都是但我还是要提醒一个细节SSD1306的I2C地址是0x3C7位地址很多人的程序写的是0x78或0x7A这是8位地址和左右移位造成的问题。用STM32的HAL库时I2C地址参数传的是7位地址0x3C用标准库自己写模拟I2C时要传的是8位地址0x78。这个地址移位问题坑了无数新人务必注意。7. 版本迭代与几个值得记录的设计取舍7.1 从V1.0到V2.0我改了什么V1.0版本用的是继电器开关控制风扇温度高于阈值就全速转低于阈值就停妥妥的开关控制。测试时发现风扇频繁启停噪音大且寿命堪忧而且温度波动范围明显。V2.0加入PWM调速后风扇可以无级变速温度稳定性大幅提升。但初期没有转速反馈低压启动问题变成了新的痛点最终催生了V2.1的转速闭环版本。走到这一步这个系统才算真正可用了。7.2 为什么没加WiFi和App控制一直有人建议我加ESP8266或ESP32实现手机App调参。技术上完全可行蓝牙或者WiFi模块几十块钱串口对接也不复杂。但我最终没加进去原因只有一个系统的核心价值在控制策略不在远程控制。加上WiFi意味着要写协议、做App、考虑网络安全项目复杂度成倍上升却不会让风扇降温能力提升哪怕一度。如果未来需要远程监控完全可以通过串口或CAN总线接一个上位机网关控制端保持极简把功能边界划分清楚。这也是很多工业级产品的设计哲学控制层和数据层分离。7.3 原理图的版本化管理和github维护这个项目我在GitHub上维护了三个分支main分支是完整代码原理图版本dev分支是开发中的代码doc分支放了设计笔记和BOM清单。原理图我用的KiCad画的工程文件也一并传到仓库里。KiCad回路的PCB源文件是完全开放的别人可以基于我的原理图直接改成自己想要的样子。开源项目最怕的是别人拿到的资料不完整、跑不起来。所以我的仓库里除了源码和原理图还放了烧录说明、Proteus仿真文件和调试记录。希望拿到项目的人能花较少的时间跑起来然后有余力去做自己的改进。8. 从烧板子到跑通我的避坑经验清单烧过几个芯片熬夜调过时序之后我把这个项目里所有踩过的坑整理成一份清单每一条都是真金白银换出来的经验。电源部分的坑12V风扇启动瞬间电流是额定电流的好几倍如果和MCU共用同一个LDOMCU会被拉复位。解决方法是负载和数字电路分开供电。AMS1117的最小压差约1V输入电压低于4.3V时输出就不稳定了所以5V输入不要降到3.3V以下再加任何重负载。电源指示灯串的限流电阻330Ω和1kΩ的亮度差别比想象中大得多1kΩ一颗ERRED在白天基本看不到亮调试时容易误判断电。传感器部分的坑DS18B20线缆超过30cm时需要在数据线上再加一个100pF的对地电容否则读回来的温度会出现随机跳变。多个DS18B20挂同一根总线时如果总线上有任何一个设备地址冲突或者时序干扰整条总线都会瘫痪所以多设备场景建议每个传感器单独一根线代码里做分时读取。OLED的I2C总线对线长也很敏感超过15cm建议降低通信速率到100kHz。代码部分的坑对STM32来说不是所有GPIO都能容忍5V输入。打开数据手册看FT5V tolerant标识只有标了FT的引脚才能直接接5V逻辑器件。DS18B20这种开漏总线虽然上拉到3.3V不存在这个问题但如果你要接5V的传感器引脚选择务必先查手册。我遇到过的一个极其隐蔽的bugSysTick中断和主循环同时操作同一个变量导致数据错乱。在中断里置标志位、在主循环里清标志位的模式下只要标志位是volatile修饰就安全但如果主循环里对标志位做了非原子操作比如flag_1s | 0x01就有可能在中断写标志位时产生竞争。稳妥的办法是中断里只置位主循环里只读取后立刻清零。使用标准库时不要忘掉RCC_APB2PeriphClockCmd和RCC_APB1PeriphClockCmd的区别TIM1在APB2总线上72MHzTIM2~TIM4在APB1总线上36MHz。如果用TIM2产生PWM还想着TIM1的时钟频率算出来的PWM频率会差一倍。调试工具的坑没有JLINK的话可以用串口ISP下载程序但注意BOOT0引脚必须拉高才能进入ISP模式下载完再拉低复位。忘了这一步会导致经常下载失败让人误以为板子坏了。串口助手显示乱码时先查波特率再查电平。STM32的USART是TTL电平不能直接连电脑串口必须通过一个CH340或者CP2102模块转换。这些坑单独看都不大但每一个都能浪费掉半天到一天的时间。把它们写出来是希望后面做类似项目的人能少走点弯路。9. 项目开源之后一些后续迭代的思考代码和原理图都开源了我还在持续更新。目前已经有不少朋友fork了仓库有人在上面加了加热丝的控制变成了恒温箱有人改了传感器类型换成了NTC热敏电阻ADC采集还有人把显示部分换成了LCD并增加了曲线绘制功能。社区反馈最大的一条建议是增加多路温度传感器支持同时接入加热和制冷两个执行器做成完整的温控模块。我在评估这个需求多路DS18B20挂总线是可行的加热和制冷可以靠同一个PWM的极性切换来实现代码改动量大概是现有工程的四分之一到三分之一。考虑下一版就做进去。如果你拿到这个项目后想自己二次开发我给的建议是先跑通仿真再点亮实物最后再改逻辑。三个步骤顺序不建议乱不然你分不清是硬件问题还是代码问题。改逻辑时优先改宏定义的参数比如温控阈值、映射区间、滞回宽度这些参数调一遍你就知道这套系统的脾气了。最后分享一个小经验任何温控系统的核心都不是温控算法本身而是传感器数据的准确性和可重复性。花一个下午写PID不如花一个下午把传感器滤波和校准做好。我这个项目里最后用的传感器滤波方法也很简单就是限幅滤波加滑动平均但效果比很多花哨算法都好——因为温度信号本身就是慢变量简单的方法在慢变量上就足够好了。
返回列表