1. 项目概述与核心价值

在伺服电机、机器人关节、精密转台这类对低速平稳性要求极高的运动控制场景里,工程师们最头疼的问题之一,就是如何精确测量极低转速下的电机速度。你可能会想,用高分辨率编码器不就行了?但实际情况是,当电机转速低到每分钟只有几转甚至更低时,编码器脉冲的间隔时间会变得非常长。如果单纯依赖CPU去定时读取位置计数器并做软件差分,不仅会消耗大量宝贵的CPU周期,更会因为软件延迟和中断响应时间的不确定性,引入难以校准的测量误差,导致速度环产生低频抖动,整个系统的控制性能大打折扣。

这时,像TI TMS320F2837xD这类高性能微控制器内置的 增强型正交编码器脉冲(eQEP) 模块的 边沿捕获单元(Edge Capture Unit) ,其价值就凸显出来了。它不是一个简单的计数器,而是一个专为“时间间隔测量”而生的硬件加速器。它的核心任务,就是自动、精准地捕获“单位位置事件”之间的时间差(ΔT)。这个“单位位置事件”可以灵活配置,比如可以是编码器产生的每1个、4个、16个正交时钟边沿。硬件会在这个事件发生的瞬间,“咔嚓”一下把内部一个高速运行的自由运行定时器的当前值锁存下来。两次锁存值之差,就是精确的ΔT,其时间基准直接来源于系统主频(SYSCLKOUT),分辨率可以轻松达到纳秒级。

这样一来,低速速度计算就从一个复杂的软件定时问题,简化成了一个简单的硬件读数问题: 速度 v = (固定的位置增量 ΔX) / (硬件捕获的时间差 ΔT) 。CPU只需要在需要的时候(比如速度环控制周期到来时),去读取两个锁存寄存器( QCPRDLAT , QPOSLAT ),就能直接获得高精度的速度信息,几乎不占用CPU资源。这对于实现高带宽、高精度的电流环、速度环、位置环全闭环控制至关重要。今天,我就结合手册和实际调参经验,把这个eQEP边沿捕获单元从原理到寄存器配置,再到实际编程和避坑指南,给你彻底讲透。

2. eQEP边沿捕获单元的核心原理与架构拆解

要玩转这个边沿捕获单元,不能只停留在“怎么配寄存器”的层面,必须吃透它的工作原理。你可以把它想象成一个专为编码器脉冲“掐表”的精密计时系统。

2.1 核心测量模型:v(k) = ΔX / ΔT

所有低速测量的理论基础都是这个公式: v(k) = ΔX / ΔT

  • ΔX (位置增量) :这是一个“固定”的单位位移量。它由你的编码器线数和 QCAPCTL[UPPS] (单位位置事件预分频器)共同决定。例如,一个1000线的编码器,四倍频后每转产生4000个QCLK(正交时钟)。如果你设置 UPPS=0010 (即除以4),那么一个“单位位置事件”(UPEVNT)就对应着 4000 / 4 = 1000 个QCLK,也就是电机正好转了1/4圈。这个 ΔX 就是1/4圈对应的机械位移(例如,如果丝杠导程是10mm,那么ΔX就是2.5mm)。 关键在于,这个ΔX在你的测量周期内是已知的常数。
  • ΔT (时间增量) :这就是边沿捕获单元要测量的核心数据。它代表电机走过上述固定位移 ΔX 所花费的真实时间。这个时间由硬件捕获定时器 QCTMR 测量,并在每个单位位置事件发生时,将其计数值锁存到 QCPRD 寄存器中。 ΔT 就等于连续两次锁存值之差( QCPRD(k) - QCPRD(k-1) )乘以捕获时钟的周期。

为什么这个模型适合低速测量? 在高速时,我们常用“M法测速”(固定时间间隔内数脉冲个数)。但在低速时,固定时间内可能只收到寥寥几个甚至零个脉冲,测量分辨率极低,误差巨大。而“T法测速”(测量固定脉冲数之间的时间间隔)在低速时,由于脉冲间隔长,时间测量值大,相对误差小,分辨率高。eQEP的边沿捕获单元就是硬件实现的T法测速。

2.2 硬件架构与数据流

