☰
STM32智能风扇项目:温湿度采集、PWM调速与红外遥控的嵌入式实战
2026/10/20 19:00:49 网站建设 项目流程

简介:这是一份围绕STM32F103C8T6设计的智能风扇嵌入式工程,适合电子、自动化及计算机相关专业学生作为课程设计或入门项目参考。项目基于洋桃一号开发板,完整演示了温度传感器DS18B20/TMP36的数据采集、STM32 ADC转换、TIM模块PWM调速、按键交互与LCD显示等关键环节,覆盖从硬件连接到C语言软件框架的搭建过程。压缩包共296个文件,约12.03MB,除C源码(.c)与头文件(.h)外,还包含Keil工程配置(.uvproj)、编译生成的目标文件(.o/.crf/.d)以及hex固件、map映射文件,可用于直接烧录、调试或对比生成过程。资源中给出了初始化代码、主循环处理逻辑、中断服务与异常处理思路,能帮助读者理解嵌入式实时控制中温度判定、占空比调节和用户界面的配合方式。已有5547人学习下载,是快速上手STM32项目并完成智能风扇功能的实用素材。

1. 智能风扇项目到底值不值得花时间学:一个能跑能改的全链路嵌入式实战

夏天从工位站起来去摸风扇旋钮,调到最大又觉得冷,再回来拧小——这个小场景背后藏着一整套典型的嵌入式开发链路:传感器采集、MCU 逻辑处理、PWM 驱动执行器、人机交互界面。某开发者把这个过程做成了基于 STM32 的智能风扇项目,它不是玩具,而是把温湿度采集、红外遥控、按键控制、LCD 显示、自动/手动双模式全都串起来的一套完整固件工程。对正在学 32 位 MCU 开发的人来说,这份资源的价值在于它覆盖了从 GPIO、定时器、中断到 PWM、I2C/单总线协议、状态机编程这些面试和实战都绕不开的知识点,而且所有模块都能烧进板子看真实效果。它不是泛泛的教程文档,是一份可以直接编译下载的源码工程,适合手里有一块 STM32F103 核心板、想从点流水灯跨越到完整小项目的读者,也适合想抄一套硬件驱动写法去改自己项目的熟手。

2. 项目硬件架构拆解:从传感器到风机的信号流向

2.1 硬件选型和模块连接关系

整个系统的主控采用 STM32F103C8T6,这是目前市面上最常见、资料最全的入门级 32 位 MCU,72MHz 主频、64KB Flash、20KB RAM,跑一个风扇控制项目绰绰有余。围绕这颗主控,硬件部分分为五个功能块:电源、感知、决策、执行和人机交互。

电源部分用 AMS1117-3.3 稳压芯片把 USB 或外部 5V 降到 3.3V 给 MCU 供电,风机驱动采用独立 5V/12V 供电轨,避免电机启停时电压跌落干扰单片机工作。感知部分核心是 DHT11 温湿度传感器,它是一根单总线数据线,通过时序模拟协议把温度和湿度以 40 位数据帧的形式传给 MCU。执行部分是 mos 管驱动电路,用 N-MOS 管(常见型号 AO3400)配合 PWM 信号控制直流风机的平均电压,以此调节转速。人机交互有三个通道:两个独立按键(模式切换和手动转速加减)、一个红外接收头(HS0038B,用于接收遥控器信号)、一块 0.96 寸 I2C 接口的 OLED 显示屏(SSD1306 驱动芯片)。

各模块的引脚连接关系是固定的物理约束,源码里的硬件配置头文件会统一管理这些宏定义。实践中最忌讳的是把引脚定义散落在各功能 C 文件里,改板子时到处找。这个项目把所有 GPIO 映射集中在bsp_init.h里,换板子只改一处,这个习惯值得保留。

功能模块型号/方案与 MCU 的接口所用引脚
主控STM32F103C8T6SWD 烧录调试SWDIO/SWCLK
温湿度DHT11单总线 GPIOPA6
PWM 输出TIM2_CH1推挽输出PA0
红外接收HS0038B外部中断 EXTIPB10
OLED 显示SSD1306I2C 软件模拟PB8/PB9
按键轻触开关 x2上拉输入PA4/PA5

2.2 PWM 调速的占空比映射策略

