29. AGT——低功耗定时器¶
RA MCU 有两种定时器外设:GPT(General PWM Timer)定时器和 AGT(Asynchronous General Purpose Timer)定时器。 在它们之间进行选择时,需要考虑以下因素:
考虑因素 |
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 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.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.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 模块配置属性描述如下:
属性 |
描述 |
---|---|
Parameter Checking |
参数检查(选择是否生成包含参数检查的代码)。 |
Pin Output Support |
引脚输出使能。“Enabled”表示使能输出信号到引脚。 |
Pin Input Support |
引脚输入使能。“Enabled”表示使能引脚输入,用于脉宽测量和周期测量模式, 或者用于从 P402、P403、P404 或者 AGTIO 这些引脚输入外部信号。 |
属性 |
描述 |
---|---|
General > Name |
模块名字。 |
General > Channel |
通道选择。指定硬件通道,也就是指定使用哪一个AGT定时器。 |
General > Mode |
可以选择周期计数、单次计数、PWM模式。
|
General > Period |
周期。 |
General > Period Unit |
周期的单位。 |
General > Count Source |
AGT 计数器的时钟源。注:分频器会基于用户设置的周期由软件自动调节。 |
属性 |
描述 |
---|---|
Interrupts > Callback |
设置中断回调函数。 |
Interrupts > Underflow Interrupt Priority |
定时器下溢中断优先级。 |
29.4.2.3. 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中断回调函数¶
/* 定时器溢出 中断回调函数 */
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”。
/* 用户头文件包含 */
#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”部分属性描述如下表所示。
属性 |
描述 |
---|---|
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初始化函数¶
/* 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占空比函数¶
/** 自定义函数:设置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入口函数¶
/* 用户头文件包含 */
#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 模块的属性:
属性 |
描述 |
---|---|
Input > Measurement Mode |
测量模式。选择 AGT 是否用于测量脉冲宽度或脉冲周期。 默认禁止测量,可选择测量低电平脉冲宽度、高电平脉冲宽度和脉冲周期。 |
Input > Input Filter |
输入滤波器。 该滤波器要求从 AGTIO 引脚输入信号在指定的滤波器频率下连续3次读取时处于同一电平。 |
Input > Enable Pin |
使能引脚。选择 AGTEE 引脚的有效边缘。仅在当P402、P403 或者 AGTIO作为时钟源的时候可用。 |
Input > Trigger Edge |
选择触发边缘。不要同时选择触发边缘与脉冲周期。 |
29.6.2.3. 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脉冲宽度测量中断回调函数¶
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入口函数¶
/* 用户头文件包含 */
#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. 下载验证¶
编译并下载程序后,复位开发板使程序重新运行,然后使用杜邦线连接 P500 和 P600 引脚, 然后打开串口助手查看串口的打印信息。 串口会打印出 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初始化函数¶
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脉冲周期测量中断回调函数¶
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入口函数¶
/* 用户头文件包含 */
#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开发板为例, 编译并下载程序后,复位开发板使程序重新运行,然后使用杜邦线连接 P500 和 P600 引脚, 然后打开串口助手查看串口的打印信息。 串口会打印出 PWM 信号的频率和占空比等信息,实验现象如下图所示。
通过计算,我们设置PWM输出波形的频率为 20 KHz,周期为 50 us, 这与我们测量得到波形周期一致。