I.MX6U 裸机开发16.EPIT定时器

  • 一、EPIT定时器的概念
  • 1. 基本概念
  • (1)时钟源
  • (2)12位预分频器
  • (3)计数器重装(Counter Reload)
  • (4)32位计数器寄存器(Counter Register)
  • (5)比较器寄存器
  • 2. EPIT 2种工作模式
  • (1) set-and-forget
  • (2)free-running
  • 3. 比较事件
  • 二、IMX6ULL 的 EPIT 定时器
  • 1. EPIT_CR
  • 2. EPITx_SR
  • 3. EPITx_LR
  • 4. EPITx_CMPR
  • 5. EPITx_CNR
  • 三、实验程序
  • 1. EPIT初始化
  • 2. 主函数


一、EPIT定时器的概念

1. 基本概念

EPIT(Enhanced Periodic Interrupt Timer)是一种增强型周期性中断定时器,主要用于嵌入式系统中产生周期性的中断信号。
以汽车电子系统为例,车辆中的一些传感器数据需要按照固定的周期进行采集和处理。EPIT 定时器就可以设置一个合适的周期,每隔一段时间产生一个中断。当发生中断时,处理器就可以暂停当前的任务,转而去执行数据采集程序,比如采集车速传感器的数据。

IMX6U的 EPIT 是一个32位的向下计数器,有12位的分频值。
从《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81》 可以看到EPIT 定时器框图如下:

I.MX6U 裸机开发16.EPIT定时器_I.MX6U

(1)时钟源

EPIT 的时钟源可以选择 ipg_clk (66MHz),经过后面的分频器。

(2)12位预分频器

分频器有12位,0 ~ 4095分别代表 1 ~ 4096分频。
预分频器的输出连接到Prescaled clock,即分频后的时钟信号。

(3)计数器重装(Counter Reload)

用于重新加截计数器值,当计数器减到 0 的时候,

(4)32位计数器寄存器(Counter Register)

这是一个 32 位的比较寄存器,用于存储比较值。
比较寄存器的值与计数器的值通过CMP(比较器)进行比较。

(5)比较器寄存器

  • CMP(比较器)用于比较计数器寄存器和比较寄存器的值。
  • 当计数器的值达到比较寄存器的值时,会产生一个中断信号。
  • ITIF(中断标志)和ITIE(中断使能)用于控制和指示中断状态。
  • EPITn_OUT是定时器的输出信号,用于外部设备的控制。

2. EPIT 2种工作模式

通过 EPIOTx_CR 可以设置EPIT的工作模式。通过手册查询该寄存器 RLD 位置作用:

I.MX6U 裸机开发16.EPIT定时器_嵌入式Linux_02

(1) set-and-forget

当 RLD为1时,计时器工作在 set-and-forget 模式,该模式即 “ 设置后就不用管 ”。
在此模式下,计数器从加载寄存器(EPIT_LR)获取数据;它不能直接从块数据总线写入。

每当计数器达到零时,EPIT_LR 中的值就会被加载到计数器中。然后这个值会递减到零。若要直接初始化计数器而不是等待计数达到零,需设置 EPIT 计数器覆写使能位(EPIT_CR [IOVW]),并使用所需的初始化值写入 EPIT_LR。

(2)free-running

当RLD为0 时,计时器工作在“自由运行”模式。在这个模式下,计数器从 0000 0000h 翻转到 FFFFFFFFh,而不会从模式寄存器重新加截值,翻转之后,计数器会继续向下计数。

要直接初始化计数器,也需要设置 EPIT 计数器覆盖使能位(EPIO_EPITCL [IOVW] ),并将所需要的初始化值写入 EPIT_LR。

3. 比较事件

当 EPIT_EPITCMPR 值与 EPIT_EPITCNR 中的值相匹配时,会设置一个比较状态标志 , 如果控制寄存器中的OCIEN 位被置位,还会产生一个中断。

下图是比较事件和中断的时序:

I.MX6U 裸机开发16.EPIT定时器_初始化_03


在对设置值、产生中断时序要求非常严格的情况下,需要细致了解其时序。 一般的情况下,可能不需要了解如此清楚。

二、IMX6ULL 的 EPIT 定时器

