29. AGT——低功耗定时器

RA MCU 有两种定时器外设:GPT(General PWM Timer)定时器和 AGT(Asynchronous General Purpose Timer)定时器。 在它们之间进行选择时,需要考虑以下因素:

考虑在 GPT和 AGT之间进行选择

考虑因素

GPT

AGT

低功耗模式

(Low Power Modes)

GPT 可以在睡眠模式(Sleep mode)下工作。

AGT 可以在所有的低功耗模式下工作。

(仅当计数源为 LOCO 或 Subclock 时)

可用通道

(定时器数目)

至少拥有 7 个 GPT 通道,

不同MCU型号拥有的GPT通道数会有不同。

所有的MCU型号都有至少 2 个AGT通道。

定时器分辨率

(Timer Resolution)

至少有一个 32-bit 的 GPT 定时器;

剩下的是 16-bit 的。

AGT 定时器都是 16-bit 的。

注:一些MCU型号拥有AGTW定时器,为 32-bit 的。

定时器的时钟源

GPT 的时钟源为 PCLKD,

可配置分频器(最高分频1024)。

也可以配置为计数 ELC 事件或外部脉冲。

AGT 的时钟源为 PCLKB、LOCO 或 Subclock。

对于 PCLKB 分频器最高可设为8分频;

对于 LOCO 或 Subclock 分频器最高可设为128分频。

29.1. AGT简介

低功耗异步通用定时器(AGT)是 16 位的定时器,可用于基本的定时、脉冲输出、外部脉冲宽度或周期测量,以及外部事件计数。 该定时器主要由一个重加载寄存器和一个递减计数器组成。

RA MCU 的不同型号之间可能拥有不同数量的 AGT 定时器, 规格也有可能不同,比如适用于电机控制的 RA6T2 型号MCU拥有 AGTW 定时器,AGTW 的计数器位数加宽到了32位。 而对于野火启明6M5开发板上板载的 RA6M5 芯片拥有 6 个16位的 AGT 定时器(AGTn (n=0~5)); 对于野火启明4M2开发板上板载的 RA4M2 芯片也拥有 6 个16位的 AGT 定时器(AGTn (n=0~5)); 对于野火启明2L1开发板上板载的 RA2L1 芯片仅有 2 个16位的 AGT 定时器(AGTn (n=0~5))。

AGT 定时器的详细功能参数如下表所示:

AGT 定时器的功能参数

参数

描述

工作模式

  • 定时器模式

  • 脉冲输出模式

  • 外部事件计数模式

  • 外部脉冲宽度测量模式

  • 外部脉冲周期测量模式

时钟源

  • PCLKB:PCLKB, PCLKB/2, PCLKB/8

  • LOCO, SUBCLK:AGTLCLK/d, AGTSCLK/d (d = 1, 2, 4, 8, 16, 32, 64, 128)

  • AGTn (n = 1, 3, 5) 也可以连接到 AGTn (n = 0, 2, 4) 的下溢信号进行计数

  • 外部事件输入(通过 AGTIO 引脚)

中断和事件链接功能

  • 下溢事件信号或测量完成事件信号

  • 比较匹配 A 事件信号

  • 比较匹配 B 事件信号

  • 可以通过 AGTn_AGTI, AGTn_AGTCMAI, AGTn_AGTCMBI (n = 1, 3)

    从 Snooze 模式或 Software Standby 模式返回

可选的比较匹配功能

该功能通过 AGT Compare Match A/B 寄存器实现,可用于输出 PWM 波形

AGT 的 I/O 引脚及其功能用途如下表所示:

引脚名称

I/O 方向

功能

AGTEEn

Input

用于 AGT 的外部事件输入使能

AGTIOn

Input/Output

AGT 的外部事件输入和脉冲输出

AGTOn

Output

AGT 脉冲输出

AGTOAn

Output

AGT 的比较匹配 A 输出

AGTOBn

Output

AGT 的比较匹配 B 输出

29.2. AGT的框图分析

图

29.2.1. 16位计数器

见图中标注 ① 处。

AGT 的计数器是一个 16 位的递减计数器,因此它仅支持递减计数。