根据手册中的图17-15,整个边沿捕获单元的硬件流水线可以分解为以下几个关键环节:

  1. 时钟源与预分频 :整个单元的时基来源于 SYSCLKOUT (系统时钟)。它首先经过一个由 QCAPCTL[CCPS] 控制的3位分频器,产生 CAPCLK ,作为捕获定时器 QCTMR 的计数时钟。 CCPS 可以从/1配置到/128,这让你能根据电机最高速度和测量精度需求来调整 QCTMR 的计数频率,避免溢出。
  2. 单位位置事件生成 :编码器输入经过解码后产生 QCLK (正交时钟)和 QDIR (方向信号)。 QCLK 再经过一个由 QCAPCTL[UPPS] 控制的4位分频器,产生“单位位置事件”( UPEVNT )信号。 UPPS 的分频系数从/1到/2048,这直接决定了你的 ΔX 有多大。 UPPS 是软件定义“单位位移”大小的关键
  3. 捕获与锁存机制 :这是最精妙的部分。 QCTMR 是一个16位的自由运行向上计数器,始终由 CAPCLK 驱动。每当一个 UPEVNT 事件发生时,硬件会立即执行两个动作:
    • 锁存 :将 QCTMR 的当前值锁存到捕获周期寄存器 QCPRD 中。
    • 复位 :将 QCTMR 清零,重新开始计数。 同时,状态寄存器 QEPSTS 中的 UPEVNT 标志位会被置1,告知CPU有新的数据可用。CPU在读取 QCPRD 之前,应先检查该标志位。
  4. 双缓冲锁存器 :为了避免CPU在读取 QCPRD 时,该值被新的捕获事件覆盖,模块提供了锁存寄存器 QCPRDLAT QCTMRLAT 。根据 QEPCTL[QCLM] 位的设置,锁存触发方式有两种:
    • QCLM=0 :当CPU读取位置计数器 QPOSCNT 时,自动将 QCTMR QCPRD 的值分别锁存到 QCTMRLAT QCPRDLAT 。这种方式适合在任意时刻采样速度。
    • QCLM=1 :当“单位超时事件”(由另一个独立的32位单元定时器 QUTMR QUPRD 比较产生)发生时,自动将 QPOSCNT QCTMR QCPRD 的值分别锁存到 QPOSLAT QCTMRLAT QCPRDLAT 。这种方式适合在固定的、周期性的控制中断里同步获取位置和速度信息,是最常用、最可靠的方式。

2.3 关键约束条件与错误处理

硬件不是万能的,它有两个重要的约束条件,在编程中必须处理:

  1. 无溢出条件 QCTMR 是一个16位计数器,最大计数值为65535。手册明确指出, 两个连续的单位位置事件之间, QCTMR 的计数值不能超过65535 。如果超过了, QEPSTS[COEF] (捕获溢出错误标志)会被置1,并且 QCPRDLAT 会被设置为0xFFFF。此时读取的ΔT无效。

    • 应对策略 :通过合理设置 CCPS 分频比来降低 CAPCLK 频率,确保在最低预期转速下, QCTMR 也不会溢出。这需要计算。
  2. 无方向改变条件 :在测量两个单位位置事件之间的时间ΔT时,电机的旋转方向必须保持不变。如果方向改变了, QEPSTS[CDEF] (捕获方向错误标志)会被置1, QCPRDLAT 同样被置为0xFFFF。

    • 应对策略 :在速度计算中断服务程序中,读取数据前必须先检查 CDEF 标志。如果置位,说明此次测量无效,应丢弃该速度值,并清除标志位。通常可以沿用上一次的有效速度值或进行滤波处理。

注意 :手册中特别用 CAUTION 强调了 QCAPCTL[UPPS] (单位事件预分频器) 不能动态修改 。也就是说,你不能在捕获单元使能后,运行时去改变 UPPS 的值,否则会导致未定义行为。 CCPS (捕获时钟预分频器)虽然可以动态修改,但 必须在捕获单元禁用( QCAPCTL[CEN]=0 )的情况下进行 。这是一个非常容易踩坑的地方,务必在初始化阶段就确定好分频参数,运行时不要更改 UPPS

