EPIT定时器实验
EPIT定时器简介
EPIT 的全称是:Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器。
EPIT 是一个 32 位定时器,EPIT 定时器有如下特点:
时钟源可选的 32 位向下计数器;
12 位的分频值;
当计数值和比较值相等的时候产生中断。
EPIT 定时器结构如图:

多路选择器,用来选择 EPIT 定时器的时钟源,EPIT 共有 3 个时钟源可选择;
ipg_clk
ipg_clk_32k
ipg_clk_highfreq
12 位的分频器,负责对时钟源进行分频,12 位对应的值是 0~4095,对应着1~4096 分频;
经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:
计数寄存器(EPIT_CNR)
加载寄存器(EPIT_LR)
比较寄存器(EPIT_CMPR)
这三个寄存器都是 32 位的。EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0,EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件。
比较器。
EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号。
产生比较中断,也就是定时中断。
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
| 位域 | 名 | 读写 | 描述 |
|---|---|---|---|
| [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
| 位域 | 名 | 读写 | 描述 |
|---|---|---|---|
| [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
| 位域 | 名 | 读写 | 描述 |
|---|---|---|---|
| [31:0] | LOAD | R/W | 加载值 |
Compare register (EPITx_CMPR)比较寄存器
EPIT比较寄存器,当主计数器的值等于比较寄存器时,产生比较事件。
img
| 位域 | 名 | 读写 | 描述 |
|---|---|---|---|
| [31:0] | COMPARE | R/W | 比较值 |
Counter register (EPITx_CNR)计数寄存器
EPIT计数器,主计数值,它是只读寄存器;读取EPIT计数器的值,不影响计数过程。如果控制寄存器的IOVW位设置的话,当往加载寄存器EPIT_IR写值时会覆盖当前的计数值。
img
| 位域 | 名 | 读写 | 描述 |
|---|---|---|---|
| [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闪烁一次
