1. GPT定时器计数与捕获源选择寄存器详解:GTUPSR、GTDNSR与GTICASR

在嵌入式开发,尤其是电机控制、数字电源和精密测量领域,定时器(Timer)的灵活性和精度直接决定了整个系统的性能上限。我们通常知道定时器可以计数、可以产生PWM波、可以捕获外部信号的脉宽,但很多开发者可能只停留在使用其“默认”或“简单”的模式上。比如,让定时器按照固定的内部时钟频率递增计数,或者用一个外部引脚触发捕获。然而,现代高端MCU的定时器外设,其能力远不止于此。它们更像一个高度可编程的“事件响应引擎”,其计数行为(是加还是减)和捕获动作,可以由一系列复杂且精细的条件来触发。

瑞萨电子的RA8P1系列微控制器,基于强大的Arm® Cortex®-M85内核,其内置的通用PWM定时器(GPT)模块正是这种“引擎”的杰出代表。今天,我们不谈GPT的基础功能,而是深入其最核心、也最能体现其灵活性的配置部分: 计数源与捕获源选择寄存器 。具体来说,就是 GTUPSR(上计数源选择寄存器)、GTDNSR(下计数源选择寄存器)和GTICASR(输入捕获源选择寄存器A) 。理解并熟练运用这三个寄存器,你就能让GPT定时器摆脱简单的时钟驱动,进化成一个能够根据外部信号电平、边沿组合甚至其他模块事件来动态改变计数行为的智能单元。这对于实现无刷电机换相检测、复杂电源拓扑的同步、多路传感器信号的条件采样等高级应用,是必不可少的关键技能。

2. 核心概念与寄存器功能总览

在深入每个比特位之前,我们必须先建立几个核心概念,否则直接看寄存器描述就像读天书。GPT定时器的核心是一个名为 GTCNT 的计数器寄存器。它的值可以增加(Count Up)、减少(Count Down)或者在特定条件下被清零。是什么决定了它何时增加、何时减少呢?传统模式下,这通常由时钟分频器(TPCS bits in GTCR)决定,计数器要么一直加,要么一直减。但GPT提供了更强大的“事件计数”模式。

事件计数模式 的精髓在于: GTCNT计数器的每一次递增或递减,都不是由固定时钟自动触发的,而是由一个被使能的“事件源”触发的 。这个“事件”可以是一个外部引脚的上升沿、下降沿,也可以是另一个引脚在特定电平下的边沿,甚至可以是来自其他外设(如ADC转换完成)通过事件链接控制器(ELC)发送过来的内部事件。 GTUPSR GTDNSR 这两个寄存器,就是用来配置哪些“事件”可以触发计数器加一(UP)或减一(DOWN)。

同理, 输入捕获 功能是将GTCNT的当前值锁存到捕获寄存器(如 GTCCRA )中。通常,我们配置为某个引脚(如GTIOCnA)的边沿触发捕获。但GPT允许你为捕获动作选择更复杂的触发源, GTICASR 寄存器就是用来配置哪些事件可以触发对 GTCCRA 寄存器的捕获。

这三个寄存器的共同特点是: 它们都提供了一组高度相似的、可独立使能的事件源选项 。这些事件源主要分为四大类:

  1. GTETRGn 外部触发引脚事件 :GTETRGA, B, C, D 四个引脚的上升沿(R)或下降沿(F)。
  2. GTIOCnA/B 输入/输出引脚的条件边沿事件 :这是最复杂也最强大的部分。它不仅仅是GTIOCnA或B的简单边沿,而是 在另一个引脚处于特定电平(高或低)时,当前引脚发生的边沿 。例如,“GTIOCnA上升沿,且此时GTIOCnB为低电平”作为一个独立事件。
  3. ELC(事件链接控制器)事件 :ELC_GPTA 到 ELCPTH 共8个内部事件输入。
  4. 外部输入电平事件 :由 USILVL[3:0] DSILVL[3:0] 位域控制,直接根据GTIOCnA/B或GTETRGn引脚的电平(高或低)来持续触发计数,而不是边沿。

关键机制 :当这些寄存器中 至少有一个比特位被置1 时,定时器就会进入相应的事件驱动模式。此时, GTCR.TPCS 选择的内部时钟将 不再 作为计数器的驱动源。计数器完全由你使能的事件来推动。如果同时使能了多个事件源,即使多个事件同时发生,计数器也 只变化一次 (加一或减一)。

3. GTUPSR:上计数源选择寄存器详解

GTUPSR 寄存器决定了哪些事件可以导致 GTCNT 计数器加一。它的位域结构清晰地反映了事件源的分类。

