# EPIT定时器实验 ## EPIT定时器简介 EPIT 的全称是:Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器。 EPIT 是一个 32 位定时器,EPIT 定时器有如下特点: 1. 时钟源可选的 32 位向下计数器; 2. 12 位的分频值; 3. 当计数值和比较值相等的时候产生中断。 EPIT 定时器结构如图: ![](media/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。 ![img](media/wps1-1629538926510.jpg) | 位域 | 名 | 读写 | 描述 | | ------- | --------- | ---- | ------------------------------------------------------------ | | [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清除掉该位。 ![img](media/wps2.jpg) | 位域 | 名 | 读写 | 描述 | | ---- | ---- | ---- | ------------------------------------------------------------ | | [0] | OCIF | R/W | Output compare interrupt flag,0:比较事件未发生;1:比较事件已发生 | ### Load register (EPITx_LR)加载寄存器 EPIT加载寄存器,如果EPIT_CR的RLD置位的话,当EPIT计数器计数到0时,会将EPIT_LR值加载到计数器中。 如果设置了IOVW位,往该寄存器写值会同时覆盖掉计数器的值。这个覆盖特性与RLD是否设置无关。 ![img](media/wps3.jpg) | 位域 | 名 | 读写 | 描述 | | ------ | ---- | ---- | ------ | | [31:0] | LOAD | R/W | 加载值 | ### Compare register (EPITx_CMPR)比较寄存器 EPIT比较寄存器,当主计数器的值等于比较寄存器时,产生比较事件。 ![img](media/wps4.jpg) | 位域 | 名 | 读写 | 描述 | | ------ | ------- | ---- | ------ | | [31:0] | COMPARE | R/W | 比较值 | ### Counter register (EPITx_CNR)计数寄存器 EPIT计数器,主计数值,它是只读寄存器;读取EPIT计数器的值,不影响计数过程。如果控制寄存器的IOVW位设置的话,当往加载寄存器EPIT_IR写值时会覆盖当前的计数值。 ![img](media/wps5.jpg) | 位域 | 名 | 读写 | 描述 | | ------ | ----- | ---- | -------------- | | [31:0] | COUNT | R/W | EPIT计数器的值 | ## 代码分析 参见【13.epit_timer】 ### bsp_epittimer.c ```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 ```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闪烁一次 ![](media/image-20210821173215322.png)