本次实验我们使用I.MX6U的GPT定时器来实现高精度延时,顺便学习一下GPT定时器,GPT定时器全称为General Purpose Timer。
GPT定时器是一个32位向上定时器(也就是从0X00000000开始向上递增计数),GPT定时器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。GPT定时器有一个12位的分频器,可以对GPT定时器的时钟源进行分频,GPT定时器特性如下:
①、一个可选时钟源的32位向上计数器。
②、两个输入捕获通道,可以设置触发方式。
③、三个输出比较通道,可以设置输出模式。
④、可以生成捕获中断、比较中断和溢出中断。
⑤、计数器可以运行在重新启动(restart)或(自由运行)free-run模式。
GPT的时钟源:
GPT的结构组成:
①、此部分为GPT定时器的时钟源,本次实验选择ipg_clk作为GPT定时器时钟源。
②、此部分为12位分频器,对时钟源进行分频处理,可设置0~4095,分别对应1~4096分频。
③、经过分频的时钟源进入到GPT定时器内部32位计数器。
④和⑤、这两部分是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定时器几个重要的寄存器,第一个就是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为时钟源。本章例程选择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,一共有两路输入捕获通道。如果使用输入捕获中断的话需要在中断处理函数中清除此位。
OF3~OF1(bit2:0):输出比较中断标志位,当输出比较事件发生以后此位置1,一共有三路输出比较通道。如果使用输出比较中断的话需要在中断处理函数中清除此位。
接着看一下GPT定时器的计数寄存器GPTx_CNT,这个寄存器保存着GPT定时器的当前计数值。最后看一下GPT定时器的输出比较寄存器GPTx_OCR,每个输出比较通道对应一个输出比较寄存器,因此一个GPT定时器有三个OCR寄存器,它们的作都是相同的。以输出比较通道1为例,其输出比较寄存器为GPTx_OCR1,这是一个32位寄存器,用于存放32位的比较值。当计数器值和寄存器GPTx_OCR1中的值相等就会产生比较事件,如果使能了比较中断的话就会触发相应的中断。
高精度延时函数的实现肯定是要借助硬件定时器,前面说了本实验使用GPT定时器来实现高精度延时。如果设置GPT定时器的时钟源为ipg_clk=66MHz,设置66分频,那么进入GPT定时器的最终时钟频率就是66/66=1MHz,周期为1us。GPT的计数器每计一个数就表示“过去”了1us。如果计10个数就表示“过去”了10us。通过读取寄存器GPTx_CNT中的值就知道计了个数,比如现在要延时100us,那么进入延时函数以后纪录下寄存器GPTx_CNT中的值为200,当GPTx_CNT中的值为300的时候就表示100us过去了,也就是延时结束。GPTx_CNT是个32位寄存器,如果时钟为1MHz的话,GPTx_CNT最多可以实现0XFFFFFFFFus=4294967295us≈4294s≈72min。也就是说72分钟以后GPTx_CNT寄存器就会回滚到0X00000000,也就是溢出,所以需要在延时函数中要处理溢出的情况。关于定时器实现高精度延时的原理就讲解到这里,原理还是很简单的,高精度延时的实现步骤如下:
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延时分别编写两个延时函数。
-
- #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
- {
- 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);
- }
- }
-
-
-
-