3.1 寄存器位域映射与功能分组

为了方便理解,我们可以将32位的 GTUPSR 寄存器划分为以下几个功能区域:

比特位范围 位域名称 功能描述
31:28 保留位,必须写0。
27:24 USILVL[3:0] 外部输入电平源计数使能 。这是一个4位的字段,用于选择特定的引脚电平作为持续计数条件。
23:16 USELC[A:H] ELC事件源计数使能 。共8位,分别对应ELC_GPTA到ELC_GPTH事件。当对应事件发生时,计数器加一。
15:8 USCBF[A:H] / USCBR[A:H] GTIOCnB引脚条件边沿事件使能 。这8位控制当GTIOCnA引脚为高(H)或低(L)电平时,GTIOCnB引脚的上升沿(R)或下降沿(F)是否触发计数。
7:0 USCAF[B:H] / USCAR[B:H] GTIOCnA引脚条件边沿事件使能 。这8位控制当GTIOCnB引脚为高(H)或低(L)电平时,GTIOCnA引脚的上升沿(R)或下降沿(F)是否触发计数。
7:0 (同时包含) USGTRG[D:A][F/R] GTETRGn外部触发引脚边沿事件使能 。这8位控制GTETRGA到D引脚的上升沿(R)或下降沿(F)是否触发计数。

注意 :实际上,比特位0-7被 USGTRG* USCA* / USCB* 两组功能共享,具体取决于引脚模式配置。在GPT的输入捕获模式下,GTIOCnA/B通常被用作输入引脚,此时 USCA* / USCB* 功能有效;而在其他模式下,这些位可能对应 USGTRG* 功能。数据手册的位描述是全集,实际应用时需要根据 GTIOR 等模式寄存器来确定具体功能。这是第一个容易混淆的点,我们后续在配置示例中会具体说明。

3.2 关键位域深度解析

1. USILVL[3:0] - 电平触发计数模式 这是与传统边沿触发截然不同的模式。它不是等待一个跳变,而是检测引脚的电平状态。例如:

  • USILVL = 0b0010 :当 GTIOCnA 引脚输入为低电平(0) 时,持续触发计数递增。只要引脚保持低电平,计数器就会在每个GPT时钟周期(或事件时钟周期)加一。这实际上将引脚电平转换成了一个门控(Gating)信号。
  • USILVL = 0b1000 :当 GTETRGA 引脚输入为低电平(0) 时,持续触发计数递增。

这种模式非常适合实现“使能”控制。比如,你可以用一个外部信号(如“电机使能”信号)的高电平来允许计数器工作,低电平则暂停计数。

2. 条件边沿事件(USCARBL, USCBFAL 等) 这是GPT最强大的特性之一,实现了 基于双引脚状态组合的逻辑触发 。我们以 USCARBL (Bit 8) 为例:

  • 功能 :使能 GTIOCnA 引脚上升沿触发计数,但 仅当 GTIOCnB 引脚输入为低电平(0)时有效
  • 应用场景 :想象一个两相正交编码器(A相和B相)。你可以配置:
    • USCARBL = 1: A相上升沿时计数,但仅在B相为低时(即A领先B 90度)。
    • USCBFAL (Bit 14) = 1: B相下降沿时计数,但仅在A相为低时(同样对应A领先B的某个状态)。 通过组合配置多个这样的条件边沿事件,你可以仅用两个引脚就实现带方向判断的正交编码器计数,而无需专用的编码器接口单元,极大地节省了外设资源。

3. ELC事件源(USELCA-USELCH) ELC是RA系列MCU的一大特色,它允许外设之间不经过CPU直接触发动作。例如,你可以配置ADC转换完成事件(ADTRGn)链接到 ELC_GPTA ,然后使能 USELCA 。这样,每次ADC转换完成,GPT计数器就自动加一。这可以实现 精确的、与模拟采样同步的计数 ,用于构建等间隔采样的数据缓冲区索引,在电机相电流采样等应用中非常有用。

3.3 配置流程与实操要点

