EPIT定时器实验

EPIT定时器简介

EPIT 的全称是:Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器。

EPIT 是一个 32 位定时器,EPIT 定时器有如下特点:

  1. 时钟源可选的 32 位向下计数器;

  2. 12 位的分频值;

  3. 当计数值和比较值相等的时候产生中断。

EPIT 定时器结构如图:

../../_images/image-20210821165947847.png

  1. 多路选择器,用来选择 EPIT 定时器的时钟源,EPIT 共有 3 个时钟源可选择;

    • ipg_clk

    • ipg_clk_32k

    • ipg_clk_highfreq

  2. 12 位的分频器,负责对时钟源进行分频,12 位对应的值是 0~4095,对应着1~4096 分频;

  3. 经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:

    • 计数寄存器(EPIT_CNR)

    • 加载寄存器(EPIT_LR)

    • 比较寄存器(EPIT_CMPR)

    这三个寄存器都是 32 位的。EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0,EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件。

  4. 比较器。

  5. EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号。

  6. 产生比较中断,也就是定时中断。

EPIT定时器工作模式

EPIT 定时器有两种工作模式:set-and-forget 和 free-running,这两个工作模式的区别如下:

  • set-and-forget 模式:EPIT_CR寄存器的 RLD 位置 1

    计数器从加载寄存器 EPIT_LR 中获取初始值,你不能直接写入初始值。每当计数器达到零时,EPIT_LR中的值就会加载到计数器中,计数器再次将此值减到零,周而复始。

  • free-running 模式:EPIT_CR 寄存器的 RLD 位清零

    计数器递减到0000 0000h时,会翻转到FFFF FFFFh。不会从加载寄存器重新加载新值。翻转后,计数器继续递减计数。如果要直接初始化计数器,需要设置EPIT计数器覆盖使能位(EPIT_CR[IOVW]),并将所需的初始化值写入EPIT_LR。

EPIT定时器重要的寄存器

Control register(EPITx_CR) 控制寄存器

EPIT控制寄存器,用来配置EPIT。

../../_images/wps1-1629538926510.jpgimg

位域 读写 描述
[25:24] CLKSRC R/W 时钟源选择,00:时钟源断开;01:Peripheral clk,即ipg_clk;10:Hign-frequency,即ipg_clk_highfreq;11:low-frequency,即ipg_clk_32k
[23:22] OM R/W 用来设置输出通道模式,00:输出引脚跟EPIT断开,即输出引脚不受影响;01:输出引脚翻转;10:输出引脚清0;11:输出引脚置位
[21] STOPEN R/W stop mode时EPIT是否使能,0:在stop mode下,EPIT禁止1:在stop mode下,EPIT仍然使能
[19] WAITEN R/W Wait mode时EPIT是否使能,0:在wait mode下,EPIT禁止1:在wait mode下,EPIT仍然使能
[18] DBGEN R/W Debug mode时EPIT是否使能,0:在debug mode下,EPIT禁止1:在debug mode下,EPIT仍然使能
[17] IOVW EPIT计数器覆盖使能位,0:写入EPIT_LR的值不会覆盖EPIT计数器;1:写入EPIT_LR的值会马上覆盖EPIT计数器的值
[16] SWR R/W 软件复位,这位会自动清零,a. EPIT在复位状态时,该位自动置1b. 复位结束时,该位自动清0c. 设置该位为1时,会把所有寄存器设置为它们的默认值,EPITx_CR中这些位不受影响:EN、ENMOD、STOPEN、WAITEN、DBGEN
[15:4] PRESCALER R/W EPIT时钟的分频系数,0x000:除以1;0x001:除以2;……0xFFF:除以4096
[3] RLD R/W 计数器模式(计数器加载模式),0:free-running mode,计数器到达0时,变为0xFFFFFFFF;1:set-and-forget mode,计数器到达0时,加载EPITx_LR的值
[2] OCIEN R/W 输出比较中断使能,0:比较事件发生时,中断禁止;1:比较事件发生时,中断使能
[1] ENMOD R/W 当EPIT重新使能后,主计数值从什么值开始计数:0:从上次关闭时的计数值继续计数;1:如果RLD为1,从加载计数器开始计数;如果RLD为0,从0xFFFF_FFFF开始计数;EPIT重新使能时,预分频计数器总是从0开始计数。
[0] EN R/W EPIT使能位,0:EPIT禁止;1:EPIT使能