3. 低速测量实战:从寄存器配置到速度计算

理论清楚了,我们直接上干货,看看在TMS320F2837xD的C代码里,如何一步步配置并读出可靠的低速值。

3.1 硬件与软件初始化配置

假设我们使用eQEP1外设,编码器为2500线(四倍频后每转10000个QCLK),系统时钟 SYSCLKOUT 为200MHz。我们希望测量低至1RPM左右的速度,并且速度环控制频率为10kHz(即每100us计算一次速度)。

步骤1:确定单位位置事件(ΔX) 我们希望速度更新率(与控制频率同步)为10kHz,但单位位置事件不能太快,否则在低速时 QCTMR 计数值太小,量化误差大。我们折中一下,让单位位置事件频率约为速度更新频率的10-100倍。这里设定单位位置事件对应电机旋转1/100转。

  • 每转QCLK数 = 2500 * 4 = 10000。
  • 对应1/100转的QCLK数 = 10000 / 100 = 100。
  • 因此,需要配置 UPPS ,使得 UPEVNT = QCLK / N 中的 N = 100。查手册, UPPS 是2的幂次分频(1,2,4,...,2048)。100不是2的幂,我们选择最接近的128( UPPS=7h )。此时,一个 UPEVNT 实际对应 10000 / 128 ≈ 78.125 个QCLK。我们的 ΔX 就是这78.125个QCLK对应的机械位移。 记住这个实际值,后续计算速度要用它

步骤2:配置捕获时钟,防止溢出 首先估算最低转速下的最大ΔT。在1RPM时:

  • 电机每秒转数 = 1 / 60 ≈ 0.01667 RPS。
  • 每转时间 = 1 / 0.01667 ≈ 60秒。
  • 一个 UPEVNT 事件的时间(对应1/100转)≈ 60 / 100 = 0.6秒。 我们需要确保在0.6秒内, QCTMR 从0计数到最大值65535也不会溢出。
  • QCTMR 计数频率 = CAPCLK = SYSCLKOUT / CCPS_DIV
  • 要求: 0.6秒 * CAPCLK < 65535
  • 推导出: CAPCLK < 65535 / 0.6 ≈ 109225 Hz
  • 已知 SYSCLKOUT =200MHz,所以分频比 CCPS_DIV > 200e6 / 109225 ≈ 1831。 查手册, CCPS 最大分频为128。200MHz / 128 = 1.5625MHz,这远大于109kHz, 在1RPM时依然会溢出! 这说明,在极低速(1RPM)且单位事件划分较细(1/100转)的情况下,16位 QCTMR 可能不够用。我们有三个选择:
  • 选择A:增大单位事件 。将 UPPS 设为256或512,让 ΔX 变大,这样相同转速下 UPEVNT 间隔时间变短,不易溢出。但会降低速度测量的“颗粒度”。
  • 选择B:接受更高的最低测量转速 。例如,将最低测量转速设定为10RPM,重新计算。
  • 选择C:在软件中处理溢出 。如果溢出发生( COEF=1 ),则本次速度值无效。这对于偶尔的极低速情况可以接受。 这里为了演示,我们选择B,假设最低测量转速为10RPM。重新计算:
  • 10RPM下,一个 UPEVNT 时间 ≈ (60/10) / 100 = 0.06秒。
  • 要求 CAPCLK < 65535 / 0.06 ≈ 1.09MHz
  • CCPS_DIV > 200e6 / 1.09e6 ≈ 183。选择 CCPS=7 (分频128), CAPCLK =1.5625MHz,满足要求。 结论 :配置 CCPS=7 CAPCLK = SYSCLKOUT/128 )。

步骤3:初始化代码示例

