您现在的位置是:首页 >技术交流 >I.MX6ull GPT高精度定时器网站首页技术交流

I.MX6ull GPT高精度定时器

江太翁 2024-10-07 00:01:03
简介I.MX6ull GPT高精度定时器

一 简介

GPT的全称是General Purpose Timer,它是一个32位的向上的定时器, GPT 定时器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。GPT 定时器有一个 12 位的分频器,可以对 GPT 定时器的时钟源进行分频。 分析方式同EPTI 

它具有以下特点:

1.一个可选时钟源的32位向上计数器。

2.两个输入捕获通道,可以设置触发方式。

3.三个输出比较通道,可以设置输出模式。

4.可以生成捕获中断、比较中断和溢出中断。

5.计数器可以运行在重新启动(restart)或(自由运行)free-run模式。

二 结构原理图

2.1  时钟源

五个时钟源,分别为:ipg_clk_24M、GPT_CLK(外部时钟)、ipg_clk=66MHz、ipg_clk_32k和ipg_clk_highfreq   

 2.2  内部结构

 2.3 工作原理

        通过设置定时器工作频率确定计数器加1耗时t,延时时间 D/t = N 确定计数器计数值,轮询读取计数器( GPTx_CNT )值为N即为延时 D.

         比如选择ipg_clk =66MHz,然后时钟源通过一个12位的分频器,分频以后进入32位的GPT定时器内部。我们选择分频参数为66,那么进入GPT定时器的频率就是1MHz了。1MHz的周期是1us,也就是说GPT的计数器每加一,就是经过了1us,如果累计加了20个数,就是20us。所以我们可以通过读取GPT计数器中的数值累加的个数,就知道经过的时间了。比如我们现在要延时50us,那么我们进入到延时函数,读取到GPT计数器的当前计数为100,那么当GPT计数器中的值变成200的时候(200-100=100个数),我们就知道延时经过了100us。     

2.3.1 GPT 定时器结构中各部分意义如下:

①、此部分为 GPT 定时器的时钟源,前面已经说过了,本章例程选择 ipg_clk 作为 GPT 定时器时钟源。

②、此部分为 12 位分频器,对时钟源进行分频处理,可设置 0~ 4095,分别对应 1~ 4096 分频。

③、经过分频的时钟源进入到 GPT 定时器内部 32 位计数器。

④和⑤、这两部分是 GPT 的两路输入捕获通道,本章不讲解 GPT 定时器的输入捕获。

⑥、此部分为输出比较寄存器,一共有三路输出比较,因此有三个输出比较寄存器,输出比较寄存器是 32 位的。

⑦、此部分位输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断。

           

2.3.2 工作模式

1 重新启动(restart)模式:

        当 GPTx_CR(x=1,2)寄存器的 FRR 位清零的时候 GPT 工作在此 模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0X00000000 开始向上计数,只有比较通道 1 才有此模式!向比较通道 1 的比较寄存器写入任何 数据都会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会 复位计数器。

2 自由运行(free-run)模式

        当 GPTx_CR(x=1,2)寄存器的 FRR 位置 1 时候 GPT 工作在此模 式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计 数,直到计数值为 0XFFFFFFFF,然后重新回滚到 0X00000000。 接下来看一下 GPT 定时器几个重要的寄存器,第一个就是 GPT 的配置寄存器 GPTx_CR, 此寄存器的结构如图 20.1.1.3 所示:

2.4 重要寄存器

GPTX_CR    GPTX_PR    GPTx_SR  GPTx_CNT

2.4.1 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 定时器。

2.4.2、GPT 定时器的分频寄存器 GPTx_PR 

PRESCALER(bit11:0),这就是 12 位分频值, 可设置 0~4095,分别对应 1~4096 分频

2.4.3 状态寄存器 GPTx_SR

ROV(bit5):回滚标志位,当计数值从 0XFFFFFFFF 回滚到 0X00000000 的时候此位置 1。

IF2~IF1(bit4:3):输入捕获标志位,当输入捕获事件发生以后此位置 1,一共有两路输入捕 获通道。如果使用输入捕获中断的话需要在中断处理函数中清除此位。

OF3~OF1(bit2:0):输出比较中断标志位,当输出比较事件发生以后此位置 1,一共有三路 输出比较通道。如果使用输出比较中断的话需要在中断处理函数中清除此位。

2.4.4 计数寄存器 GPTx_CNT

三 使用实验

通过 GPT 延时500ms 控制LED0 的亮灭 

3.1 配置步骤

  • 1、设置 GPT1 定时器
  • 首先设置 GPT1_CR 寄存器的 SWR(bit15)位来复位寄存器 GPT1。复位完成以后设置寄存 器 GPT1_CR 寄存器的 CLKSRC(bit8:6)位,选择 GPT1 的时钟源为 ipg_clk。设置定时器 GPT1的工作模式, Restart模式,当CNT等于OCR1的时候就产生中断,为了实现较大的计数,我们将比较值设置为最大的0XFFFFFFFF
  • 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 延时分别编写两个延时函数。 

3.2 代码 

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_delay.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : 延时文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/4 左忠凯创建

		 V2.0 2019/1/15	左忠凯修改
		 使用定时器GPT实现高精度延时,添加了:
		 delay_init 延时初始化函数
		 gpt1_irqhandler gpt1定时器中断处理函数
		 delayus us延时函数
		 delayms 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<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);
	}
}


风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。