Status register (EPITx_SR)状态寄存器

EPIT状态寄存器,只有一个状态位,用来表示输出比较事件是否发生,写1清除掉该位。

../../_images/wps2.jpgimg

位域 读写 描述
[0] OCIF R/W Output compare interrupt flag,0:比较事件未发生;1:比较事件已发生

Load register (EPITx_LR)加载寄存器

EPIT加载寄存器,如果EPIT_CR的RLD置位的话,当EPIT计数器计数到0时,会将EPIT_LR值加载到计数器中。

如果设置了IOVW位,往该寄存器写值会同时覆盖掉计数器的值。这个覆盖特性与RLD是否设置无关。

../../_images/wps3.jpgimg

位域 读写 描述
[31:0] LOAD R/W 加载值

Compare register (EPITx_CMPR)比较寄存器

EPIT比较寄存器,当主计数器的值等于比较寄存器时,产生比较事件。

../../_images/wps4.jpgimg

位域 读写 描述
[31:0] COMPARE R/W 比较值

Counter register (EPITx_CNR)计数寄存器

EPIT计数器,主计数值,它是只读寄存器;读取EPIT计数器的值,不影响计数过程。如果控制寄存器的IOVW位设置的话,当往加载寄存器EPIT_IR写值时会覆盖当前的计数值。

../../_images/wps5.jpgimg

位域 读写 描述
[31:0] COUNT R/W EPIT计数器的值

代码分析

参见【13.epit_timer】

bsp_epittimer.c


#include "bsp_epittimer.h"
#include "bsp_int.h"

volatile uint32_t g_sys_tick_cnt = 0;
/*
 * @description		: 初始化EPIT定时器.
 *					  EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz		 
 * @param - frac	: 分频值,范围为0~4095,分别对应1~4096分频。
 * @param - value	: 倒计数值。
 * @return 			: 无
 */
void epit1_init(unsigned int frac, unsigned int value)
{
	if(frac > 0XFFF)
		frac = 0XFFF;
		
	EPIT1->CR = 0;	/* 先清零CR寄存器 */
	
	/*
     * CR寄存器:
     * bit25:24 01 时钟源选择Peripheral clock=66MHz
     * bit15:4  frac 分频值
     * bit3:	1  当计数器到0的话从LR重新加载数值
     * bit2:	1  比较中断使能
     * bit1:    1  初始计数值来源于LR寄存器值
     * bit0:    0  先关闭EPIT1
     */
	EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);
	
	EPIT1->LR = value;	/* 倒计数值 */
	EPIT1->CMPR	= 0;	/* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 */

	/* 使能GIC中对应的中断 			*/
	GIC_EnableIRQ(EPIT1_IRQn);

	/* 注册中断服务函数 			*/
	system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL);	

	EPIT1->CR |= 1<<0;	/* 使能EPIT1 */ 
}

/*
 * @description			: EPIT中断处理函数
 * @param				: 无
 * @return 				: 无
 */
void epit1_irqhandler(void)
{ 

	if(EPIT1->SR & (1<<0)) 			/* 判断比较事件发生 */
	{
		g_sys_tick_cnt++;
	}
	
	EPIT1->SR |= 1<<0; 				/* 清除中断标志位 */
}

main.c

int main(void)
{
	int_init(); 		/* 初始化中断(一定要最先调用!) */
	imx6u_clkinit();
	clk_enable();		/* 使能所有的时钟 			*/
	rgb_led_init();		/* 初始化led 			*/
	uart_init();		/* 初始化串口,波特率115200 */
  	beep_init();
	key_int_init();
	epit1_init(0, 66000000);	/* 初始化EPIT1定时器,1分频 如果是66M时钟,那么计数初始值66M 即1s产生一个中断 */

    printf("EPIT timer demo c=%d\r\n",c);
	while(1)			
	{	
		 if(g_sys_tick_cnt>=1){
			 g_sys_tick_cnt = 0;
			 LED_RGB_BLUE_TOG(); // 蓝色灯1s翻转一次
		 }
	}

	return 0;
}

实验现象

可以看出1s时间输出一次,且蓝灯1s闪烁一次

../../_images/image-20210821173215322.png