// 假设使用eQEP1
void InitEQEP1_EdgeCapture(void)
{
    // 1. 首先禁用eQEP模块,进行整体配置
    EQep1Regs.QEPCTL.bit.QPEN = 0; // 禁用位置计数器,复位内部状态

    // 2. 配置解码器控制 (根据实际硬件接线)
    EQep1Regs.QDECCTL.bit.QSRC = 0; // 00: 正交计数模式(标准编码器模式)
    EQep1Regs.QDECCTL.bit.XCR = 1;  // 1x分辨率(仅上升沿计数)。对于高分辨率编码器,也可选0(2x,上升下降沿都计数)
    // QAP, QBP, QIP, QSP 根据编码器信号极性配置,默认0(不取反)

    // 3. 配置边沿捕获单元
    EQep1Regs.QCAPCTL.bit.UPPS = 7;   // UPPS=7: UPEVNT = QCLK / 128 (对应我们约1/100转的事件)
    EQep1Regs.QCAPCTL.bit.CCPS = 7;   // CCPS=7: CAPCLK = SYSCLKOUT / 128 = 200MHz/128 ≈ 1.5625MHz
    EQep1Regs.QCAPCTL.bit.CEN = 1;    // 使能捕获单元

    // 4. 配置单元定时器,用于周期性锁存(QCLM=1模式)
    // 速度环频率10kHz,即周期100us。SYSCLKOUT=200MHz,周期为5ns。
    // 单元定时器计数值 = 时间间隔 / 时钟周期 = 100us / 5ns = 20000
    EQep1Regs.QUPRD = 20000;          // 设置单元定时器周期
    EQep1Regs.QEPCTL.bit.UTE = 1;     // 使能单元定时器
    EQep1Regs.QEPCTL.bit.QCLM = 1;    // 锁存模式:在单元超时事件时锁存(推荐!)

    // 5. 配置中断(可选,如果需要利用UTO中断触发速度计算)
    EQep1Regs.QEINT.bit.UTO = 1;      // 使能单元超时中断

    // 6. 最后,使能eQEP位置计数器
    EQep1Regs.QEPCTL.bit.QPEN = 1;

    // 7. 初始化变量(软件层面)
    g_i32PrevQcpLat = 0;
    g_i32PrevPosLat = 0;
    g_fSpeedRpm = 0.0;
}

3.2 速度计算与数据处理流程

配置好后,速度计算通常在单元超时中断(UTO)服务程序或主循环的定时任务中进行。这里以UTO中断服务程序为例:

// 全局变量
int32_t g_i32PrevQcpLat = 0; // 上一次锁存的捕获周期值
int32_t g_i32PrevPosLat = 0; // 上一次锁存的位置值
float g_fSpeedRpm = 0.0;     // 计算得到的转速(RPM)
const float g_fMechScaler = 60.0f / (10000.0f / 128.0f); // 速度计算标尺,后面解释

