目录
概述
1 General PWM介绍
1.1 特性
1.2 定时器选择注意点
2 时钟配置
3 应用案例
3.1 基本定时器应用
3.2 定时器回调函数案例
3.3 输入捕捉功能案例
3.4 更新周期案例
3.5 更新占空比案例
3.6 单次触发脉冲案例
4 测试
4.1 代码介绍
4.2 验证
概述
本文主要介绍Renesas R7FA8D1BH (Cortex®-M85)的 General PWM各种模式的使用方法,包括PWM的特性介绍,并编写多个案例,详细介绍了各种模式下API函数的使用方法,还编写一个具体的案例,实现PWM的输出,并使用逻辑分析仪捕捉器波形。
1 General PWM介绍
1.1 特性
GPT模块可用于计数事件,测量外部输入信号,产生周期性中断,或输出周期性或PWM信号到GTIOC引脚。该模块支持GPT外设GPT32EH、GPT32E、GPT32和GPT16。GPT16是一个16位定时器。其他外设(GPT32EH、GPT32E和GPT32)是32位定时器。从API的角度来看,这个模块中的32位计时器都是一样的。
GPT模块具有以下特点:
1)支持周期模式、单次模式和PWM模式。
2)支持计数源的PCLK, gtegg引脚,GTIOC引脚,或ELC事件。
3)支持GTIOC引脚上的脱波滤波器。
4)信号可以输出到一个引脚。
5)可配置周期(每个计时器周期的计数)。
6)可配置占空比PWM模式。
7)支持运行时周期的重新配置。
8)支持在PWM模式下运行时重构占空比。
9)支持运行时比较匹配值的重新配置。
10)提供了用于启动、停止和重置计数器的api。
11)提供api来获取当前周期、源时钟频率和计数方向。
12)提供api来获取当前定时器状态和计数器值。
13)支持启动、停止、清除、向上计数、向下计数,并通过外部源从gtegg引脚、GTIOC引脚或ELC事件捕获。
14)支持对称和非对称PWM波形生成。
15)支持一次同步脉冲波形生成。
16)支持自动添加死区时间。
17)支持生成ELC事件,以比较匹配值启动ADC扫描(参见事件链接控制器(r_elc))并更新比较匹配值。
18)支持与POEG通道连接,当检测到错误条件时自动禁用GPT输出。
19)支持定时器停止时设置计数器值。
20)支持启用和禁用输出引脚。
21)支持一次跳过最多七个溢出/下流(波峰/波谷)中断
22)支持通过配置引脚的输出电平在每个比较匹配和周期结束产生自定义PWM波形。
1.2 定时器选择注意点
RA mcu有两个定时器外设:通用PWM定时器(GPT)和异步通用定时器(AGT)。在他们之间进行选择时,要考虑以下因素:
| GPT | AGT | |
|---|---|---|
| Low Power Modes | The GPT can operate in sleep mode. | The AGT can operate in all low power modes. | 
| Available Channels | The number of GPT channels is device specific. All currently supported MCUs have at least 7 GPT channels. | All MCUs have 2 AGT channels. | 
| Timer Resolution | All MCUs have at least one 32-bit GPT timer. | The AGT timers are 16-bit timers. | 
| Clock Source | The GPT runs off PCLKD with a configurable divider up to 1024. It can also be configured to count ELC events or external pulses. | The AGT runs off PCLKB, LOCO, or subclock. | 
2 时钟配置
GPT时钟基于PCLKD频率。您可以使用RA Configuration编辑器的Clocks选项卡或在运行时使用CGC接口来设置PCLKD频率。
该模块可以使用GTETRGA、GTETRGB、GTETRGC、GTETRGD、GTIOCA和GTIOCB引脚作为计数源。该模块可以使用GTIOCA和GTIOCB引脚作为周期性或PWM信号的输出引脚。该模块可以使用GTIOCA和GTIOCB作为输入引脚来测量输入信号。

3 应用案例
3.1 基本定时器应用
这是在应用程序中最少使用GPT的一个基本示例。
void gpt_basic_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Start the timer. */
    (void) R_GPT_Start(&g_timer0_ctrl);
} 
3.2 定时器回调函数案例
定时器回调函数的应用案例,范例代码如下:
/* Example callback called when timer expires. */
void timer_callback (timer_callback_args_t * p_args)
{
    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {
        /* Add application code to be called periodically here. */
    }
}
 