风机调速的核心是 PWM 占空比控制。项目里把 PWM 频率设定在 25kHz,这个频率高于人耳可听范围上限,风机运转时不会产生刺耳的啸叫声。定时器 TIM2 的 ARR 寄存器设置为 1000 - 1,意味着一个 PWM 周期由 1000 个计数步组成,因此占空比的分辨率是 0.1%,足够平滑地控制转速。

实际驱动逻辑在motor.c里的Motor_SetSpeed(uint8_t percent)函数。这个函数接收 0-100 的整数百分比,内部换算成 CCR 寄存器的值。它不是简单地把百分比抄进寄存器,而是先做了一次限幅处理:当输入小于 5% 时直接输出 0,防止风机在极低占空比下转不动、绕组长时间通电过热。这是一个非常实际的产品化细节,很多新手直接写TIM_SetCompare1(percent * 10),结果风扇在 3% 占空比时嗡嗡响却不转,电流还不小。

void Motor_SetSpeed(uint8_t percent) { if (percent <= 5) { percent = 0; /* 低于 5% 直接停转,避免堵转过热 */ } else if (percent > 100) { percent = 100; /* 上限保护 */ } TIM_SetCompare1(TIM2, percent * 10); /* ARR=1000,所以乘 10 映射到 CCR */ }

参数说明:percent是 0-100 的整数转速百分比,与 OLED 上显示的进度条对应;TIM_SetCompare1是标准外设库的接口,第二个参数直接写入 TIM2 的 CCR1 寄存器。1000 的 ARR 值是硬件配置时定死的,因此这里乘 10 而不是乘别的系数。如果想提高调速精度到 0.01%,可以改 ARR 到 10000,但 PWM 频率会变成 7.2kHz,可能落入可听范围,需要同步调整定时器分频系数。

2.3 DHT11 单总线协议的位时序读取

DHT11 是这个项目里最考验时序功底的部分。它用一根数据线传输 40 位数据,每一位的区分靠高低电平的持续时间,而不是电平本身。整个读时序分三步:拉低总线 18ms 以上触发传感器响应,然后释放总线等待传感器拉低应答,最后逐位读取 40 位数据。

uint8_t DHT11_ReadByte(void) { uint8_t i, data = 0; for (i = 0; i < 8; i++) { while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == RESET); /* 等待高电平开始 */ Delay_us(40); if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == SET) { data |= (0x80 >> i); /* 高电平持续超过 40us 判定为 1 */ } while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == SET); /* 等待位结束 */ } return data; }

这段代码的判定逻辑是典型的宽度解码:50us 的高电平表示逻辑 1,26-28us 的高电平表示逻辑 0。代码在检测到高电平起始后延时 40us 再采样,如果此时引脚仍然为高,说明高电平总宽度超过 40us,判定为 1;否则是 0。这里最关键的坑在于Delay_us的精度必须由定时器或系统时钟精确计算,不能用简单的空循环凑合,否则在编译器优化等级变化后延时漂移,导致误判率暴增。实际调试时可以在循环里加一个变量记录读取到的原始字节,串口打印出来和温湿度计对比,能快速定位是时序问题还是数据校验问题。

3. 软件框架与状态机设计:裸机下如何同时管好五件事

3.1 前后台轮询架构,不跑操作系统的取舍

这个项目没有上 FreeRTOS,而是采用经典的前后台轮询(超级循环)架构。主循环里顺序执行按键扫描、红外解码更新、OLED 刷新、PID 温控运算,各子模块由标志位驱动,DHT11 的采集周期用定时器中断喂狗。选择裸机的原因很实际:系统功能线性度强、无复杂阻塞等待、内存占用可以被精确预测,20KB RAM 跑状态机和显示缓冲还有余量。对于教学和二次开发来说,裸机代码比 RTOS 更容易理解每条指令的执行路径。

int main(void) { SystemInit(); BSP_Init(); /* 时钟、GPIO、定时器、串口初始化 */ OLED_Init(); DHT11_Init(); while (1) { Key_Scan(); /* 按键检测,带消抖状态机 */ IR_Scan(); /* 红外 NEC 协议解码 */ Mode_Update(); /* 自动/手动模式切换逻辑 */ Motor_Update(); /* 根据模式计算并输出 PWM */ OLED_Refresh(); /* 温度、湿度、转速、模式显示 */ Delay_ms(20); /* 主循环周期 20ms,约 50Hz 刷新率 */ } }

