高精度延时简介
GPT定时器简介
延时函数方式:普通循环函数、硬件定时器
本文使用硬件定时器GPT(General Purpose Timer) 来实现高精度延时。
GPT 定时器是一个 32 位向上定时器(也就是从 0X00000000 开始向上递增计数), GPT 定时器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。GPT 定时器有一个 12 位的分频器,可以对 GPT 定时器的时钟源进行分频, GPT 定时器特性如下:
①、一个可选时钟源的 32 位向上计数器。
②、两个输入捕获通道,可以设置触发方式。
③、三个输出比较通道,可以设置输出模式。
④、可以生成捕获中断、比较中断和溢出中断。
⑤、计数器可以运行在重新启动(restart)或(自由运行)free-run 模式。
GPT 定时器的可选时钟源如图:
共有五个时钟源,本文选择ipg_clk 为 GPT 的时钟源66M。
①、此部分为 GPT 定时器的时钟源,前面已经说过了,本章例程选择 ipg_clk 作为 GPT 定时器时钟源。
②、此部分为 12 位分频器,对时钟源进行分频处理,可设置 0~ 4095,分别对应 1~ 4096 分频。
③、经过分频的时钟源进入到 GPT 定时器内部 32 位计数器。
④和⑤、这两部分是 GPT 的两路输入捕获通道,本章不讲解 GPT 定时器的输入捕获。
⑥、此部分为输出比较寄存器,一共有三路输出比较,因此有三个输出比较寄存器,输出比较寄存器是 32 位的。
⑦、此部分位输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断。
GPT 定时器有两种工作模式:重新启动(restart)模式和自由运行(free-run)模式。
重新启动(restart)模式:当 GPTx_CR(x=1, 2)寄存器的 FRR 位清零的时候 GPT 工作在此模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0X00000000 开始向上计数, 只有比较通道 1 才有此模式! 向比较通道 1 的比较寄存器写入任何数据都会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会复位计数器。
自由运行(free-run)模式:当 GPTx_CR(x=1, 2)寄存器的 FRR 位置 1 时候 GPT 工作在此模式下,此模式适用于所有三个比通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为 0XFFFFFFFF,然后重新回滚到 0X00000000。
GPT 的配置寄存器 GPTx_CR:
寄存器 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 为时钟源。
本文选择 ipg_clk 作为 GPT 定时器的时钟源,因此此位设置位 1(0b001)。
ENMOD(bit1): GPT 使能模式,
此位为 0 的时候如果关闭 GPT 定时器,计数器寄存器保存定时器关闭时候的计数值。
此位为 1 的时候如果关闭 GPT 定时器,计数器寄存器就会清零。
EN(bit): GPT 使能位,
为 1 的时候使能 GPT 定时器,
为 0 的时候关闭 GPT 定时器。
GPT 定时器的分频寄存器 GPTx_PR:
寄存器 GPTx_PR 我们用到的重要位就一个: PRESCALER(bit11:0),这就是 12 位分频值,可设置 0~ 4095,分别对应 1~ 4096 分频。
GPT 定时器的状态寄存器 GPTx_SR:
寄存器 GPTx_SR 重要的位如下:
ROV(bit5): 回滚标志位,当计数值从 0XFFFFFFFF 回滚到0X00000000 的时候此位置 1。
IF2~IF1(bit4:3): 输入捕获标志位,当输入捕获事件发生以后此位置 1,一共有两路输入捕获通道。如果使用输入捕获中断的话需要在中断处理函数中写1清除此位。
OF3~OF1(bit2:0):输出比较中断标志位,当输出比较事件发生以后此位置 1,一共有三路输出比较通道。如果使用输出比较中断的话需要在中断处理函数中写1清除此位。
计数寄存器 GPTx_CNT,32 位寄存器。
三个输出比较寄存器GPTx_OCR,32寄存器。
定时器实现高精度延时原理
如果设置 GPT 定时器的时钟源为 ipg_clk=66MHz,设置 66 分频,那么进入 GPT定时器的最终时钟频率就是 66/66=1MHz,周期为 1us。GPT 的计数器每计一个数就表示“过去”了 1us。如果计 10 个数就表示“过去”了 10us。通过读取寄存器 GPTx_CNT 中的值就知道计了个数,比如现在要延时 100us,那么进入延时函数以后纪录下寄存器 GPTx_CNT 中的值为 200,当 GPTx_CNT 中的值为 300 的时候就表示 100us 过去了,也就是延时结束。
高精度延时的实现步骤如下:
1、设置 GPT1 定时器
首先设置 GPT1_CR 寄存器的 SWR(bit15)位来复位寄存器 GPT1。复位完成以后设置寄存器 GPT1_CR 寄存器的 CLKSRC(bit8:6)位,选择 GPT1 的时钟源为 ipg_clk。设置定时器 GPT1的工作模式,
2、设置 GPT1 的分频值
设置寄存器 GPT1_PR 寄存器的 PRESCALAR(bit111:0)位,设置分频值。
3、设置 GPT1 的比较值
如果要使用 GPT1 的输出比较中断,那么 GPT1 的输出比较寄存器 GPT1_OCR1 的值可以根据所需的中断时间来设置。本文不使用比较输出中断,所以将 GPT1_OCR1 设置为最大值,即:0XFFFFFFFF。
4、 使能 GPT1 定时器
设置好 GPT1 定时器以后就可以使能了,设置 GPT1_CR 的 EN(bit0)位为 1 来使能 GPT1 定时器。
5、编写延时函数
GPT1定时器已经开始运行了,可以根据前面介绍的高精度延时函数原理来编写延时函数,针对 us 和 ms 延时分别编写两个延时函数。
小结
硬件定时器GPT和上一章硬件定时器EPIT结构类似,关键还是脑子里有那个结构图,对于寄存器的配置将会得心应手。
驱动编写
bsp_delay.h
#ifndef _BSP_DELAY_H
#define _BSP_DELAY_H
#include "imx6ul.h"
void delay_init(void);
void delayus(unsigned int usvalue);
void delayms(unsigned int msdelay);
#endif
bsp_delay.c
#include "bsp_delay.h"
/*延时有关硬件初始化,主要是 GPT 定时器 GPT 定时器时钟源选择 ipg_clk=66Mhz */
void delay_init(void)
{
//1、CR寄存器
GPT1->CR = 0;
GPT1->CR |= (1<<15); //bit15 置 1 进入软复位
while((GPT1->CR>>15) & 0x01) ; //等待复位完成
GPT1->CR &= ~(1<<9); //重新启动(restart)模式
GPT1->CR |= (1<<6); //定时器时钟源选择 ipg_clk=66Mhz
//2、PR寄存器 66分频
GPT1->PR = 66-1;
//3、OCR1
GPT1->OCR[0] = 0xffffffff;
//使能 GPT1
GPT1->CR |= (1<<0);
}
/*微秒(us)级延时 原子哥这种方法很妙 就是记录计数走过的总时间*/
void delayus(unsigned int usvalue)
{
unsigned long oldcnt,newcnt,tcntvalue=0; //tcntvalue为总计数
oldcnt=GPT1->CNT; //记录一下旧计数值
while(1)
{
newcnt=GPT1->CNT; //每次循环记录一下当前计数值
if(newcnt!=oldcnt)
{
if(newcnt>oldcnt)
tcntvalue += newcnt-oldcnt; //GPT 是向上计数器,并且没有溢出
else
tcntvalue += 0xffffffff-oldcnt+newcnt; //发生溢出
oldcnt = newcnt; //更新一下旧计数值
if(tcntvalue>=usvalue)
break; //延时时间到了 跳出
}
}
}
/*毫秒(ms)级延时 */
void delayms(unsigned int msdelay)
{
while(msdelay--)
delayus(1000);
}
小结
这个使用GPT硬件定时器延时的函数其实很简单,先像EPIT那样配置寄存器,设置循环计数(1MHz每次计数1us),延时函数中只通过CNT寄存器读取计数值作为旧计数值和当前计数值,记录总的计数时间,达到输入的计数值即完成延时任务。