重装载寄存器和递减计数器被分配到相同的地址,并且可以通过 AGT 计数器寄存器(AGT Counter Register)访问。 当我们向该地址写入值的时候,写入的值会被写入重载寄存器,读取的值会从计数器中读取。

29.2.2. 16位重装载寄存器

见图中标注 ② 处。

16 位的重装载寄存器对应的是 16 位的计数器。

一般当计数器产生下溢时,重装载寄存器会对计数器进行重装载。

29.2.3. 计数时钟源

见图中标注 ③ 处。

主要有四类时钟输入源:

  • PCLKB:PCLKB, PCLKB/2, PCLKB/8

  • LOCO, SUBCLK:AGTLCLK/d, AGTSCLK/d (d = 1, 2, 4, 8, 16, 32, 64, 128)

  • AGTn (n = 1, 3, 5) 也可以连接到 AGTn (n = 0, 2, 4) 的下溢信号进行计数

  • 外部事件输入(通过 AGTIO 引脚)

29.2.4. 比较匹配功能

见图中标注 ④ 处。

比较匹配功能可以用来生成 PWM 波形。

29.2.5. 比较匹配输出引脚

见图中标注 ⑤ 处。

比较匹配的结果输出到 AGTOAn、AGTOBn 引脚。

29.2.6. 输出引脚

见图中标注 ⑥ 处。

这部分连接到 AGTOn 输出引脚。

29.2.7. 下溢事件信号/测量完成事件信号输出

见图中标注 ⑦ 处。

下溢事件信号/测量完成事件信号属于内部信号,通过这两个信号均可触发中断。

29.3. AGT工作模式详解

定时器模式

在定时器模式下,计数值在计数源的每个上升沿递减1。 当计数值到达 0x0000 并输入下一个计数源时,发生计数器下溢事件并产生中断请求。

脉冲输出模式

可以从 AGTIOn 和 AGTOn 引脚输出脉冲。每次发生下溢时,输出电平都会反转。

事件计数器模式

在事件计数器模式下,计数器由输入到 AGTIOn 引脚的外部事件信号(计数源)驱动(递减计数)。

脉冲宽度测量模式

在脉冲宽度测量模式下,测量输入到 AGTIOn 引脚的外部信号的脉冲宽度。

脉冲周期测量模式

在脉冲周期测量模式下,测量输入到 AGTIOn 引脚的外部信号的脉冲周期。 仅测量周期长于计数源周期两倍的输入脉冲。此外,低电平和高电平宽度都必须长于计数源的周期。 如果输入比这些条件短的脉冲周期,输入可能会被忽略。

比较匹配功能:PWM模式

比较匹配功能可用于 PWM 输出。

29.4. 实验1:基本定时功能

本实验内容适用于野火启明6M5、启明4M2、启明2L1开发板。 本实验较为简单,我们将使用 AGT 定时器进行定时并触发中断请求产生中断,然后通过这个中断,切换 LED 的电平。

29.4.1. 硬件设计

本次实验需要使用到 LED 灯来展示定时的效果,LED 灯具体的电路讲解请读者参考本教程第8章“8.4. 实验:使用寄存器点亮LED灯”小节的内容。 这里不再赘述。

注:本实验仅用到 LED1~3 当中的其中一盏。

29.4.2. 软件设计

29.4.2.1. 新建工程

由于本实验需要用到LED,也会用到串口打印提示信息, 因此我们在前面串口通信章节的“实验1:UART收发回显”例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “28_AGT_Basic_Timing”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “28_AGT_Basic_Timing”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “agt” 文件夹, 再进入 “agt” 文件夹里面新建源文件和头文件:“bsp_agt_timing.c” 和 “bsp_agt_timing.h”。 工程文件结构如下。

文件结构
28_AGT_Basic_Timing
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ agt
   │  ├─ bsp_agt_timing.c
   │  └─ bsp_agt_timing.h
   └─ hal_entry.c

29.4.2.2. FSP配置

首先打开 FSP 配置界面,在 Stacks 中加入 AGT,如下图所示。

图

我们使用 AGT0 来实现定时和触发中断功能, 需要修改的只有“General”和“Interrupts”部分,其他按照默认即可。 AGT 模块的 FSP 配置属性如下图所示:

图
特别注意:

这里的计数源(Count Source)时钟需要更改为 LOCO 或者 SUBCLOCK(两者的时钟频率都是32.768KHz), 这是因为如果默认用 PCLKB,它的时钟频率很高——50MHz(作为 AGT 计数源可设置最高8分频), 而 AGT 计数器仅为 16 位,最高计数 2 的 16 次方,即 65536,通过如下计算可以知道, 这种情况下 AGT 定时器无法定时到足够的 1 秒钟。

  • 当使用 50MHz 的PCLKB计数源时,AGT最大可定时时间为:65536 * (1 / (50/8)) = 10485.76 us = 10.48576 ms。

  • 10.48576 毫秒远小于 1 秒,因此使用 PCLKB = 50MHz / 8分频作为时钟源的情况下 AGT 无法实现定时 1 秒。

对此的解决方法就是降低 PCLKB 的时钟频率,或者干脆直接换用另一个时钟频率较低的计数源:LOCO 或者 SUBCLOCK。 实际上还有另一个方法是定时器仅定时 1 毫秒产生中断,然后在中断里用程序计数1000次即 1 秒, 但是CPU频繁地进入中断是有可能会影响应用程序的性能的,这需要根据实际情况做出判断。

AGT 模块配置属性描述如下:

AGT属性描述:“Common”部分

属性

描述

Parameter Checking

参数检查(选择是否生成包含参数检查的代码)。

Pin Output Support

引脚输出使能。“Enabled”表示使能输出信号到引脚。

Pin Input Support

引脚输入使能。“Enabled”表示使能引脚输入,用于脉宽测量和周期测量模式,

或者用于从 P402、P403、P404 或者 AGTIO 这些引脚输入外部信号。

AGT属性描述:“General”部分

属性

描述

General > Name

模块名字。

General > Channel

通道选择。指定硬件通道,也就是指定使用哪一个AGT定时器。

General > Mode

可以选择周期计数、单次计数、PWM模式。

  • Periodic

  • One-Shot(该模式使用软件来实现,即使不设置中断回调函数也必须开启相应的中断)

  • PWM 模式

General > Period

周期。

General > Period Unit

周期的单位。

General > Count Source

AGT 计数器的时钟源。注:分频器会基于用户设置的周期由软件自动调节。

AGT属性描述:“Interrupts”部分

属性

描述

Interrupts > Callback

设置中断回调函数。

Interrupts > Underflow Interrupt Priority

定时器下溢中断优先级。

29.4.2.3. AGT初始化函数

代码清单28-1:AGT初始化函数
/* AGT初始化函数 */
void AGT_Timing_Init(void)
{
    /* 初始化 AGT 模块 */
    R_AGT_Open(&g_timer_agt0_ctrl, &g_timer_agt0_cfg);

    /* 启动 AGT 定时器 */
    R_AGT_Start(&g_timer_agt0_ctrl);
}

在AGT初始化函数里面,首先调用 R_AGT_Open 函数初始化 AGT 模块, 随后调用 R_AGT_Start 函数来启动 AGT 定时器。

29.4.2.4. AGT中断回调函数

代码清单28-2:AGT中断回调函数
/* 定时器溢出 中断回调函数 */
void agt0_timing_callback(timer_callback_args_t * p_args)
{
    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {
        /* 翻转 LED1 */
        LED1_TOGGLE;    //每秒翻转一次
    }
}

中断回调函数非常简单,使用 if 语句判断引起中断的事件, 若是定时器溢出中断(TIMER_EVENT_CYCLE_END)则翻转一次 LED1 的电平。

29.4.2.5. hal_entry入口函数

注:以下 hal_entry 函数是以启明6M5开发板的代码为例, 读者需要注意另外两块板子的调试串口初始化函数名不是“Debug_UART4_Init”。

代码清单28-3:hal_entry函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "gpt/bsp_gpt_timing.h"