主循环周期 20ms 是一个做了权衡的值。太快了,OLED 刷新会占掉大量 CPU 时间,且 DHT11 两次读取之间需要至少 1s 间隔才能保证数据稳定;太慢了,按键和红外的响应会显得迟钝,旋转编码器这类高速输入会丢事件。50Hz 的刷新率对显示温湿度和转速变化完全够用,对按键扫描也不会漏掉。Mode_Update和Motor_Update分开写是刻意的设计:前者只修改模式状态的结构体字段,后者才根据模式去操作硬件,这样逻辑单元之间没有耦合,单测时可以拿假的模式状态去喂Motor_Update。

前后台架构最怕的是某个子模块阻塞主循环。项目里所有耗时操作都做了非阻塞化处理:OLED 写显存不等待应答、DHT11 的 18ms 触发信号由定时器回调延迟执行、红外解码靠 EXTI 中断打时间戳。这是一种值得关注的软件设计习惯——机械地让所有外设工作在中断或短时轮询模式下,主循环永远不被拖住。

3.2 自动温控模式的 PID 简化实现

自动模式下,系统根据环境温度自动调节风扇转速。项目没有直接写完整的位置式 PID,而是用一种带滞回区间的分段线性映射:20℃ 以下风机停转,20-28℃ 之间转速线性从 20% 升到 80%,28℃ 以上封顶 100%。同时增加一个重要的滞回机制:当温度下降到比开启点低 2℃ 时风扇才允许停转,避免温度在临界点附近抖动导致风扇频繁启停。

void Auto_Mode_Tick(float temperature) { static uint8_t last_output = 0; if (temperature < AUTO_OFF_TEMP) /* 低于 18℃ 强制停 */ { last_output = 0; } else if (temperature > AUTO_ON_TEMP) /* 高于 22℃ 启动调速 */ { if (temperature >= 28.0f) { last_output = 100; } else { last_output = (uint8_t)((temperature - 20.0f) / 8.0f * 80.0f + 20.0f); } } Motor_SetSpeed(last_output); }

这个函数里的滞回逻辑体现在AUTO_OFF_TEMP(18℃)和AUTO_ON_TEMP(22℃)之间的 4℃ 盲区:已经停转的风扇要等温度越过 22℃ 才启动,已经运转的风扇要等温度降到 18℃ 以下才停止。这样设计避免了继电器或 mos 管在高频通断下过热损坏。这里没有用标准 PID 是因为风扇本身是一阶惯性系统,温度变化又很缓慢,比例控制加滞回已经能获得很好的体验。如果想升级成 PID,可以把last_output换成误差积分项,但需要额外加积分限幅和输出限幅,否则温度突变时会出现转速冲击。

3.3 红外 NEC 协议解码与按键消抖状态机

红外部分采用的是 NEC 协议,逻辑 1 和逻辑 0 用载波长度区分:逻辑 1 是 2.25ms 高电平,逻辑 0 是 1.125ms 高电平。解码在外部中断回调中完成,通过记录相邻两次中断的时间间隔来判断 0 还是 1。

void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line_10) != RESET) { static uint32_t last_time = 0; uint32_t now = TIM_GetCounter(TIM3); /* 用 TIM3 作为微秒时间戳 */ uint32_t delta = now - last_time; last_time = now; if (delta > 13500) /* 9ms 引导码 + 4.5ms 间隔 */ { ir_index = 0; } else if (delta > 2000) /* 逻辑 1:2.25ms */ { ir_data |= (0x80000000 >> ir_index); ir_index++; } else if (delta > 1000) /* 逻辑 0:1.125ms */ { ir_data &= ~(0x80000000 >> ir_index); ir_index++; } if (ir_index == 32) { IR_ProcessCommand(ir_data); ir_index = 0; } EXTI_ClearITPendingBit(EXTI_Line_10); } }

红外解码的难点不在于协议本身,而在于中断服务函数里不能做耗时操作,因此只更新时间戳和移位数据,解析交给主循环里的IR_ProcessCommand。判断delta > 13500用的是相对时间而不是绝对时间,考虑了定时器溢出和中断嵌套带来的抖动。值得说明的是,这里读取按键值时用了多次采样确认的方式,而不是简单的延时消抖,因为红外中断里不能用Delay_ms阻塞。

