在RA4M2的系统重,提供了三种定时器,从手册之中可以了解:
三种定时器分别是:
- 普通PWM 32位定时器:GPT32
- 普通PWM 16位定时器:GPT16
- 低功耗用用异步定时器:AGT
其中普通GPT比较常用,主要是用于生成PWM波形,从而控制例如无刷电机等外部设备。当然,也可以不进行输出,直接当做定时器使用,在定时回调中,进行我们需要的处理。
从手册上可以了解这些定时器的具体功能规范:
下面,就以点灯的实例,来展示GPT的基础使用。
一、FPS设置
要启用GPT,首先就需要在FPS种进行设置。
关于LED部分的设置,就不重新说了,这个是基础。
GPT设置步骤如下:
上述步骤,就是先添加一个GPT的Stack,然后设置其初始化参数和回调参数:
初始化参数:
-
Name: 名称
-
Channel: 通道
-
Mode: 模式,选择周期性
-
Period: 周期位1
-
Period Unit: 周期的单位位秒
也就是定时器1秒触发一次
周期单位可以详细设置,具体如下:
在bsp_delay.h中,也有类似的定义:
typedef enum
{
BSP_DELAY_UNITS_SECONDS = 1000000, ///< Requested delay amount is in seconds
BSP_DELAY_UNITS_MILLISECONDS = 1000, ///< Requested delay amount is in milliseconds
BSP_DELAY_UNITS_MICROSECONDS = 1 ///< Requested delay amount is in microseconds
} bsp_delay_units_t;
回调参数:
-
Callback:回调函数名称,定时器触发时,会调用该函数,在其中进行具体的操作处理
-
Overflow/Crest Interrupts Priority:定时器的中断登记,用10就可以
其他的先不用管,默认即可。
设置好,生成代码以后,就转入代码部分的操作了。
在代码部分,我们需要添加两个调用,一个是GPT初始化,一个是回调函数,具体如下:
static bool status = false;
void GPT_Timing_Init(void)
{
R_GPT_Open(&g_timer_gpt0_ctrl, &g_timer_gpt0_cfg);
R_GPT_Start(&g_timer_gpt0_ctrl);
}
void gpt0_timing_callback(timer_callback_args_t * p_args)
{
if (TIMER_EVENT_CYCLE_END == p_args->event)
{
status = ! status;
if(status) {
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_15, BSP_IO_LEVEL_HIGH);
} else {
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_15, BSP_IO_LEVEL_LOW);
}
}
}
在上述代码终,GPT_Timing_Init()负责初始化,因为我们只用了gpt0,所以只需要初始化gpt0即可。
gpt0对应的控制变量,以及回调函数的预定义,在hal_data.h终有定义,具体如下:
extern gpt_instance_ctrl_t g_timer_gpt0_ctrl;
extern const timer_cfg_t g_timer_gpt0_cfg;
#ifndef gpt0_timing_callback
void gpt0_timing_callback(timer_callback_args_t * p_args);
#endif
在hal_data.c中,有关于g_timer_gpt0_ctrl和g_timer_gpt0_cfg的具体定义:
const timer_cfg_t g_timer_gpt0_cfg =
{
.mode = TIMER_MODE_PERIODIC,
.period_counts = (uint32_t) 0x5f5e100,
.duty_cycle_counts = 0x2faf080,
.source_div = (timer_source_div_t)0,
.channel = 0,
.p_callback = gpt0_timing_callback,
#if defined(NULL)
.p_context = NULL,
#else
.p_context = &NULL,
#endif
.p_extend = &g_timer_gpt0_extend,
.cycle_end_ipl = (10),
#if defined(VECTOR_NUMBER_GPT0_COUNTER_OVERFLOW)
.cycle_end_irq = VECTOR_NUMBER_GPT0_COUNTER_OVERFLOW,
#else
.cycle_end_irq = FSP_INVALID_VECTOR,
#endif
};
const timer_instance_t g_timer_gpt0 =
{
.p_ctrl = &g_timer_gpt0_ctrl,
.p_cfg = &g_timer_gpt0_cfg,
.p_api = &g_timer_on_gpt
};
在前面代码终,还添加了一个status变量,用于表示LED的状态,true则电量LED1,false则熄灭LED1。其中,BSP_IO_PORT_04_PIN_15对应LED1的引脚。
然后,在hal_entry()终调用初始化,就能够启动定时器了,具体如下:
void hal_entry(void)
{
fsp_err_t err;
err = R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
assert(FSP_SUCCESS == err);
GPT_Timing_Init();
while(1)
{ R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
}
#if BSP_TZ_SECURE_BUILD
R_BSP_NonSecureEnter();
#endif
}
使用GPT_Timing_Init()进行初始化后,定时器就会启动,并按照预定的周期,调用gpt0_timing_callback()。
代码终的R_BSP_SoftwareDelay()表示演示,以免while(1)疯狂运行。
编写好代码之后,编译下载到开发板上,就可以看到LED1,每秒闪烁一次了。
在前面的gpt cfg定义中,有两个很重要,但设置为1秒的时候,对应的值为:
.period_counts = (uint32_t) 0x5f5e100,
.duty_cycle_counts = 0x2faf080,
当设置为100毫秒的时候,对应的值为:
.period_counts = (uint32_t) 0x989680,
.duty_cycle_counts = 0x4c4b40,
在上述配置中,period_counts表示定时器一个周期的计数次数,达到计数次数,则调用一次回调函数。
duty_cycle_counts表示占空比的技术次数,上述两个值,都为period_counts的一半,所以如果是PWM输出,则其对应的占空比为50%。
如果使用0.1seconds的配置,然后编译烧录,就可以看到LED快速闪烁了。
如果将1秒的period_counts除以0.1秒的period_counts,可以得到倍差值:0x5f5e100/0x989680 = 0xa,也就是10倍。
明白了这一点,那我们想要及时修改定时器的定时周期测试的时候,就不用再去FSP中操作了。等调整了定时器,达到实际需要了,再到FSP中进行一次设置,使得配置能够写入到xml配置文件中。