配置 GTUPSR 实现事件计数,需要遵循一个清晰的流程,避免功能冲突:

  1. 确定工作模式 :首先通过 GTCR.MD GTIOR 等寄存器,将GPT设置为 事件计数模式 或其变体。通常,这涉及到停止计数器( GTCR.CST=0 ),并选择适当的计数模式。
  2. 禁用内部时钟源 :在事件计数模式下, GTCR.TPCS 选择的时钟源应设置为“无”或一个不影响事件的时钟(具体取决于模式)。核心原则是:一旦 GTUPSR GTDNSR 有任何位使能, TPCS 就被忽略。
  3. 清除寄存器 :在配置前,将 GTUPSR 寄存器写为 0x00000000 ,确保从一个干净的状态开始。
  4. 使能目标事件源 :根据你的应用需求,将 GTUPSR 中对应的比特位置1。例如,如果只想用GTETRGA的上升沿触发计数,则设置 USGTRGAR = 1
  5. 配置相关引脚 :通过端口控制寄存器,将对应的 GTETRGn GTIOCnA/B 引脚功能设置为输入,并配置上下拉电阻以适应外部信号。
  6. 启动计数器 :最后,将 GTCR.CST 位置1,启动计数器。此时,计数器将静止不动,直到你使能的事件发生。

重要提示 GTUPSR GTDNSR 可以同时使能 。例如,你可以配置GTIOCnA上升沿使计数器加一,GTIOCnB上升沿使计数器减一。这样,两个引脚就可以直接实现一个增/减计数器,常用于手动调速或位置微调界面。

4. GTDNSR:下计数源选择寄存器详解

GTDNSR 寄存器是 GTUPSR 的“镜像”,其位域结构、命名规则和功能与 GTUPSR 完全一一对应,唯一的区别在于它控制的是计数器 递减(Down Count) 的事件源。

  • DSGTRGAR 对应 USGTRGAR ,但功能是使能GTETRGA上升沿触发 减一
  • DSCARBL 对应 USCARBL ,功能是使能“GTIOCnA上升沿且GTIOCnB为低”时触发 减一
  • DSILVL[3:0] 对应 USILVL[3:0] ,功能是选择特定引脚电平来持续触发 递减
  • DSELCA 对应 USELCA ,功能是使能ELC_GPTA事件触发 减一

应用场景:双向事件计数 结合 GTUPSR GTDNSR ,你可以构建一个完全由外部事件控制的双向计数器。一个经典的应用是 带方向信号的单脉冲计数 。假设你有两个传感器:一个检测“正转脉冲”(信号A),一个检测“反转脉冲”(信号B)。

  • 配置 GTUPSR :使能信号A的上升沿作为上计数源。
  • 配置 GTDNSR :使能信号B的上升沿作为下计数源。 这样,GTCNT的值就实时反映了净位置,无需软件判断方向。这在简单的直线位移编码或卷绕机计圈应用中非常高效。

电平触发递减的独特用途 : 配置 DSILVL 为某个引脚高电平触发递减。你可以将此引脚连接到一个“复位”或“归零”按钮。当按钮按下(高电平)时,计数器持续递减直到0;松开后,计数器停止。这实现了一个硬件辅助的快速归零功能。

5. GTICASR:输入捕获源选择寄存器详解

GTICASR 寄存器用于配置哪些事件可以触发对 GTCCRA 寄存器的输入捕获。它的位域布局与 GTUPSR 高度相似,只是将前缀“US”换成了“AS”,并且功能从“计数”变成了“捕获”。

5.1 寄存器功能与位域解析

GTICASR 的比特位定义与 GTUPSR 几乎逐位对应:

  • ASGTRGAR :GTETRGA上升沿触发对GTCCRA的捕获。
  • ASCARBL :GTIOCnA上升沿(且GTIOCnB为低)触发对GTCCRA的捕获。
  • ASELCA :ELC_GPTA事件触发对GTCCRA的捕获。
  • ASILVL 位域在 GTICASR 中不存在,捕获通常是边沿触发行为,电平触发不适合捕获动作。

一个特殊的位:ASOC (Bit 24) 这是 GTICASR 独有的一个位,称为“其他通道源捕获使能”。当此位置1时,允许 其他GPT通道 的特定事件来触发本通道GTCCRA的捕获。

  • 应用价值 :这在多通道GPT同步或联动时非常有用。例如,你有两个GPT通道(GPT0和GPT1)分别用于测量两个相关信号。你可以配置GPT1的某个事件(如其计数器溢出)去触发GPT0的捕获,从而精确记录两个事件之间的时间差,实现跨通道的精密时间关联测量。

5.2 输入捕获的高级应用模式

传统的输入捕获是“一个引脚,一个边沿,捕获一个时间戳”。GPT通过 GTICASR 将其扩展为“多种复杂条件,捕获一个时间戳”。这开启了更多可能性:

1. 窗口式捕获 假设你想测量一个脉冲的宽度,但只关心当另一个控制信号为有效状态时的脉冲。例如,在电机驱动的“使能窗口”内测量霍尔传感器脉冲。

  • 配置 GTICASR :使能 ASCARBH (GTIOCnA上升沿,且GTIOCnB为高)作为捕获触发源。这里,GTIOCnA接霍尔信号,GTIOCnB接“使能窗口”信号。
  • 结果:只有当“使能窗口”信号为高时,霍尔信号的上升沿才会触发捕获。无效窗口期的噪声脉冲被硬件自动过滤,无需软件干预,提高了系统的抗干扰能力和可靠性。