__interrupt void eQEP1_ISR(void)
{
    // 1. 检查中断源
    if(EQep1Regs.QFLG.bit.UTO == 1) // 单元超时中断
    {
        // 2. 读取锁存寄存器(在QCLM=1模式下,UTO事件会自动锁存)
        int32_t i32CurrQcpLat = (int32_t)EQep1Regs.QCPRDLAT; // ΔT对应的计数值
        int32_t i32CurrPosLat = (int32_t)EQep1Regs.QPOSLAT;   // 当前位置值

        // 3. 检查捕获错误标志
        if(EQep1Regs.QEPSTS.bit.CDEF == 1)
        {
            // 方向在测量期间发生变化,本次ΔT无效
            // 可以丢弃本次计算,或使用上一次的有效速度值
            // 清除错误标志
            EQep1Regs.QEPSTS.bit.CDEF = 1;
        }
        else if(EQep1Regs.QEPSTS.bit.COEF == 1)
        {
            // 捕获定时器溢出,本次ΔT无效(发生在转速过低时)
            // 处理策略:可以标记速度无效,或尝试增大CCPS分频比(需重新初始化)
            // 清除错误标志
            EQep1Regs.QEPSTS.bit.COEF = 1;
        }
        else
        {
            // 4. 计算时间增量ΔT(单位:秒)
            // ΔT = (当前捕获值 - 上一次捕获值) / CAPCLK频率
            // 注意:QCTMR是16位计数器,但QCPRDLAT也是16位,相减可能为负(由于计数器复位),需处理。
            // 实际上,因为UTO周期固定,且QCTMR在每个UPEVNT事件复位,两次UTO间的QCPRDLAT差值
            // 直接反映了最后一个UPEVNT周期的时间。
            // 更稳健的方法是:ΔT = i32CurrQcpLat / CAPCLK_Freq
            // 但i32CurrQcpLat是本次UTO时刻锁存的QCTMR值,它记录的是上个UTO到本次UTO之间,
            // 最后一个UPEVNT事件发生后的“残余”时间。这不是一个完整的UPEVNT周期。
            // 因此,更准确的方法是利用UPEVNT标志和QCPRD(非锁存)寄存器。
            // 这里演示一种在UTO中断中,且确保一个UTO周期内至少发生一次UPEVNT的简化方法:
            if(g_i32PrevQcpLat != 0) // 非第一次进入
            {
                // 计算ΔT(假设一个UTO周期内只有一个主要的UPEVNT,其周期被锁存在QCPRD)
                // 注意:需要将QCPRD的值从QCTMR计数值转换为时间。
                float fDeltaT = (float)EQep1Regs.QCPRD / CAPCLK_FREQ; // CAPCLK_FREQ = 1.5625e6 Hz

                // 5. 计算速度
                // ΔX = (1转的位移) / (每转的UPEVNT数)
                // 每转UPEVNT数 = (编码器线数 * 4) / UPPS分频系数 = 10000 / 128 = 78.125
                // 因此,每��UPEVNT对应的机械角度 = 360度 / 78.125 ≈ 4.608度
                // 或者,每个UPEVNT对应的转数 = 1 / 78.125 ≈ 0.0128 转。
                // 速度 v (转/秒) = ΔX (转) / ΔT (秒) = (1/78.125) / fDeltaT
                // 速度 v (RPM) = v (转/秒) * 60 = 60 / (78.125 * fDeltaT)
                // 代入 fDeltaT = QCPRD / CAPCLK_FREQ
                // 得到:v_rpm = 60 * CAPCLK_FREQ / (78.125 * QCPRD)
                // 定义常量 g_fMechScaler = 60.0 * CAPCLK_FREQ / 78.125
                if(EQep1Regs.QCPRD != 0) // 避免除零
                {
                    g_fSpeedRpm = g_fMechScaler / (float)EQep1Regs.QCPRD;
                }
            }

            // 更新上一次的捕获值(用于错误检测或备用计算)
            g_i32PrevQcpLat = i32CurrQcpLat;
            g_i32PrevPosLat = i32CurrPosLat;
        }

        // 6. 清除中断标志
        EQep1Regs.QCLR.bit.UTO = 1; // 写1清除UTO中断标志
        EQep1Regs.QCLR.bit.INT = 1; // 写1清除全局中断标志
    }

    // 其他中断源处理...
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP4; // 确认PIE组中断
}

关于速度计算标尺 g_fMechScaler 的详细推导: 这是将硬件读数转换为物理速度的关键常数。

  • 公式: v_rpm = (ΔX_revolution / ΔT_second) * 60
  • 其中:
    • ΔX_revolution = 1 / UPEVNT_per_revolution (每个单位位置事件对应的转数)
    • UPEVNT_per_revolution = (Encoder_PPR * 4) / UPPS_Divider
      • Encoder_PPR : 编码器每转脉冲数(线数),本例为2500。
      • 4 : 四倍频。
      • UPPS_Divider : UPPS 设置的分频系数,本例为128。
    • 所以, ΔX_revolution = 1 / ((2500*4)/128) = 128 / 10000 = 0.0128 转
  • ΔT_second = QCPRD / CAPCLK_Freq
    • QCPRD : 硬件捕获的寄存器值(无单位,计数个数)。
    • CAPCLK_Freq = SYSCLKOUT / CCPS_Divider = 200e6 / 128 = 1.5625e6 Hz
  • 代入公式: v_rpm = (0.0128 / (QCPRD / 1.5625e6)) * 60 = (0.0128 * 1.5625e6 * 60) / QCPRD = (1200000) / QCPRD
  • 因此,本例中的 g_fMechScaler = 1200000.0

这个推导过程至关重要 。在实际项目中,你需要根据自己电机的编码器参数和 UPPS CCPS 的配置,重新计算这个标尺因子。

4. 高级应用与配置技巧

