嵌入式实时系统捕获单元FIFO与中断机制深度解析
1. 捕获单元FIFO与中断机制:实时控制系统的“哨兵”与“信箱”
在电机控制、电源管理或者任何需要精确测量外部事件时间间隔的嵌入式系统里,我们常常需要知道一个信号边沿(比如编码器的脉冲、按键的按下、过流信号的跳变)发生的精确时刻。这个“时刻”在数字世界里,通常用一个自由运行的定时器(GP Timer)的计数值来标记。事件管理器(Event Manager, EV)里的捕获单元(Capture Unit),就是专门干这个活的“高速快门”。但问题来了:外部事件是异步的、不可预测的,而CPU可能正在处理其他任务。如果事件发生时CPU没空理它,这个宝贵的“时间戳”不就丢了吗?这就是为什么捕获单元的设计,远不止一个简单的“捕获-存储”那么简单。它内部精巧的两级FIFO(先进先出)栈和与之联动的中断机制,共同构成了确保数据不丢失、系统能及时响应的核心保障。你可以把它想象成一个配备了双缓冲邮箱的哨兵站:哨兵(捕获单元)负责记录事件发生的“邮戳”(定时器值),并投入邮箱(FIFO);而中断机制就是邮箱的“新邮件”提醒铃,告诉CPU:“嘿,有重要时间信息到了,快来取!”
理解CAPxFIFO状态位(如00空、01有一个条目、10有两个条目、11已满且发生覆盖)的变迁,以及CAPxINT中断标志何时置位,是编写稳定、高效捕获程序的关键。这不仅关乎功能实现,更直接影响系统的实时性、可靠性和资源利用率。搞懂了这套机制,你就能在纷繁的寄存器配置中抓住主线,设计出既能应对突发高频事件,又不会无谓消耗CPU资源的优雅方案。
2. 捕获单元FIFO栈的深度解析:两级缓冲的艺术
为什么是两级FIFO,而不是一级或者三级?这是一个典型的工程权衡。一级缓冲(即一个寄存器)太脆弱:如果CPU在读取第一个捕获值之前,第二个事件就发生了,那么第一个值会被直接覆盖丢失。三级或更多级缓冲固然能容纳更多未读数据,但会显著增加硬件的复杂度和成本,同时让中断响应逻辑和软件处理流程变得复杂。对于大多数电机控制、脉冲测量应用,两个连续事件的时间间隔通常有足够的余量让CPU响应。两级FIFO提供了一个简单而有效的折中方案:它允许捕获单元在CPU尚未响应第一次中断时,安全地暂存第二个事件的时间戳,为CPU争取了宝贵的响应时间窗口。
2.1 FIFO栈的物理结构与寄存器映射
每个捕获单元(例如EVA的CAP1、CAP2、CAP3,或EVB的CAP4、CAP5、CAP6)都独立拥有一个专用的2级深度FIFO栈。这个栈在物理上由两个16位的寄存器组成:
- 顶部栈寄存器(CAPxFIFO):这是一个只读寄存器。它总是存放着该捕获单元FIFO中“最老”(即最早捕获到但尚未被读取)的定时器计数值。当我们对捕获单元执行读操作时(通常是读取CAPxFIFO),读出的就是这个值。
- 底部栈寄存器(CAPxFBOT):这个寄存器通常对用户不可见(在大多数芯片的数据手册中,你可能找不到直接访问它的地址),它是作为顶部寄存器的缓冲池存在。
关键点在于,这个FIFO栈的访问机制是设计好的:你只能通过读取顶部寄存器(CAPxFIFO)来获取数据。这个读取操作会触发FIFO内部数据的“上推”动作。这种设计强制了“先进先出”的语义,避免了软件管理读指针的麻烦。
2.2 CAPxFIFO状态位:FIFO的“水位计”
CAPxCON(捕获控制寄存器)中的CAPxFIFO状态位(通常是2个比特)是这个FIFO栈的“灵魂指示灯”。它实时反映了FIFO的填充状态,其编码含义是软件决策的基础:
| 状态位值 | 含义 | 说明与软件应对策略 |
|---|---|---|
| 00 | 空 (Empty) | FIFO栈中没有任何有效的捕获值。这是初始状态,或者在CPU读取了最后一个值后的状态。此时发生捕获事件,会直接进入“第一次捕获”流程。 |
| 01 | 有一个条目 (Has one entry) | FIFO栈的顶部寄存器(CAPxFIFO)中有一个有效值,底部寄存器为空。这通常发生在第一次捕获后,或者CPU读取一个值后(从10或11状态变为01)。这是触发中断的典型状态之一(如果之前是00)。软件应准备读取这个值。 |
| 10 | 有两个条目 (Has two entries) | FIFO栈的顶部和底部寄存器各有一个有效值。这发生在第二次捕获完成,且CPU尚未读取任何数据时。这是触发中断的典型状态(当从01变为10时)。软件需要连续读取两次才能清空FIFO。 |
| 11 | 已满且发生覆盖 (Had two entries and captured another) | 这是一个“溢出”或“数据丢失”的标志。当FIFO已处于10状态(两个条目已满)时,第三个捕获事件发生。此时,最老的(顶部寄存器的)值被丢弃,原来的底部值被推到顶部,新值存入底部,状态位变为11。这个状态本身也会触发中断,但软件必须意识到,最早的一个数据点已经永久丢失。这通常意味着CPU响应太慢,或者事件频率超过了系统处理能力,需要优化代码或重新评估设计。 |
实操心得:状态位查询的时机在中断服务程序(ISR)中,读取捕获值之前,先查看一下CAPxFIFO状态位是很好的习惯。如果看到
11,你就知道发生了数据丢失,可能需要记录一个错误标志,或者采取一些恢复措施(比如重置测量)。不要假设FIFO里永远只有你期望的数据量。
3. 捕获、存储与中断触发的完整流程
理解了状态位,我们就能像看电影一样,还原出捕获单元在外部事件触发下的完整工作流程。这个过程清晰地分为三个场景,对应着FIFO从空到满再到可能溢出的生命周期。
3.1 场景一:第一次捕获(FIFO从空到有)
- 初始状态:FIFO栈为空,CAPxFIFO状态位 =
00。 - 事件发生:在捕获单元输入引脚(如CAP1)上,检测到预设的边沿(上升沿、下降沿或两者)。
- 动作:事件管理器立即“冻结”当前被监控的通用定时器(例如,CAP1/2/3通常对应GPT2,CAP4/5/6对应GPT4)的计数器值(T2CNT/T4CNT)。
- 存储:由于栈是空的,这个被捕获的计数器值被直接写入顶部栈寄存器(CAPxFIFO)。
- 状态更新:CAPxFIFO状态位从
00变为01。 - 中断?:此时不会设置捕获中断标志(CAPxINT),也不会产生中断请求。因为中断产生的条件是“当一次捕获发生,且FIFO中已经至少有一个有效值(即状态位不为
00)”。第一次捕获时,FIFO是从空变非空,不满足“已有一个”的条件。这个设计很巧妙,它避免了单次、孤立的捕获事件也产生中断,减少了不必要的CPU开销。软件可以通过轮询状态位(从00变01)来知道有数据到达,如果不需要实时性,可以稍后再读。
3.2 场景二:第二次捕获(中断的触发)
- 前置状态:FIFO中已有一个值,状态位 =
01。CPU尚未读取。 - 新事件发生:同一个捕获单元的输入引脚上,再次发生了有效的边沿事件。
- 动作:再次捕获当前定时器的计数器值。
- 存储:由于顶部寄存器已满,新捕获的值被存入底部栈寄存器(CAPxFBOT)。
- 状态更新:CAPxFIFO状态位从
01变为10。 - 中断触发:关键点来了!因为这次捕获发生时,FIFO中已经有一个有效值(状态位
01),满足了中断触发条件。因此,硬件会自动将对应的捕获中断标志(例如CAP1INT)置为1。 - 中断请求:如果该中断在中断屏蔽寄存器(如EVAIMRC)中已被使能(相应位为1),那么一个外设中断请求就会被发送到PIE(外设中断扩展)控制器,最终可能导致CPU跳转到对应的中断服务程序(ISR)。
为什么是第二次捕获才中断?这是一个非常实用的设计。在许多测量应用,如测量脉冲宽度或周期时,我们需要两个时间点才能计算出一个时间间隔(例如,一个上升沿和一个下降沿)。FIFO设计为两级,并在第二次捕获(即凑够一对数据)时触发中断,正好让ISR可以一次性读取两个相关的计数值,从而直接计算出脉冲宽度或周期,效率最高。如果每次捕获都中断,ISR会被频繁调用,且每次只能处理一个数据,软件还需要额外的逻辑来配对,增加了复杂度和开销。
3.3 场景三:第三次及后续捕获(溢出与数据保护)
- 前置状态:FIFO已满,状态位 =
10。两个寄存器(顶和底)都有未读数据。 - 新事件发生:第三个捕获事件到来。
- 动作与存储(覆盖):
- 最老的、存放在顶部寄存器(CAPxFIFO)的值被丢弃(溢出)。
- 原来存放在底部寄存器(CAPxFBOT)的值被移动(推送)到顶部寄存器。
- 新捕获的第三个值被存入现在已空的底部寄存器。
- 状态更新:CAPxFIFO状态位变为
11。这个11状态明确告诉你:“曾经满过,并且有新数据进来导致旧数据丢失了”。 - 中断触发:同样,因为捕获发生时FIFO非空(状态位
10),所以捕获中断标志(CAPxINT)再次被置1,产生中断请求(如果使能)。 - 软件应对:在ISR中,当你读取状态位发现是
11时,你就知道此时读取到的顶部寄存器值,实际上是第二次捕获的值(它从底部被推上来了),而底部寄存器里是第三次捕获的值。第一次捕获的值已经丢失。你的软件逻辑必须能够处理这种数据丢失的情况,可能需要进行错误计数或采用上一次有效的测量结果。
这个流程揭示了FIFO的核心价值:它用有限的硬件资源(两个寄存器),为CPU争取了处理时间,在中等事件频率下能保证数据不丢;同时在数据即将溢出时,通过状态位11和中断机制,明确告知软件发生了异常,而不是静默地覆盖数据让你无从察觉。
4. 软件操作FIFO:读取、状态查询与高级技巧
硬件机制再精妙,也需要正确的软件配合才能发挥作用。对FIFO栈的操作主要集中在“读”和“状态监控”上。
4.1 正确的读取顺序与FIFO内部变化
读取FIFO数据的唯一正确方式是读取对应的捕获单元FIFO寄存器(例如,对于CAP1,就是读取CAP1FIFO寄存器的地址)。这个操作会引发FIFO内部状态的自动管理:
- 当FIFO状态为
01(有一个条目)时:读取操作会取出顶部寄存器的值。读取后,底部寄存器为空,所以状态位从01变回00。 - 当FIFO状态为
10(有两个条目)时:- 第一次读取:取出顶部寄存器(最老的值)。读取完成后,硬件自动将底部寄存器的值“推送”到顶部寄存器。
- FIFO状态从
10变为01。 - 第二次读取:取出现在顶部寄存器的值(即原来的第二个值)。读取后,状态变为
00。
- 当FIFO状态为
11(溢出后)时:- 第一次读取:取出顶部寄存器的值(这是原来的第二个值,第一个已丢失)。
- 读取后,底部寄存器的值(第三个值)被推到顶部,状态从
11变为01。 - 第二次读取:取出现在的顶部值(即第三个值)。读取后,状态变为
00。
重要提示:数据手册中提到,也可以直接读取底部寄存器(CAPxFBOT),但这会改变FIFO的状态逻辑,通常不推荐在常规应用中使用,除非你有特殊的调试或高级控制需求。标准做法永远是读CAPxFIFO。
4.2 中断服务程序(ISR)的标准模板
一个健壮的捕获中断服务程序,通常遵循以下步骤:
// 假设是CAP1的中断服务程序 interrupt void CAP1_ISR(void) { volatile unsigned int cap_value1, cap_value2; volatile unsigned int fifo_status; // 1. 读取FIFO状态,判断情况 fifo_status = (Cap1Regs.CAPCON.bit.CAP1FIFO); // 假设这样访问状态位 // 2. 根据状态读取数据 if (fifo_status == 0x2 || fifo_status == 0x3) { // 状态10或11,有两个值可读 cap_value1 = Cap1Regs.CAP1FIFO; // 读取第一个(最老的)值 cap_value2 = Cap1Regs.CAP1FIFO; // 读取第二个值 // 此时,如果之前状态是11,则cap_value1是第二次捕获的值,cap_value2是第三次的值。 // 计算时间差、处理数据... } else if (fifo_status == 0x1) { // 状态01,只有一个值 // 这通常不应该在标准的中断触发下发生,因为中断是第二次捕获触发的。 // 但如果中断被其他方式触发,或者FIFO被手动操作过,则可能进入此分支。 cap_value1 = Cap1Regs.CAP1FIFO; // 处理单个数据... } else { // 状态00,空。理论上中断不应发生,可作为错误处理。 } // 3. 清除中断标志!!!这是最关键的一步,否则会一直产生中断。 EvaRegs.EVAIFRC.bit.CAP1INT = 1; // 写1清除CAP1中断标志 // 4. 如果需要,重新使能PIE组中断应答(取决于具体DSP的PIE操作) PieCtrlRegs.PIEACK.all = PIEACK_GROUP3; // 例如,CAP1中断在PIE组3 // 5. 返回 return; }4.3 一个“欺骗”硬件的编程技巧
数据手册中提到了一个有趣的技巧:可以向CAPxFIFO状态位写入01。这个操作会让事件管理器模块“认为”FIFO中已经有一个条目了。这样做的目的是什么?
假设你在系统初始化后,希望第一次捕获事件就能立即触发中断,而不是等到第二次。你可以先手动将CAP1FIFO状态位写为01。那么,当第一个真实的捕获事件发生时:
- 硬件发现FIFO状态非零(
01),满足中断触发条件。 - 它会将捕获值存入FIFO(此时状态可能变为
10,如果写入成功的话,具体看硬件实现),并立即设置中断标志。 - 这样,你就实现了“单次捕获即中断”的模式。
注意事项:这个技巧需要仔细查阅你所使用芯片的详细数据手册,确认写入操作的具体行为。滥用可能导致FIFO状态机混乱。在大多数需要成对数据(如周期测量)的应用中,使用默认的“第二次捕获中断”模式是最自然和高效的。
5. 中断系统的协同工作:从捕获事件到CPU响应
捕获单元产生的CAPxINT中断标志,只是整个事件管理器庞大中断体系中的一员。理解它如何融入整个中断系统,对于配置和调试至关重要。
5.1 事件管理器(EV)的中断分组与优先级
事件管理器的中断源被分成了三组(A, B, C),每组有自己的中断标志寄存器(EVxIFR)和中断屏蔽寄存器(EVxIMR)。捕获单元的中断通常被分配在C组。
- 对于EVA(事件管理器A):CAP1INT, CAP2INT, CAP3INT 属于 EVAIFRC / EVAIMRC。
- 对于EVB(事件管理器B):CAP4INT, CAP5INT, CAP6INT 属于 EVBIFRC / EVBIMRC。
每组内的中断有固定的硬件优先级。例如在EVA的C组中,CAP1INT优先级最高,其次是CAP2INT,最后是CAP3INT。这个优先级决定了当同一组内多个中断标志同时置位时,哪个中断向量会被优先响应。
5.2 中断的产生、响应与清除流程
- 标志置位:如前所述,当捕获单元发生满足条件的捕获事件(第二次及以后的捕获)时,硬件自动将对应的CAPxINT标志位置1。
- 屏蔽判断:硬件检查该中断在中断屏蔽寄存器(EVxIMRC)中对应的位是否被使能(=1)。
- 如果使能,则产生一个外设中断请求,发送给PIE控制器。
- 如果被屏蔽(=0),则中断请求不会被发出,但标志位依然为1,可供软件查询(轮询模式)。
- PIE仲裁:PIE控制器收到请求后,会结合所有外设的中断请求,根据优先级进行仲裁。
- CPU响应:如果CPU全局中断使能,且该中断在PIE中未被屏蔽,CPU会暂停当前任务,跳转到对应的中断服务程序(ISR)。
- 向量获取:在跳转前,PIE控制器会将最高优先级的、已使能且已置位的中断的向量号(Vector ID)加载到PIVR(外设中断向量寄存器)中。例如,CAP1INT的向量号是0033h。
- ISR中的关键操作:
- 读取PIVR(可选但推荐):通过读取PIVR,可以确认是哪个具体的中断源触发了本次ISR,这在多个中断共享一个ISR入口时非常有用。
- 处理数据:读取捕获单元的FIFO值,进行相关计算。
- 清除中断标志:必须在ISR结束前,通过向EVxIFRC寄存器的对应位写1,来清除CAPxINT标志位。如果忘记清除,该中断将无法再次产生,因为硬件会认为中断一直未处理。
- 清除PIE应答位:向PIEACK寄存器的对应组位写1,告知PIE控制器本组中断已处理完毕,允许该组产生新的中断。
- 中断返回:执行中断返回指令,CPU恢复原先的任务。
5.3 轮询模式与中断模式的抉择
中断并非唯一的使用方式。数据手册明确指出,如果不需要中断,可以通过轮询两种方式来检查数据是否就绪:
- 轮询中断标志位(CAPxINT):即使中断被屏蔽,标志位依然会在条件满足时置1。软件可以定期检查这个位。
- 轮询FIFO状态位(CAPxFIFO):直接检查状态位是否从
00变为01或10。
如何选择?
- 中断模式:适用于事件发生频率不确定、且要求低延迟响应的场景。CPU可以专注于其他任务,仅在数据准备好时被“打断”处理。这是实时系统的典型选择。
- 轮询模式:适用于事件发生频率固定且已知、或者对实时性要求不高的简单应用。也可以用于系统初始化、调试阶段。轮询模式没有中断上下文切换的开销,但会持续占用CPU资源。
避坑指南:中断嵌套与响应时间在复杂的实时控制系统中,中断可能嵌套发生。高优先级的中断可以打断低优先级的ISR。你需要合理规划中断优先级,确保最紧急的事件(如过流保护的PDPINTx)能得到最快响应。同时,要注意在ISR中执行的操作尽可能精简,长时间的操作会影响其他中断的响应,甚至可能导致捕获FIFO溢出(状态
11)。计算一下你的最坏情况中断响应时间(从事件发生到ISR第一条指令执行),确保它小于连续捕获事件的最小时间间隔。
6. 正交编码脉冲(QEP)电路:捕获单元的特殊应用模式
捕获单元除了独立的边沿捕获功能,还与QEP(正交编码脉冲)电路紧密相关。QEP是用于连接光电编码器、获取电机位置和速度信息的专用接口。理解QEP如何复用捕获单元引脚和资源,能让你更全面地掌握事件管理器。
6.1 QEP电路与捕获单元的引脚复用
以EVA为例,CAP1/QEP1和CAP2/QEP2是同一个物理引脚的两个功能。通过配置捕获控制寄存器(CAPCONA)的相应位,可以选择该引脚是作为捕获单元1/2的输入,还是作为QEP电路的QEP1/QEP2输入。当配置为QEP模式时,这两个引脚用于接收来自增量式编码器的两路正交(相位差90度)脉冲信号。
6.2 QEP模式下的定时器与计数原理
在QEP模式下,通用定时器2(GPT2)被用作QEP电路的时基和计数器。
- 时钟源:QEP电路内部的方向解码逻辑会对两路正交信号(QEP1和QEP2)进行四倍频处理(对两个信号的上升沿和下降沿都计数),产生一个频率为输入信号四倍的“正交时钟”(Quadrature Clock)。
- 方向判定:解码逻辑还会判断哪路信号相位领先,从而产生一个方向信号(DIR)。如果QEP1领先,方向为上(增计数);如果QEP2领先,方向为下(减计数)。
- 定时器配置:GPT2必须被设置为定向增/减计数模式,并且将其时钟源选择为QEP电路。此时,GPT2的计数方向由QEP电路的方向信号控制,计数时钟由QEP电路的四倍频时钟驱动。
- 位置获取:GPT2的计数器值(T2CNT)就直接反映了编码器的累计位置(计数值)和方向(增/减)。通过定期读取T2CNT并计算差值,就可以得到速度信息。
6.3 QEP中断与捕获单元中断的关系
在QEP模式下,原本的捕获单元引脚功能被占用,但捕获单元本身的中断(CAP1INT, CAP2INT)在QEP模式下通常不再用于位置捕获。因为位置信息直接由定时器计数器连续提供。
然而,与GPT2相关的其他中断在QEP模式下仍然有效且有用:
- 定时器下溢(T2UFINT)/上溢(T2OFINT)中断:当GPT2在QEP模式下向上计数到FFFFh后归零,或向下计数到0000h后跳转到FFFFh,会触发这些中断。这可用于处理编码器计数值的“圈数”计数。
- 定时器周期(T2PINT)/比较(T2CINT)中断:可以用于在固定的位置间隔(基于计数值)产生中断,例如用于换向或周期性速度计算。
重要区别:QEP模式提供的是连续、累积的位置计数,而捕获单元模式提供的是离散事件的精确时间戳。前者适合连续轨迹跟踪(如电机位置伺服),后者适合测量脉冲特征(如频率、占空比、脉冲间隔)。
7. 实战配置与常见问题排查
理论最终要落到代码和调试上。这里给出一个典型的捕获单元初始化与使用流程,并总结几个最容易踩坑的地方。
7.1 捕获单元初始化步骤(以EVA的CAP1为例)
假设我们要用CAP1测量一个外部脉冲的周期(上升沿到上升沿),使用GPT2作为时基,并启用中断。
- 配置GPIO引脚:将CAP1/QEP1引脚功能设置为捕获输入(CAPCONA相关配置位),而非通用IO或QEP。
- 配置通用定时器2(GPT2):
- 设置T2CON寄存器,选择连续增计数模式(或你需要的模式),设置预分频,使能定时器。
- 设置T2PR(周期寄存器)为一个足够大的值(如0xFFFF),避免在测量期间溢出(除非你希望用溢出中断来扩展计数范围)。
- 启动GPT2(置位T2CON.6, TENABLE)。
- 配置捕获单元1(CAP1):
- 设置CAPCONA寄存器:
- 选择CAP1的边沿检测方式(如上升沿)。
- 选择CAP1的定时器源为GPT2。
- 使能CAP1。
- 可选但重要:清除CAP1FIFO状态位(通过读CAP1FIFO寄存器,确保状态为
00)。
- 设置CAPCONA寄存器:
- 配置中断:
- 清除EVAIFRC中的CAP1INT标志位(写1清除)。
- 设置EVAIMRC寄存器,使能CAP1INT中断(对应位置1)。
- 在PIE控制器中,使能对应中断组(如CAP1INT属于INT3)的中断。
- 编写CAP1_ISR中断服务程序。
- 全局使能中断(开总中断)。
7.2 常见问题与排查技巧实录
下表列出了开发过程中常见的问题、可能原因及解决方法:
| 问题现象 | 可能原因 | 排查步骤与解决方法 |
|---|---|---|
| 根本进不了中断 | 1. 中断未使能(EVAIMRC, PIE, 全局)。 2. 中断标志早已置位但未清除,阻止新中断。 3. 引脚功能配置错误(仍是GPIO)。 4. 捕获单元未使能(CAPCONA)。 | 1. 逐级检查:EVAIMRC对应位->PIE组使能及具体位->总中断位。 2. 在初始化时和ISR中,都确保对CAP1INT标志写1清除。 3. 检查CAPCONA中对应引脚的功能选择位。 4. 检查CAPCONA中CAP1的使能位。 |
| 中断只进一次 | ISR中忘记清除中断标志。这是最常见的原因!标志位不清除,硬件认为中断一直未处理,不会产生新的请求。 | 在ISR末尾,务必执行EvaRegs.EVAIFRC.bit.CAP1INT = 1; |
| 读取的捕获值总是0或不变 | 1. 定时器源选择错误(CAPCONA配置)。 2. 定时器(GPT2)没有运行。 3. 边沿检测配置错误(想测上升沿却配成了下降沿)。 4. 信号本身有问题(用示波器看)。 | 1. 确认CAPCONA中为CAP1选择了正确的定时器(如GPT2)。 2. 检查T2CON的TENABLE位,并确认时钟有输入。 3. 检查CAPCONA中CAP1的边沿检测设置位。 4. 用示波器检查CAP1引脚是否有预期的跳变。 |
FIFO状态经常为11(数据丢失) | CPU响应太慢,或事件频率过高。ISR执行时间太长,在CPU处理完之前,第三个事件已经到来,覆盖了第一个数据。 | 1.优化ISR:只做最必要的操作(读数据、存数据、清标志),复杂计算放到主循环。 2.提高CPU优先级:如果可能,给捕获中断分配更高优先级,减少被其他中断阻塞的时间。 3.评估需求:事件频率是否超出系统处理能力?考虑使用更快的CPU,或降低采样要求。 |
| 计算的时间间隔不准 | 1. 定时器时钟源频率不清楚或配置错误。 2. 定时器在捕获期间发生了溢出/下溢,但软件未处理。 3. 读取FIFO的顺序或次数错误,导致值配对错误。 | 1. 确认系统时钟、定时器预分频,计算出计数器每个Tick对应的实际时间。 2. 如果测量时间可能超过定时器周期,需在ISR中处理溢出中断,维护一个溢出计数器。 3. 严格按照 10或11状态读两次,01状态读一次的规则操作。在ISR开始时读取状态位指导操作。 |
| 同时使用多个捕获单元时相互干扰 | 1. 中断标志清除错误(清错了位)。 2. 在ISR中未判断中断源(读PIVR)。 3. FIFO数据读取混乱。 | 1. 确保清除的是自己中断的标志位。 2. 如果多个捕获中断共用ISR,必须先读PIVR判断是哪个单元触发。 3. 每个捕获单元有独立的FIFO,读取时使用各自的数据寄存器地址。 |
7.3 一个高级技巧:利用FIFO状态进行流控
在数据吞吐量大的应用中,你可以利用FIFO状态位来实现简单的软件流控。例如,在主循环中,你可以定期检查所有活跃捕获单元的FIFO状态。如果发现某个单元的状态为10(两个数据就绪),你可以选择:
- 立即处理(读取数据),防止其变为
11。 - 或者,如果系统当前负载很高,可以暂时不处理,但记录一个“高水位”警告,待负载下降后再处理。这比等到中断发生、在ISR中才发现溢出要更主动。
捕获单元的FIFO和中断机制,是嵌入式实时系统中硬件与软件协同的典范。硬件提供了高效、可靠的数据暂存和事件通知机制,而软件则需要以正确的“姿势”去理解和驾驭它。从理解00、01、10、11这四个状态位的含义开始,到写出能稳定处理数据、及时清除标志的中断服务程序,每一步都需要对硬件行为有清晰的认知。记住,11状态是你的朋友而不是敌人,它明确地告诉你系统遇到了压力。处理好它,你的应用就能在复杂的实时环境中游刃有余。