2. 多事件触发单次捕获 你可以同时使能 ASGTRGAR (GTETRGA上升沿) 和 ASELCA (ELC事件)。这样,无论是外部硬件信号还是内部ADC转换完成,都能触发捕获。这在多源同步触发系统中非常有用,你可以用一个捕获寄存器来记录多种不同类型事件发生的时刻。

3. 通过ELC实现间接捕获 使能 ASELCA ,并将某个外部中断引脚(如IRQ)连接到 ELC_GPTA 事件。这样,任何可以产生IRQ的外部信号(甚至来自其他芯片的信号)都能触发GPT的捕获。这相当于扩展了GPT的捕获触发输入,不再局限于自身的几个GTIOC和GTETRG引脚。

6. 实战配置:构建一个条件触发的频率计

让我们通过一个完整的例子,将理论转化为代码。目标:使用GPT2通道,测量输入到GTIOC2A引脚的信号频率,但 仅在GTIOC2B引脚为高电平的“测量使能”期间进行 。我们使用输入捕获功能,捕获两个连续的上升沿,计算时间差。

步骤1:外设与引脚初始化

/* 启用GPT2模块时钟 */
R_GPT_Open(&g_timer2_ctrl, &g_timer2_cfg);

/* 配置GTIOC2A为输入捕获功能,GTIOC2B为通用输入(用于使能) */
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_XX_PIN_XX,  // 替换为GTIOC2A实际引脚
                (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_GPT2));
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_YY_PIN_YY,  // 替换为GTIOC2B实际引脚
                (uint32_t) (IOPORT_CFG_PORT_DIRECTION_INPUT));

步骤2:配置GPT基础模式

/* 停止计数器 */
gpt2_regs->GTCR_b.CST = 0;

/* 设置计数器为上升沿触发的外部事件计数模式(模式取决于具体型号,假设为MD=0b101) */
gpt2_regs->GTCR_b.MD = 0x5;

/* 设置GTCCRA为输入捕获寄存器,捕获GTCNT的值 */
gpt2_regs->GTIOR_b.GTIOA = 0x2; // 例如,设置为输入捕获模式

/* 设置分频器,选择内部高速时钟作为计数器时钟源(用于计时) */
gpt2_regs->GTCR_b.TPCS = 0x0; // PCLK/1

步骤3:精细配置GTICASR(核心步骤) 这是关键。我们不想用普通的GTIOC2A上升沿捕获,而是要用“GTIOC2A上升沿且GTIOC2B为高”这个条件。

/* 先清除GTICASR寄存器 */
gpt2_regs->GTICASR = 0x00000000UL;

/* 使能条件边沿捕获:GTIOC2A Rising, when GTIOC2B is High.
 * 根据手册,这对应位 ASCARBH (Bit 9)。
 * 注意:位9在GTICASR中可能是 ASELCB 或 ASCARBH,具体取决于GTIOR对GTIOC2B的模式设置。
 * 假设我们将GTIOC2B配置为通用输入(非PWM输出),则此位功能为 ASCARBH。
 */
gpt2_regs->GTICASR_b.ASCARBH = 1;

/* 其他所有捕获源保持禁用 */

步骤4:配置中断与启动

/* 使能GTCCRA的输入捕获中断 */
gpt2_regs->GTBER_b.CCRA = 1; // 允许GTCCRA缓冲操作(如果需要)
gpt2_regs->GTIER_b.GTICAE = 1; // 使能GTCCRA输入捕获中断

/* 清除可能存在的挂起中断标志 */
gpt2_regs->GTSR_b.GTICAF = 0;

/* 注册中断服务程序 */
R_BSP_IrqWrite(BSP_IRQ_GPT2_COUNTER_OVERFLOW, my_gpt2_capture_isr);
R_BSP_IrqEnable(BSP_IRQ_GPT2_COUNTER_OVERFLOW);

/* 最后,启动计数器 */
gpt2_regs->GTCR_b.CST = 1;

步骤5:中断服务程序(ISR)实现

volatile uint32_t first_capture_value = 0;
volatile uint32_t period_ticks = 0;
volatile bool is_first_capture = true;