void hal_entry(void)
{
    /* TODO: add your own code here */

    LED_Init();         // LED 初始化
    Debug_UART4_Init(); // SCI4 UART 调试串口初始化
    AGT_Timing_Init();  // AGT 初始化

    printf("这是一个 AGT 的基本定时功能实验\r\n");

    while(1)
    {
        printf("The program is running ...\r\n");
        R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
    }


#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

29.4.3. 下载验证

编译并下载到开发板后,复位板子让程序运行,然后可以观察到板载 LED1(红色) 每秒钟翻转一次状态,即以两秒为周期在缓慢闪烁。

29.5. 实验2:比较匹配功能——PWM输出

29.5.1. 硬件设计

野火启明6M5开发板例程使用的 PWM 输出引脚为额外引出的 IO 引脚:P500 ,如下图所示。

图

注:

  • 野火启明6M5开发板例程使用的 PWM 输出引脚为额外引出的 IO 引脚: P500 (AGTOA0)。

  • 野火启明4M2开发板例程使用的 PWM 输出引脚为额外引出的 IO 引脚: P211 (AGTOA5)。

  • 野火启明2L1开发板例程使用的 PWM 输出引脚为额外引出的 IO 引脚: P208 (AGTOB0)。

29.5.2. 软件设计

29.5.2.1. 新建工程

由于本实验需要用到LED,也会用到串口打印提示信息, 因此我们在前面串口通信章节的“实验1:UART收发回显”例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “28_AGT_PWM_Output”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “28_AGT_PWM_Output”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “agt” 文件夹, 再进入 “agt” 文件夹里面新建源文件和头文件:“bsp_agt_pwm_output.c” 和 “bsp_agt_pwm_output.h”。 工程文件结构如下。

文件结构
28_AGT_PWM_Output
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ agt
   │  ├─ bsp_agt_pwm_output.c
   │  └─ bsp_agt_pwm_output.h
   └─ hal_entry.c

29.5.2.2. FSP配置

接下来我们要以启明6M5开发板为例来说明进行FSP配置的方法,另外两块板子的配置步骤是一样的, 读者可根据实际使用的引脚参照下面的步骤来进行配置。

因为 PWM 输出需要使用 IO 口进行输出,因此需要先在“Pins”配置页中为 AGT 配置引脚, 我们将 AGT 的 AGTOA0 信号输出连接到 P500 引脚,如下图所示。

图

随后在“Stacks”配置页中加入 AGT 模块,并对其作如下图所示的配置。

图

AGT 的“Output”部分属性描述如下表所示。

AGT属性描述:“Output”部分

属性

描述

Output > Duty Cycle Percent

(only applicable in PWM mode)

占空比(必须是 0~100)。仅用于PWM模式。

这里默认设为 50% 的占空比。

Output > AGTOA Output

配置 AGTOA 输出。

Output > AGTOB Output

配置 AGTOB 输出。

Output > AGTO Output

配置 AGTO 输出。

29.5.2.3. AGT初始化函数

代码清单28-4:AGT初始化函数
/* AGT初始化函数 */
void AGT_PWM_Init(void)
{
    /* 初始化 AGT 模块 */
    R_AGT_Open(&g_timer_agt0_ctrl, &g_timer_agt0_cfg);

    /* 启动 AGT 定时器 */
    R_AGT_Start(&g_timer_agt0_ctrl);

    /* 重新设置占空比为 80% */
    AGT_PWM_SetDuty(80);
}

需要注意的是,在 AGT 初始化函数里面,我们重新设置了占空比:50% → 80%。 设置PWM占空比使用了我们自己写的 AGT_PWM_SetDuty 函数。

29.5.2.4. 设置PWM占空比函数

代码清单28-5:设置PWM占空比函数
/** 自定义函数:设置PWM占空比
    @param duty 占空比范围:0~100 %
*/
void AGT_PWM_SetDuty(uint8_t duty)
{
    timer_info_t info;
    uint32_t current_period_counts;
    uint32_t duty_cycle_counts;

    if (duty > 100)
        duty = 100; //限制占空比范围:0~100

    /* 获得AGT的信息 */
    R_AGT_InfoGet(&g_timer_agt0_ctrl, &info);

    /* 获得计时器一个周期需要的计数次数 */
    current_period_counts = info.period_counts;

    /* 根据占空比和一个周期的计数次数计算比较匹配寄存器的值 */
    duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);

    /* 最后调用FSP库函数设置占空比 */
    R_AGT_DutyCycleSet(&g_timer_agt0_ctrl, duty_cycle_counts, AGT_OUTPUT_PIN_AGTOA);
}

