3. 瑞萨RA系列定时器详解¶
3.1. 定时器与电机的关系¶
对于电机而言用什么去控制至关重要,具体的控制方法这与电机的内部构造和原理有关;
一般电机控制可以分成两种:电压控制和电流控制;小的时候应该都玩过四驱车并且拆过,四驱车里面的小马达是上电就可以转的, 那么什么情况可以使得这个小马达转速变快或者变慢呢?答案很简单,两节干电池串联接在一起与一节干电池相比,一定是两节干电池的 马达转速快;还有一节干电池,满电与没电时的马达的转速差距也是很大的,这就是典型的 电压控制,通过改变电压的大小来改变电机的特性; 四驱车中的电机是直流电机,拆过小电机的都知道电机外壳的内测有两块磁铁,那么如果说我可以控制两块磁铁的磁性是不是也能控制电机转动呢? 还记得小时候做过的将一根铁钉外缠慢带有绝缘皮的铜线,通电后可以吸引起缝衣针的实验么,这就是典型的利用电流流向产生磁性,如果电机定子两侧 换成这种结构那就可以通过控制电流来控制旋转了。这种就是步进电机的大致原理(具体在步进电机基础章节会详细介绍),也是典型的 电流控制。
那么怎么样才能更好的控制电压和电流呢?
手动控制?数字电路控制?貌似都不是一个有效并且高效的办法,最有效且性价比较高的就是MCU控制了,也就是单片机控制。单片机具有定时器这一外设, 其实最主要的就是利用MCU可以精准定时计时这一特性,采用瑞萨专门为电机控制而设计的MCU,不仅定时器功能丰富,并且具有电机控制计算的硬件加速器,12位ADC/DAC通道, 能够更好的满足在电机控制方面的要求。
驱动电机需要定时器输出的信号满足电机的最小频率或者最小电压值,但是MCU功能再强也是不能够直接驱动电机的,需要使用驱动器来驱动,关系图如下:
瑞萨RA主要是利用通用定时器来输出控制信号来控制驱动器,然后由驱动器将信号放大或转换后驱动电机。所以说如何配置定时器就显得尤为重要了, 接下来的章节将着重讲解定时器相关内容,为之后的提高做好基础。
3.2. GPT-通用定时器¶
3.2.1. GPT简介¶
通用 PWM 定时器(GPT,General PWM Timer)是 RA MCU 的其中一种 32/16 位的定时器外设。 在 GPT 当中,可分为 GPT32 和 GPT16,它们最主要的区别是计数器的不同。 GPT32 是 32 位的定时器,包含的计数器是 32 位的,所能计数的范围为:0 ~ 0xFFFF_FFFF; 而 GPT16 是 16 位的定时器,包含的计数器是 16 位的,所能计数的范围为:0 ~ 0xFFFF。
定时器(Timer)最基本的功能就是定时,比如定时发送串口数据、定时采集AD数据、定时触发中断处理其它事务等等。 如果把定时器与 GPIO 引脚结合起来使用的话可以实现更加丰富的功能, 可以对输入信号进行计数,可以测量输入信号的脉冲宽度,可以输出单个脉冲、PWM 等波形,等等。 通过定时器生成 PWM 波形信号来控制电机状态是工业控制的普遍方法,这方面知识非常值得深入了解。
GPT 模块可用于计数事件、测量外部输入信号、作为通用计时器并产生周期性中断、以及输出周期性或 PWM 信号到 GTIOC 引脚。 GPT 也可用于输出单个脉冲,但是注意这是通过软件来实现的,GPT 硬件本身不支持输出单个脉冲(One-Shot)功能。 当使用单个脉冲(One-Shot)模式时,必须要开启中断,计时器需要在脉冲周期结束后在 ISR 中断服务函数中被停止。
下面是野火启明系列的4T1与6T2的GPT通道情况:
野火启明4T1开发板板载的 RA4T1 芯片拥有共 6 个GPT定时器(即6个通道), 其中包括 6 个16位GPT定时器(GPT32n (n=0~5))。
野火启明6T2开发板板载的 RA6T2 芯片拥有共 10 个GPT定时器(即10个通道), 其中包括 10 个32位GPT定时器(GPT32n (n=0~9))。
RA4T1 的 GPT 定时器通道与模块名如下图所示:
RA6T2 的 GPT 定时器通道与模块名如下图所示:
以 RA6T2 为例,GPT 定时器的详细功能参数如下表所示:
参数 |
描述 |
---|---|
功能 |
|
计数时钟源 |
|
中断源(9个源) |
|
中断跳跃功能 |
|
以 RA6T2 为例,GPT 的 I/O 引脚及其功能用途如下表所示:
通道 |
引脚名称 |
I/O 方向 |
功能 |
---|---|---|---|
共用 |
GTETRGx |
Input |
外部触发输入引脚 x (input through the POEG)x: A ~ D |
GTADSM0 |
Output |
AD转换开始请求监视器0输出引脚 |
|
GTADSM1 |
Output |
AD转换开始请求监视器1输出引脚 |
|
GPT32n n: 0 ~ 9 |
GTIOCnA |
Input/Output |
GPT 的输入输出引脚 A |
GTIOCnB |
Input/Output |
GPT 的输入输出引脚 B |
|
GTIOCPPOk k:0~4,7 |
Output |
与PWM周期同步切换输出 |
|
GPT_OPS |
GTIU |
Input |
霍尔传感器 U 相输入 |
GTIV |
Input |
霍尔传感器 V 相输入 |
|
GTIW |
Input |
霍尔传感器 W 相输入 |
|
GTOUUP |
Output |
BLDC 电机控制三相PWM输出(U相上桥臂) |
|
GTOULO |
Output |
BLDC 电机控制三相PWM输出(U相下桥臂) |
|
GTOUUP |
Output |
BLDC 电机控制三相PWM输出(V相上桥臂) |
|
GTOULO |
Output |
BLDC 电机控制三相PWM输出(V相下桥臂) |
|
GTOUUP |
Output |
BLDC 电机控制三相PWM输出(W相上桥臂) |
|
GTOULO |
Output |
BLDC 电机控制三相PWM输出(W相下桥臂) |
3.2.2. GPT功能框图¶
以 RA6T2 芯片 为例,GPT 外设模块的结构框图如下:
3.2.2.1. 计数器¶
见图中标注 1 处。
GTCNT 是 GPT 定时器模块内部的计数器,实际上,计数器是实现定时器外设的各种功能的基础。 因此,了解计数器的规格和功能非常重要。
对于 RA6T2,共有 10 个 GPT 定时器(GPT0~9),计数器为 32bit,在上图中用 GPT320~GPT329 来表示.
GPT 的计数器支持递增计数,递减计数和递增/递减计数(即递增与递减计数轮流进行)。
3.2.2.2. 时钟输入¶
见图中标注 2 处。
GPT 定时器的时钟输入可以选择内部的 PCLKD 分频后输入或者选择通过 GTETRGn 引脚输入外部时钟。 这两类中只能选择一个,若选择外部时钟输入则定时器不能对内部时钟输入进行计数。
GTCLK / n(n = 1/2/4/8/16/32/64/256/1024)
GTETRGA, GTETRGB, GTETRGC, GTETRGD(外部触发通过 POEG)
注:PCLKD / 1 表示的是不分频。
3.2.2.3. 周期设置和周期设置缓冲寄存器¶
见图中标注 3 处。
GTPR 周期设置寄存器是一个可读写的寄存器,用户可通过该寄存器设置计数器 GTCNT 的最大计数值,计数超过该值就会溢出,因此该值决定了计数器的计数周期。 GTPR 的有效大小与 GTCNT(16位或32位)相同。如果 GTPR 的有效大小为16位,则用户读取其高16位始终为0,并且其高16位的写入作会被忽略。
GTPBR 周期设置缓冲寄存器同样也是一个可读写的寄存器,用作 GTPR 的缓冲寄存器,GPT 计数器每计数溢出一次,就会将 GTPBR 的值写入 GTPR。 GTPBR 的有效大小也与 GTCNT(16位或32位)相同。如果 GTPBR 的有效大小为16位,则用户读取其高16位始终为0,并且其高16位的写入作会被忽略。
3.2.2.4. 控制寄存器¶
见图中标注 4 处。
这部分的寄存器较多、也较复杂,它们都属于用于控制定时器功能的寄存器。 而我们主要关注的是定时器的实际功能,对其过于深入的实现细节不做过多研究,因此这里不对这些寄存器进行详细讲解。
3.2.2.5. 比较器和比较/输入捕获寄存器¶
见图中标注 5 处。
这部分包含一个比较器和6个 GTCCRx 寄存器(x = A,B,C,D,E,F)。 这6个 GTCCRx 寄存器的功能并不完全相同:
GTCCRA 和 GTCCRB 是用于输出比较和输入捕捉的寄存器。
GTCCRC 和 GTCCRE 可用作比较匹配寄存器,也可以分别作为 GTCCRA 和 GTCCRB 的缓冲寄存器 (构成 GTCCRA 和 GTCCRB 的单缓冲寄存器)。
GTCCRD 和 GTCCRF 可用作比较匹配寄存器,也可以分别作为 GTCCRC 和 GTCCRE 的缓冲寄存器 (构成 GTCCRA 和 GTCCRB 的双缓冲寄存器:即 GTCCRC 和 GTCCRD 作为 GTCCRA 的双缓冲,GTCCRE 和 GTCCRF 作为 GTCCRB 的双缓冲)。
比如,在普通的 PWM 输出模式下,比较器将计数器 GTCNT 与 GTCCRA 和 GTCCRB 进行比较,若匹配(比较结果相等), 则根据 GTIOR(General PWM Timer I/O Control Register)寄存器的 GTIOA[4:0] 和 GTIOB[4:0] 的配置来切换 GTIOCA 和 GTIOCB 的输出电平。 可以将 GTIOCA 和 GTIOCB 切换为低电平、高电平、或者反转电平。
输入捕获与输出比较功能将在稍后详细介绍。
3.2.2.6. 中断请求信号¶
见图中标注 6 处。
图中显示的是 GPT0 的中断请求信号,中断请求信号用于产生中断、或者通过 ELC 链接到其他模块。
GPT 提供以下中断源:
GTCCR 输入捕捉/比较匹配
GTADTR 比较匹配
GTCNT 计数器上溢(超出 GTPR 设置的值)/下溢
周期计数功能完成
每个中断源都有自己的状态标志。 当一个中断源信号产生时,相关的状态标志会被硬件自动设置为 1。 状态标志可以通过写入 0 来清除。需要注意的是,如果标志设置和标志清除同时发生,标志清除优先于标志设置。
3.2.2.7. IO输入输出引脚¶
见图中标注 7 处。
GTIOCnA 和 GTIOCnB 是 GPT 的 IO 输入输出引脚,用于信号输出和输入捕获。 它们还配备了噪声滤波器(Noise Filter),噪声滤波器以采样时钟对输入信号进行采样,并去除长度小于3个采样周期的脉冲。 用户可设置是否启用噪声滤波器。
噪声滤波器功能的时序图如下所示:
3.2.2.9. 输出相位切换¶
见图中标注 9 处。
输出相位切换(GPT_OPS)功能通过输出相位切换控制寄存器(OPSCR)进行控制,用于实现轻松控制无刷直流电机运行的功能。 图中的三相 PWM 波形生成器接收来自比较器的 GPT320.GTIOC0A 的 PWM 信号输入后,输出三相 PWM 用于驱动直流无刷电机。
需要注意的是:GPT_OPS 功能在 RA6T2 中只有一个,并不是每个 GPT 定时器都对应有一个,也就是说通过该功能只能轻松控制一个直流无刷电机。
3.2.2.10. AD转换开始请求时序/AD转换开始请求时序缓冲寄存器¶
见图中标注 10 处。
这部分包含1个比较器和 A B 两组寄存器。
AD转换请求在GTCNT计数器与GTADTRA或GTADTRB寄存器之间的比较匹配时发出AD转换开始请求。通过设置GTINTAD寄存器,可以指定 仅递增计数、仅递减计数或同时递增计数和递减计数。在互补PWM模式下,可以在与主通道的GTCNT计数器比较匹配时发出AD转换开始请求。
在事件计数操作期间,不能产生AD转换开始请求。AD转换开始请求作为事件信号输出到ELC。
GTADTRA和GTADTRB寄存器各有两个缓冲寄存器。可以使用与GTADTBRA和GTADTDBRA寄存器一起使用的GTAD TRA寄存器进行缓冲操作,以及使用与GTADTBRB和GTADTDBRB寄存器一起使用的GTADTRB寄存器进行缓冲操作。
3.2.3. GPT比较匹配功能详解¶
比较匹配主要是用来实现输出 PWM 波形功能的一种机制。 其原理简单来讲就是通过比较 GTCNT 计数器的值与 GTCCRA 或 GTCCRB 的值,当比较匹配发生时,会生成相应的比较匹配事件信号, 同时,GPT 可以切换 GTIOCnA 或 GTIOCnB 输出引脚的输出信号,可以选择输出低电平、高电平或翻转输出。 在 GTCNT 完成一个周期的计数时,也可以切换 GTIOCnA 或 GTIOCnB 输出引脚的输出信号。
PWM 输出就是对外输出脉宽(即占空比)可调的方波信号, 信号频率由周期设定寄存器 GTPR 的值决定,占空比由比较寄存器 GTCCR 的值决定。
GPT 的输出模式有:
锯齿波 PWM 模式(Saw-wave PWM mode)(该模式下单缓冲或双缓冲都可使用)
锯齿波单脉冲模式(Saw-wave one-shot pulse mode)(该模式下使用固定的缓冲操作)
三角波 PWM 模式1(Triangle-wave PWM mode 1)(波谷32位传输)(该模式下单缓冲或双缓冲都可使用)
三角波 PWM 模式2(Triangle-wave PWM mode 2)(波峰和波谷32位传输)(该模式下单缓冲或双缓冲都可使用)
三角波 PWM 模式3(Triangle-wave PWM mode 3)(波谷64位传输)(该模式下使用固定的缓冲操作)
我们在前面有提到过每个GPT定时器模块内部都有 6个 GTCCRx 寄存器(x = A,B,C,D,E,F), 下面就先来了解一下有关 GTCCRx 的缓冲操作。
单缓冲操作:
以 GTIOCA 输出为例,若需要修改占空比,则需要在 GTCCRC 写入要修改的比较值, GTCNT 计数完成后则会将 GTCCRC 的值写入 GTCCRA,如下图所示。
双缓冲操作:
在三角波 PWM 输出模式下,GTCCRA / GTCCRB 各有两个缓存,缓存传输的顺序分别为:
GTCCRD -> GTCCRC -> GTCCRA
GTCCRF -> GTCCRE -> GTCCRB
以 GTIOCA 为例,如下图所示。
3.2.4. GPT输入捕获功能详解¶
输入捕获是定时器的一项非常重要的功能。通过输入捕获功能, 我们可以测量高低电平脉冲的脉宽、信号的周期、频率和 PWM 占空比等。
在检测到在 GTICASR 和 GTICBSR 中设置的硬件源时,可以将 GTCNT 计数器值传输到 GTCCRA 或 GTCCRB,这便是 GPT 的输入捕获功能。 “输入捕获”根据“输入”来触发“捕获” GTCNT 计数器的值,更加具体地说: 硬件在检测到我们所设置的硬件源时,“捕获” GTCNT 计数器的值并转存到 GTCCRA 或 GTCCRB 寄存器。
我们可以设置如下硬件源来触发执行输入捕获:
在 GTETRGA 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGA 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGB 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGB 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGC 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGC 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGD 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
在 GTETRGD 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnB 输入为 0 时,在 GTIOCnA 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnB 输入为 1 时,在 GTIOCnA 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnB 输入为 0 时,在 GTIOCnA 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnB 输入为 1 时,在 GTIOCnA 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnA 输入为 0 时,在 GTIOCnB 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnA 输入为 1 时,在 GTIOCnB 引脚输入的上升沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnA 输入为 0 时,在 GTIOCnB 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
当 GTIOCnA 输入为 1 时,在 GTIOCnB 引脚输入的下降沿启用或禁用 GTCCRA / GTCCRB 的输入捕捉。
下图所示的示例清晰地展示了输入捕获的功能。 在该示例中,GTCNT 计数器通过计数时钟进行递增计数, 并且设置为在 GTIOCnA 输入引脚的两个边沿执行 GTCCRA 的输入捕捉,在 GTIOCnB 输入引脚的上升沿执行 GTCCRB 的输入捕捉。
3.2.5. 实验1:GPT定时实验¶
本实验较为简单,我们将使用 GPT 定时器进行定时并产生中断请求触发中断,然后通过这个中断,切换 LED 的电平。
我们使用基本定时器循环定时 1s 并使能定时器中断,每到 1s 就在定时器中断服务函数翻转 LED 灯, 使得最终效果 LED 灯灭 1s,亮 1s,如此循环。
3.2.5.1. 硬件设计¶
基本定时器没有相关 GPIO的使用,这里我们只用定时器的定时功能,不需要其他外部引脚, 至于 LED 灯的硬件部分可参考 野火《瑞萨RA系列FSP库开发实战指南》的 GPIO输出 章节。
3.2.5.2. 软件设计¶
3.2.5.2.1. 新建工程¶
由于本实验需要用到LED,也会用到串口打印提示信息, 因此我们在串口收发的“003_Basic_UART_Receive_Send”例程的基础上修改程序。
对于 e2 studio 开发环境: 拷贝一份我们之前的 e2s 工程 “003_Basic_UART_Receive_Send” , 然后将工程文件夹重命名为 “005_GPT_Basic_Timing” ,最后再将它导入到我们的 e2 studio 工作空间中。
对于 Keil 开发环境: 拷贝一份我们之前的 Keil 工程 “003_Basic_UART_Receive_Send” , 然后将工程文件夹重命名为 “005_GPT_Basic_Timing” ,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “gpt” 文件夹, 再进入 “gpt” 文件夹里面新建源文件和头文件:“bsp_gpt_timing.c” 和 “bsp_gpt_timing.h”。
工程文件结构如下。
005_GPT_Basic_Timing
├─ ......
└─ src
├─ beep
│ ├─ bsp_beep.c
│ └─ bsp_beep.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ key
│ ├─ bsp_key_irq.c
│ └─ bsp_key_irq.h
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ gpt
│ ├─ bsp_gpt_timing.c //新建文件
│ └─ bsp_gpt_timing.h //新建文件
└─ hal_entry.c
3.2.5.2.2. FSP配置¶
首先打开 FSP 配置界面,在 Stacks 中加入 GPT模块,如下图所示。
我们使用 GPT0 来实现定时和触发中断功能, 需要修改的只有“General”和“Interrupts”部分,其他按照默认即可,如下图所示。
在上图中我们可以将 GPT 模块属性进行区域性地划分为: “Common”、“General”、“Output”、“Input”、“Interrupts”、“Extra Features”、“Pins”这几个部分。
本实验主要配置“General”和“Interrupts”这几个部分的属性,因此在这里会对它们进行详细讲解。 而“Common”和“Extra Features”暂未用到,因此这里不需要对其进行配置。 由于本实验不需要 GPT 的 IO 输出输入,故“Output”和“Input”以及“Pins”会放在后面的实验讲解。 其中,“Output” 属性会在后面的实验2中使用到 PWM 输出功能时进行讲解, “Input” 属性会在实验3中使用到输入捕获功能时进行讲解。
“Common”部分的配置属性:
属性 |
描述 |
---|---|
Parameter Checking |
参数检查(选择是否生成包含参数检查的代码)。 |
Pin Output Support |
支持引脚输出。 “Enabled”或“Disabled” 用于使能或禁止 GTIOCx 输出PWM信号。 “Enabled with Extra Features” 用于支持三角波输出模式, 以及用于支持“Extra Features”部分的特性。 |
Write Protect Enabled |
GPT寄存器的写保护。选择使能/禁止写保护并应用于所有GPT通道。 若使能了这一项,则无法对GPT寄存器进行写操作,需要使用 r_gpt_write_protect_disable() 函数取消写保护,才能写入寄存器。 |
Clock Source |
时钟源选择。 只能选择 PCLKD 作为内部时钟源。 |
“General”部分的配置属性:
属性 |
描述 |
---|---|
Name |
模块名字。 |
Channel |
通道选择。指定硬件通道,也就是指定使用哪一个GPT定时器。 |
Mode |
可以选择周期计数,单次计数,PWM模式,三角波对称PWM模式和三角波不对称PWM模式。 Periodic One-Shot PWM One-Shot Pulse Triangle-Wave Symmetric PWM Triangle-Wave Asymmetric PWM Triangle-Wave Asymmetric PWM (Mode 3) |
Period |
计数器的计数周期。 |
Period Unit |
计数器计数周期的单位。 |
“Interrupts”部分的配置属性:
属性 |
描述 |
---|---|
Callback |
中断回调函数名。 |
Overflow/Crest Interrupts Priority |
选择上溢中断的优先级。如果选择 Disabled 则禁止该中断。 该中断亦是三角波的波峰中断。 |
Capture A Interrupts Priority |
选择捕获A中断的优先级。如果选择 Disabled 则禁止该中断。 |
Capture B Interrupts Priority |
选择捕获B中断的优先级。如果选择 Disabled 则禁止该中断。 |
Underflow/Trough Interrupts Priority |
选择下溢中断的优先级。如果选择 Disabled 则禁止该中断。 该中断亦是三角波的波谷中断。 |
“Extra Features”部分的配置属性:
属性 |
描述 |
---|---|
Output Disable > Output Disable POEG Trigger |
选择一个信号触发禁用 POEG。 |
Output Disable > POEG Link |
选择哪一个 POEG 连接到该 GPT 通道。 |
Output Disable > GTIOCA Disable Setting |
GTIOCA 的禁用设定。 |
Output Disable > GTIOCB Disable Setting |
GTIOCB 的禁用设定。 |
ADC Trigger > Start Event Trigger (Channels with GTINTAD only) |
选择触发 ADC 转换的时机 |
Dead Time > Dead Time Count Up (Raw Counts) |
选择死区时间(应用到向上计数) |
Dead Time > Dead Time Count Down (Raw Counts) (Channels with GTDVD only) |
选择死区时间(应用到向下计数) |
ADC Trigger (Channels with GTADTRA only) > ADC A Compare Match (Raw Counts) |
设置生成 GPTn AD TRIG A 事件的比较匹配值。 |
ADC Trigger (Channels with GTADTRB only) > ADC B Compare Match (Raw Counts) |
设置生成 GPTn AD TRIG B 事件的比较匹配值。 |
Interrupt Skipping (Channels with GTITC only) > Interrupt to Count |
选择跳过中断的计数源。中断跳过计数器在每个源事件之后递增。 当中断跳过计数器非零时,将跳过所有顶部/溢出中断和槽/底部中断。 |
Interrupt Skipping (Channels with GTITC only) > Interrupt Skip Count |
选择要跳过的中断数。 |
Interrupt Skipping (Channels with GTITC only) > Skip ADC Events |
当中断跳过计数不为零时,选择要禁止的 ADC 事件。 |
Extra Features |
选择是否使能该通道的“Extra Features”(额外特性) |
3.2.5.2.3. GPT初始化函数¶
GPT 初始化函数用于初始化 GPT 模块,并且启动 GPT 定时器,该函数如下所示。
/* GPT 初始化函数 */
void GPT_Timing_Init(void)
{
/* 初始化 GPT0 模块 */
R_GPT_Open(&g_timer_gpt0_ctrl, &g_timer_gpt0_cfg);
/* 启动 GPT0 定时器 */
R_GPT_Start(&g_timer_gpt0_ctrl);
}
在 GPT 初始化函数里面,首先通过 R_GPT_Open 函数初始化 GPT 模块, 随后调用 R_GPT_Start 函数来启动 GPT 定时器。
3.2.5.2.4. GPT中断回调函数¶
我们在 FSP 配置里配置了 GPT 的中断,溢出中断优先级为10,GPT 中断回调函数如下。
#include "led/bsp_led.h"
/* GPT 中断回调函数 */
void gpt0_timing_callback(timer_callback_args_t * p_args)
{
/* 定时器溢出事件 */
if (TIMER_EVENT_CYCLE_END == p_args->event)
{
/* 翻转 LED1 */
LED1_TOGGLE; //每秒翻转一次
}
}
该中断回调函数非常地简单,直接在回调函数里面使用 if 语句判断触发中断的事件是否是定时器溢出事件, 若是定时器溢出事件则翻转一次 LED1 引脚的电平。 由于定时器被我们配置为1秒的计时周期,因此每1秒会溢出一次,控制 LED1 的引脚的电平会每秒钟翻转一次。
3.2.5.2.5. hal_entry入口函数¶
以启明6T2开发板的 RA6T2 工程为例,hal_entry 入口函数如下所示。
/* 用户头文件包含 */
#include "hal_data.h"
#include "led/bsp_led.h"
#include "beep/bsp_beep.h"
#include "key/bsp_key_irq.h"
#include "debug_uart/bsp_debug_uart.h"
#include "gpt/bsp_gpt_timing.h"
void hal_entry(void)
{
/* TODO: add your own code here */
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Debug_UART9_Init(); //串口9初始化
GPT_Timing_Init(); // GPT 初始化
printf("这是一个 启明 6T2 GPT 的基本定时功能实验\r\n");
while(1)
{
printf("程序正在运行 ...\r\n");
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
3.2.5.3. 下载验证¶
编译并下载到开发板后,复位板子让程序运行,然后可以观察到板载 LED1(红色) 每秒钟翻转一次状态,即以两秒为周期在缓慢闪烁。
3.2.6. 实验2:PWM互补输出¶
3.2.6.1. 硬件设计¶
在本实验中使用 启明6T2 开发板上 直流有/无刷电机驱动接口1 中的两个 PWM 输出引脚: PE10 和 PE13 来进行实验,需要使用示波器来观察实验结果。
3.2.6.2. 软件设计¶
3.2.6.2.1. 新建工程¶
我们直接在实验1的 “005_GPT_Basic_Timing” 例程的基础上修改程序。
- 对于 e2 studio 开发环境:
拷贝一份我们之前的 e2s 工程 “005_GPT_Basic_Timing”, 然后将工程文件夹重命名为 “006_GPT_PWM_Output”,最后再将它导入到我们的 e2 studio 工作空间中。
- 对于 Keil 开发环境:
拷贝一份我们之前的 Keil 工程 “005_GPT_Basic_Timing”, 然后将工程文件夹重命名为 “006_GPT_PWM_Output”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下的 “gpt” 文件夹里面新建源文件和头文件:“bsp_gpt_pwm_output.c” 和 “bsp_gpt_pwm_output.h”。 工程文件结构如下。
005_GPT_Basic_Timing
├─ ......
└─ src
├─ beep
│ ├─ bsp_beep.c
│ └─ bsp_beep.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ key
│ ├─ bsp_key_irq.c
│ └─ bsp_key_irq.h
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ gpt
│ ├─ bsp_gpt_timing.c
│ ├─ bsp_gpt_timing.h
│ ├─ bsp_gpt_pwm_output.c //新建文件
│ └─ bsp_gpt_pwm_output.h //新建文件
└─ hal_entry.c
3.2.6.2.2. FSP配置¶
下面以野火启明6T2开发板为例来讲解相关的 FSP 配置。
因为 PWM 互补输出需要使用两个 IO 引脚进行输出,因此需要先在“Pins”配置页中为 GPT 配置引脚, 我们将 GPT4 的 GTIOC4A 和 GTIOC4B 信号输出分别连接到 PE10 和 PE13 引脚,如下图所示。
然后在“Stacks”配置页中加入 GPT 模块,并对其作如下图所示的配置。
上图中框起来的部分是需要我们去修改的区域,其他的配置属性按照默认即可。 图中需要更改的一部分配置如下:
Pin Output Support:这一项配置允许输出 PWM 信号到引脚,我们改为使能引脚输出。
Name 和 Channel:这两项分别设置 GPT 模块名字为 “g_pwm4” 和选择第 4 个 GPT 定时器(第4个通道)。
Mode:配置 GPT 的工作模式为 PWM 输出模式。
Period 和 Period Unit:我们将PWM频率设为 20 KHz, 因此“Period”设置为 20,单位“Period Unit”设置为 Kilohertz,即千赫兹(KHz)。
GTIOCA Output Enabled:使能 GTIOCA 输出。
GTIOCB Output Enabled:使能 GTIOCB 输出。
GTIOCA Stop Level:设置定时器停止时 GTIOCA 输出的电平为低电平。
GTIOCB Stop Level:设置定时器停止时 GTIOCB 输出的电平为低电平。
GTIOC4A:选择连接到 PE10 引脚,这个软件会自动设置的,我们只要确认了就好。
GTIOC4B:选择连接到 PE13 引脚,这个软件会自动设置的,我们只要确认了就好。
GPT的“Output”部分的属性描述如下表所示。 这一部分也是使用互补PWM的关键,将 GTIOA 和 GTIOB 的初始电平一个设置为高电平,另一个设置为低电平,在PWM信号输出时就可以达到互补的状态。
属性 |
描述 |
---|---|
Custom Waveform > GTIOA/GTIOB > Initial Output Level |
设置GPT初始化时 GTIOCxA/GTIOCxB 的电平(低电平或高电平)。 |
Custom Waveform > GTIOA/GTIOB > Cycle End Output Level |
完成一个计数周期时 GTIOCxA/GTIOCxB 的输出电平(保持不变、输出高、输出低或反转电平)。 |
Custom Waveform > GTIOA/GTIOB > Compare Match Output Level |
发生比较匹配时 GTIOCxA/GTIOCxB 的输出电平(保持不变、输出高、输出低或反转电平)。 |
Custom Waveform > GTIOA/GTIOB > Retain Output Level at Count Stop |
在计数停止时保持 GTIOCxA/GTIOCxB 电平。 |
Custom Waveform > Custom Waveform Enable |
使能自定义波形的配置。 |
Duty Cycle Percent (only applicable in PWM mode) |
设定 PWM 占空比(仅用于 PWM 模式) |
GTIOCA Output Enabled |
使能 GTIOCA 输出到相应引脚 |
GTIOCA Stop Level |
当 GPT 定时器停止时 GTIOCA 的输出电平 |
GTIOCB Output Enabled |
使能 GTIOCB 输出到相应引脚 |
GTIOCB Stop Level |
当 GPT 定时器停止时 GTIOCB 的输出电平 |
3.2.6.2.3. GPT初始化函数¶
void GPT_PWM_Init(void)
{
/* 初始化 GPT 模块 */
R_GPT_Open(&g_pwm4_ctrl, &g_pwm4_cfg);
/* 启动 GPT 定时器 */
R_GPT_Start(&g_pwm4_ctrl);
/* 重新设置占空比为 50% */
GPT_PWM_SetDuty(50);
}
3.2.6.2.4. 设置PWM占空比函数¶
/** 自定义函数:设置PWM占空比
@param duty 占空比范围:0~100 %
*/
void GPT_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
/* 获得GPT的信息 */
R_GPT_InfoGet(&g_pwm4_ctrl, &info);
/* 获得计时器一个周期需要的计数次数 */
current_period_counts = info.period_counts;
/* 根据占空比和一个周期的计数次数计算GTCCR寄存器的值 */
duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);
/* 最后调用FSP库函数设置占空比 */
R_GPT_DutyCycleSet(&g_pwm4_ctrl, duty_cycle_counts, GPT_IO_PIN_GTIOCA_AND_GTIOCB);
}
3.2.6.2.5. hal_entry入口函数¶
/* 用户头文件包含 */
#include "hal_data.h"
#include "led/bsp_led.h"
#include "beep/bsp_beep.h"
#include "key/bsp_key_irq.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_UART9_Init(); // SCI9 UART 调试串口初始化
GPT_PWM_Init(); // GPT 初始化
printf("这是一个 GPT 的PWM互补输出功能实验\r\n");
printf("使用示波器测量 PE10和PE13 输出的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
}
3.2.6.3. 下载验证¶
以野火启明6T2开发板为例, 编译并下载程序后,复位开发板使程序重新运行,然后使用示波器测量 PE10 和 PE13 引脚输出的PWM波形。 实现现象如下。
3.2.7. 实验3:输入捕获——脉宽和周期测量¶
上一个实验使用了 GPT 的输出功能,本次实验要使用 GPT 的输入功能,通过 GPT 的输入捕获进行脉宽和周期测量。 其测量的原理如下图所示。
首先启动 GPT 定时器,在上升沿 A 处进行捕获,记此时时间为 A; 紧接着在下降沿 B 处进行捕获,记此时时间为 B; 最后再次在上升沿 C 处进行捕获,记此时时间为 C 或者 A’。 设 PWM 的周期为 T,占空比为 D,频率为 f,则:
3.2.7.1. 硬件设计¶
以野火启明6T2开发板为例, 由于上一节实验已经使用过 PWM 输出,所以本实验使用 就在上一节实验的基础上额外使用 GPT5->GTIOC5A->PB06引脚 用于输入捕获。 将 PE10 或 PE13 与 PB06 使用杜邦线连接。
提示
在启明6T2开发板上,PB06引脚位于 直流有/无刷电机驱动接口1 中。
3.2.7.2. 软件设计¶
3.2.7.2.1. 新建工程¶
由于本实验的目标是测量PWM的周期、占空比,因此我们在上一个实验的基础上修改程序。
- 对于 e2 studio 开发环境:
拷贝一份我们之前的 e2s 工程 ““006_GPT_PWM_Output””, 然后将工程文件夹重命名为 “007_GPT_Input_Capture”,最后再将它导入到我们的 e2 studio 工作空间中。
- 对于 Keil 开发环境:
拷贝一份我们之前的 Keil 工程 ““006_GPT_PWM_Output””, 然后将工程文件夹重命名为 “007_GPT_Input_Capture”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src/gpt” 路径下面, 新建源文件和头文件:“bsp_gpt_input_capture.c” 和 “bsp_gpt_input_capture.h”。 工程文件结构如下。
005_GPT_Basic_Timing
├─ ......
└─ src
├─ beep
│ ├─ bsp_beep.c
│ └─ bsp_beep.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ key
│ ├─ bsp_key_irq.c
│ └─ bsp_key_irq.h
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ gpt
│ ├─ bsp_gpt_timing.c
│ ├─ bsp_gpt_timing.h
│ ├─ bsp_gpt_pwm_output.c
│ ├─ bsp_gpt_pwm_output.h
│ ├─ bsp_gpt_input_capture.c //新增文件
│ └─ bsp_gpt_input_capture.h //新增文件
└─ hal_entry.c
3.2.7.2.2. FSP配置¶
下面以野火启明6T2开发板为例来讲解相关的 FSP 配置。
由于在上一个实验中我们已经配置好了 PE10 和 PE13 引脚用于输出PWM信号, 因此本实验中只需要添加输入捕获需要使用到 IO 引脚 PB06,需要在“Pins”配置页面中配置 GPT5 所使用的引脚。
在“Stacks”配置页面中加入 GPT,按如下图所示的配置进行设置。 需要注意的是,因为“Input”里面的各类配置项目若是展开来的话篇幅比较长,因此在图中并未展开出来, 我们要注意勾上以下几个选项:
- 展开 Input->Capture A Source 并勾上(在 GTIOCA 的上升沿启用输入捕获 A,无论 GTIOCB 引脚是高电平还是低电平):
GTIOCA Rising Edge While GTIOCB Low
GTIOCA Rising Edge While GTIOCB High
- 展开 Input->Capture B Source 并勾上(在 GTIOCA 的下降沿启用输入捕获 B,无论 GTIOCB 引脚是高电平还是低电平):
GTIOCA Falling Edge While GTIOCB Low
GTIOCA Falling Edge While GTIOCB High
GPT的“Input”选项卡的属性描述
属性 |
描述 |
---|---|
Count Up Source |
选择计数器向上计数的外部源。 如果某个外部向上计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。 |
Count Down Source |
选择计数器向下计数的外部源。 如果某个外部向下计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。 |
Start Source |
选择外部源来启动定时器。 |
Stop Source |
选择外部源来停止定时器。 |
Clear Source |
选择外部源来清除定时器。 |
Capture A Source |
选择外部源来触发一个捕获 A 事件。 |
Capture B Source |
选择外部源来触发一个捕获 B 事件。 |
Noise Filter A Sampling Clock Select |
GTIOCA 输入噪声滤波器的采样时钟选择。 |
Noise Filter B Sampling Clock Select |
GTIOCB 输入噪声滤波器的采样时钟选择。 |
3.2.7.2.3. GPT初始化¶
初始化 GPT 模块用于输入捕获的函数如下。 在这个函数中,我们先初始化 GPT 模块,然后调用 R_GPT_InfoGet 函数获取 GPT 的配置信息, 主要是为了获取计数器的计数周期,即 GPT 一个周期的计数次数,并保存到全局变量 period 中。
最后使能输入捕获,并启动 GPT 定时器。
timer_info_t info; //用于获取定时器参数信息
uint32_t period; //输入捕获计数器的计数周期
/* GPT初始化函数 */
void GPT_InputCapture_Init(void)
{
/* 初始化 GPT 模块 */
R_GPT_Open(&g_timer_gpt5_ctrl, &g_timer_gpt5_cfg);
/*获取当前参数*/
(void) R_GPT_InfoGet(&g_timer_gpt5_ctrl, &info);
/* 获取计数周期:GPT的一个周期的计数次数 */
period = info.period_counts;
/* 使能输入捕获 */
R_GPT_Enable(&g_timer_gpt5_ctrl);
/* 启动 GPT 定时器 */
R_GPT_Start(&g_timer_gpt5_ctrl);
}
3.2.7.2.4. GPT中断回调函数¶
GPT 输入捕获中断回调函数如下。
/* 保存所测量的PWM信号的信息 */
uint32_t pwm_period; //PWM周期
uint32_t pwm_high_level_time; //PWM高电平的时间
uint32_t pwm_freq; //PWM频率
uint32_t pwm_duty; //PWM占空比
/* GPT 输入捕获中断回调函数 */
void gpt7_input_capture_callback(timer_callback_args_t * p_args)
{
static uint32_t a_time; // A 上升沿捕获的时间
static uint32_t b_time; // B 下降沿捕获的时间
static uint32_t c_time; // C 上升沿捕获的时间(其实也就是 A 可以用A'表示)
static uint32_t overflow_times; //计数器溢出次数
static uint8_t one_period_flag=0; //用于表示是否完成对一个完整周期的测量
switch(p_args->event)
{
/* 捕获到上升沿 -- 有可能是 A 或者 C (A') 位置 */
case TIMER_EVENT_CAPTURE_A:
/* A 开始对某个周期进行测量 */
if (0 == one_period_flag)
{
a_time = p_args->capture; //记录捕获的时间 A
overflow_times = 0; //初始化计数器溢出次数
one_period_flag ++; //表示即将完成对某个周期的测量
}
/* C (A') 如果测量完了一个周期,则计算PWM信号周期和高电平的时间 */
else if (1 == one_period_flag)
{
c_time = p_args->capture + overflow_times * period; //记录捕获的时间 C
//计算 PWM 周期
pwm_period = c_time - a_time;
//清零所有标志位
overflow_times = 0;
one_period_flag = 0; //标志位清0,重新进入下一轮的测量
}
break;
/* 捕获到下降沿 -- 是 B 位置 */
case TIMER_EVENT_CAPTURE_B:
//如果是在测量周期内检测到下降沿
if (1 == one_period_flag)
{
b_time = p_args->capture + overflow_times * period; //记录捕获的时间 B
pwm_high_level_time = b_time - a_time; //计算高电平时间
}
break;
/* 定时器计数溢出事件 */
case TIMER_EVENT_CYCLE_END:
/* 输入捕获期间计数器溢出,则记录溢出次数+1 */
overflow_times++;
break;
default:
break;
}
}
在 GPT 中断回调函数中,我们使用 switch 语句判断触发中断的事件类型。 GPT 在引脚输入 PWM 信号的上升沿触发捕获 A 事件,在 PWM 信号的下降沿触发捕获 B 事件, 并在计数器溢出时触发定时器计数溢出事件。
警告
注意:建议不要在中断回调函数中打印数据,因为 PWM 频率一般很高, 而 printf 函数执行所消耗的时间比较长,可能会阻塞中断服务函数的运行。同时也应该时刻注意中断优先级的问题。
3.2.7.2.5. hal_entry入口函数¶
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "gpt/bsp_gpt_pwm_output.h"
#include "gpt/bsp_gpt_input_capture.h"
// 外部变量声明
extern timer_info_t info; //用于获取定时器参数信息
extern uint32_t pwm_period; //PWM周期
extern uint32_t pwm_high_level_time; //PWM高电平的时间
extern uint32_t pwm_freq; //PWM频率
extern uint32_t pwm_duty; //PWM占空比
void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
GPT_PWM_Init(); // GPT PWM输出初始化
GPT_InputCapture_Init(); // GPT 输入捕获初始化
printf("这是一个 GPT 的PWM输出 + 输入捕获功能实验\r\n");
printf("使用杜邦线连接 PE10 或 PE13 与 PB06 引脚,然后打开串口助手查看串口的打印信息\r\n");
while(1)
{
/* 计算PWM的频率 */
pwm_freq = info.clock_frequency / pwm_period;
/* 计算PWM的占空比 */
pwm_duty = pwm_high_level_time * 100 / pwm_period;
// 打印
printf("High=%lu, Period=%lu, ", pwm_high_level_time, pwm_period);
printf("Friquency = %lukHz, Duty_Cycle = %d%%\r\n", pwm_freq / 1000, pwm_duty);
pwm_period = pwm_high_level_time = pwm_freq = 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
}
3.2.7.3. 下载验证¶
以野火启明6T2开发板为例, 编译并下载程序后,复位开发板使程序重新运行,然后使用杜邦线连接 PE10 或 PE13 与 PB06 引脚,然后打开串口助手查看串口的打印信息。 串口会打印出 PWM 信号的频率和占空比等信息,实验现象如下图所示。