目录
[一. EPIT 定时器](#一. EPIT 定时器)
[1.1 EPIT 的特点](#1.1 EPIT 的特点)
[1.2 EPIT 的结构](#1.2 EPIT 的结构)
[1.3 EPIT 的两种工作模式](#1.3 EPIT 的两种工作模式)
[1.3.1 set-and-forget 模式](#1.3.1 set-and-forget 模式)
[1.3.2 free-running 模式](#1.3.2 free-running 模式)
[1.4 EPIT 相关寄存器](#1.4 EPIT 相关寄存器)
[1.4.1 EPITx_CR 配置寄存器](#1.4.1 EPITx_CR 配置寄存器)
[1.4.2 EPITx_SR 状态寄存器](#1.4.2 EPITx_SR 状态寄存器)
[1.4.3 其余寄存器](#1.4.3 其余寄存器)
[1.5 EPIT 的配置步骤](#1.5 EPIT 的配置步骤)
[二. GPT 定时器](#二. GPT 定时器)
[2.1 GPT 简介](#2.1 GPT 简介)
[2.2 GPT 的特点](#2.2 GPT 的特点)
[2.3 GPT 可选的时钟源](#2.3 GPT 可选的时钟源)
[2.4 GPT 结构图](#2.4 GPT 结构图)
[2.5 GPT的两种工作模式](#2.5 GPT的两种工作模式)
[2.5.1 重新启动(restart)模式](#2.5.1 重新启动(restart)模式)
[2.5.2 自由运行(free-run)模式](#2.5.2 自由运行(free-run)模式)
[2.6 GPT 相关寄存器](#2.6 GPT 相关寄存器)
[2.6.1 GPTx_CR 寄存器](#2.6.1 GPTx_CR 寄存器)
[2.6.2 GPTx_PR 分频寄存器](#2.6.2 GPTx_PR 分频寄存器)
[2.6.3 GPTx_CNT 计数寄存器](#2.6.3 GPTx_CNT 计数寄存器)
[2.6.4 GPTx_OCR 输出比较寄存器](#2.6.4 GPTx_OCR 输出比较寄存器)
[2.7 GPT 初始化示例代码](#2.7 GPT 初始化示例代码)
[2.8 使用 GPT 实现高精度延时函数](#2.8 使用 GPT 实现高精度延时函数)
[2.8.1 原理](#2.8.1 原理)
[2.8.2 示例代码](#2.8.2 示例代码)
一. EPIT 定时器
EPIT 的全称是:Enhanced Periodic Interrupt Timer ,直译过来就是增强型周期中断定时器 , 它主要是完成周期性中断定时的。一般,定时器还有很多其它的功能,比如输入捕获 、PWM 输出 等等。但是 I.MX6ULL 的 EPIT 定时器只完成周期性中断定时,仅此一项功能!至于输入捕获、PWM 输出等这些功能,I.MX6ULL 由其它的外设来完成。
I.MX6ULL中有两个EPIT 定时器,分别是 EPIT1 和 EPIT2
1.1 EPIT 的特点
EPIT 是一个 32 位定时器,在处理器几乎不用介入的情况下提供精准的定时中断,软件使能以后 EPIT 就会开始运行,EPIT 定时器有如下特点:
①:**时钟源可选的32 位向下计数器----**计数范围:0 ~ 0xFFFFFFFF
②:**12 位的分频值----**可进行 1 ~ 4096 的分频
③:当计数值和比较值相等的时候产生中断。
1.2 EPIT 的结构

各部分的功能如下:
①:这是个多路选择器,用来选择 EPIT 定时器的时钟源,EPIT 共有 3 个时钟源可选择, ipg_clk 、ipg_clk_32k (RTC--实时时钟)和 ipg_clk_highfreq
②:这是一个 12 位的分频器,负责对时钟源进行分频,12 位对应的值是0~4095 ,对应着 1~4096 分频
③:经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:计数寄存器(E PIT_CNR) 、加载寄存器(EPIT_LR) 和比较寄存器(EPIT_CMPR) ,这三个寄存器都是 32 位 的。E PIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在set-and-forget模式下,当计数寄存器里面的值减少到 0,EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件
④:比较器
⑤:EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号,当产生中断时,相应的引脚就会发生电平翻转
⑥:产生比较中断,也就是定时中断
1.3 EPIT 的两种工作模式
1.3.1 set-and-forget 模式
set-and-forget 模式:EPITx_CR(x=1,2)寄存器 的 RLD 位置 1 的时候 EPIT 工作在此模式 下,在此模式下 EPIT 的计数器从加载寄存器 EPITx_LR中获取初始值,不能直接向计数器寄存 器写入数据。不管什么时候,只要计数器计数到 0,那么就会从加载寄存器 EPITx_LR 中重新 加载数据到计数器中,周而复始。
1.3.2 free-running 模式
free-running 模式:EPITx_CR 寄存器的 RLD 位清零的时候 EPIT 工作在此模式下,当计数器计数到 0 以后会重新从 0XFFFFFFFF 开始计数,并不是从加载寄存器 EPITx_LR 中获取数 据。
1.4 EPIT 相关寄存器
1.4.1 EPITx_CR 配置寄存器

|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CLKSRC(bit25:24) | 作用:EPIT 时钟源选择位 00 :关闭时钟源, 01:选择 Per ipheral 时钟(ipg_clk), 10:选择 High-frequency 参考时钟(ipg_clk_highfreq), 11:选择 Low-frequency 参考时钟(ipg_clk_32k)。 一般情况,选择 ipg_clk 作 为 EPIT 的时钟源,ipg_clk = 66MHz |
| IOVW(bit17) | 当写入数据到加载寄存器时是否立即覆盖当前的计数值。 0:不覆盖 1:覆盖 |
| PRESCALAR(bit15:4) | EPIT 时钟源分频值,可设置范围 0~4095,分别对应 1~4096 分频 0x000 Divide by 1 0x001 Divide by 2... 0xFFF Divide by 4096 |
| RLD(bit3) | EPIT 工作模式 0 :工作在 free-running 模式, 1: 工作在 setand-forget 模式 |
| OCIEN(bit2) | 比较中断使能位 0 :关闭比较中断, 1 :使能比较中断 |
| ENMOD(bit1) | 设置计数器初始值 0 :计数器初始值等于上次关闭 EPIT 定时器以后计数器里面的值, 1:来源于加载寄存器 |
| EN(bit0) | EPIT 使能位 0:关闭 EPIT 1:使能 EPIT |
1.4.2 EPITx_SR 状态寄存器

|----------------|------------------------------------------------------------------------------------------------------------|
| OCIF(bit0) | 这个位是比较中断标志位----Output compare interrupt flag 0 :表示没有比较事件发生 1:表示有比较事件发生 当比较中断发生以后需要手动清除此位,此位是写 1 清零 |
1.4.3 其余寄存器
寄存器 EPITx_LR 、EPITx_CMPR 和EPITx_CNR 分别为加载寄存器 、比较寄存器 和计数寄 存器,这三个寄存器都是用来存放数据的
1.5 EPIT 的配置步骤
配置步骤:
设置 EPIT1 的时钟源--->设置分频值--->设置工作模式--->设置计数器的初始值来源--->使能比较中断--->设置加载值和比较值--->EPIT1 中断设置和中断服务函数编写--->使能 EPIT1 定时器
示例代码如下:
cpp
//定时器中断服务函数
void epit1_interrupt_handler(void)
{
if( (EPIT1->SR & (1 << 0)) != 0 )
{
led_nor();
//清除标志位--写一清零
EPIT1->SR |= (1 << 0);
}
}
//初始化EPIT1
void epit1_init(void)
{
//配置EPIT1控制寄存器CR
EPIT1->CR = 0;
//EPIT1->CR |= ( (1 << 24) | (1 << 17) | (65 << 4) | (1 << 3) | (1 << 2) | (1 << 1) );
//1、选择Peripheral clock作为时钟源 66MHz
EPIT1->CR |= (1 << 24);
//2、写入数据到加载寄存器,立即覆盖当前计数值
EPIT1->CR |= (1 << 17);
//3、设置66分频
EPIT1->CR |= (65 << 4);
//4、选择工作模式为:set-and-forget mode
EPIT1->CR |= (1 << 3);
//5、使能比较中断
EPIT1->CR |= (1 << 2);
//6、使能EPIT
EPIT1->CR |= (1 << 1);
//7、向加载寄存器中写入计数值
EPIT1->LR = 1000000;
//8、向比较寄存器中写入值
EPIT1->CMPR = 0;
//注册EPIT1中断服务函数
system_interrupt_register(EPIT1_IRQn, epit1_interrupt_handler);
//设置中断优先级
GIC_SetPriority(EPIT1_IRQn, 0);
//使能EPIT1中断
GIC_EnableIRQ(EPIT1_IRQn);
//打开EPIT1,开始定时
EPIT1->CR |= (1 << 0);
}
二. GPT 定时器
2.1 GPT 简介
GPT 定时器全称为 General Purpose Timer --- 通用目的定时器
GPT 定时器是一个 32 位向上定时器(也就是从 0X00000000 开始向上递增计数),GPT 定时 器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。 GPT 定时器有一个 12 位的分频器,可以对 GPT 定时器的时钟源进行分频
2.2 GPT 的特点
①一个可选时钟源的 32 位向上计数器。
②两个输入捕获通道,可以设置触发方式。
③三个输出比较通道,可以设置输出模式。
④可以生成捕获中断 、比较中断 和溢出中断。
⑤计数器可以运行在重新启动(restart) 或**(自由运行)free-run** 模式
2.3 GPT 可选的时钟源
GPT 可选的时钟源如图所示:
从图可以看出一共有五个时钟源,分别为:ipg_clk_24M 、GPT_CLK(外部时钟) 、 ipg_clk 、ipg_clk_32k 和 ipg_clk_highfreq 。一般选择 ipg_clk 为 GPT 的时钟源,ipg_clk=66M Hz。

2.4 GPT 结构图
GPT 定时器结构如图所示:

①此部分为 GPT 定时器的时钟源,前面已经说过,选择 ipg_clk 作为 GPT 定时器时钟源。
②此部分为 12 位分频器,对时钟源进行分频处理,可设置 0~4095 ,分别对应 1~4096 分频
③经过分频的时钟源进入到 GPT 定时器内部 32 位计数器:Timer counter。
④和⑤这两部分是 GPT 的两路输入捕获通道。
作用: 测量方波周期。④和⑤是芯片上的引脚,可以接到要需要测量的方波的引脚上,当引脚电平第一次发生变换时,Counter Value Bus 总线会将Timer counter 中的值加载到 Timer Input Reg 1 ,当引脚电平第二次发生变换时,Counter Value Bus 总线会将Timer counter 中的值加载到 Timer Input Reg 2 ,两个Timer Input Reg中的值相减即可得到需要测量的波的周期
⑥此部分为输出比较寄存器,一共有三路输出比较,因此有三个输出比较寄存器,输出比较寄存器是 32 位的。
作用: 可以输出指定的方波。Timer Outputt Reg1,2,3 中可分别装入不同的数值,并且不断与与 Timer counter 中的值做比较,当相等时,对应的⑦会引脚会发生电平翻转
⑦此部分为输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断。
2.5 GPT的两种工作模式
2.5.1 重新启动(restart)模式
重新启动(restart)模式:当 GPTx_CR(x=1,2)寄存器 的 FRR 位清零的时候 GPT 工作在此模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从 0X0 0000000 开始向上计数
**只有比较通道 1 才有此模式!**向比较通道 1 的比较寄存器写入任何数据都会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会复位计数器
2.5.2 自由运行(free-run)模式
自由运行(free-run)模式:当 GPTx_CR(x=1,2)寄存器 的 FRR 位置 1 时候 GPT 工作在此模 式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计 数,直到计数值为 0XFFFFFFFF,然后重新回滚到 0X00000000
2.6 GPT 相关寄存器
2.6.1 GPTx_CR 寄存器

|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OM3(28 - 26) | 作用: 当 GPT计数器 CNT 递增 == 对应通道比较寄存器 OCRx 的值 (发生比较匹配事件 Compare Event )时,硬件自动根据 OMn 的配置,控制外部 GPT_COMPAREn IO 引脚电平如何变化 000:输出断开 ,匹配事件发生时,外部 IO 引脚完全不受 GPT 控制 001:匹配时引脚电平翻转(取反) 010:匹配时引脚清零(拉低为低电平) 011:匹配时引脚置 1(拉高为高电平) 1xx:产生 1 个宽度 = 1 个 GPT 输入时钟周期的低脉冲 |
| IM2(19 - 18) | 作用:配置外部输入引脚电平发生哪种跳变沿 时,触发输入捕获事件 **00:**输入捕获功能关闭 **01:**仅上升沿触发捕获 **10:**仅下降沿触发捕获 **11:**双边沿都触发捕获 |
| SWR(15) | 复位 GPT 定时器,向此位写 1 就可以复位 GPT 定时器,当 GPT 复位完成以后此为会自动清零 在 GPT 初始化时,复用 GPT 时要使用循环: while((GPT1->CR & (1 << 15)) != 0); |
| FRR(9) | 运行模式 选择当此位为 0 的时候比较通道 1 工作在重新启动(restart)模式 当此位为 1 的时候所有的三个比较通道均工作在自由运行模式(free-run) |
| CLKSRC(8 - 6) | GPT 定时器时钟源选择位, 000:关闭时钟源 001:选择 ipg_clk 作为时钟源 010:选择 ipg_clk_highfreq 为时钟源 011:选择外部时钟为时钟源 100:选择 ipg_clk_32k 为时钟源 101:选择 ip_clk_24M 为时钟源 |
| ENMOD(1) | GPT 使能模式 此位为 0 的时候如果关闭 GPT 定时器,计数器寄存器保存定时器关闭时候的计数值。 此位为 1 的时候如果关闭 GPT 定时器,计数器寄存器就会清零 |
| EN(0) | GPT 使能位 为 1 的时候使能 GPT 定时器 为 0 的时候关闭 GPT 定时器 |
2.6.2 GPTx_PR 分频寄存器

|-----------------------|-----------------------------------------------------------------------------------------------------------|
| PRESCALER(11 - 0) | 这就是 12 位分频值, 可设置 0~4095,分别对应 1~4096 分频 0x000 Divide by 1 0x001 Divide by 2 ... ... 0xFFF Divide by 4096 |
2.6.3 GPTx_CNT 计数寄存器
**作用:**这个寄存器保存着 GPT 定时器的当前 计数值

2.6.4 GPTx_OCR 输出比较寄存器
作用: 每个输出比较通道对应一个输出比较寄存器,因此一个 GPT 定时器有三个 OCR 寄存器,它们的作都是相同的。以输出比较通道 1 为例,其输出比较寄存器为 GPTx_OCR1,这是一个 32 位寄存器,用于存放 32 位的比较值。当计数器值和寄存器 GPTx_OCR1 中的值相等就会产生比较事件,如果使能了比较中断的话就会触发相应的中断

2.7 GPT 初始化示例代码
示例代码:
初始化流程:
失能GPT--->软件复位--->选择工作模式--->选择时钟源--->选择分频因子--->使能GPT
cpp
//初始化GPT
void gpt_init()
{
//1、先将CR寄存器清零
GPT1->CR = 0;
//2、软件复位 GPT,并且循环判断是否复位完成
GPT1->CR |= (1 << 15);
while((GPT1->CR & (1 << 15)) != 0);
//3、选择工作模式为 free-run 模式
GPT1->CR |= (1 << 9);
//4、选择时钟源为 ipg_clk
GPT1->CR |= (1 << 6);
//5、选择66分频:66/66 = 1MHz
GPT1->PR = 65;
//6、使能 GPT
GPT1->CR |= (1 << 0);
}
2.8 使用 GPT 实现高精度延时函数
2.8.1 原理
如果设置 GPT 定时器的时钟源为ipg_clk=66MHz ,设置 66 分频 ,那么进入 GPT 定时器的最终时钟频率就是 66/66=1MHz ,周期为 1us 。GPT 的计数器每计一个数就表示过去了 1us 。如果计 10 个数就表示过去了 10us。通过读取寄存器 GPTx_CNT中的值就知道计了个数,比如现在要延时 100us,那么进入延时函数以后纪录下寄存器 GPTx_CNT 中的值为 200,当 GPTx_CNT 中的值为 300 的时候就表示 100us 过去了,也就是延时结束。
GPTx_CN T 是个 32 位寄存器,如果时钟为 1MHz 的话,GPTx_CNT 最多可以实现 0XFFFFFFFFus=4294967295us ≈ 4294s ≈ 72min。也就是说 72 分钟以后 GPTx_CNT 寄存器就会回滚到 0X00000000, 也就是溢出,所以需要在延时函数中要处理溢出的情况
2.8.2 示例代码
情况一:不发生溢出

情况二:发生溢出

高精度定时原理:

cpp
//微秒延时
void delay_us(unsigned int n)
{
unsigned int new_count = 0, old_count = 0, count = 0;
old_count = GPT1->CNT;
while (1)
{
new_count = GPT1->CNT;
if(new_count != old_count)
{
if(new_count > old_count)
{
count += new_count - old_count;
}
else
{
count += 0xFFFFFFFF - old_count + new_count;
}
if(count >= n)
{
return;
}
old_count = new_count;
}
}
}
//毫秒延时
void delay_ms(unsigned int n)
{
while(n--)
{
delay_us(1000);
}
}