掌握了基础的速度计算,我们再来看看如何利用边沿捕获单元实现更稳定、更鲁棒的应用。

4.1 使用“单位超时”模式实现同步采样

如前所述,将 QEPCTL[QCLM] 设置为1是最推荐的方式。在这种模式下,位置 QPOSLAT 、捕获定时器 QCTMRLAT 、捕获周期 QCPRDLAT 这三个值会在 同一个时刻 (即单元定时器 QUTMR 匹配 QUPRD 产生 UTO 事件的时刻)被硬件自动锁存。这保证了位置和速度采样在时间上是严格同步的,对于控制算法来说消除了采样不同步带来的相位误差。

配置要点

  1. 根据你的控制频率(如10kHz)设置 QUPRD
  2. 使能单元定时器( UTE=1 )。
  3. 设置 QCLM=1
  4. UTO 中断中,同时读取 QPOSLAT QCPRDLAT (或 QCTMRLAT ),分别用于位置环和速度环计算。

4.2 低速测量的滤波与抗扰处理

在极低速或电机堵转时,测量容易受到干扰,产生跳变。可以加入软件滤波:

  1. 中值滤波/滑动平均滤波 :连续采样多个速度值(如5个),取中值或平均值作为最终输出。这能有效抑制偶然的野值。

    #define SPEED_FILTER_SIZE 5
    float speedBuffer[SPEED_FILTER_SIZE] = {0};
    int speedIndex = 0;
    float GetFilteredSpeed(float newSpeed)
    {
        speedBuffer[speedIndex] = newSpeed;
        speedIndex = (speedIndex + 1) % SPEED_FILTER_SIZE;
        float sum = 0;
        for(int i=0; i<SPEED_FILTER_SIZE; i++) sum += speedBuffer[i];
        return sum / SPEED_FILTER_SIZE;
    }
    
  2. 无效数据剔除 :在中断服务程序中,如果检测到 CDEF COEF 错误标志,或者计算出的速度值超过物理极限(如额定转速的120%),则丢弃该次数据,维持上一次的有效速度或输出零速。

  3. 死区处理 :当计算出的速度绝对值小于一个很小的阈值(例如0.1%额定转速)时,直接将其设为零。这可以避免因测量噪声或微小抖动导致电机在零速指令下仍有微动。

4.3 全速度范围测量:结合M/T法思想

eQEP的边沿捕获单元本质是T法(测周法),在低速时优势明显,但在高速时, UPEVNT 事件间隔很短, QCTMR 计数值很小,量化误差占比会变大。对于宽速度范围的应用,理想的方案是 低速用T法,高速用M法

eQEP模块本身没有直接提供M法测量,但我们可以利用其资源实现一个“软M/T法”:

  • M法(高速区) :在固定的单元超时周期( UTO ,如100us)内,读取 QPOSLAT 的增量 ΔX_cnt (位置计数器差值)。速度 v = ΔX_cnt / (T * K) ,其中T是固定周期,K是每个QCLK对应的位移。
  • 切换逻辑 :在软件中判断。当用T法计算出的速度值高于某个阈值(例如额定转速的5%),且 QCPRD 值小于某个阈值(表示脉冲周期短,T法误差大)时,切换到M法计算。反之,则使用T法。

实现提示 QPOSLAT UTO 事件时锁存,正好提供了固定周期T内的位置增量,非常适合做M法计算。你可以在同一个 UTO 中断里,同时用T法(基于 QCPRD )和M法(基于本次和上次的 QPOSLAT 差)计算两个速度值,然后根据逻辑选择输出哪一个。

5. 常见问题排查与调试心得

调过这个模块的工程师,多少都踩过一些坑。下面是我总结的几个典型问题和解决方法。

5.1 问题排查清单