IMX6ULL 共有两个 EPIT 定时器,主要包含以下寄存器。

1. EPIT_CR

EPIT_CR 是EPIT的控制寄存器,主要用于配置和控制EPIT 工作模式、时钟源、分频值、中断使能等。

I.MX6U 裸机开发16.EPIT定时器_寄存器_04


其主要有以下位域:

  • bit0:EN:EPIT 使能位,0 表示关闭 EPIT,1 表示使能 EPIT。
  • bit1:ENMOD:设置计数器初始值来源,0 时计数器初始值等于上次关闭 EPIT 定时器以后计数器里面的值,1 时来源于加载寄存器。
  • bit2:OCIEN:比较中断使能位,0 关闭比较中断,1 使能比较中断,当计数器的值与比较寄存器中的值相等时,如果该位为 1,则会触发中断。
  • bit3:RLD:EPIT 工作模式选择位,0 时工作在 free-running 模式,1 时工作在 set-and-forget 模式.
  • bit4-15:PRESCALAR:EPIT 时钟源分频值,可设置范围 0-4095,分别对应 1-4096 分频,用于对选定的时钟源进行分频,以得到合适的计数时钟频率。
  • bit25-24:CLKSRC:EPIT 时钟源选择位,00 时关闭时钟源,01 选择 Peripheral 时钟(ipg_clk),10 选择 High-frequency 参考时钟(ipg_clk_highfreq),11 选择 Low-frequency 参考时钟(ipg_clk_32k)。

2. EPITx_SR

EPIT_SR 是状态寄存器,主要用于记录和提供与定时器相关的状态信息,只有bit0(OCIF) 有效,表示中断状态。 写1时清零。
当OCIF 位值为1时,表示发生中断, 值为0的时候表示中断没有发生。
处理完定时器中断,需要清除该标志位。

3. EPITx_LR

加载寄存器,设置计数器的加载值。

4. EPITx_CMPR

比较计数器,当计数器值和CMPR一致时,产生中断。

5. EPITx_CNR

计数器寄存器,当前计数值。

三、实验程序

本实验实现 500ms 周期的定时器,在EPIT的中断服务函数中让LED闪烁。

1. EPIT初始化

/**
 * @brief EPIT初始化, 工作在 set-and-forget 模式
 */
void epit_init(EPIT_Type *base, unsigned int frac, unsigned int value) {
    if(frac > 4095){
        frac = 4095;
    }
    // 配置EPIT设置寄存器,先清0
    base->CR = 0;
    // 设置初始值为加载值 | 使能EPIT模块 | 使能中断 | 分频值 | 使能时钟
    base->CR = (1 << 1) | (1 << 2) | (1 << 3) | (frac << 4) | (1 << 24);
    // 设置加载值
    base->LR = value;
    // 设置比较值
    base->CMPR = 0;

    // 使能GIC中断
    if(base == EPIT1)
        GIC_EnableIRQ(EPIT1_IRQn);
    else
        GIC_EnableIRQ(EPIT2_IRQn);

    // 注册中断服务函数
    sys_irq_handle_register(EPIT1_IRQn, (system_irq_handler_t)epit_irqhandler, NULL);

    // 定时器使能
    base->CR |= 1 << 0;
}

分频值计算:
I.MX6U 裸机开发16.EPIT定时器_#include_05
其中 Tclk = 66Mhz
如果分频值是1,则1秒进中断 66M次。 如果要500ms进一次,分频值设置1,计数器值设置为 66M/2。

2. 主函数

#include "inc/main.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "bsp_int.h"
//#include "bsp_exti.h"
#include "bsp_epit.h"

int main(void)
{
    bsp_int_init();  /* 初始化中断 */
    imx6u_clkinit();    /* 初始化系统时钟 */
    clk_enable();   /* 使能外设时钟 */
    led_init();     /* 初始化LED */
    beep_init();    /* 初始化蜂鸣器 */
//    exti_init();    /* 初始化外部中断 */
    epit_init(EPIT1, 0, 66000000 / 2);  /* 初始化EPIT1, 1分频, 500ms中断一次 */

    while(1) {
    }
    return 0;
}

本文代码开源地址:
https://gitee.com/xundh/learn_i.mx6u.git