4. 工程目录结构与关键模块的代码走读

4.1 分层设计的固件工程

源码包解压后是标准的 STM32 标准外设库工程结构,分为硬件抽象层、功能模块层、应用层三层。这种分层的核心收益是:应用层不直接调用寄存器,底层换 MCU 时只动 BSP 层。目录具体是这样组织的:

SmartFan/ ├── Core/ │ ├── inc/ │ │ ├── main.h │ │ └── stm32f1xx_it.h │ └── src/ │ ├── main.c │ ├── system_stm32f1xx.c │ └── stm32f1xx_it.c ├── Drivers/ │ ├── BSP/ │ │ ├── bsp_key.c/h -- 按键扫描与消抖 │ │ ├── bsp_ir.c/h -- 红外接收与 NEC 解码 │ │ ├── bsp_oled.c/h -- SSD1306 驱动 │ │ └── bsp_usart.c/h -- 串口调试输出 │ └── Middlewares/ │ ├── dht11.c/h -- 温湿度传感器时序驱动 │ └── motor.c/h -- PWM 调速与占空比映射 └── App/ ├── app_mode.c/h -- 自动/手动模式状态机 └── app_display.c/h -- OLED 界面组织

BSP(板级支持包)层只做寄存器读写和时序拉高拉低,Middlewares层把硬件能力包装成函数接口,App层处理业务逻辑。三层之间的调用关系是单向的,App层不直接 includemain.h里的寄存器宏,而是通过Middlewares的函数操作外设。这套结构和大型工程里的 HAL 分层思路一致,只是代码更短更容易读。新手容易犯的错误是在main.c和各个模块的.c文件里重复定义相同的 GPIO 初始化函数,导致控制权混乱。正确做法是每个外设的初始化只写一次,放在对应的bsp_xxx.c中,并在.h里导出。

4.2 OLED 环形缓冲区刷新机制

OLED 部分设计了一个 128x32 像素的显存数组,大小是 512 字节。SSD1306 的显存是逐像素映射的,每次刷新如果全屏重传需要 512 字节 I2C 数据,在 400kHz 的 I2C 总线上耗时约 10ms。项目做了一个优化:只把变化过的区域传给屏幕驱动芯片。

void OLED_Refresh(void) { static uint8_t cache[512]; uint8_t changed_count = 0; for (uint16_t i = 0; i < 512; i++) { if (cache[i] != display_buffer[i]) { OLED_WriteGRAM(i, display_buffer[i]); cache[i] = display_buffer[i]; changed_count++; } } if (changed_count == 0) { return; /* 无变化时跳过刷新,省电 */ } }

cache数组保存上一次发送到屏的显存副本,每次刷新前先逐字节对比,只有不同的字节才通过 I2C 写入。这个优化在静态界面下把 I2C 负载降到零,而温度变化时每秒最多刷新 1 次,显示区域小,实际单次只有几十字节需要传输。对比整个屏幕重绘,这样能明显降低 CPU 占用和总线冲突的概率。

4.3 串口调试信息与 log 输出接口

项目bsp_usart.c里的Debug_Printf封装了printf重定向,通过串口 1 以 115200 波特率输出调试信息。这个输出通道在联调阶段几乎承担了全部观测任务——DHT11 采集到的原始数据和解析结果、红外解码收到的是哪个按键、自动模式里温度换算到多少占空比,全部实时打印。

void Debug_Printf(const char *fmt, ...) { char buffer[128]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); for (char *p = buffer; *p != '\0'; p++) { while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, (uint8_t)(*p)); } }

vsnprintf做格式化,缓冲区限制 128 字节,防止过长的调试信息撑爆栈。串口收发用的是阻塞轮询发送而非中断发送,因为调试输出频率低,阻塞也不影响主流程。调试时建议把波特率固定为 115200,解析上位机用串口助手或minicom都方便。这部分的取舍值得参考:正式产品的日志输出都是异步非阻塞的,项目里为了简单写成阻塞,但如果调高了Debug_Printf的调用频率(比如放在主循环每圈都打),会造成主循环周期不稳定。

5. 从零跑通项目的完整步骤与避坑指南

5.1 硬件接线、编译烧录与验证顺序

