目录
GPT 定时器简介
相关寄存介绍
定时器实现高精度延时原理
实验源码
学过STM32应该知道,在使用STM32 的时候可以使用SYSTICK来实现高精度延时。IMX6U没有SYSTICK定时器,但是I.MX6U有其他定时器,EPIT定时器。使用I.MX6U的GPT 定时器来实现高精度延时,顺便学习一下GPT定时器,GPT定时器全称为General Purpose Timer.GPT定时器是一个32位向上定时器(也就是从0X00000000开始向上递增计数), GPT定时器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。GPT定时器有一个12位的分频器,可以对GPT定时器的时钟源进行分频, GPT定时器特性如下:
1.一个可选时钟源的32位向上计数器。
2.两个输入捕获通道,可以设置触发方式。
3.三个输出比较通道,可以设置输出模式。
4.可以生成捕获中断、比较中断和溢出中断。
5.计数器可以运行在重新启动(restart)或(自由运行)free-run模式。
GPT定时器的可选时钟源如图所示:
从图可以看出一共有五个时钟源,分别为:ipg_clk_24M、GPT_CLK(外部时钟)、ipg_clk、ipg_clk_32k和ipg_clk_highfreq
GPT定时器结构如图所示:
1.此部分为GPT定时器的时钟源,前面已经说过了
2.此部分为12位分频器,对时钟源进行分频处理,可设置0~4095,分别对应1~4096分频。
3.经过分频的时钟源进入到 GPT 定时器内部 32 位计数器。
4和5这两部分是GPT的两路输入捕获通道。
6.此部分为输出比较寄存器,一共有三路输出比较,因此有三个输出比较寄存器,输出比较寄存器是32位的。
7.此部分位输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断。
GPT定时器有两种工作模式:重新启动(restart)模式和自由运行(free-run)模式,这两个工作模式的区别如下:
重新启动(restart)模式:当 GPTx_CR(x=1, 2)寄存器的FRR位清零的时候GPT工作在此模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0X0000000 开始向上计数,只有比较通道1才有此模式!向比较通道1的比较寄存器写入任何数据都会复位GP 计数器。对于其他两路比较通道(通道2和3),当发生比较事件以后不会复位计数器。
自由运行(free-run)模式:当GPTx_CR(x=1, 2)寄存器的FRR位置1时候GPT工作在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为0xFFFFFFFF,然后重新回滚到0x00000000。
GPT的配置寄存器GPTx_CR,此寄存器的结构如图所示:
SWR(bit15):复位GPT定时器,向此位写1就可以复位GPT定时器,当GPT复位完成以后此为会自动清零。
FRR(bit9):运行模式选择,当此位为0的时候比较通道1工作在重新启动(restart)模式。当此位为1的时候所有的三个比较通道均工作在自由运行模式(free-run)。
CLKSRC(bit8:6):GPT定时器时钟源选择位,为0的时候关闭时钟源,为1的时候选择ipg_clk作为时钟源,为2的时候选择ipg_clk_highfreq为时钟源,为3的时候选择外部时钟为时钟源,为4的时候选择ipg_clk_32k为时钟源,为5的时候选择ip_clk_24M为时钟源。
ENMOD(bit1): GPT使能模式,此位为0的时候如果关闭GPT定时器,计数器寄存器保存定时器关闭时候的计数值。此位为1的时候如果关闭GPT定时器,计数器寄存器就会清零。
EN(bit): GPT使能位,为1的时候使能GPT定时器,为0的时候关闭GPT定时器。
GPT定时器的分频寄存器GPTx_PR,此寄存器结构如图所示:
PRESCALER(bit11:0),这就是12位分频值,可设置0-4095,分别对应1-4096分频。
GPT定时器的状态寄存器GPTx_SR,此寄存器结构如图所示:
ROV(bit5):回滚标志位,当计数值从0XFFFFFFFF回滚到0X00000000的时候此位置1。
IF2-IF1(bit4:3):输入捕获标志位,当输入捕获事件发生以后此位置1,一共有两路输入捕获通道。如果使用输入捕获中断的话需要在中断处理函数中清除此位。
OF3-OF1(bit2:0):输出比较中断标志位,当输出比较事件发生以后此位置1,一共有三路输出比较通道。如果使用输出比较中断的话需要在中断处理函数中清除此位。
GPT定时器的计数寄存器GPTx_CNT,这个寄存器保存着GPT定时器的当前器计数值。
GPT定时器的输出比较寄存器GPTx_OCR,每个输出比较通道对应一个输出比较寄存器,因此一个 GPT 定时器有三个OCR寄存器,它们的作都是相同的。以输出比较通道1为例,其输出比较寄存器为GPTx_OCR1,这是一个32位寄存器,用于存放32位的比较值。当计数器值和寄存器 GPTx_OCRI中的值相等就会产生比较事件,如果使能了比较中断的话就会触发相应的中断。
高精度延时函数的实现肯定是要借助硬件定时器,如果设置 GPT 定时器的时钟源为ipg_clk=66MHz,设置66分频,那么进入GPT定时器的最终时钟频率就是66/66=1MHz,周期为1us。GPT的计数器每计一个数就表示“过去”了1us,如果计10个数就表示“过去”了10us。通过读取寄存器 GPTx_CNT中的值就知道计了个数,比如现在要延时100us,那么进入延时函数以后纪录下寄存器 GPT×_CNT中的值为200,当GPTx_CNT中的值为300的时候就表示100us过去了,也就是延时结束。GPTx_CNT是个32位寄存器,如果时钟为1MHz的话, GPTx_CNT最多可以实现0XFFFFFFFFus=4294967295us≈4294s≈72min。也就是说72分钟以后GPTx_CNT寄存器就会回滚到0X00000000,,也就是溢出,所以需要在延时函数中要处理溢出的情况。
高精度延时的实现步骤如下:
1.设置GPT定时器首先设置GPT_CR寄存器的SWR(bit15)位来复位寄存器GPT复位完成以后设置寄存器GPTI_CR寄存器的CLKSRC(bit8:6)位,选择GPT的时钟源为ipg_clk。设置定时器GPT的工作模式。
2.设置GPT的分频值
设置寄存器GPT-PR寄存器的PRESCALAR(bit111:0)位,设置分频值。
3.设置GPT的比较值
如果要使用GPT的输出比较中断,那么GPT的输出比较寄存器GPT1_OCR的值可以根据所需的中断时间来设置。
4.使能GPT定时器设置好GPT定时器以后就可以使能了,设置GPTI_CR的EN(bit0)位为1来使能GPT定时器。
5.编写延时函数GPT定时器已经开始运行了,可以根据前面介绍的高精度延时函数原理来编写延时函数,针对us和ms延时分别编写两个延时函数。
#ifndef __BSP_DELAY_H
#define __BSP_DELAY_H
#include "imx6ul.h"
/* 函数声明 */
void delay(volatile unsigned int n);
void gpt1_irqhandler(unsigned int gicciar, void *param);
void delay_init(void);
void delay(volatile unsigned int n);
void delay_short(volatile unsigned int n);
void delay_us(unsigned int usdelay);
void delay_ms(unsigned int msdelay);
#endif
#include "bsp_delay.h"
#include "bsp_interrupt.h"
#include "bsp_led.h"
/*
* @description : 延时初始化函数
* @param : 无
* @return : 无
*/
void delay_init(void)
{
GPT1->CR =0;
/*软复位*/
GPT1->CR = 1<<15;
while((GPT1->CR = 1<<15) & 0x01);
/*设置GPT1时钟源为ipg clk=66M, restart模式
默认计数寄存器从0开始。*/
GPT1->CR = (1<<6);
/*分频设置*/
GPT1->PR = 65;/* 66分频,频率=66000000/66=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*/
}
/*
* @description : 微秒(us)级延时
* @param - value : 需要延时的us数,最大延时0XFFFFFFFFus
* @return : 无
*/
void delay_us(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 delay_ms(unsigned int msdelay)
{
int i = 0;
for(i=0; i
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key_filter.h"
/*
* @description : main函数
* @param : 无
* @return : 无
*/
int main(void)
{
unsigned char state = OFF;
int_init(); /* 初始化中断(一定要最先调用!)*/
imx6u_clkinit(); /* 初始化系统时钟*/
clk_enable(); /* 使能所有的时钟*/
delay_init(); /* 延时初始化 */
led_init(); /* 初始化led*/
beep_init(); /* 初始化凤鸣器*/
while(1)
{
state =! state;
led_switch(LED0, state);
delay_ms(100);
}
return 0;
}