文章目录

1.定时器输入捕获

在这里插入图片描述
在这里插入图片描述

1.1输入捕获测量脉宽

在这里插入图片描述

1.2 输入捕获的使用方法

在这里插入图片描述

1.3 第一个阶段:输入滤波

在这里插入图片描述

1.4第二个阶段:边沿检测

在这里插入图片描述

  • 节点4选择好之后,选择上升沿直接,到节点5,每遇到一个上升沿 就向外发送一个脉冲

1.5 第三个阶段:信号选择

  • 节点4选择好之后,选择上升沿直接,到节点5,每遇到一个上升沿 就向外发送一个脉冲

1.5.1 通道1 选择 上升沿脉冲,直接

在这里插入图片描述

1.5.2 通道1 选择 下升沿脉冲,直接

在这里插入图片描述

1.5.3 通道1 选择 上升沿脉冲,间接

  • 捕获的是通道2的上升沿
    在这里插入图片描述

1.5.3 通道1 选择 下升沿脉冲,间接

在这里插入图片描述

1.5.4 第四个阶段:分频

在这里插入图片描述

在这里插入图片描述

1.5测试

  • 由于通道之间可以相互引用的,只需要把脉冲信号输入到一个通道即可,其他通道保持悬空
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

第一、定时器输入捕获:如何获取脉冲个数时间值(终极总结)

我给你用最清晰、最直白、最不容易忘的方式总结,你以后永远不会混淆!


一、输入捕获能获取两种东西

  1. 时间值(两次脉冲之间的时间 / 周期)
  2. 脉冲个数(来了多少个脉冲)

但获取方式完全不一样!


二、如何获取【时间值】(输入捕获本职工作 ✅)

原理:

输入捕获硬件自动记录脉冲到来时的定时器CNT值

获取方法:

不用计数,直接读捕获寄存器!

// 启动 TIM5 通道3 输入捕获(无中断,纯硬件跑)
HAL_TIM_IC_Start(&htim5, TIM_CHANNEL_3);
  • 模式 A:等待捕获(阻塞式)必须读到最新一次脉冲才继续运行
//脉冲到来的时间点
uint32_t Get_Motor_Speed(void)
{
    // 1. 等待硬件捕获到一个上升沿(无中断)
    while(__HAL_TIM_GET_FLAG(&htim5, TIM_FLAG_CC3) == RESET);

    // 2. 读取硬件抓到的计数值(脉冲发生时刻的定时器值)
    uint32_t capture_val = HAL_TIM_ReadCapturedValue(&htim5, TIM_CHANNEL_3);

    // 3. 清除标志,准备下一次捕获
    __HAL_TIM_CLEAR_FLAG(&htim5, TIM_FLAG_CC3);

    return capture_val;
}
  • 模式 B:直接读取(非阻塞、你现在要用的)不需要等待\不需要清除标志位\读到的就是:最后一次脉冲的时间点
val = HAL_TIM_ReadCapturedValue(&htim5, TIM_CHANNEL_3);

算周期(时间差):

now = 本次时间点
last = 上一次时间点
周期 = now - last

结论:

时间值 = 直接读硬件 → 不用中断也能读


算周期:

必须用输入捕获中断:每来一个上升沿进中断,保存本次时间点,和上一次时间点相减,直接得到精准单个周期T

// 全局变量
uint32_t last_cap = 0;
float rpm = 0;
// 输入捕获中断回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM5 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
    {
        uint32_t now = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
        uint32_t period = now - last_cap;    // 精准单个脉冲周期!
        last_cap = now;
        
        // 直接套公式算转速(你的定时器1MHz,1计数=1us)
        float T = period / 1000000.0f;
        rpm = (1.0f / T) / 4.0f * 60.0f / 3.0f;
    }
}

优点

  1. 精准100%,单个脉冲周期无误差,完全贴合驱动器原厂公式
  2. 脉冲来一次,更新一次最新转速,响应极快

缺点

  1. 必须开输入捕获中断
    电机高速时(2500rpm,500Hz脉冲),1秒进500次中断,频繁抢占CPU,会影响你PID!