void my_gpt2_capture_isr(void) {
    /* 检查是否是GTCCRA输入捕获中断 */
    if (gpt2_regs->GTSR_b.GTICAF) {
        /* 读取捕获值 */
        uint32_t current_capture = gpt2_regs->GTCCRA;

        if (is_first_capture) {
            first_capture_value = current_capture;
            is_first_capture = false;
        } else {
            /* 计算周期(注意计数器溢出处理) */
            period_ticks = current_capture - first_capture_value;
            first_capture_value = current_capture;

            /* 将周期 ticks 转换为频率 */
            uint32_t pclk_freq = BSP_FEATURE_BSP_HZ_PCLKB;
            float frequency = (float)pclk_freq / period_ticks;
            // 使用 frequency...
        }

        /* 清除中断标志 */
        gpt2_regs->GTSR_b.GTICAF = 0;
    }
}

通过以上配置,只有当GTIOC2B(测量使能)为高电平时,GTIOC2A的上升沿才会被捕获。这完美实现了“条件测量”的需求,所有逻辑均由硬件完成,软件只需处理结果,实时性极高。

7. 常见问题与高级调试技巧

在实际使用这些高级源选择功能时,你可能会遇到一些棘手的问题。以下是我在项目中总结的一些经验和排查思路。

问题1:配置了事件源,但计数器完全不计数。

  • 检查点1: GTUPSR/GTDNSR GTCR.TPCS 的优先级 。记住,只要 GTUPSR GTDNSR 有任何一位为1, TPCS 选择的内部时钟源就被忽略。确认你不是在期待内部时钟和事件源同时作用。
  • 检查点2:引脚复用和功能 。确认你使用的 GTETRGn GTIOCnA/B 引脚已正确配置为外设功能(而非通用IO)。对于 GTETRGn ,还需注意它们经过POEG(可编程输出使能门控)单元,可能需要检查POEG的极性设置。
  • 检查点3:事件是否真的发生 。使用逻辑分析仪或示波器直接测量引脚信号,确认边沿或电平符合预期。特别注意条件边沿事件中“另一个引脚”的电平状态。
  • 检查点4:GPT模式是否正确 。确保 GTCR.MD 设置成了支持外部事件计数的模式(如PWM模式单边计数、事件计数模式等)。某些简单的定时器模式可能不支持这些复杂触发源。

问题2:输入捕获触发了,但捕获的值不正确或不稳定。

  • 检查点1:中断延迟与溢出 。在高频信号下,两次捕获中断之间的时间可能很短。确保你的中断服务程序(ISR)足够快,能在下一次捕获发生前完成读取和标志清除。考虑使用DMA将GTCCRA的值直接传输到内存,或者使能缓冲寄存器( GTBER )并利用其溢出中断。
  • 检查点2:信号抖动与消抖 。条件边沿捕获对信号质量要求较高。如果“条件引脚”(如GTIOCnB)有抖动,可能导致意外的捕获触发。需要在硬件(RC滤波)或软件(在ISR中二次判断引脚状态)上增加消抖措施。
  • 检查点3: ASOC 位的误用 。如果你只使用本通道的引脚触发捕获,确保 GTICASR.ASOC 位为0。如果意外置1,其他通道的事件可能会干扰你的捕获。

问题3:同时使能多个事件源,行为不符合预期。

  • 核心原则 :即使多个使能的事件同时发生,计数器也 只变化一次 (加一或减一)。这不是“或”逻辑,而是“优先或一次性”逻辑。设计时要避免依赖同时发生的事件来产生多次计数。
  • 电平触发与边沿触发混用 :如果同时使能了 USILVL (电平触发)和某个边沿触发源,计数器会非常频繁地动作。电平触发会在每个时钟周期尝试计数,而边沿触发在跳变时计数。这通常会导致混乱,除非有特殊设计需求,否则应避免混用。

高级技巧:利用ELC实现无CPU干预的闭环控制 这是RA系列ELC与GPT联动的精髓。设想一个电机电流控制场景:

  1. GPT生成中心对齐的PWM驱动电机。
  2. 配置ADC在PWM周期的中心点(通过GPT的周期匹配事件触发)对电流进行采样。
  3. ADC转换完成后,通过ELC产生 ELC_GPTA 事件。
  4. 使能 GTUPSR.USELCA = 1
  5. 结果:每次ADC采样完成,GPT计数器自动加一。你可以将GTCNT的值作为一个“采样索引”,同时用另一个比较匹配事件(当GTCNT等于某个值时)来触发下一次PWM参数的更新(通过GTCCR缓冲寄存器自动重载)。 这样,从采样、计数到PWM更新,形成了一个完全由硬件事件驱动的闭环,CPU只在需要改变控制算法参数时才介入,极大提升了实时性和确定性。

更多推荐