3.3 输入捕捉功能案例
这是一个使用GPT捕获脉冲宽度或脉冲周期测量的例子。
/* Example callback called when a capture occurs. */
uint64_t g_captured_time     = 0U;
uint32_t g_capture_overflows = 0U;
void timer_capture_callback (timer_callback_args_t * p_args)
{
    if ((TIMER_EVENT_CAPTURE_A == p_args->event) || (TIMER_EVENT_CAPTURE_B == p_args->event))
    {
        /* (Optional) Get the current period if not known. */
        timer_info_t info;
        (void) R_GPT_InfoGet(&g_timer0_ctrl, &info);
        uint64_t period = info.period_counts;
        /* The maximum period is one more than the maximum 32-bit number, but will be reflected as 0 in
         * timer_info_t::period_counts. */
        if (0U == period)
        {
            period = UINT32_MAX + 1U;
        }
        g_captured_time     = (period * g_capture_overflows) + p_args->capture;
        g_capture_overflows = 0U;
    }
    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {
        /* An overflow occurred during capture. This must be accounted for at the application layer. */
        g_capture_overflows++;
    }
}
void gpt_capture_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Enable captures. Captured values arrive in the interrupt. */
    (void) R_GPT_Enable(&g_timer0_ctrl);
    /* (Optional) Disable captures. */
    (void) R_GPT_Disable(&g_timer0_ctrl);
} 
3.4 更新周期案例
#define GPT_EXAMPLE_MSEC_PER_SEC           (1000)
#define GPT_EXAMPLE_DESIRED_PERIOD_MSEC    (20)
/* This example shows how to calculate a new period value at runtime. */
void gpt_period_calculation_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Start the timer. */
    (void) R_GPT_Start(&g_timer0_ctrl);
    /* Get the source clock frequency (in Hz). There are 3 ways to do this in FSP:
     *  - If the PCLKD frequency has not changed since reset, the source clock frequency is
     *    BSP_STARTUP_PCLKD_HZ >> timer_cfg_t::source_div
     *  - Use the R_GPT_InfoGet function (it accounts for the divider).
     *  - Calculate the current PCLKD frequency using R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKD) and right shift
     *    by timer_cfg_t::source_div.
     *
     * This example uses the 3rd option (R_FSP_SystemClockHzGet).
     */
    uint32_t pclkd_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKD) >> g_timer0_cfg.source_div;
    /* Calculate the desired period based on the current clock. Note that this calculation could overflow if the
     * desired period is larger than UINT32_MAX / pclkd_freq_hz. A cast to uint64_t is used to prevent this. */
    uint32_t period_counts =
        (uint32_t) (((uint64_t) pclkd_freq_hz * GPT_EXAMPLE_DESIRED_PERIOD_MSEC) / GPT_EXAMPLE_MSEC_PER_SEC);
    /* Set the calculated period. */
    err = R_GPT_PeriodSet(&g_timer0_ctrl, period_counts);
    assert(FSP_SUCCESS == err);
} 
3.5 更新占空比案例
#define GPT_EXAMPLE_DESIRED_DUTY_CYCLE_PERCENT    (25)
#define GPT_EXAMPLE_MAX_PERCENT                   (100)
/* This example shows how to calculate a new duty cycle value at runtime. */
void gpt_duty_cycle_calculation_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Start the timer. */
    (void) R_GPT_Start(&g_timer0_ctrl);
    /* Get the current period setting. */
    timer_info_t info;
    (void) R_GPT_InfoGet(&g_timer0_ctrl, &info);
    uint32_t current_period_counts = info.period_counts;
    /* Calculate the desired duty cycle based on the current period. Note that if the period could be larger than
     * UINT32_MAX / 100, this calculation could overflow. A cast to uint64_t is used to prevent this. The cast is
     * not required for 16-bit timers. */
    uint32_t duty_cycle_counts =
        (uint32_t) (((uint64_t) current_period_counts * GPT_EXAMPLE_DESIRED_DUTY_CYCLE_PERCENT) /
                    GPT_EXAMPLE_MAX_PERCENT);
    /* Set the calculated duty cycle. */
    err = R_GPT_DutyCycleSet(&g_timer0_ctrl, duty_cycle_counts, GPT_IO_PIN_GTIOCB);
    assert(FSP_SUCCESS == err);
} 
3.6 单次触发脉冲案例
/* Example callback called when timer overflows. */
void gpt_overflow_callback (timer_callback_args_t * p_args)
{
    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {
        /* Use R_GPT_DutyCycleSet() API to set new values for each cycle.
         * - Use GPT_IO_PIN_ONE_SHOT_LEADING_EDGE to set the leading edge transition match value (GTCCRC or GTCCRE register).
         * - Use GPT_IO_PIN_ONE_SHOT_TRAILING_EDGE to set the trailing edge transition match value (GTCCRD or GTCCRF register).
         */
    }
}
#define GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRD_GTCCRF    (0x1000U)
#define GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRC           (0x800U)
#define GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRE           (0x100U)
void gpt_one_shot_pulse_mode_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Set the duty cycle for One-Shot-Pulse mode */
    /* GPT_IO_PIN_ONE_SHOT_LEADING_EDGE sets the initial value for the GTCCRC register if GTIOCnA is the selected pin for output. */
    err = R_GPT_DutyCycleSet(&g_timer0_ctrl,
                             GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRC,
                             GPT_IO_PIN_GTIOCA | GPT_IO_PIN_ONE_SHOT_LEADING_EDGE);
    assert(FSP_SUCCESS == err);
    /* GPT_IO_PIN_ONE_SHOT_LEADING_EDGE sets the initial value for the GTCCRE register if GTIOCnB is the selected pin for output. */
    err = R_GPT_DutyCycleSet(&g_timer0_ctrl,
                             GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRE,
                             GPT_IO_PIN_GTIOCB | GPT_IO_PIN_ONE_SHOT_LEADING_EDGE);
    assert(FSP_SUCCESS == err);
    /* GPT_IO_PIN_ONE_SHOT_TRAILING_EDGE sets the initial value for the GTCCRD register if GTIOCnA is the selected pin for output. */
    err = R_GPT_DutyCycleSet(&g_timer0_ctrl,
                             GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRD_GTCCRF,
                             GPT_IO_PIN_GTIOCA | GPT_IO_PIN_ONE_SHOT_TRAILING_EDGE);
    assert(FSP_SUCCESS == err);
    /* GPT_IO_PIN_ONE_SHOT_TRAILING_EDGE sets the initial value for the GTCCRF register if GTIOCnB is the selected pin for output.
     * GPT_BUFFER_FORCE_PUSH pushes set values to temporary registers to prepare for the initial output cycle. This must be done
     * when setting the duty cycle before starting the timer. */
    err = R_GPT_DutyCycleSet(&g_timer0_ctrl,
                             GPT_ONE_SHOT_EXAMPLE_DUTY_CYCLE_GTCCRD_GTCCRF,
                             GPT_IO_PIN_GTIOCB | GPT_IO_PIN_ONE_SHOT_TRAILING_EDGE | GPT_BUFFER_FORCE_PUSH);
    assert(FSP_SUCCESS == err);
    /* Start the timer. */
    (void) R_GPT_Start(&g_timer0_ctrl);
    /* (Optional) Stop the timer. */
    (void) R_GPT_Stop(&g_timer0_ctrl);
}
GPT Compare Match Set Example
This example demonstrates the configuration and use of compare match with GPT timer.
/* Example callback called when compare match occurs. */
void gpt_compare_match_callback (timer_callback_args_t * p_args)
{
    if (TIMER_EVENT_COMPARE_A == p_args->event)
    {
        /* Add application code to be called periodically here. */
    }
}
#define GPT_COMPARE_MATCH_EXAMPLE_VALUE    (0x2000U)
void gpt_compare_match_set_example (void)
{
    fsp_err_t err = FSP_SUCCESS;
    /* Initializes the module. */
    err = R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);
    /* Set the compare match value (GPT_COMPARE_MATCH_EXAMPLE_VALUE). This value must be less than or equal to period value. */
    err = R_GPT_CompareMatchSet(&g_timer0_ctrl, GPT_COMPARE_MATCH_EXAMPLE_VALUE, TIMER_COMPARE_MATCH_A);
    assert(FSP_SUCCESS == err);
    /* Start the timer. */
    (void) R_GPT_Start(&g_timer0_ctrl);
    /* (Optional) Stop the timer. */
    (void) R_GPT_Stop(&g_timer0_ctrl);
}
 
