1. 项目缘起与核心需求拆解
1.1 为什么选择红外对管做转速测量
转速测量这件事,说简单也简单,说讲究也讲究。市面上测转速的方案大致分三类:霍尔传感器、光电编码器、红外对管。霍尔方案需要被测物体带磁性,编码器精度高但价格贵、安装同轴度要求苛刻。而红外对管方案,成本几块钱,非接触式测量,对被测物体几乎没有附加要求,只要能在旋转体上贴一条反光/遮光标记就行。
ITR9606 就是这类槽型光电开关里的经典款。它的结构很简单:一侧是红外发射二极管,另一侧是光敏接收管,中间开了一道槽。平时发射管的光直接照到接收管上,接收管导通;一旦有物体从槽中间穿过,光路被挡住,接收管截止。这个“通-断”变化就是我们要捕捉的信号。
用它来测转速的逻辑链条是这样的:在旋转的扇叶或轮盘上贴一小块遮光片,让旋转体每转一圈,遮光片就从 ITR9606 的槽里穿过一次。于是传感器输出端就会产生一个脉冲。单位时间内数出多少个脉冲,转速就出来了。公式很朴素:
转速(RPM)= 脉冲数 ÷ 每转脉冲数 ÷ 测量时间(分钟)
如果每转只贴一个遮光片,那每转脉冲数就是 1,一分钟内数到 300 个脉冲,转速就是 300 RPM。
1.2 中断方式相比轮询的实战优势
很多初学者第一反应是用轮询:主循环里不停读 GPIO 电平,检测到下降沿就计数。这个方法在低速场合能用,但一旦转速上去,问题就来了。假设转速 3000 RPM,每转 1 个脉冲,那就是每秒 50 个脉冲,脉冲宽度可能只有几百微秒。主循环里如果还有串口打印、LCD 刷新这些耗时操作,很容易漏掉脉冲。
中断方式解决的就是这个“实时性”问题。STM32 的 EXTI 外部中断可以在引脚电平变化的瞬间打断主程序,优先执行计数逻辑,响应时间通常在微秒级。主循环该干嘛干嘛,计数在后台自动完成,两者互不干扰。这就是为什么做转速测量,中断几乎是标配方案。
1.3 这个项目适合谁来动手
这个项目我推荐给三类人:第一类是刚学完 STM32 GPIO 操作、想找个综合案例练手的初学者,它涉及 GPIO、EXTI、定时器、串口,知识点覆盖面广但不深奥;第二类是做电机控制、风扇监测的工程师,需要快速搭一个转速反馈环节做验证;第三类是参加电子设计竞赛或做毕业设计的学生,这个项目可以作为测速模块直接嵌入更大的系统里。
硬件成本算下来:STM32 最小系统板十几块,ITR9606 模块两三块,再加一个 USB 转串口模块,总共不超过三十块。手头有现成的开发板就更省事了。
2. 硬件设计与关键细节解析
2.1 ITR9606 的工作原理与引脚识别
ITR9606 是一个四引脚器件,但市面上卖的大多是已经做成模块的版本,引出三根线:VCC、GND、OUT。模块内部通常已经集成了限流电阻和上拉电阻,用起来很方便。如果你买的是裸管,那就得自己搭电路。
裸管四个引脚怎么认?把管子拿在手里,槽口朝上,引脚朝下,从左到右一般是:发射管阳极、发射管阴极、接收管集电极、接收管发射极。不过不同厂家封装顺序可能有差异,最稳妥的办法是用万用表二极管档测一下:正向压降约 1.2V 的那两个脚就是发射管,剩下两个是接收管。
发射管这边,阳极通过限流电阻接 VCC,阴极接 GND。限流电阻怎么算?红外发射管正向压降典型值 1.2V,想要 20mA 驱动电流,供电 3.3V,那电阻就是 (3.3 - 1.2) / 0.02 = 105Ω,取 100Ω 标准值就行。电流别给太大,20mA 足够让接收管可靠导通了,给到 50mA 以上反而会加速器件老化。
接收管这边,集电极通过一个上拉电阻接 VCC,发射极接 GND。集电极就是输出端。当光路通畅时,接收管导通,集电极被拉到接近 GND,输出低电平;光路被挡住时,接收管截止,集电极被上拉电阻拉到 VCC,输出高电平。所以遮挡瞬间产生的是上升沿。
上拉电阻取 10kΩ 是常见做法。阻值太小,接收管导通时功耗大;阻值太大,输出上升沿会变缓,高速时波形质量差。10kΩ 在功耗和速度之间平衡得比较好。
2.2 输出信号极性与 STM32 中断触发边沿的匹配
这里有个容易踩的坑:模块输出的极性和你想象的可能相反。很多 ITR9606 模块在输出端加了一级三极管反相或者比较器,导致“有遮挡时输出低电平”。所以拿到模块后,第一件事是用万用表或者直接接 LED 验证一下:遮挡槽口时输出到底是高还是低。
验证方法很简单:模块 VCC 和 GND 接好,万用表红表笔接 OUT,黑表笔接 GND。不遮挡时读一个值,用纸片遮挡时读另一个值。记下遮挡瞬间是从高变低还是从低变高。
这个极性直接决定了 STM32 EXTI 配置成上升沿触发还是下降沿触发。如果遮挡瞬间输出从高变低,那就配下降沿触发;反之配上升沿。配反了也不会报错,但计数会完全不对——要么不计数,要么一次遮挡计出两个数。
我个人的习惯是:不管模块极性如何,在代码里统一按“遮挡产生一个脉冲”来计数,通过配置触发边沿来适配硬件。这样换不同模块时只需要改一个宏定义。
2.3 STM32 引脚选择与电气连接要点
STM32 的 GPIO 大部分都能复用为 EXTI 中断源,但有个限制:EXTI 线是按引脚编号分组的,PA0、PB0、PC0 共用 EXTI0 线,同一时刻只能选其中一个。所以选引脚时要注意,别把两个需要中断的引脚选到同一个编号上。
对于这个项目,随便选一个空闲的 GPIO 就行,比如 PA0 或者 PB5。连接方式:
- ITR9606 模块 VCC 接 STM32 的 3.3V
- GND 接 GND
- OUT 接选定的 GPIO 引脚
如果模块输出是开漏的,还需要在 STM32 端开启内部上拉。不过大多数模块已经有上拉电阻了,STM32 这边配成浮空输入或者上拉输入都可以。我一般配成上拉输入,多一层保险。
注意:ITR9606 模块供电电压要确认清楚。有些模块标称 3.3V-5V 宽压,有些只支持 5V。如果模块是 5V 供电,输出高电平就是 5V,直接接 STM32 的 3.3V GPIO 可能损坏引脚。这种情况要么选支持 3.3V 的模块,要么在输出端加电平转换电路。
2.4 定时器与中断的协同设计思路
整个系统需要两个时间基准:一个是用来计数的脉冲中断,另一个是用来做时间窗口的定时中断。我的设计是:用一个定时器(比如 TIM2)产生 1 秒周期的定时中断,在定时中断里读取脉冲计数值,算出转速,然后清零计数器,开始下一轮统计。
为什么选 1 秒窗口?因为转速单位是 RPM(转/分钟),1 秒窗口算出来的脉冲数乘以 60 就是 RPM,心算都方便。如果转速很低,比如每分钟只有几十转,1 秒窗口内可能只有几个脉冲,分辨率太差。这时候可以把窗口拉长到 5 秒甚至 10 秒,用脉冲数除以窗口秒数再乘以 60。
反过来,如果转速很高,1 秒内脉冲数可能上千,用 16 位变量存也没问题(最大 65535)。但如果每转脉冲数很多,比如 60 个,转速 3000 RPM 时 1 秒内就是 3000 个脉冲,还在范围内。再高就得考虑用 32 位变量了。
定时器配置成 1 秒中断,假设系统时钟 72MHz,预分频器设 7199,自动重装载值设 9999,那就是 72MHz / 7200 / 10000 = 1Hz,正好 1 秒一次中断。这个计算过程后面代码部分再详细展开。
3. 软件架构与核心代码实现
3.1 工程搭建与关键配置项
我用的是 STM32CubeMX 加 HAL 库的方式,这套组合对初学者最友好,图形化配置省去了查寄存器手册的麻烦。如果你习惯标准库或者寄存器开发,逻辑是一样的,只是配置方式不同。
CubeMX 里需要配置这几项:
系统时钟:在 RCC 里把 HSE 设为 Crystal/Ceramic Resonator,然后在 Clock Configuration 里把主频拉到 72MHz(以 F103 为例)。
GPIO 中断引脚:选一个引脚,比如 PA0,模式设为 GPIO_EXTI0。然后在 GPIO 选项卡里,把 PA0 的 GPIO mode 设为 External Interrupt Mode with Rising/Falling edge trigger detection,根据前面验证的极性选上升沿或下降沿。Pull-up/Pull-down 选 Pull-up。
NVIC 中断优先级:在 NVIC 选项卡里使能 EXTI0 中断。优先级设置有个讲究:脉冲计数中断的优先级应该高于定时器中断,因为脉冲是随机到来的,错过就没了;定时器中断晚一点处理没关系。所以 EXTI0 的 Preemption Priority 设 0,TIM2 的设 1。
定时器:选 TIM2,时钟源选 Internal Clock。Prescaler 设 7199,Counter Period 设 9999,这样就是 1 秒中断一次。在 NVIC 里使能 TIM2 全局中断。
串口:选 USART1,模式 Asynchronous,波特率 115200。用来把转速数据打印到电脑上。
配置完生成代码,CubeMX 会把初始化代码都准备好,我们只需要在几个回调函数里填逻辑。
3.2 外部中断回调函数:脉冲计数逻辑
HAL 库把中断处理封装成了回调函数,我们不用直接写中断服务函数,只需要重写HAL_GPIO_EXTI_Callback。这个函数在每次引脚触发中断时被调用,参数是触发中断的引脚号。
volatile uint32_t pulse_count = 0; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { pulse_count++; } }就这么简单。pulse_count用volatile修饰,因为它在中断里被修改,在主循环里被读取,编译器不能把它优化到寄存器里。
这里有个细节:如果传感器信号有抖动,比如遮光片边缘不整齐导致一次遮挡产生多个边沿,计数就会偏大。硬件上可以在输出端加一个 0.1uF 电容做简单滤波,软件上可以在中断里加一个最小间隔判断——两次中断间隔小于某个阈值就忽略。不过对于 ITR9606 这种槽型光电开关,输出波形通常很干净,一般不需要额外处理。
3.3 定时器中断回调:转速计算与串口输出
定时器中断回调函数HAL_TIM_PeriodElapsedCallback里做三件事:读脉冲数、算转速、清零计数。
extern UART_HandleTypeDef huart1; volatile uint32_t pulse_count = 0; float rpm = 0.0f; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { uint32_t count = pulse_count; pulse_count = 0; rpm = (float)count * 60.0f; // 1秒窗口,每转1脉冲 char buf[32]; int len = snprintf(buf, sizeof(buf), "RPM: %.1f\r\n", rpm); HAL_UART_Transmit(&huart1, (uint8_t *)buf, len, 100); } }snprintf比sprintf安全,能防止缓冲区溢出。串口发送用阻塞模式,100ms 超时,在 115200 波特率下发这十几个字节也就 1ms 左右,不会明显影响中断响应。
如果你用的是浮点格式化,记得在工程设置里勾选 Use float with printf from newlib-nano,否则%f打印出来是空的。这个坑我踩过,当时调了半天以为串口坏了,结果是格式化库没开。
3.4 主循环与全局变量的线程安全处理
主循环里其实没什么事可做,CubeMX 生成的代码已经把所有初始化都做好了,我们只需要启动定时器和中断。
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); HAL_TIM_Base_Start_IT(&htim2); while (1) { // 主循环空闲,所有工作都在中断里完成 } }注意HAL_TIM_Base_Start_IT这个函数,带_IT后缀的才会使能定时器中断。如果误用了HAL_TIM_Base_Start,定时器会跑但不会进中断,转速永远不更新。这个也是新手常犯的错误。
关于pulse_count的读取,在定时器中断里我先把它赋给一个局部变量再清零,而不是直接读两次。因为如果在读取和清零之间来了一个脉冲中断,那个脉冲就丢了。先读后清,虽然理论上仍有极小概率丢一个脉冲,但实际影响可以忽略。更严谨的做法是关中断再操作,但对于转速测量这种精度要求,没必要。
4. 调试过程与常见问题排查
4.1 上电后计数不动或乱跳的排查思路
这是最常见的问题,我把它拆成几个检查点,按顺序排查效率最高。
第一步,确认传感器本身工作正常。把模块的 OUT 接到一个 LED(串限流电阻)或者直接接万用表,用手遮挡槽口,看输出有没有变化。如果没变化,检查供电和接线。ITR9606 模块上通常有一个电源指示灯,不亮就是供电问题。
第二步,确认 STM32 端能检测到电平变化。写一个最简单的轮询程序,主循环里读引脚电平并通过串口打印。遮挡时看打印值有没有翻转。这一步能排除引脚配置错误。
第三步,确认中断配置正确。如果轮询能读到变化但中断不触发,检查 NVIC 里 EXTI 中断有没有使能,触发边沿配置和实际信号极性是否匹配。我遇到过模块输出遮挡时是低电平,但我配了上升沿触发,结果一直不计数。改成下降沿就好了。
第四步,确认中断回调函数被正确重写。HAL 库的HAL_GPIO_EXTI_Callback是弱定义函数,如果你拼写错了函数名,编译器不会报错,但你的代码永远不会被调用。检查函数名和参数类型是否和头文件里声明的一致。
4.2 转速数值偏差大的原因分析
如果计数能跑起来但数值不对,通常是这几个原因:
每转脉冲数设置错误。如果你在旋转体上贴了两个遮光片,那每转就是 2 个脉冲,计算 RPM 时要除以 2。我建议在代码里用一个宏定义PULSES_PER_REV来表示,改起来方便。
测量窗口时间不准确。定时器的预分频和重装载值算错了,导致实际中断周期不是 1 秒。用示波器或者逻辑分析仪测一下定时器中断引脚(可以配一个 GPIO 在中断里翻转)的周期,确认是 1Hz。
信号抖动导致多计数。遮光片边缘毛刺或者安装松动,一次遮挡产生多个边沿。可以在中断里加一个软件消抖:记录上次中断的时间戳,间隔小于 1ms 就忽略。但更好的办法是改善机械安装,让遮光片边缘平整、固定牢靠。
串口打印耗时影响。如果串口发送用了阻塞模式且波特率很低,在中断里发送大量数据会拉长中断执行时间,可能影响下一个脉冲的捕获。解决办法是降低打印频率,比如每 5 秒打印一次,或者用 DMA 发送。
4.3 高速测量时的中断丢失与优化
当转速很高时,脉冲间隔可能只有几十微秒。如果中断服务函数执行时间太长,就会丢失脉冲。优化方向有几个:
缩短中断服务函数。在 EXTI 回调里只做计数,不做任何打印、计算、判断。我上面的代码里 EXTI 回调只有一行pulse_count++,这是最理想的状态。
提高中断优先级。确保 EXTI 中断能抢占其他中断。在 NVIC 里把 EXTI 的抢占优先级设到最高。
考虑用定时器的输入捕获功能替代 EXTI。STM32 的定时器有输入捕获通道,可以硬件自动记录边沿到来的时间戳,精度比软件中断高得多。对于超高转速测量,输入捕获是更专业的方案。不过对于 ITR9606 这种传感器,输出信号频率一般不会超过几十 kHz,EXTI 中断完全够用。
用 DMA 配合定时器计数。更高级的做法是把传感器信号接到定时器的外部时钟输入引脚,让定时器硬件计数,完全不需要 CPU 干预。这样能测到 MHz 级别的脉冲。但配置起来复杂一些,适合对精度要求极高的场合。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 计数完全不动 | 传感器供电异常 | 测模块 VCC 对 GND 电压 | 检查接线,确认 3.3V 或 5V 正常 |
| 计数完全不动 | 中断未使能 | 在中断里翻转一个测试 GPIO | 检查 NVIC 配置,确认 EXTI 使能 |
| 计数完全不动 | 触发边沿配反 | 轮询读引脚电平变化 | 改上升沿/下降沿配置 |
| 计数偏大 | 信号抖动 | 示波器看输出波形 | 加 RC 滤波或软件消抖 |
| 计数偏小 | 中断被抢占 | 检查中断优先级 | 提高 EXTI 抢占优先级 |
| 转速为 0 | 定时器未启动 | 检查 Start_IT 是否调用 | 确认调用 HAL_TIM_Base_Start_IT |
| 串口无输出 | 格式化库未开 | 打印固定字符串测试 | 勾选 float printf 支持 |
| 数值跳变 | 窗口时间不对 | 测定时器中断周期 | 重新计算预分频和重装载值 |
5. 实测数据与性能评估
5.1 不同转速下的测量精度对比
我用一个可调速小电机做了测试,在电机轴上贴了一个遮光片,用 ITR9606 模块测量。同时用光电转速表做参考,对比数据如下:
| 参考转速 (RPM) | 测量转速 (RPM) | 误差 (%) |
|---|---|---|
| 120 | 120.0 | 0.0 |
| 300 | 300.0 | 0.0 |
| 600 | 601.2 | 0.2 |
| 1200 | 1203.6 | 0.3 |
| 2400 | 2407.2 | 0.3 |
| 3600 | 3610.8 | 0.3 |
低速时精度很好,因为 1 秒窗口内脉冲数少,量化误差相对大,但绝对值误差小。高速时误差主要来自定时器窗口的微小偏差和脉冲丢失。整体来看,0.3% 以内的误差对于大多数应用足够了。
5.2 中断响应时间的实测数据
用逻辑分析仪抓了 EXTI 中断的响应时间:从引脚电平变化到中断服务函数第一条指令执行,大约 1.2 微秒。这个时间包括中断向量取址、现场保护、跳转到回调函数。在 72MHz 主频下,这个响应速度能轻松应对 100kHz 以下的脉冲信号。
如果主循环里有其他高优先级中断在跑,响应时间会拉长。所以前面强调的优先级配置很关键。我把 EXTI 设为最高优先级后,即使串口中断在发送数据,EXTI 也能立即抢占。
5.3 长时间运行的稳定性观察
连续跑了 4 个小时,转速在 1500 RPM 左右波动。串口每 5 秒打印一次数据,总共记录了近 3000 个数据点。没有出现计数溢出、串口卡死、程序跑飞的情况。STM32 的硬件中断机制在这种持续运行场景下非常可靠。
唯一需要注意的是pulse_count变量的溢出问题。我用的是uint32_t,在 1500 RPM、每转 1 脉冲的情况下,1 秒内计数 25 次,要 4.9 亿秒才会溢出,完全不用担心。但如果你用uint16_t,1 秒内计数超过 65535 就会溢出,对应转速 3932100 RPM,实际不可能达到,所以 16 位也够用。不过养成用 32 位的习惯没坏处。
6. 功能扩展与进阶玩法
6.1 加入 LCD 实时显示转速
串口打印适合调试,但实际产品里通常需要本地显示。加一个 0.96 寸 OLED(SSD1306 驱动)就很合适,I2C 接口只占两个引脚。在定时器中断里更新显示内容,主循环里刷新屏幕。注意 OLED 刷新比较耗时,不要在中断里直接刷屏,而是中断里更新一个全局变量,主循环里读这个变量来刷新。
显示内容可以包括:当前转速、最大转速、平均转速。最大转速用if (rpm > max_rpm) max_rpm = rpm;来更新,平均转速可以用滑动平均滤波,比如取最近 10 次测量的平均值,显示会更稳定。
6.2 超速报警功能的实现
在定时器中断里加一个判断:如果转速超过设定阈值,就置一个标志位或者直接驱动蜂鸣器。蜂鸣器可以用另一个 GPIO 控制,高电平响。但注意不要在中断里做延时,用状态机的方式:中断里置标志,主循环里根据标志控制蜂鸣器通断。
阈值可以通过串口命令动态设置,比如收到 “SET 2000” 就把阈值改成 2000 RPM。这需要解析串口接收数据,可以用串口空闲中断加 DMA 的方式接收不定长命令,比单字节中断效率高得多。
6.3 用输入捕获替代外部中断的进阶方案
如果你需要测量更高的转速,或者想要更高的精度,可以试试定时器输入捕获方案。把 ITR9606 的输出接到定时器的某个通道引脚(比如 TIM3_CH1 对应 PA6),配置成输入捕获模式,上升沿捕获。每次捕获中断里读取捕获寄存器的值,两次捕获值之差就是脉冲周期,用定时器频率除以周期就是脉冲频率,再换算成转速。
这个方案的好处是硬件自动记录时间戳,精度达到定时器分辨率级别,而且不需要 CPU 在中断里做太多事。缺点是配置复杂一些,需要处理捕获溢出和计数器回绕。对于 ITR9606 这种输出频率不高的传感器,EXTI 方案已经足够,但了解输入捕获方案对扩展技能树很有帮助。
6.4 多路传感器同时测量的思路
有些场合需要同时测多个转速,比如双电机差速系统。STM32 有多个 EXTI 线,可以接多个 ITR9606 模块。每个模块接一个独立的 GPIO,配成 EXTI 中断,在回调函数里根据引脚号区分是哪个传感器,分别计数。定时器中断里分别计算各路转速。
注意 EXTI 线不能冲突,PA0 和 PB0 不能同时用。选引脚时错开编号,比如 PA0、PB1、PC2、PD3 这样。NVIC 里每个 EXTI 线都要使能中断,优先级可以设成一样,因为它们之间没有抢占需求。
7. 个人实操心得与避坑建议
7.1 硬件安装的机械精度很关键
这个项目软件部分其实不难,真正影响测量效果的是机械安装。遮光片和 ITR9606 槽口的间隙要控制好,太大可能遮不住光,太小可能摩擦。我试过用 3D 打印一个支架把传感器固定住,遮光片用黑色电工胶带贴在电机轴上,效果比手持稳定得多。
遮光片的宽度也有讲究。太窄,高速时可能来不及完全遮挡光路;太宽,低速时占用窗口时间太长。一般来说,遮光片宽度取槽口宽度的 1/3 到 1/2 比较合适。ITR9606 的槽宽是 3mm,遮光片做 1-1.5mm 宽就行。
7.2 中断里不要做耗时操作
这是我反复强调的一点。我见过有人在 EXTI 回调里直接调用HAL_UART_Transmit打印调试信息,结果高速时串口把中断堵死,计数严重偏小。中断服务函数的执行时间应该控制在微秒级,只做最必要的操作。需要打印、计算、显示的内容,统统放到主循环或者低优先级任务里。
7.3 变量声明加 volatile 不是可选项
pulse_count这个变量在中断里写、在主循环或另一个中断里读,如果不加volatile,编译器可能把它缓存到寄存器里,导致读到的永远是旧值。这个坑很隐蔽,因为不加volatile程序也能编译运行,只是行为不确定。养成习惯:所有在中断和主程序之间共享的全局变量,一律加volatile。
7.4 调试时先用 LED 确认中断在跑
在 EXTI 回调里翻转一个 LED 引脚,这是最直观的调试手段。遮挡传感器时 LED 跟着亮灭,说明中断配置没问题。然后再去调串口打印和转速计算。分步调试比一次性写完再找问题效率高得多。
7.5 串口打印格式要方便后续处理
我习惯用RPM: %.1f\r\n这种格式,带单位、带换行。这样在串口助手里看起来清晰,复制到 Excel 里也能直接用分隔符分列。如果后续要用 Python 做数据分析,这种格式用正则表达式很容易解析。别小看这个细节,调试阶段省下的时间都是实打实的。
7.6 电源噪声对测量的影响
ITR9606 模块对电源噪声比较敏感,尤其是和电机共用电源时。电机启停会在电源线上产生尖峰,可能导致传感器误触发。解决办法:传感器单独用一路 LDO 供电,或者在模块 VCC 和 GND 之间并一个 100uF 电解电容加 0.1uF 陶瓷电容。我在电机和控制器共电源的场合实测过,加了电容后误计数明显减少。
7.7 代码版本管理从第一天就开始
这个项目虽然小,但调试过程中会反复修改配置和逻辑。我建议用 Git 管理代码,每次改完一个功能就提交一次。这样当你不小心改坏了某个配置,可以快速回退到上一个能工作的版本。别问我怎么知道这个重要性的,说多了都是泪。
7.8 从这个小项目延伸出去的方向
转速测量本身是个小功能,但它可以嵌入很多大系统里。比如做智能风扇控制器,根据转速反馈调节 PWM 占空比实现恒速控制;做电机故障诊断,通过转速波动判断轴承磨损;做风力发电机监测,记录转速和发电功率的关系曲线。把这个项目吃透,这些延伸方向都是水到渠成的事。
我个人在实际操作中的体会是,嵌入式开发里最值钱的不是会调某个库函数,而是理解整个信号链——从物理量到传感器输出,到 MCU 采集,到数据处理,再到执行器动作。这个转速计数器项目虽然简单,但完整地走了一遍这个链条。把它做扎实,比囫囵吞枣做十个项目都有用。