三、如何获取【脉冲个数】(输入捕获不能自动做到 ❗)

重点:

输入捕获本身不会自动计数!
必须靠中断通知你脉冲来了,你手动 +1

正确代码(你写的那种):

// 启动 TIM5 通道3 输入捕获中断 
HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_3);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
    {
        pulse_left++;  // 手动计数
    }
}

结论:

脉冲个数 = 必须开中断 + 软件手动++


四、最核心的一句话总结(背这个)

**时间值:硬件自动存 → 直接读 CCR

脉冲个数:必须开中断 → 软件自己 +1**


五、一张表彻底看懂

要获取什么 方法 是否需要中断
时间值(周期/频率) 读 HAL_TIM_ReadCapturedValue() ❌ 不需要
脉冲个数 中断里手动 pulse++ 必须开

六、你最终可以记住的超级口诀

输入捕获,
存时间不存数;
要数必须开中断,
自己手动加一加。


第二、 输入捕获 / 外部时钟计数 / 编码器模式 终极总结(必背)


一、详细对比(最清晰版)

1. 输入捕获 Input Capture

作用:记录脉冲到来的时间点
能算:周期、频率、转速
不能:统计总脉冲数、不能知道方向
引脚:1个通道(CHx)
硬件行为
上升沿 → 把当前CNT锁存到CCR
不自动+1
使用场景:电机SPEED引脚测速


2. 外部时钟 External Clock Mode 1

作用统计脉冲个数
能算:10ms内多少个脉冲、总脉冲数
引脚:ETR引脚 或 通道引脚
硬件行为
来一个脉冲 → CNT 自动 +1
不用中断、不用代码
使用场景:定时统计脉冲数 → 算转速


3. 编码器模式 Encoder Mode

作用:解码电机A/B正交信号
能测:位置、距离、方向、转速
引脚:2个通道(CH1 + CH2)
硬件行为
自动判断方向 → CNT 自动 +1 或 -1
使用场景:你小车行走距离、10mm拍照 刚需!


三、最关键区别(你最容易混淆)

输入捕获:不计数!只记时间!

外部时钟:只计数!不知道方向!

编码器:计数 + 方向 + 位置全能!


三、针对你驱动器SPEED引脚,3套测速方案 完整优劣对比(结合你的所有需求)

结合你驱动器官方公式、STM32三个定时器模式、你的PID控制、双电机、不影响中断、不影响编码器拍照全部需求,我给你分方案讲透,并且给出唯一最优解

先回顾你驱动器SPEED引脚官方公式(永远基准)

N ( r p m ) = F P × 60 3 \boldsymbol{N(\mathrm{rpm}) = \frac{F}{P} \times \frac{60}{3}} N(rpm)=PF×360

  • N N N:电机转速(rpm)
  • F F FSPEED引脚脉冲频率(1秒多少个脉冲,Hz)
  • P = 4 P=4 P=4:你的电机固定极对数

公式变形:
F = 脉冲个数 统计时间 \boldsymbol{F = \frac{脉冲个数}{统计时间}} F=统计时间脉冲个数
测速的本质,永远是: 频率 F = 时间段内脉冲总数 统计时长 \boldsymbol{频率F = \frac{时间段内脉冲总数}{统计时长}} 频率F=统计时长时间段内脉冲总数


方案1:【相邻脉冲测周期法】(输入捕获本职原生用法,无任何误差)

原理

抓住每相邻2个上升沿,精准算出单个脉冲周期 T \boldsymbol{T} T
T = T 第 n + 1 个脉冲 − T 第 n 个脉冲 T = T_{第n+1个脉冲} - T_{第n个脉冲} T=Tn+1个脉冲Tn个脉冲
F = 1 T F=\frac1T F=T1

硬件实现(输入捕获)

必须用输入捕获中断:每来一个上升沿进中断,保存本次时间点,和上一次时间点相减,直接得到精准单个周期T

// 全局变量
uint32_t last_cap = 0;
float rpm = 0;
// 输入捕获中断回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM5 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
    {
        uint32_t now = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
        uint32_t period = now - last_cap;    // 精准单个脉冲周期!
        last_cap = now;
        
        // 直接套公式算转速(你的定时器1MHz,1计数=1us)
        float T = period / 1000000.0f;
        rpm = (1.0f / T) / 4.0f * 60.0f / 3.0f;
    }
}