拿到源码后,第一步不是直接打开 Keil 点编译,而是先确认板子上的硬件连接与bsp_init.h里的引脚定义一致。以常见的 STM32F103C8T6 最小系统板为例,把 DHT11 的信号线接到 PA6,IR 接收头输出接 PB10,OLED 的 SCL/SDA 分别接 PB8/PB9,按键一端接 PA4/PA5、另一端接地,mos 管栅极接 PA0。电源部分注意风扇电机必须单独供电,不能从 MCU 的 3.3V 引脚取电,否则电机启动瞬间会把电压拉低导致单片机复位。

用 Keil MDK 打开工程后,先做三步检查:Options for Target里 Device 选择 STM32F103C8,C/C++选项卡的 Define 里确认有USE_STDPERIPH_DRIVER,STM32F10X_MD,Debug 选项卡选择 ST-Link 并设置为 SWD 模式。然后编译。如果编译报错缺少头文件,多半是工程里的 Include Paths 没有添加Core/inc、Drivers/BSP这些目录,手动添加即可。

# 如果用命令行编译(需要安装 arm-none-eabi-gcc 工具链) make clean make # 烧录用 ST-Link CLI: st-flash write build/smartfan.bin 0x08000000

烧录成功后,上电先看 OLED 是否点亮并显示当前温湿度。显示异常不一定是接线错,可能是 I2C 地址不匹配——SSD1306 的 7 位地址默认是 0x3C,但有些屏是 0x3D,这个改bsp_oled.c开头的宏即可。接着用手触碰 DHT11,温度数值应该缓慢上升;按下按键 2 切换到自动模式,对着传感器哈气,风扇转速应随温度升高而加快。最后用遥控器对着 HS0038B 按电源键,看串口是否打印对应的键码。

5.2 常见坑:DHT11 卡死、I2C 无响应、红外收不到信号

这批源码在转移和移植过程中有几个高频翻车点,整理成排查清单,每一条都是实际踩过的。

现象一:程序卡在 DHT11 读取的 while 循环里出不来。原因:DHT11 是单总线器件,如果上一次读取还没释放总线(数据线被拉低),下一次读会永远等不到高电平。解决:每次读取周期强制把 GPIO 拉高并延时 20ms,保证总线空闲后再发起新的触发信号。另外确认代码里是否在 DHT11 读取后执行了超过 1s 的延时,如果采集频率高于 1Hz,传感器会直接不响应。

现象二:OLED 上电后全黑或花屏。原因:多数 SSD1306 屏幕上电后需要发送初始化命令序列,缺一段开显示指令就会全黑。花屏则基本是 I2C 速率太高或时序不对。解决:初始化序列对比官方 datasheet,确认最后一条指令是0xAF(Display ON)。I2C 速率从 400kHz 降到 100kHz 再试,排除是线材过长导致上升沿太缓。

现象三:红外遥控器按键没反应,但用逻辑分析仪能看到波形。原因:NEC 协议解码时使用了定时器时间戳,如果定时器分频系数不对,读到的delta值全部按比例偏移,落在错误的判定区间。解决:检查 TIM3 的时钟配置是否和代码里假设的 1MHz 时基一致。最直接的手段是串口打印delta原始值,对比数据手册里 560us、1.125ms、2.25ms 的理论值,偏差超过 20% 就要调分频。

现象四:自动模式下风扇在临界温度点反复启停。原因:温度检测误差叠加,导致实际温度在开启/关闭阈值附近来回穿越。解决:确认滞回区间代码是否被裁剪掉了,或者阈值定义是否留了足够的差值。在调试里把AUTO_OFF_TEMP和AUTO_ON_TEMP分开设置,差值至少 2℃。

现象五:改了某个外设初始化后整个工程编译出一堆 undefined reference。原因:在 Keil 里新加了.c文件但没有勾选进工程编译列表。解决:在 Project 栏右键源文件组,选择 Add Existing Files,重新全量编译。如果用的是 GCC Makefile 工程,要同步修改Makefile里的源文件列表。

5.3 验证程序逻辑正确性的最小测试方法

跑通只是第一步,验证逻辑正确需要一套低成本的观测手段。最有效的方案是串口把所有关键中间量打出来:每 500ms 打印一次温度、湿度、当前模式、占空比输出、红外键码。先手动模式逐步调占空比,记录实际转速对应的电流值,确认 PWM 映射曲线线性;再切自动模式,用吹风机模拟温度变化,画出温度-转速对应曲线,验证滞回区间是否生效;最后用遥控器逐一按键,对照映射表确认没有误触。

