13. 高精度延时
- GPT 定时器简介
- GPT 定时器结构
- GPT 定时器工作模式
- GPT 定时器相关寄存器
- GPTx_CR
- GPTx_PR
- GPTx_SR
- GPTx_CNT
- GPTx_OCR
- GPT 配置步骤
- 程序编写
- bsp_delay.h
- bsp_delay.c
- main
GPT 定时器简介
GPT 定时器是一个 32 位向上定时器,也就是从0x00000000 开始向上递增计数,GPT 定时器也可以跟一个值进行比较,当计数器值和这个值相等的时候就会发生比较时间,产生比较中断。GPT 定时器有一个 12 位的分频器,可以对 GPT 定时器的时钟源进行分频,GPT 定时器特性:
- 一个可选时钟源的 32 位向上计数,本次选择 ipg_clk = 66MHz
- 两个输入捕获通道,可以设置触发方式
- 三个输出比较通道,可以设置输出模式
- 可以生成捕获中断、比较中断和溢出中断
- 计数器可以运行在重新启动(restart)或自由运行(free-run) 模式
GPT 定时器结构
1. 此部分是 GPT 定时器的时钟源
2. 此部分为 12 位分频器,对时钟源进行分频处理,可设置 0 ~ 4095 分频,分别对应 1 ~ 4096 分频
3. 经过分频的时钟源进入到 GPT 定时器内部 32 位计数器
4. GPT 的两路输入捕获通道
5. GPT 的两路输入捕获通道
6. 此部分为输出比较寄存器,一共有三路输出比较,因此由三个 32 位输出比较寄存器,
7. 此部分为输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断
GPT 定时器工作模式
重新启动模式: 当 GPTx_CR(x=1,2)寄存器的 FRR 位清零的时候 GPT 工作在此模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0x00000000 开始向上计数,只有比较通道 1 才有此模式。向比较通道 1 的比较寄存器写入任何数据都会复位 GPT 计数器。对于其他两路比较通道,当发生比较事件以后不会复位计数器
自由运行模式: 当 GPTx_CR 寄存器的 FRR 位置 1 的时候 GPT 工作在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为 0xFFFFFFFF,然后重新回滚到 0x00000000
GPT 定时器相关寄存器
GPTx_CR
SWR(bit15): 复位 GPT 定时器,向此位写 1 就可以复位 GPT 定时器,当 GPT 复位完成后会自动清零
FRR(bit9): 运行模式选择,当此位为 0 的时候比较通道 1 工作在重新启动,为 0 的时候三个比较通道均工作在自由运行模式
CLKSRC(bit8:6): GPT 定时器时钟选择位,为 0 的时候关闭时钟源;为 1 的时候选择 ipg_clk 作为时钟源;为 2 的时候选择 ipg_clk_highfrq 为时钟源;为 3 的时候选择外部时钟作为时钟源;为 4 的时候选择 ipg_clk_32k 为时钟源;为 5 的时候选择 ipg_clk_24M 为时钟源
ENMOD(bit1): GPT 使能模式,此位为 0 的时候如果关闭 GPT 定时器,计数器寄存器保存定时器关闭时候的计数值,为 1 的时候如果关闭 GPT 定时器,计数器寄存器就会清零
EN(bit0): GPT 使能位,1 打开 GPT 定时器,0 关闭 GPT 定时器
GPTx_PR
PRESCALER(bit11:0): 这就是 12 位分频值
GPTx_SR
ROV(bit5): 回滚标志位,当计数值从 0xFFFFFFFF 回滚到 0x00000000 的时候此位置 1
IF2 ~ IF1(bit4:3): 输入捕获标志位,当输入捕获事件发生以后此位置 1,一共有两路输入捕获通道,如果使用输入捕获中断的话需要在中断处理函数中清除此位
OF3 ~ OF1(bit2:0): 输出比较中断标志位,当输出比较事件发生以后此位置 1,一共有三路输出比较通道。如果使用输出比较中断的话需要在中断处理函数中清除此位
GPTx_CNT
这个寄存器保存着 GPT 定时器的当前计数值
GPTx_OCR
这个寄存器是输出比较寄存器,每个输出比较通道对应一个输出比较寄存器,因此一个 GPT 定时器有三个 OCR 寄存器,他们的作用都是相同的。这时一个 32 位的寄存器,用于存放 32 位的比较值,当计数器值和比较寄存器的值相等就会触发比较事件,如果使能了比较中断就会触发相应的中断
GPT 配置步骤
- 设置 GPT1 定时器
首先设置 GPT1_CR 寄存器的 SWR(bit15) 位来复位 GPT1,然后设置 GPT1_CR 的CLKSRC(bit8:6) 位,选择时钟源为 ipg_clk,设置定时器工作模式 - 设置 GPT1 的分频值
- 设置 GPT1 的比较值
- 使能 GPT1 定时器
- 编写延时函数
程序编写
bsp_delay.h
#pragma once#include "imx6ul.h"
void delay_init(void);
void delayus(unsigned int usdelay);
void delayms(unsigned int msdelay);
void delay(volatile unsigned int n);
void gpt1_irqhandler(void);
bsp_delay.c
#include "bsp_delay.h"/** @description : 延时有关硬件初始化,主要是GPT定时器GPT定时器时钟源选择ipg_clk=66Mhz* @param : 无* @return : 无*/
void delay_init(void)
{GPT1->CR = 0; /* 清零,bit0也为0,即停止GPT */GPT1->CR = 1 << 15; /* bit15置1进入软复位 */while((GPT1->CR >> 15) & 0x01); /*等待复位完成 *//** GPT的CR寄存器,GPT通用设置* bit22:20 000 输出比较1的输出功能关闭,也就是对应的引脚没反应* bit9: 0 Restart模式,当CNT等于OCR1的时候就产生中断* bit8:6 001 GPT时钟源选择ipg_clk=66Mhz* bit*/GPT1->CR = (1<<6);/** GPT的PR寄存器,GPT的分频设置* bit11:0 设置分频值,设置为0表示1分频,* 以此类推,最大可以设置为0XFFF,也就是最大4096分频*/GPT1->PR = 65; /* 设置为65,即66分频,因此GPT1时钟为66M/(65+1)=1MHz *//** GPT的OCR1寄存器,GPT的输出比较1比较计数值,* GPT的时钟为1Mz,那么计数器每计一个值就是就是1us。* 为了实现较大的计数,我们将比较值设置为最大的0XFFFFFFFF,* 这样一次计满就是:0XFFFFFFFFus = 4294967296us = 4295s = 71.5min* 也就是说一次计满最多71.5分钟,存在溢出*/GPT1->OCR[0] = 0XFFFFFFFF;GPT1->CR |= 1<<0; //使能GPT1/* 一下屏蔽的代码是GPT定时器中断代码,* 如果想学习GPT定时器的话可以参考一下代码。*/
#if 0/** GPT的PR寄存器,GPT的分频设置* bit11:0 设置分频值,设置为0表示1分频,* 以此类推,最大可以设置为0XFFF,也就是最大4096分频*/GPT1->PR = 65; //设置为1,即65+1=66分频,因此GPT1时钟为66M/66=1MHz/** GPT的OCR1寄存器,GPT的输出比较1比较计数值,* 当GPT的计数值等于OCR1里面值时候,输出比较1就会发生中断* 这里定时500ms产生中断,因此就应该为1000000/2=500000;*/GPT1->OCR[0] = 500000;/** GPT的IR寄存器,使能通道1的比较中断* bit0: 0 使能输出比较中断*/GPT1->IR |= 1 << 0;/** 使能GIC里面相应的中断,并且注册中断处理函数*/GIC_EnableIRQ(GPT1_IRQn); //使能GIC中对应的中断system_register_irqhandler(GPT1_IRQn, (system_irq_handler_t)gpt1_irqhandler, NULL); //注册中断服务函数
#endif}#if 0
/* 中断处理函数 */
void gpt1_irqhandler(void)
{ static unsigned char state = 0;state = !state;/** GPT的SR寄存器,状态寄存器* bit2: 1 输出比较1发生中断*/if(GPT1->SR & (1<<0)) {led_switch(LED2, state);}GPT1->SR |= 1<<0; /* 清除中断标志位 */
}
#endif/** @description : 微秒(us)级延时* @param - value : 需要延时的us数,最大延时0XFFFFFFFFus* @return : 无*/
void delayus(unsigned int usdelay)
{unsigned long oldcnt,newcnt;unsigned long tcntvalue = 0; /* 走过的总时间 */oldcnt = GPT1->CNT;while(1){newcnt = GPT1->CNT;if(newcnt != oldcnt){if(newcnt > oldcnt) /* GPT是向上计数器,并且没有溢出 */tcntvalue += newcnt - oldcnt;else /* 发生溢出 */tcntvalue += 0XFFFFFFFF-oldcnt + newcnt;oldcnt = newcnt;if(tcntvalue >= usdelay)/* 延时时间到了 */break; /* 跳出 */}}
}/** @description : 毫秒(ms)级延时* @param - msdelay : 需要延时的ms数* @return : 无*/
void delayms(unsigned int msdelay)
{int i = 0;for(i=0; i<msdelay; i++){delayus(1000);}
}/** @description : 短时间延时函数* @param - n : 要延时循环次数(空操作循环次数,模式延时)* @return : 无*/
void delay_short(volatile unsigned int n)
{while(n--){}
}/** @description : 延时函数,在396Mhz的主频下* 延时时间大约为1ms* @param - n : 要延时的ms数* @return : 无*/
void delay(volatile unsigned int n)
{while(n--){delay_short(0x7ff);}
}
main
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_keyfilter.h"/** @description : main函数* @param : 无* @return : 无*/
int main(void)
{unsigned char state = OFF;int_init(); /* 初始化中断(一定要最先调用!) */imx6u_clkinit(); /* 初始化系统时钟 */delay_init(); /* 初始化延时 */clk_enable(); /* 使能所有的时钟 */led_init(); /* 初始化led */beep_init(); /* 初始化beep */while(1) { state = !state;led_switch(LED0, state);delayms(500);}return 0;
}