优点

  1. 精准100%,单个脉冲周期无误差,完全贴合驱动器原厂公式
  2. 脉冲来一次,更新一次最新转速,响应极快

缺点

  1. 必须开输入捕获中断
    电机高速时(2500rpm,500Hz脉冲),1秒进500次中断,频繁抢占CPU,会影响你PID、编码器、拍照中断,就是你最一开始担心的问题!

方案2:【定时器外部时钟模式(ETR)】

原理(完全贴合频率原始定义)

F = 10 m s 内统计到的脉冲个数 0.01 s \boldsymbol{F = \frac{10ms内统计到的脉冲个数}{0.01s}} F=0.01s10ms内统计到的脉冲个数
固定10ms时间窗口,统计这段时间一共来了多少个脉冲,直接算频率,再算转速

硬件实现(你之前纠结的点全部闭环)

定时器外部时钟模式(ETR)

  1. 定时器配置:外部时钟模式,SPEED脉冲作为时钟源
  2. 来1个上升沿脉冲,硬件CNT自动+1,纯硬件计数,全程不开任何中断
  3. 你原有的TIM3 1ms中断,每10ms固定调度一次
  4. 10ms到:读取CNT计数值 = 这10ms内脉冲总数,读完清零计数器,开启下一轮统计

代码(完美适配你的工程,无任何漏洞)

// 全局变量
uint8_t time_10ms = 0;
uint32_t pulse_cnt_10ms = 0;    // 10ms内脉冲个数
float speed_left = 0.0f;
#define P 4                     // 电机极对数4

// 你的TIM3 1ms基础中断(原有代码完全不动)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim == &htim3)
    {
        time_10ms++;
        if(time_10ms >= 10) // 10ms定时到
        {
            time_10ms = 0;
            // 1. 读取硬件统计的【10ms内所有脉冲总数】
            pulse_cnt_10ms = __HAL_TIM_GET_COUNTER(&htim5);
            // 2. 计数器清零,开始下一个10ms统计周期
            __HAL_TIM_SET_COUNTER(&htim5, 0);

            // 3. 计算脉冲频率 F = 脉冲数 / 统计时间(秒)
            float F = (float)pulse_cnt_10ms / 0.01f;

            // 4. 严格套用驱动器原厂官方公式 计算转速rpm
            speed_left = (F / P) * 60.0f / 3.0f;

            // 5. 此处直接调用PID函数!完美10ms固定周期PID更新
            // PID_Control(speed_left, target_speed);
        }
    }
}

优点(完全命中你所有需求)

  1. 完全无漏洞:不管10ms里来了1个、5个、10个脉冲,全部精准统计个数,频率计算100%准确
  2. 零中断占用:脉冲计数全部是硬件自动完成,不开任何输入捕获中断,完全不抢占、不影响你任何其他程序
  3. 固定10ms更新PID:移动机器人差速小车调速黄金控制周期,速度平稳不抖动,响应速度刚好
  4. 抗干扰强:多个脉冲做平均,单次脉冲噪声不会影响转速测量

你之前接触的所有选项,全部给你逐个定性:
① Trigger Mode 触发模式
功能:外部脉冲上升沿,启动定时器内部时钟开始计时
CNT 计数器是跟着芯片内部时钟不停自增,不是来 1 个外部脉冲 + 1
记录的是时间,不是脉冲个数,和你计数需求完全无关
② Gated Mode 门控模式
功能:外部引脚高电平时,内部时钟运行计时;低电平暂停
依旧是内部时钟计时,不是脉冲个数计数
③ Reset Mode 重置模式
功能:外部脉冲上升沿,把 CNT 计数器清零
完全不计数,只做清零
三、补充:之前所有「外部时钟 Mode2」的完整真相(彻底闭环)

  1. 外部时钟模式 2(TIxFPx 通道引脚硬件自动 + 1)
    来 1 个通道引脚(CHx)脉冲 → CNT 硬件自动 + 1
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