该函数的主要思路是需要先知道计数器的计数周期(即当前输出的PWM信号的一个周期需要计数的值 current_period_counts), 然后计算与要设定的占空比(duty)对应的计数值(duty_cycle_counts), 最后调用FSP库函数 R_AGT_DutyCycleSet 写入该占空比对应的计数值。

调用 R_AGT_DutyCycleSet 函数时需要注意传入的第三个参数是 AGT_OUTPUT_PIN_AGTOA, 因为我们使用的PWM输出引脚 P500 连接到的是 AGT0 的 AGTOA 信号。 如果换用别的引脚,需要注意检查这个参数是否需要修改。

29.5.2.5. hal_entry入口函数

代码清单28-6:hal_entry入口函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "gpt/bsp_gpt_pwm_output.h"


void hal_entry(void)
{
    /* TODO: add your own code here */

    LED_Init();         // LED 初始化
    Debug_UART4_Init(); // SCI4 UART 调试串口初始化
    AGT_PWM_Init();     // AGT 初始化

    printf("这是一个 AGT 的PWM输出功能实验\r\n");
    printf("使用示波器测量 P500 输出的PWM波形\r\n");

    // LED1 闪烁指示程序正在运行...
    while(1)
    {
        LED1_ON;
        R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
        LED1_OFF;
        R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
    }


#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

29.5.3. 下载验证

以野火启明6M5开发板为例, 编译并下载程序后,复位开发板使程序重新运行,然后使用示波器测量 P500 引脚输出的PWM波形。 实现现象如下。

图中PWM波形的频率为 20 KHz,占空比为 80%:

图

29.6. 实验3:脉冲宽度测量模式

29.6.1. 硬件设计

以野火启明6M5开发板例程为例, 本实验需要使用两个引脚,使用的 PWM 输出引脚为额外引出的 IO 引脚为:P500 ; 用于脉冲输入的测量引脚使用的开发板引出的 IO 引脚为: P600。如下图所示。

图

注:

  • 野火启明6M5开发板例程选用的 PWM 输出引脚为:P500 (AGTOA0);选用的测量引脚为:P600 (AGTIO3)。

  • 野火启明4M2开发板例程选用的 PWM 输出引脚为:P211 (AGTOA5);选用的测量引脚为:P415 (AGTIO4)。

  • 野火启明2L1开发板例程选用的 PWM 输出引脚为:P208 (AGTOB0);选用的测量引脚为:P402 (AGTIO1)。

  • 本次实验需要将 PWM 输出引脚与测量引脚使用杜邦线连接起来。

29.6.2. 软件设计

29.6.2.1. 新建工程

由于本实验需要用到 PWM 波形信号, 因此我们直接在前面的“实验2:比较匹配功能——PWM输出”例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “28_AGT_PWM_Output”, 然后将工程文件夹重命名为 “28_AGT_Pulse_Width_Measurement”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “28_AGT_PWM_Output”, 然后将工程文件夹重命名为 “28_AGT_Pulse_Width_Measurement”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录下,进入到 “src/agt” 文件夹里面新建源文件和头文件: “bsp_agt_pulse_width_measurement.c” 和 “bsp_agt_pulse_width_measurement.h”。 工程文件结构如下。

文件结构
28_AGT_Pulse_Width_Measurement
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ agt
   │  ├─ bsp_agt_pwm_output.c
   │  ├─ bsp_agt_pwm_output.h
   │  ├─ bsp_agt_pulse_width_measurement.c
   │  └─ bsp_agt_pulse_width_measurement.h
   └─ hal_entry.c

29.6.2.2. FSP配置

接下来我们要以野火启明6M5开发板为例来说明进行FSP配置的方法,另外两块板子的配置步骤是一样的, 读者可根据实际使用的引脚参照下面的步骤来进行配置。

打开 FSP 配置界面,在前面实验2的基础上,本实验需要通过 IO 引脚输入脉冲信号, 因此首先配置AGT及其引脚,然后配置 Stacks。

首先在“Pins”配置页中配置 AGT3 的操作模式配置为“Count Measurement”, 并且为该 AGT 配置相应的引脚,也就是将 AGT 的 AGTIO3 信号连接到 P600 引脚,如下图所示。

图

接着在 Stacks 中加入第二个 AGT 模块实例,并按如下图所示配置该 AGT 模块的属性:

图
AGT属性描述:“Input”部分

属性

描述

Input > Measurement Mode

测量模式。选择 AGT 是否用于测量脉冲宽度或脉冲周期。

默认禁止测量,可选择测量低电平脉冲宽度、高电平脉冲宽度和脉冲周期。

Input > Input Filter

输入滤波器。

该滤波器要求从 AGTIO 引脚输入信号在指定的滤波器频率下连续3次读取时处于同一电平。

Input > Enable Pin

使能引脚。选择 AGTEE 引脚的有效边缘。仅在当P402、P403 或者 AGTIO作为时钟源的时候可用。

Input > Trigger Edge

选择触发边缘。不要同时选择触发边缘与脉冲周期。

29.6.2.3. AGT初始化函数

代码清单28-7:AGT初始化函数
 timer_info_t info;  //用于获取定时器参数信息
 uint32_t period;    //用于保存计数器的计数周期

 /* AGT初始化函数 */
 void AGT_Pulse_Width_Measurement_Init(void)
 {
     /* 初始化 AGT 模块 */
     R_AGT_Open(&g_timer_agt3_ctrl, &g_timer_agt3_cfg);

     /*获取当前参数*/
     (void) R_AGT_InfoGet(&g_timer_agt3_ctrl, &info);
     /* 获取计数周期:AGT的一个周期的计数次数 */
     period = info.period_counts;

     /* 使能 AGT 定时器 */
     R_AGT_Enable(&g_timer_agt3_ctrl);   //注:对于脉冲宽度/周期测量模式下,使用 R_AGT_Enable 或 R_AGT_Start 函数效果是一致的

     /* 启动 AGT 定时器 */
     //R_AGT_Start(&g_timer_agt3_ctrl);
 }

29.6.2.4. AGT脉冲宽度测量中断回调函数

代码清单28-8:AGT 脉冲宽度测量中断回调函数
 uint32_t pulse_width_time;          //测量的脉冲高电平宽度时间
 volatile uint8_t print_enble_flag;  //允许打印测量结果标志

 /* AGT 脉冲宽度测量中断回调函数 */
 void agt3_pulse_width_measurement_callback(timer_callback_args_t * p_args)
 {
     static uint32_t overflow_times = 0; //计数器溢出次数

     /* 测量完成事件 */
     if (TIMER_EVENT_CAPTURE_A == p_args->event)
     {
         pulse_width_time = p_args->capture + overflow_times * period; //记录时间 B
         overflow_times = 0;

         print_enble_flag = 1;   //测量完成后 允许打印测量结果
     }
     /* 定时器计数溢出事件 */
     else if (TIMER_EVENT_CYCLE_END == p_args->event)
     {
         /* 输入捕获期间计数器溢出,则记录溢出次数+1 */
         overflow_times++;
     }
 }

29.6.2.5. hal_entry入口函数

代码清单28-9:hal_entry入口函数
 /* 用户头文件包含 */
 #include "led/bsp_led.h"
 #include "debug_uart/bsp_debug_uart.h"
 #include "agt/bsp_agt_pwm_output.h"
 #include "agt/bsp_agt_pulse_width_measurement.h"

 // 外部变量声明
 extern timer_info_t info;  //用于获取定时器参数信息
 extern uint32_t pulse_width_time;   //PWM高电平的时间
 extern volatile uint8_t print_enble_flag;

 void hal_entry(void)
 {
     /* TODO: add your own code here */

     LED_Init();         // LED 初始化
     Debug_UART4_Init(); // SCI4 UART 调试串口初始化

     AGT_PWM_Init();                     // AGT PWM输出初始化
     AGT_Pulse_Width_Measurement_Init(); // AGT 脉冲宽度测量初始化

     printf("这是一个 AGT 的PWM输出 + 脉冲宽度测量功能实验\r\n");
     printf("使用杜邦线连接 P500 和 P600 引脚,然后打开串口助手查看串口的打印信息\r\n");


     while(1)
     {
         float pulse_width_time_us;

         if (print_enble_flag)
         {
             // 打印PWM高电平的计数
             printf("High=%d, ", pulse_width_time);
             /* 计算PWM高电平的时间 */
             pulse_width_time_us = (float)pulse_width_time / ((float)info.clock_frequency / (float)1000000);
             printf("Time=%f us\r\n", pulse_width_time_us);

             pulse_width_time = 0;   //测量结果打印完后旧数据清零
             print_enble_flag = 0;   //允许打印测量结果标志位清零
         }

         // LED1 闪烁指示程序正在运行...
         LED1_TOGGLE;
         // 间隔1s
         R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
     }


 #if BSP_TZ_SECURE_BUILD
     /* Enter non-secure code */
     R_BSP_NonSecureEnter();
 #endif
 }

29.6.3. 下载验证

编译并下载程序后,复位开发板使程序重新运行,然后使用杜邦线连接 P500P600 引脚, 然后打开串口助手查看串口的打印信息。 串口会打印出 PWM 信号的频率和占空比等信息,实验现象如下图所示。

图

通过计算,我们设置PWM输出波形的频率为 20 KHz(周期为 50 us)、占空比为 80%(正脉宽应为 40 us), 这与我们测量得到正脉宽一致。

29.7. 实验4:脉冲周期测量模式

29.7.1. 硬件设计

以野火启明6M5开发板例程为例, 本实验需要使用两个引脚,使用的 PWM 输出引脚为额外引出的 IO 引脚为:P500 ; 用于脉冲输入的测量引脚使用的开发板引出的 IO 引脚为: P600。如下图所示。

图

注:

  • 野火启明6M5开发板例程选用的 PWM 输出引脚为:P500 (AGTOA0);选用的测量引脚为:P600 (AGTIO3)。

  • 野火启明4M2开发板例程选用的 PWM 输出引脚为:P211 (AGTOA5);选用的测量引脚为:P415 (AGTIO4)。

  • 野火启明2L1开发板例程选用的 PWM 输出引脚为:P208 (AGTOB0);选用的测量引脚为:P402 (AGTIO1)。

  • 本次实验需要将 PWM 输出引脚与测量引脚使用杜邦线连接起来。

29.7.2. 软件设计

29.7.2.1. 新建工程

由于本实验需要用到 PWM 波形信号, 因此我们直接在前面的“实验2:比较匹配功能——PWM输出”例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “28_AGT_PWM_Output”, 然后将工程文件夹重命名为 “28_AGT_Pulse_Period_Measurement”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “28_AGT_PWM_Output”, 然后将工程文件夹重命名为 “28_AGT_Pulse_Period_Measurement”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录下,进入到 “src/agt” 文件夹里面新建源文件和头文件: “bsp_agt_pulse_period_measurement.c” 和 “bsp_agt_pulse_period_measurement.h”。 工程文件结构如下。

文件结构
28_AGT_Pulse_Period_Measurement
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ agt
   │  ├─ bsp_agt_pwm_output.c
   │  ├─ bsp_agt_pwm_output.h
   │  ├─ bsp_agt_pulse_period_measurement.c
   │  └─ bsp_agt_pulse_period_measurement.h
   └─ hal_entry.c

29.7.2.2. FSP配置

以野火启明6M5开发板为例, 打开 FSP 配置界面,在前面实验2的基础上,本实验需要通过 IO 引脚输入脉冲信号, 因此首先配置AGT及其引脚,然后配置 Stacks。

首先在“Pins”配置页中配置 AGT3 的操作模式配置为“Count Measurement”, 并且为该 AGT 配置相应的引脚,也就是将 AGT 的 AGTIO3 信号连接到 P600 引脚,如下图所示。

图

接着在 Stacks 中加入第二个 AGT 模块实例,并按如下图所示配置该 AGT 模块的属性:

图

29.7.2.3. AGT初始化函数

代码清单28-10:AGT初始化函数
 timer_info_t info;  //用于获取定时器参数信息
 uint32_t period;    //用于保存计数器的计数周期

 /* AGT初始化函数 */
 void AGT_Pulse_Period_Measurement_Init(void)
 {
     /* 初始化 AGT 模块 */
     R_AGT_Open(&g_timer_agt3_ctrl, &g_timer_agt3_cfg);

     /*获取当前参数*/
     (void) R_AGT_InfoGet(&g_timer_agt3_ctrl, &info);
     /* 获取计数周期:AGT的一个周期的计数次数 */
     period = info.period_counts;

     /* 使能 AGT 定时器 */
     R_AGT_Enable(&g_timer_agt3_ctrl);   //注:对于脉冲宽度/周期测量模式下,使用 R_AGT_Enable 或 R_AGT_Start 函数效果是一致的

     /* 启动 AGT 定时器 */
     //R_AGT_Start(&g_timer_agt3_ctrl);
 }

29.7.2.4. AGT脉冲周期测量中断回调函数

代码清单28-11:AGT 脉冲周期测量中断回调函数
 uint32_t pulse_period_time;         //测量的脉冲周期时间
 volatile uint8_t print_enble_flag;  //允许打印测量结果标志

 /* AGT 脉冲周期测量中断回调函数 */
 void agt3_pulse_period_measurement_callback(timer_callback_args_t * p_args)
 {
     static uint32_t overflow_times = 0; //计数器溢出次数

     /* 测量完成事件 */
     if (TIMER_EVENT_CAPTURE_A == p_args->event)
     {
         pulse_period_time = p_args->capture + overflow_times * period; //记录时间 B
         overflow_times = 0;

         print_enble_flag = 1;   //测量完成后 允许打印测量结果
     }
     /* 定时器计数溢出事件 */
     else if (TIMER_EVENT_CYCLE_END == p_args->event)
     {
         /* 输入捕获期间计数器溢出,则记录溢出次数+1 */
         overflow_times++;
     }
 }

29.7.2.5. hal_entry入口函数

代码清单28-12:hal_entry入口函数
 /* 用户头文件包含 */
 #include "led/bsp_led.h"
 #include "debug_uart/bsp_debug_uart.h"
 #include "agt/bsp_agt_pwm_output.h"
 #include "agt/bsp_agt_pulse_period_measurement.h"

 // 外部变量声明
 extern timer_info_t info;  //用于获取定时器参数信息
 extern uint32_t pulse_period_time;  //PWM周期的时间计数
 extern volatile uint8_t print_enble_flag;

 void hal_entry(void)
 {
     /* TODO: add your own code here */

     LED_Init();         // LED 初始化
     Debug_UART4_Init(); // SCI4 UART 调试串口初始化

     AGT_PWM_Init();                     // AGT PWM输出初始化
     AGT_Pulse_Period_Measurement_Init(); // AGT 脉冲周期测量初始化

     printf("这是一个 AGT 的PWM输出 + 脉冲周期测量功能实验\r\n");
     printf("使用杜邦线连接 P500 和 P600 引脚,然后打开串口助手查看串口的打印信息\r\n");


     while(1)
     {
         float pulse_period_time_us;

         if (print_enble_flag)
         {
             // 打印PWM脉冲周期测量的计数
             printf("Period=%d, ", pulse_period_time);
             /* 计算PWM脉冲周期测量的时间 */
             pulse_period_time_us = (float)pulse_period_time / ((float)info.clock_frequency / (float)1000000);
             printf("Time=%f us\r\n", pulse_period_time_us);

             pulse_period_time = 0;  //测量结果打印完后旧数据清零
             print_enble_flag = 0;   //允许打印测量结果标志位清零
         }

         // LED1 闪烁指示程序正在运行...
         LED1_TOGGLE;
         // 间隔1s
         R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
     }


 #if BSP_TZ_SECURE_BUILD
     /* Enter non-secure code */
     R_BSP_NonSecureEnter();
 #endif
 }

29.7.3. 下载验证

以野火启明6M5开发板为例, 编译并下载程序后,复位开发板使程序重新运行,然后使用杜邦线连接 P500P600 引脚, 然后打开串口助手查看串口的打印信息。 串口会打印出 PWM 信号的频率和占空比等信息,实验现象如下图所示。

图

通过计算,我们设置PWM输出波形的频率为 20 KHz,周期为 50 us, 这与我们测量得到波形周期一致。