1. 项目概述与核心价值
在电机控制、机器人关节、数控机床这类对位置和速度精度要求极高的领域,工程师们每天都在和“反馈”打交道。系统需要知道“我现在在哪”、“我动了多远”、“我速度多快”,才能精确地指挥电机到达目标位置。这个过程,就像蒙着眼睛走路,你需要一个可靠的向导不断告诉你方向和步数。在嵌入式世界里,这个“向导”常常就是正交编码器。
正交编码器输出的,是两路相位差90度的方波脉冲(通常称为A相和B相)。通过分析这两路信号的边沿顺序和数量,我们可以解算出旋转的角度、方向和速度。这听起来简单,但在高速、高精度的场景下,处理这些脉冲是个技术活:你需要精准地捕获每个边沿的时刻来计算速度,需要可靠地计数来获取绝对或相对位置,还需要在特定位置触发动作(比如换向或采样)。如果所有这些工作都让主CPU(比如ARM Cortex系列)来中断处理,CPU很快就会不堪重负,系统实时性也会大打折扣。
这就是为什么像德州仪器(TI)的C2000系列DSP这样的控制器,会内置一个高度集成的硬件模块:增强型正交编码器脉冲(eQEP)模块。它本质上是一个专为编码器信号设计的“协处理器”,能硬件级完成脉冲解码、位置计数、速度捕获和位置比较等复杂任务,极大解放了CPU。而为了进一步优化系统,让数据流动更高效,TI还配备了强大的增强型直接内存访问(EDMA3)控制器。你可以把eQEP想象成一个高速、精准的“传感器数据采集卡”,而EDMA3则是一个不知疲倦的“数据搬运工”。eQEP采集到的海量位置、速度数据,可以通过EDMA3自动、零CPU开销地搬运到指定的内存区域(比如用于PID计算的数组或用于图形显示的缓冲区),从而实现采集、传输、处理的完美流水线。
本文将从一线开发者的视角,深入剖析eQEP模块的关键寄存器配置逻辑,并拆解EDMA3控制器如何与eQEP协同工作,构建高效的数据通路。我不会只罗列寄存器手册,而是结合常见的伺服驱动开发场景,解释每个配置项背后的设计意图、参数计算方法和避坑指南。无论你是正在调试第一个伺服电机的新手,还是希望优化现有系统性能的资深工程师,相信这些从实际项目中沉淀下来的细节和经验,都能给你带来直接的参考价值。
2. eQEP模块寄存器深度解析与实战配置
eQEP模块的功能可以概括为四大核心:位置计数、速度测量、位置比较和中断管理。其寄存器配置就是围绕这四点展开的。理解寄存器,关键是理解其字段如何映射到硬件行为。
2.1 捕获单元控制寄存器(QCAPCTL):精准测速的核心
速度测量是闭环控制的基础。eQEP通过测量两个单位位置事件(Unit Position Event)之间的时间来计算速度。QCAPCTL寄存器就是控制这个“计时器”的总开关和精度调节器。
// 假设我们基于TI的C2000 Header文件进行编程 #include "F28x_Project.h" // 以TMS320F28379D为例 void EQEP_Capture_Init(void) { // 1. 配置单位位置事件预分频器 (UPPS) // 假设编码器线数为1000线(每转4000个边沿),电机最高转速3000RPM。 // 最大单位位置事件频率 = (3000 RPM / 60) * 4000 脉冲/转 = 200,000 Hz // 我们希望捕获定时器(CAPCLK)的时钟不要太快,以免溢出。假设系统时钟SYSCLKOUT=200MHz。 // 设置UPEVNT = QCLK / 32。QCLK通常由eQEP外部输入时钟分频而来,假设我们设置QCLK = SYSCLKOUT/2 = 100MHz。 // 则单位位置事件频率 = 100MHz / 32 = 3.125MHz。这远高于200kHz,可以确保每个脉冲都能被计数。 EQep1Regs.QCAPCTL.bit.UPPS = 5; // 对应QCLK/32 // 2. 配置捕获定时器时钟预分频器 (CCPS) // 捕获定时器CAPCLK用于给一个16位定时器(QCTMR)提供时钟,该定时器记录两个UPEVNT之间的时间。 // 我们希望定时器在测量最低速时也不要溢出。假设最低测速为1RPM。 // 最低速时的UPEVNT周期 T_upe = 1 / ( (1 RPM / 60) * 4000 ) = 0.015秒 = 15ms // 我们需要CAPCLK的周期T_cap足够小,使得在15ms内定时器计数值不会超过65535。 // 最大计数值 N_max = T_upe / T_cap < 65535 // 因此 T_cap < T_upe / 65535 = 15ms / 65535 ≈ 229 ns // 对应的 CAPCLK 频率 > 1 / 229ns ≈ 4.37 MHz // 选择 CAPCLK = SYSCLKOUT / 32 = 200MHz / 32 = 6.25MHz (周期160ns),满足要求。 EQep1Regs.QCAPCTL.bit.CCPS = 5; // 对应SYSCLKOUT/32 // 3. 使能捕获单元 EQep1Regs.QCAPCTL.bit.CEN = 1; // 使能捕获 }关键理解与避坑指南:
- UPPS与分辨率:
UPPS决定了“单位位置事件”是什么。UPPS=0(QCLK/1)意味着每个QEP时钟边沿都算一个事件,这会产生巨量的中断和捕获事件,通常用于极低速或超高分辨率场景。一般设置为QCLK/16或QCLK/32,在分辨率和CPU负载间取得平衡。误区:认为UPPS值越大,测速精度越高。实际上,过大的分频会丢失高速时的脉冲细节,导致高速测速不准。应根据最大转速和编码器分辨率来计算。- CCPS与量程:
CCPS和16位捕获定时器(QCTMR)共同决定了速度测量的范围。核心公式:速度 = (单位距离) / (捕获周期值 * CAPCLK周期)。你必须确保在最低速时,定时器不会溢出(计数值<65535);在最高速时,定时器仍有足够的计数(比如大于10),以保证精度。上述计算过程是配置前必须完成的。- 读取速度值:使能后,速度值可通过读取捕获周期锁存寄存器
QCPRDLAT获得。软件通常会在单位超时中断(UTO)或定期读取该寄存器,并通过上述公式换算成实际速度(RPM或m/s)。
2.2 位置比较控制寄存器(QPOSCTL):实现精准定位动作
位置比较功能用于在编码器到达一个特定位置时,自动产生一个同步输出信号(SYNCOUT)。这在电机控制中极其有用,例如在特定转子位置触发ADC采样(用于无传感器控制的BEMF采样),或生成精确的换相信号。
void EQEP_PositionCompare_Init(void) { // 1. 设置位置比较值 // 假设我们希望电机每转1000个编码器计数(对应250个机械圈,因为4倍频后)产生一个同步信号。 EQep1Regs.QPOSCMP = 1000; // 2. 配置位置比较控制寄存器 EQep1Regs.QPOSCTL.bit.PCE = 1; // 使能位置比较单元 EQep1Regs.QPOSCTL.bit.PCSHDW = 1; // 使能影子寄存器。这是关键! EQep1Regs.QPOSCTL.bit.PCLOAD = 0; // 影子寄存器在QPOSCNT=0时重载 EQep1Regs.QPOSCTL.bit.PCPOL = 0; // 同步输出高电平有效 EQep1Regs.QPOSCTL.bit.PCSPW = 0x00; // 设置同步脉冲宽度为 1 * 4 * SYSCLKOUT周期 }核心机制与实战要点:
- 影子寄存器(PCSHDW)的重要性:这是最容易出错的地方。如果
PCSHDW=0(立即加载),当你软件修改QPOSCMP寄存器时,如果当前位置计数器QPOSCNT的值刚好等于旧值或在新值附近,可能导致不可预测的立即触发或错过触发。务必设置PCSHDW=1,让新的比较值先写入影子寄存器,在指定的重载时刻(PCLOAD决定)才生效,保证了同步信号的确定性。- 重载模式(PCLOAD)的选择:
PCLOAD=0:在QPOSCNT归零时重载。适用于需要与机械零点(索引信号)严格同步的周期性动作,例如每转一次的操作。PCLOAD=1:在QPOSCNT等于当前QPOSCMP值时重载。这允许你在一个周期内动态更新下一个比较点,实现更复杂的序列控制,但软件逻辑更复杂。- 脉冲宽度(PCSPW):这个脉冲宽度是以
SYSCLKOUT周期为单位的。例如,PCSPW=0产生4个系统时钟周期宽度的脉冲。你需要确保这个脉冲宽度足够你的下游电路(如ADC的SOC信号)可靠捕获,但又不会过长干扰其他逻辑。通常几个时钟周期就足够了。
2.3 中断管理寄存器簇(QEINT, QFLG, QCLR, QFRC):高效的事件响应
eQEP的中断系统是典型的状态标志型中断。理解QEINT(使能)、QFLG(标志)、QCLR(清除)和QFRC(强制)这四个寄存器的协同工作方式,是编写稳定中断服务程序(ISR)的关键。
| 寄存器 | 功能 | 操作类型 | 开发者须知 |
|---|---|---|---|
| QEINT | 中断使能寄存器 | 读/写 | 上电默认全0。需要使能哪个中断,就将对应位置1。例如使能位置比较匹配中断:QEINT.bit.PCM = 1。 |
| QFLG | 中断标志寄存器 | 只读 | 当事件发生时,硬件自动置1。注意:即使中断未使能(QEINT对应位为0),事件发生时QFLG标志位也会被置1。 |
| QCLR | 中断清除寄存器 | 写1清除 | 在ISR中,必须向QCLR的对应位写1来清除QFLG中的标志位。这是响应中断的必要步骤。 |
| QFRC | 中断强制寄存器 | 写1强制 | 通过软件模拟事件,强制置位QFLG标志。主要用于测试。 |
一个健壮的位置比较匹配中断服务例程框架如下:
// 假设在初始化中已使能位置比较中断:EQep1Regs.QEINT.bit.PCM = 1; // 并且配置好了PIE和CPU中断。 __interrupt void EQEP1_ISR(void) { // 1. 首先判断具体中断源 if (EQep1Regs.QFLG.bit.PCM == 1) { // 位置比较匹配 // 2. 执行你的关键操作,例如触发ADC采样 AdcaRegs.ADCSOCFRC1.bit.SOC0 = 1; // 强制触发ADC SOC0 // 3. 清除eQEP模块级中断标志(至关重要!) EQep1Regs.QCLR.bit.PCM = 1; // 写1清除PCM标志位 // 4. (可选)更新下一次比较值,实现动态位置触发 // EQep1Regs.QPOSCMP = new_compare_value; } // 检查其他中断源,例如单位超时(用于速度计算) else if (EQep1Regs.QFLG.bit.UTO == 1) { // 读取QCPRDLAT计算速度... speed_calculate(); EQep1Regs.QCLR.bit.UTO = 1; } // 必须清除PIE组应答位,否则无法响应后续中断 PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; }中断处理黄金法则:
- 先判源,再清除:进入ISR后,首先读取
QFLG判断是哪个事件触发了中断,然后再对QCLR的相应位写1进行清除。顺序错误可能导致标志位被误清或中断丢失。QFLG与QEINT独立:即使你不使能某个中断(QEINT.x=0),也应当定期或在初始化后检查并清除QFLG中的意外标志位(如上电噪声可能置位某些标志),否则这些遗留的标志位可能在后续使能中断时立即触发一次误中断。- 使用
QFRC进行测试:在调试阶段,你可以不接编码器,通过QFRC寄存器强制产生中断事件,来测试你的ISR逻辑是否正确。例如:EQep1Regs.QFRC.bit.PCM = 1;。- 注意索引信号中断(IEL):索引信号通常用于寻找机械零点。它的中断处理要求更高,因为可能发生在高速旋转时。ISR应尽可能短,通常只做标记,在主循环中处理零位校准逻辑。
2.4 状态寄存器(QEPSTS)与错误处理
QEPSTS寄存器提供了eQEP模块的运行状态和错误信息,是诊断问题的关键。
- 方向标志(QDF, QDLF, FIDF):
QDF实时指示当前方向。QDLF锁存了上一个索引脉冲出现时的方向,用于判断旋转是否越过零点。FIDF锁存了第一个索引脉冲时的方向,用于系统初始化时的方向标定。 - 错误标志(PCEF, CDEF, COEF):
PCEF(位置计数器错误):当索引信号到来时,硬件会将QPOSCNT锁存到QPOSILAT,并检查锁存值是否在QPOSMAX范围内。如果超出,此位置1。这通常意味着编码器计数与机械位置已不同步,需要执行回零操作。CDEF(捕获方向错误):在速度捕获过程中,如果检测到旋转方向改变,此位置1。在匀速或定向测量中,这可能是异常。COEF(捕获溢出错误):捕获定时器QCTMR在两次单位位置事件间溢出。这说明你的CCPS分频设置得太小,或者电机速度低于你设计的最低可测速度。需要调整CCPS或处理超低速情况。
错误处理策略:这些错误标志位大多是“粘滞”的(写1清除),需要在软件中增加监控逻辑。一个简单的做法是在主循环或低优先级任务中定期检查并处理:
void EQEP_Error_Check(void) { if (EQep1Regs.QEPSTS.bit.PCEF == 1) { // 发生位置计数器错误,严重!可能需要停机并报警 handle_position_error(); EQep1Regs.QEPSTS.bit.PCEF = 1; // 写1清除错误标志 } if (EQep1Regs.QEPSTS.bit.CDEF == 1) { // 捕获期间方向改变,记录日志或判断是否合理 log_direction_change(); EQep1Regs.QEPSTS.bit.CDEF = 1; // 写1清除 } if (EQep1Regs.QEPSTS.bit.COEF == 1) { // 捕获定时器溢出,速度过低或配置不当 adjust_speed_measurement_range(); // 或标记速度无效 EQep1Regs.QEPSTS.bit.COEF = 1; // 写1清除 } }3. EDMA3控制器架构精要与eQEP数据搬运实战
当eQEP以高频率(如10kHz以上)产生数据(如每次捕获周期都更新速度值)时,如果每个数据都触发CPU中断,CPU负载会急剧上升。EDMA3就是为了解决此类“数据搬运”问题而生的。它可以在没有CPU干预的情况下,在外设(如eQEP的捕获寄存器)和内存(如用于存储速度曲线的数组)之间直接传输数据。
3.1 EDMA3核心概念与架构拆解
EDMA3控制器分为两大块:通道控制器(EDMA3CC)和传输控制器(EDMA3TC)。
通道控制器(EDMA3CC):这是程序员配置的接口。你在这里定义“传输任务”(通过参数集PaRAM),并指定由什么事件(如eQEP的捕获完成)来触发这个任务。它的核心组件是:
- PaRAM(参数RAM):存储128个“传输任务描述符”,每个描述符占32字节,定义了源地址、目的地址、传输维度和次数等所有信息。
- 事件队列:外设(如eQEP)或软件触发的事件在这里排队等待处理。EDMA3CC有两个事件队列(Q0和Q1),你可以将不同的DMA通道映射到不同队列,实现优先级控制(Q0优先级高于Q1)。
- 完成检测与中断逻辑:当EDMA3TC完成一个传输任务后,会通知EDMA3CC,后者可以产生中断或触发另一个“链式”DMA任务。
传输控制器(EDMA3TC):这是实际的“搬运工”。它从EDMA3CC接收“传输请求包(TR)”,然后通过系统总线执行具体的读(从源地址)和写(到目的地址)操作。一个芯片可能有多个TC,可以并行搬运数据。
关键术语理解:
- A/B/C三维传输:这是EDMA3传输的抽象模型。假设你要搬运一个
M行 x N列的二维图像数据到另一个地方���- ACNT:一行里连续字节的数量(比如一幅灰度图像一行有N个像素,每个像素1字节,ACNT=N)。
- BCNT:行数(M行)。
- CCNT:帧数(比如连续传输K帧图像,CCNT=K)。
- 索引(BIDX, CIDX):每传输完一行(ACNT),地址增加
SRCBIDX(源B索引)或DSTBIDX(目的B索引),跳到下一行开头。每传输完一帧(BCNT行),地址增加SRCCIDX(源C索引)或DSTCIDX(目的C索引),跳到下一帧开头。
- 同步类型(SYNCDIM):
- A-同步:每收到一个触发事件,传输一个ACNT数组(即一行数据)。需要BCNT个事件才能传完一帧。
- AB-同步:每收到一个触发事件,传输一整个帧(BCNT个ACNT数组)。只需要一个事件就能传完一帧。这对于eQEP捕获非常有用,一次触发可以搬运一组完整的数据结构。
3.2 为eQEP捕获数据配置EDMA3传输
场景:我们希望将eQEP速度捕获寄存器QCPRDLAT的值,自动地、周期性地搬运到一个名为speed_buffer的循环数组中,用于后续的速度环PID计算。eQEP的“单位超时”事件(UTO)作为触发源。
步骤1:规划传输参数
- 源地址(SRC):
&EQep1Regs.QCPRDLAT(eQEP捕获周期锁存寄存器的地址)。 - 目的地址(DST):
&speed_buffer[0](内存中数组的首地址)。 - 传输内容:每次UTO事件发生时,将
QCPRDLAT(16位)的值搬运到数组下一个元素。speed_buffer是一个uint16_t数组。 - 传输维度:
ACNT = 2 bytes(因为QCPRDLAT是16位寄存器)。BCNT = 1(每次事件只搬一个数据)。CCNT = 256(我们定义数组大小为256,让EDMA3自动循环覆盖)。
- 同步类型:A-同步。因为每个UTO事件只对应一个速度值(一个ACNT)。
- 地址模式:源地址固定(
SRC不变,总是读同一个寄存器),目的地址递增(DST每次增加2字节)。
步骤2:配置PaRAM参数集
我们需要选择一个DMA通道(例如通道0)并将其映射到一个PaRAM集(例如集0)。
#include "F28x_Project.h" #define SPEED_BUFFER_SIZE 256 volatile uint16_t speed_buffer[SPEED_BUFFER_SIZE]; void EDMA3_For_EQEP_Init(void) { // 1. 使能EDMA3模块时钟(具体寄存器依芯片而异,此处为示意) SysCtrlRegs.PCLKCR0.bit.DMA = 1; // 2. 配置DMA通道0映射到事件(例如,假设UTO事件映射到DMA_REQ0) // 首先需要查芯片手册,找到eQEP UTO事件对应的EDMA3事件编号。假设是8。 volatile Uint16 *DMACha0Regs = (volatile Uint16 *)0x4000; // DMA通道0配置寄存器基址(示例) // 设置事件映射寄存器,将事件8映射到通道0 *((volatile Uint32 *)(DMACha0Regs + 0x10)) |= (8 << 0); // EVENT寄存器的低16位用于映射 // 3. 配置PaRAM Set 0 (对应通道0) // 获取PaRAM基址指针(示例地址) volatile struct EDMA3CCPaRAMEntry *paramSet0 = (volatile struct EDMA3CCPaRAMEntry *)0x40040000; // 3.1 配置源地址和目的地址 paramSet0->SRC = (uint32_t)&EQep1Regs.QCPRDLAT; // 源:eQEP捕获寄存器 paramSet0->DST = (uint32_t)&speed_buffer[0]; // 目的:内存数组 // 3.2 配置传输维度 paramSet0->CNT = (BCNT << 16) | (ACNT); // BCNT=1, ACNT=2 paramSet0->CNT = (1 << 16) | (2); // 等同于上一行 // 3.3 配置索引(地址偏移) paramSet0->BIDX = 2; // 目的地址B索引:每个数组元素占2字节 paramSet0->CIDX = 0; // C索引:因为是A-同步,且CCNT>1,但每次事件只触发一个ACNT传输, // 所以CIDX在A-同步模式下,是在完成BCNT个ACNT后才加。这里BCNT=1,所以CIDX在每次事件后都会加上。 // 但为了循环覆盖,我们需要在CCNT用完后,让DST回到数组开头,这通过“链接(Link)”功能实现。 // 3.4 配置链接地址(用于传输完成后自动重载PaRAM) // 我们将PaRAM Set 0链接到它自己,实现循环传输。 paramSet0->LINK = 0; // LINK字段存储的是PaRAM Set的索引偏移量。0表示链接到自身(Set 0)。 // 更常见的做法是使用另一个PaRAM Set作为“重载集”,这里简化处理。 // 3.5 配置选项寄存器(OPT)——这是最复杂的部分 paramSet0->OPT = 0; paramSet0->OPT |= (0 << 24); // 同步类型: 0 for A-sync, 1 for AB-sync paramSet0->OPT |= (0 << 20); // 目的地址模式: 0 for Increment, 1 for Constant paramSet0->OPT |= (1 << 19); // 源地址模式: 1 for Constant (固定地址) paramSet0->OPT |= (0 << 18); // 传输完成中断使能(TCINTEN):我们先不使能中断 paramSet0->OPT |= (0 << 12); // 传输完成代码(TCC):用于中断识别和链式触发,暂设为0 // 4. 使能DMA通道0 // 设置通道控制寄存器,使能通道并关联到PaRAM Set 0 *((volatile Uint32 *)(DMACha0Regs + 0x4)) = 0x00000001; // 使能通道,并指定PaRAM Set索引为0 // 5. 使能eQEP的UTO中断(用于触发EDMA,但不触发CPU中断) // 注意:这里我们只让UTO事件触发EDMA,而不触发CPU中断。 // 通常需要配置事件触发寄存器(如ESR)或直接配置eQEP的DMA使能位(如果存在)。 // 假设eQEP有一个寄存器位可以将UTO事件输出到EDMA请求线。 EQep1Regs.QDMAEN.bit.UTO_DMAEN = 1; // 启用UTO事件的DMA请求(寄存器名称为示例) }步骤3:理解“链接(Linking)”实现循环缓冲
上面的简化配置有一个问题:当传输完CCNT次后,EDMA3会停止。为了实现循环缓冲,我们需要使用PaRAM的链接(Linking)功能。
更专业的做法是使用两个PaRAM集(Set 0和Set 1)进行“乒乓链接”:
- Set 0 (工作集):配置为传输
CCNT=256次。在OPT中设置TCINTEN=1(传输完成中断)和TCC=0(传输完成代码0)。 - Set 1 (重载集):内容与Set 0完全相同。
- 在Set 0的
LINK字段中,填入Set 1的地址偏移量。在Set 1的LINK字段中,填入Set 0的地址偏移量。 - 当Set 0的传输完成(256次后),EDMA3会自动将Set 1的内容加载到通道0的当前PaRAM中,并开始新一轮传输。同时,可以产生一个传输完成中断(
TCC=0),通知CPU“前半部分缓冲区已满”,可以进行处理。 - 当Set 1传输完成时,又会链接回Set 0,并产生另一个中断(可以设置不同的
TCC代码),通知CPU“后半部分缓冲区已满”。
这样,就实现了双缓冲(Double Buffer)机制,CPU在处理一个缓冲区时,EDMA3可以向另一个缓冲区写入新数据,两者互不干扰,极大地提高了效率。
3.3 EDMA3与eQEP协同工作流程总结
- 初始化:
- 配置eQEP的捕获单元(
QCAPCTL),使其能周期性地产生单位位置事件和UTO事件。 - 配置EDMA3的PaRAM集,定义从
QCPRDLAT到speed_buffer的传输任务,并设置好链接以实现循环缓冲。 - 将eQEP的UTO事件映射到EDMA3的某个DMA通道,并使能该通道的事件触发。
- 配置eQEP的捕获单元(
- 运行:
- 电机旋转,eQEP产生UTO事件。
- UTO事件作为EDMA3的触发信号,放入事件队列。
- EDMA3CC从队列中取出事件,根据关联的PaRAM集(Set 0)发起传输请求(TR)给EDMA3TC。
- EDMA3TC执行一次传输:从
QCPRDLAT读取2字节数据,写入speed_buffer的当前位置,并更新目的地址。 - 全程无需CPU介入。
- 数据处理:
- CPU可以定期检查
speed_buffer的写索引(可以通过EDMA3传输完成中断来更新),或者简单地以固定周期读取缓冲区中最新的速度值进行PID计算。
- CPU可以定期检查
4. 常见问题排查与调试技巧
在实际项目中,eQEP和EDMA3的配置出错是常态。以下是几个最常见的“坑”和解决方法。
4.1 eQEP模块无计数或计数异常
- 症状:
QPOSCNT寄存器不变化,或变化混乱。 - 排查清单:
- 引脚复用配置:首先确认MCU的GPIO引脚是否正确配置为eQEP功能(例如,对于TI C2000,检查
GPxMUX和GPxGMUX寄存器)。这是最容易被忽略的一步。 - 输入信号质量:用示波器检查QEPA和QEPB信号的波形。确保是标准的方波,相位差90度,电压幅值符合要求,没有过冲或振铃。噪声可能导致误计数。
- 滤波设置:eQEP输入通常有数字滤波器(通过
QDECCTL寄存器配置)。如果滤波器窗口设置过宽,可能会滤掉正常的脉冲;过窄则无法滤除噪声。根据信号频率调整QDECCTL.bit.QSRC和QDECCTL.bit.QFLT。 - 计数模式:检查
QDECCTL.bit.QSRC,确认是否为所需的“正交计数模式”(通常为00)。如果误设为“方向计数模式”,则只会对一路信号计数。 - 索引信号:如果使用了索引信号(QEPI),检查其极性(
QDECCTL.bit.SWI)和是否使能。索引信号错误可能导致QPOSCNT在零点附近跳变。
- 引脚复用配置:首先确认MCU的GPIO引脚是否正确配置为eQEP功能(例如,对于TI C2000,检查
4.2 eQEP捕获的速度值不准或为0
- 症状:
QCPRDLAT值恒为0或明显不合理。 - 排查清单:
- 捕获单元使能:确认
QCAPCTL.bit.CEN = 1。 - 单位位置事件:确认
QCAPCTL.bit.UPPS设置正确。如果分频过大,在低速时可能长时间没有单位事件,QCPRDLAT不会更新。可以通过监控QEPSTS.bit.UPEVNT标志位来确认是否有单位事件产生。 - 捕获时钟与溢出:检查
QEPSTS.bit.COEF是否置1。如果置1,说明捕获定时器溢出了,需要增大QCAPCTL.bit.CCPS的分频系数,降低CAPCLK频率。 - 读取时机:
QCPRDLAT是锁存寄存器。确保你是在UTO中断发生后或UPEVNT标志置位后读取它。在事件发生前读取的是旧值。
- 捕获单元使能:确认
4.3 EDMA3传输不触发或数据错误
- 症状:目的内存数组没有更新,或更新了错误的数据。
- 排查清单:
- 事件映射:这是最大的坑。必须精确查阅芯片的《Technical Reference Manual (TRM)》,找到eQEP UTO事件对应的EDMA3事件编号。这个编号需要写入DMA通道的事件映射寄存器。不同型号的芯片,事件编号可能不同。
- PaRAM配置:
- 地址对齐:确保源地址和目的地址符合总线访问的对齐要求(例如32位对齐)。访问
uint16_t类型时,地址通常是2字节对齐。 - CNT寄存器:
ACNT和BCNT的位置不要搞反。CNT[15:0]是ACNT,CNT[31:16]是BCNT。 - OPT寄存器:仔细检查
SYNCDIM(同步类型)、DSTAMODE和SRCAMODE(地址模式)。对于从固定寄存器读取,SRCAMODE必须是常量模式。
- 地址对齐:确保源地址和目的地址符合总线访问的对齐要求(例如32位对齐)。访问
- 链接地址:
LINK字段存储的是16位有符号偏移量,单位是32字节(一个PaRAM集的大小)。计算方式:(目标PaRAM集地址 - 当前PaRAM集地址) / 32。如果计算错误,链接会跳转到错误的PaRAM集,导致传输停止或行为异常。 - 缓冲区溢出:如果EDMA3的写入速度超过了CPU的读取速度,循环缓冲区会被覆盖。需要在软件中设计合理的生产-消费模型,例如使用双缓冲和中断通知。
4.4 使用CCS调试工具
TI的Code Composer Studio (CCS)是强大的调试工具:
- 寄存器查看:在调试视图中,可以直接查看eQEP和EDMA3的所有寄存器,实时监控其状态。
- 内存浏览器:查看目的数组
speed_buffer的内存内容,确认数据是否正确写入。 - 事件触发跟踪:一些高级仿真器支持跟踪EDMA3事件和传输状态,可以图形化地看到事件何时进入队列,传输何时开始和结束。
- 脚本功能:可以编写CCS脚本,在特定时刻(如进入中断时)自动读取并记录一组寄存器的值,帮助分析时序问题。
配置eQEP和EDMA3是一个需要耐心和细致的过程,尤其是面对上百页的芯片手册时。我的经验是,从一个最简单的功能开始验证:比如先让eQEP正常计数,再添加捕获功能,最后再引入EDMA3。每步都通过寄存器查看和内存检查来确认结果是否符合预期。一旦打通了这个数据自动采集的管道,你会发现系统的实时性和CPU利用率得到质的提升,可以将宝贵的CPU算力留给更复杂的控制算法和状态机逻辑。