I.MX6U 裸机开发18.GPT定时器实现高精度延时
一、GPT定时器简介
1. GPT 功能
IMX6ULL 的GPT定时器(General Purpose Timer)是一种通用定时器,可以用于生成精确的时间延迟、测量时间间隔、产生周期性中断等,以下是它的几个特性:
- GPT 有一个 32 位的向上计数器。定时器的计数值可以通过外部引脚上的事件被捕获到一个寄存器中。
- 捕获触发可以被编程为上升沿和/或下降沿。
- GPT 还可以在输出比较引脚上生成一个事件,并在定时器达到编程值时产生一个中断。
- GPT 有一个 12 位的预分频器,它从多个时钟源中提供一个可编程的时钟频率。
相比较,EPIT:
- 32位向下计数器
- 没有捕获功能
2. 时钟源
本文使用 ipg_clk 作为时钟源,即66MHz。
3. 框图
- 从左上角开始,输入时钟
- 时钟进入 Prescaler 12位分频器(值0~4095)
- 分频后进入 Timer Counter,即32位计数器
- GET_CAPTURE1, GET_CAPTURE2 实现捕获功能
- TIMER Output Reg1~3, 是三路时钟输出,Timer Count要与这三路值进行比较,
- GET_COMPARE1~3,三路比较输出
4. 运行模式
(1)Restart mode
自由运行模式,计数器从零开始计数,并在达到最大值后回到零,继续计数。这个模式通常用于测量时间间隔或生成周期性中断。
比较的时候,是比较定时器的计数值和比较寄存器OCR。
特点:
- 计数器不断循环计数。
- 可以配置中断,在计数器溢出时触发中断。
- 只有比较通道1适用这种模式。
(2)Free-Run Mode
输入捕获模式,计数器在检测到外部事件(如信号边沿)时捕获当前计数值,并将其存储在捕获寄存器中。这个模式通常用于测量外部事件的时间间隔。
特点:
- 计数器在检测到外部事件时捕获当前计数值。
- 可以配置中断,在捕获事件发生时触发中断。
- 三个通道都适用, 从0开始一直加到 0xFFFFFFFF,然后重新从0开始。
5. 中断类型
(1)溢出中断 Rollover Interrupt
计数器达到其最大值并回到零时,会触发溢出中断。这个中断通常用于周期性任务或需要精确时间间隔的应用。
(2)输入捕获中断 Input Capture Interrupt 1,2
可以捕获外部事件的时间戳。GPT有两个输入捕获通道,分别为输入捕获中断1(Input Capture Interrupt 1)和输入捕获中断2(Input Capture Interrupt 2)。
(3)输出捕获中断 Output Compare Interrupt 1,2,3
在计数器达到预设值时触发中断。GPT有三个输出比较通道,分别为输出比较中断1(Output Compare Interrupt 1)、输出比较中断2(Output Compare Interrupt 2)和输出比较中断3(Output Compare Interrupt 3)。
6. 寄存器
(1) GPTx_CR
GPTx_CR 是 GPT的控制寄存器,用于配置和控制 GPT 定时器的各种功能。以下是 GPTx_CR 寄存器的各个功能位的详细说明:
bit 0
GPT Enable: 使能GPT定时器,1使能
bit 1
GPT Enable Mode: 使能模式,0表示GPT定时器计数值默认为上次关闭的时候值,为1表示默认值为0
bit 2
GPT Debug Enable: 调试模式使能
bit 3
GPT Wait Mode Enable: 等待模式使能
bit 4
GPT Doze Mode Enable: 休眠模式使能
bit 5
GPT Stop Mode Enable: 停止模式使能
bits 6-8
GPT Clock Source: 时钟源选择
- 000: 时钟源关闭
- 001: 使用IPG时钟源
- 010: 使用高频IPG时钟源
- 011: 使用外部时钟源
- 100: 使用外设时钟源
- 101: 使用晶体振荡器作为参考时钟,频率为24 MHz
bit 9
GPT Free-Run or Restart Mode: 自由运行或重启模式,0为Restart 模式, 1为 Free-Run模式。
bit 10
EN_24M: 使能24 MHz晶体振荡器输入时钟。
- 硬件复位会重置EN_24M位。
- 软件复位不会影响EN_24M位。
bit 11-14
未使用
bit 15
SWR: 软件复位,这是GPT模块的软件复位位。它是一个自清除位。
- 当模块处于复位状态时,SWR位被置位。
- 当复位过程完成时,SWR位被清除。
- 设置SWR位会将所有寄存器重置为默认复位值,除了GPT控制寄存器中的EN、ENMOD、STOPEN、WAITEN和DBGEN位。
- 0: GPT不处于复位状态
- 1: GPT处于复位状态
bit22-20 OM1, bit25-23 OM2, bit 28-26 OM3
设置比较输出功能,比较事件发生以后,相应的IO输出:
- 000: 输出断开。引脚上无响应。
- 001: 切换输出引脚,翻转输出;
- 010: 清除输出引脚,清0。
- 011: 设置输出引脚,置1。
1xx: 在输出引脚上生成一个有效低脉冲(即一个输入时钟宽度)。在比较事件发生时,输出引脚立即设置为1,并在下一个输入时钟时生成一个低脉冲。
bit 29 FO1, bit30 FO2, bit31 FO3
强制输出比较通道3
- 0: 写0无效。
- 1: 在定时器输出比较通道3引脚上执行编程的引脚操作;OF3标志不设置。
本文实验使用 IPG时钟源, Restart Mode。
(2) GPTx_PR 分频寄存器
用来设置分频值。
bits 0-11: 分频值(Prescaler Value)
这些位用于设置预分频值。预分频值的范围通常为0到4095。
实际的分频因子为(分频值 + 1),即如果分频值为0,则分频因子为1;如果分频值为1,则分频因子为2,以此类推。
这个用来设置 Crystal Oscillator 24M 的分频。
(3) GPTx_SR 状态寄存器
GPTx_SR(Status Register)是GPT的状态寄存器,用于指示定时器的各种状态和事件。功能位的作用如下:
bit 0: Output Compare 1 Flag (OF1)
- 当输出比较通道1发生比较事件时,该位被置位。
- 0: 没有发生比较事件
- 1: 发生了比较事件
bit 1: Output Compare 2 Flag (OF2)
- 当输出比较通道2发生比较事件时,该位被置位。
- 0: 没有发生比较事件
- 1: 发生了比较事件
bit 2: Output Compare 3 Flag (OF3)
- 当输出比较通道3发生比较事件时,该位被置位。
- 0: 没有发生比较事件
- 1: 发生了比较事件
bit 3: Input Capture 1 Flag (IF1)
- 当输入捕获通道1发生捕获事件时,该位被置位。
- 0: 没有发生捕获事件
- 1: 发生了捕获事件
bit 4: Input Capture 2 Flag (IF2)
- 当输入捕获通道2发生捕获事件时,该位被置位。
- 0: 没有发生捕获事件
- 1: 发生了捕获事件
bit 5: Rollover Flag (ROV)
- 当计数器发生溢出时,该位被置位。
- 0: 没有发生溢出
- 1: 发生了溢出
(4) GPTx_IR 中断寄存器
GPTx_IR 中断寄存器
GPTx_IR(Interrupt Register)是GPT的中断寄存器,用于配置和控制定时器的中断功能,各位作用如下:
-
bit 0: Output Compare 1 Interrupt Enable (OF1IE)
- 0: 禁用输出比较通道1中断
- 1: 使能输出比较通道1中断
-
bit 1: Output Compare 2 Interrupt Enable (OF2IE)
- 0: 禁用输出比较通道2中断
- 1: 使能输出比较通道2中断
-
bit 2: Output Compare 3 Interrupt Enable (OF3IE)
- 0: 禁用输出比较通道3中断
- 1: 使能输出比较通道3中断
-
bit 3: Input Capture 1 Interrupt Enable (IF1IE)
- 0: 禁用输入捕获通道1中断
- 1: 使能输入捕获通道1中断
-
bit 4: Input Capture 2 Interrupt Enable (IF2IE)
- 0: 禁用输入捕获通道2中断
- 1: 使能输入捕获通道2中断
-
bit 5: Rollover Interrupt Enable (ROVIE)
- 0: 禁用溢出中断
- 1: 使能溢出中断
二、定时器中断实现500ms定时
在 bsp_delay.c 里实现下面功能。
1. 初始化 GPT 定时器
/**
* @brief 初始化GPT定时器
*/
void gpt1_timer_init(void)
{
// bit0 软复位
GPT1->CR = 0;
GPT1->CR = 1 << 15;
while(GPT1->SR & (1 << 15));
// bit1, GPT 定时器计数器的初始值,为1表示默认值为0,0表示为上次关闭时候的值
GPT1->CR |= (1 << 1);
// bit6~8 时钟源,001表示ipg_clk
GPT1->CR |= (1 << 6);
// bit9 运行模式,设置为Restart模式(默认就是0,也可以不设置)
GPT1->CR &= ~(1 << 9);
// 设置分频值,使用PR寄存器,设置为66分频,这样进入GPT1的时钟为66MHz/66=1MHz
GPT1->PR = 65;
// 设置输出比较寄存器1的值,设置为1000000,即500ms
GPT1->OCR[0] = TIMER_DURATION;
// bit0 使能输出比较中断
GPT1->IR = (1 << 0);
// 设置GIC中断
GIC_EnableIRQ(GPT1_IRQn);
// 注册中断服务函数
sys_irq_handle_register(GPT1_IRQn, (system_irq_handler_t)gpt1_timer_irqhandler, NULL);
// 使能GPT1定时器
GPT1->CR |= (1 << 0);
}
2. 中断函数里实现LED闪烁
/**
* @brief GPT中断服务函数
*/
void gpt1_timer_irqhandler(unsigned int gicciar, void *param)
{
static unsigned char state = 0;
if(GPT1->SR & (1 << 0)) {
state = !state;
led_switch(LED0, state);
}
// 清除中断标志位
GPT1->SR |= (1 << 0);
}
三、实现高精度延时
1. 实现高精度延时的做法
上面代码段里,
// bit0 软复位
GPT1->CR = 0;
GPT1->CR = 1 << 15;
while(GPT1->SR & (1 << 15));
// bit1, GPT 定时器计数器的初始值,为1表示默认值为0,0表示为上次关闭时候的值
GPT1->CR |= (1 << 1);
// bit6~8 时钟源,001表示ipg_clk
GPT1->CR |= (1 << 6);
// bit9 运行模式,设置为Restart模式(默认就是0,也可以不设置)
GPT1->CR &= ~(1 << 9);
// 设置分频值,使用PR寄存器,设置为66分频,这样进入GPT1的时钟为66MHz/66=1MHz
GPT1->PR = 65;
这里实现了1MHz的时钟设置,即每个计数是1uS,只要计算 GPT1->CNT 数值即可。
2. 实现us延时
/**
* @brief 延时us
*/
void delay_us(unsigned int us){
unsigned long old_cnt;
unsigned long tcntvalue = 0;
old_cnt = GPT1->CNT;
while(1){
unsigned long new_cnt = GPT1->CNT;
if(new_cnt != old_cnt){
if(new_cnt > old_cnt){
tcntvalue += new_cnt - old_cnt;
}else{
tcntvalue += 0xffffffff - old_cnt + new_cnt;
}
}
if(tcntvalue >= us){
break;
}
}
}
3. 实现 ms 延时
/**
* @brief 延时ms
*/
void delay_ms(unsigned int ms){
while(ms--){
delay_us(1000);
}
}
main 函数:
#include "inc/main.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "bsp_int.h"
//#include "bsp_exti.h"
#include "bsp_epit.h"
#include "bsp_keyfilter.h"
int main(void)
{
bsp_int_init(); /* 初始化中断 */
imx6u_clkinit(); /* 初始化系统时钟 */
clk_enable(); /* 使能外设时钟 */
led_init(); /* 初始化LED */
beep_init(); /* 初始化蜂鸣器 */
// exti_init(); /* 初始化外部中断 */
// epit_init(EPIT1, 0, 66000000 / 2); /* 初始化EPIT1, 1分频, 500ms中断一次 */
// keyfilter_init(); /* 初始化按键 */
// gpt1_timer_init(); /* 初始化GPT1定时器 */
gpt1_delay_init(); /* 初始化GPT1高精度延时 */
while(1) {
delay_ms(500);
led_switch(LED0, 0);
delay_ms(500);
led_switch(LED0, 1);
}
return 0;
}
本文代码开源地址;
https://gitee.com/xundh/learn_i.mx6u.git