现象 可能原因 排查步骤与解决方案
读出的速度值始终为0或极大 1. 捕获单元未使能。
2. UPPS 设置过大,单位事件未产生。
3. 编码器信号未正确接入或极性配置错误。
1. 检查 QCAPCTL[CEN] 是否为1。
2. 用示波器或IO翻转调试,检查 UPEVNT 信号是否产生。减小 UPPS 值。
3. 检查 QDECCTL QSRC XCR QAP QBP 配置。用示波器看QEPA、QEPB波形,并监控 QPOSCNT 是否随电机转动变化。
速度值波动大,噪声明显 1. 转速过低,接近测量下限。
2. CCPS 分频太小, CAPCLK 过高, QCTMR 计数值太小,量化误差大。
3. 机械振动或编码器信号噪声。
1. 尝试增大 UPPS ,以增大 ΔX ,提高T法信噪比。
2. 增大 CCPS 分频比,降低 CAPCLK ,使 QCTMR 在低速下也有足够大的计数值。
3. 检查编码器连接,使用双绞线,并确保电源稳定。在软件中加入滤波。
偶尔出现速度跳变到极大值 1. QCPRD 寄存器读到了0。
2. 发生了捕获溢出( COEF )或方向错误( CDEF )。
1. 在速度计算前判断 if(QCPRD == 0) ,则跳过本次计算或使用上次值。
2. 在读取速度前,先检查 QEPSTS[COEF] [CDEF] 标志,若置位则处理错误并清除标志。
UTO 中断不产生或速度更新慢 1. 单元定时器未使能或 QUPRD 设置过大。
2. eQEP模块全局中断未使能,或PIE、CPU中断未配置。
3. QCLM 模式配置错误。
1. 检查 QEPCTL[UTE] 是否为1,计算 QUPRD 值是否正确。
2. 检查 QEINT 、PIEIER、CPU IER等中断使能位。在中断服务程序中确认标志位清除。
3. 如果希望在 UTO 中断中读取锁存值,确保 QEPCTL[QCLM]=1
测量值存在固定偏差 速度计算标尺因子 g_fMechScaler 计算错误。 重新核对公式:编码器线数、四倍频、 UPPS 分频系数、 SYSCLKOUT 频率、 CCPS 分频系数。最好通过实际测试校准:让电机以恒定已知转速(如用高精度测速仪标定)运行,反算出实际的比例因子。

5.2 调试心得与最佳实践

  1. 先调通位置,再调速度 :务必先确保 QPOSCNT 能正确反映电机正反转。这是所有功能的基础。可以通过让电机缓慢手动旋转,观察 QPOSCNT 的变化方向和增量是否正确。
  2. 善用GPIO翻转调试 :在初始化代码和中断服务程序的关键位置(如进入中断、读完寄存器后)用GPIO输出高低电平,用示波器观察时序。这能帮你确认中断是否按时发生,程序执行时间是否超预期。
  3. 寄存器配置顺序 :建议按照“先功能后使能”的顺序。例如,先配置好 QCAPCTL 的所有分频参数,最后再置位 CEN ;先配置好 QUPRD ,再使能 UTE 。对于 UPPS ,务必在初始化时一次设好,运行中切勿改动。
  4. 理解“锁存”与“实时”寄存器的区别 QCPRD 是实时寄存器,每次 UPEVNT 事件都会更新。而 QCPRDLAT 是锁存寄存器,其更新时刻由 QCLM 位决定。在 QCLM=1 模式下, UTO 事件发生时,硬件会把 那一刻 QCPRD 值锁存到 QCPRDLAT 。这意味着 QCPRDLAT 捕获的可能是两个 UPEVNT 中间某个时刻的 QCPRD 值,不一定是一个完整的 UPEVNT 周期。对于精确的T法,更推荐在 UPEVNT 中断(如果使能了)里直接读 QCPRD ,或者在 UTO 中断里采用我上面提到的简化处理/滤波方法。
  5. 为极端情况做好准备 :你的代码必须能处理电机堵转( COEF 溢出)、瞬间反转( CDEF 错误)、以及上电初始化期间寄存器值为0的情况。健壮的错误处理是产品稳定性的保证。

最后,再强调一下那个最容易忽略的坑: UPPS 不能动态修改! 如果你需要在运行中改变测量分辨率,一个可行的方案是准备两套配置(例如一套 UPPS=7 用于低速,一套 UPPS=3 用于高速),然后在确保捕获单元禁用( CEN=0 )的情况下,切换整个 QCAPCTL 寄存器的配置,再重新使能。但这会带来速度测量的短暂中断,需要软件做平滑过渡。

更多推荐