如果不想频繁担心温度变化,也可以直接在Auto_Mode_Tick里临时传一个模拟温度值进去,比如写死Auto_Mode_Tick(25.0f),看风扇输出占空比是否落在 20%-80% 的区间内。这种软件注入测试比实际加热 DHT11 更快更可重复。整条验证链路走完,程序逻辑基本就稳了。

6. 二次开发方向:给这个智能风扇加上网络控制和语音

6.1 升级到 RTOS 与多任务拆解思路

这个项目最大的改造空间是从裸机前后台升级到实时操作系统。DHT11 采集、OLED 刷新、红外解码在裸机下需要人工错开时间片,而用 FreeRTOS 可以自然拆成三个任务:Sensor_Task(1s 周期,阻塞等待 DHT11 数据)、Display_Task(100ms 周期,刷新 OLED)、Control_Task(20ms 周期,执行模式状态机)。任务间通过队列传递温湿度值,旋转编码器和红外的输入走中断+信号量模型。

void Sensor_Task(void *argument) { for (;;) { if (DHT11_Read() == SUCCESS) { xQueueOverwrite(sensor_queue, &sensor_data); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void Control_Task(void *argument) { for (;;) { sensor_data_t data; if (xQueuePeek(sensor_queue, &data, 0) == pdTRUE) { Auto_Mode_Tick(data.temperature); } vTaskDelay(pdMS_TO_TICKS(20)); } }

改造时有个容易踩坑的细节:Auto_Mode_Tick里的静态变量last_output在 RTOS 下仍然保留,但多任务访问同一变量时要注意临界区保护。此外 DHT11 的时序读取在任务里是阻塞的,必须保证Sensor_Task优先级不是最高,防止它抢占红外解码中断太久导致丢码。手头没有 RTOS 工程模板的,可以先拿裸机版本跑通逻辑,再逐个任务搬移,不要一次性全量重构。

6.2 加入 Wi-Fi 模块和 MQTT 上报的思路

第二个高价值改造方向是接入 ESP8266 或 ESP32 模块,把风扇状态上报到局域网。硬件上只需在 USART2 上接 ESP8266,通过 AT 指令集完成 TCP 连接和 MQTT 协议封装。软件上要注意缓冲区管理——ESP8266 回包长度不固定,不能用固定大小的数组直接解析,建议做一个环形队列,按\r\n分帧提取有效载荷。

void WiFi_ProcessATResponse(void) { uint8_t byte; while (UART2_ReceiveByte(&byte) == SUCCESS) { ringbuf_push(&at_ringbuf, byte); if (byte == '\n') { char line[128]; if (ringbuf_getline(&at_ringbuf, line, sizeof(line))) { if (strstr(line, "+MQTTSUBRECV")) { MQTT_ParsePayload(line); } } } } }

把这条链路加进去之后,整个项目就从单机设备变成了物联网终端,控制指令可以是云端下发到 MQTT 主题,风扇状态反过来回传。调试时先用串口助手直接给 ESP8266 发 AT 指令确认固件版本和网络连接,再用手机开热点排除路由器的 5G/2.4G 频段兼容问题。这个方向的技术栈跳到网络编程,价值密度很高,算是在原项目上最划算的一次扩展。

6.3 代码持久化的几个实用习惯

二开过程中最容易被忽视的是版本管理和配置隔离。第一次拿到这套代码时,某开发者吃过一次亏——改了bsp_ir.c的引脚映射,没同步更新文档,几天后自己都忘了接的是哪个引脚。从那以后,我每次收到这类单片机工程,第一件事就是先把引脚映射表、接线图和代码里的宏定义对照一遍,然后git init提交一个初始版本,把硬件连接写成pinmap.md放进工程根目录。改一处代码,提交一次,commit message 里写清楚改了什么硬件行为或算法参数。另一个习惯是所有的阈值参数集中到一个config.h里,转速曲线、温度阈值、滞回差值都在里面,改需求时只动一个文件,不用满工程去搜魔数。这套习惯我在后续几个小项目上一直沿用,确实降低了在自己代码里迷路的速度,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询