4 测试
4.1 代码介绍
编写一个Demo实现4路PWM信号输出,其详细代码如下:
 /*
 FILE NAME  :  bsp_pwm.c
 Description:  pwm interface
 Author     :  tangmingfei2013@126.com
 Date       :  2024/06/03
 */
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "bsp_pwm.h"
#include "hal_data.h"
void GPT_PWM_SetDuty3(uint8_t duty, uint32_t pin);
void GPT_PWM_SetDuty2(uint8_t duty, uint32_t pin);
void GPT_PWM_SetDuty1(uint8_t duty, uint32_t pin);
void pwm_init( void )
{
    // timer 1   GPT_IO_PIN_GTIOCA
    R_GPT_Open(&g_timer1_ctrl, &g_timer1_cfg);
    R_GPT_Start(&g_timer1_ctrl);
   /* Set the calculated duty cycle. */
    GPT_PWM_SetDuty1(60, GPT_IO_PIN_GTIOCA);
    
    // timer 2   GPT_IO_PIN_GTIOCB
    R_GPT_Open(&g_timer2_ctrl, &g_timer2_cfg);
    R_GPT_Start(&g_timer2_ctrl);
    // set duty 
    GPT_PWM_SetDuty2(80, GPT_IO_PIN_GTIOCB); 
    
    // timer 6   GPT_IO_PIN_GTIOCA & GPT_IO_PIN_GTIOCB
    R_GPT_Open(&g_timer3_ctrl, &g_timer3_cfg);
    R_GPT_Start(&g_timer3_ctrl);
    // set duty 
    GPT_PWM_SetDuty3( 50, GPT_IO_PIN_GTIOCA);
    // set duty 
    GPT_PWM_SetDuty3( 20, GPT_IO_PIN_GTIOCB);
}
void GPT_PWM_SetDuty3(uint8_t duty, uint32_t pin)
{
    fsp_err_t  err;
    timer_info_t info;
    uint32_t current_period_counts;
    uint32_t duty_cycle_counts;
 
    if (duty > 100)
        duty = 100; 
 
    R_GPT_InfoGet(&g_timer3_ctrl, &info);
    
    current_period_counts = info.period_counts;
    
    duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);
 
 
    err = R_GPT_DutyCycleSet(&g_timer3_ctrl, duty_cycle_counts, pin);
    assert(FSP_SUCCESS == err);
}
void GPT_PWM_SetDuty2(uint8_t duty, uint32_t pin)
{
    fsp_err_t  err;
    timer_info_t info;
    uint32_t current_period_counts;
    uint32_t duty_cycle_counts;
 
    if (duty > 100)
        duty = 100; 
 
    R_GPT_InfoGet(&g_timer2_ctrl, &info);
    
    current_period_counts = info.period_counts;
    
    duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);
 
 
    err = R_GPT_DutyCycleSet(&g_timer2_ctrl, duty_cycle_counts, pin);
    assert(FSP_SUCCESS == err);
}
void GPT_PWM_SetDuty1(uint8_t duty, uint32_t pin)
{
    fsp_err_t  err;
    timer_info_t info;
    uint32_t current_period_counts;
    uint32_t duty_cycle_counts;
 
    if (duty > 100)
        duty = 100; 
 
    R_GPT_InfoGet(&g_timer1_ctrl, &info);
    
    current_period_counts = info.period_counts;
    
    duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);
 
 
    err = R_GPT_DutyCycleSet(&g_timer1_ctrl, duty_cycle_counts, pin);
    assert(FSP_SUCCESS == err);
}
/* End of this file */
 
4.2 验证
编译和下载代码到板卡中,运行结果如下:




















