一,EPIT定时器
这张图是 i.MX6ULL 芯片中 EPIT 定时器模块的框图

(1)EPIT 定时器工作原理:
-
时钟源 → 多路选择器选时钟(可选关闭、IPG 时钟、32K 时钟、高速时钟)
-
分频 → 12 位预分频器(分频系数 1~4096)
-
计数 → 32 位计数器(从加载寄存器值开始向下计数)
-
比较 → 与比较寄存器值匹配时触发比较器
-
输出控制 → 比较触发中断(ITIF/ITIE)和输出引脚(EPITn_OUT)
-
重载 → 计数到 0 时自动从加载寄存器重载,继续计数
核心流程:时钟 → 分频 → 计数 → 比较 → 中断/输出 → 重载循环
应用:定时中断、PWM 生成、精确延时。

**Set-and-forget mode(设置即忘模式)**
一次性配置加载值和比较值,定时器自动循环工作,无需软件反复干预。
**Free-running mode(自由运行模式)**
计数器从初始值不断递减→归零→自动重载,持续循环计数,永不停止。
配置定时器
(2)EPITx_CR





(3)配置代码
cs
void epit1_init(irq_handler_t handler)
{
// 配置EPIT1控制寄存器(EPIT1->CR)
unsigned int tmp = EPIT1->CR;
// 清除时钟源选择位(CLCKSRC, bit25:24)
tmp &= ~(0x3 << 24);
// 设置时钟源为Peripheral时钟(ipg_clk, 66MHz),CLCKSRC=01
tmp |= (0x1 << 24);
// 使能比较中断(OIEN, bit17)
tmp |= (1 << 17);
// 清除分频值(PRESCALAR, bit15:4)
tmp &= ~(0xfff << 4);
// 设置分频值=65 → 66分频(65+1),时钟=66MHz/66=1MHz(1us计数)
tmp |= (65 << 4);
// 设置工作模式为set-and-forget(bit3=1)
tmp |= (1 << 3);
// 使能比较中断(bit2=1)
tmp |= (1 << 2);
// 设置计数器初始值来源为加载寄存器(ENMOD, bit1=1)
tmp |= (1 << 1);
// 写入配置到CR
EPIT1->CR = tmp;
// 设置加载寄存器(LR)=1000000 → 计数1秒(1MHz计数频率)
EPIT1->LR = 1000000;
// 设置当前计数值(CNR)=1000000 → 初始值与LR相同
EPIT1->CNR = 1000000;
// 设置比较值(CMPR)=0 → 计数到0时产生中断
EPIT1->CMPR = 0;
// 注册EPIT1中断处理函数
request_irq(EPIT1_IRQn, handler);
// 使能EPIT1(EN, bit0=1)
EPIT1->CR |= (1 << 0);
}
二 ,GPT
延时函数是很常用的 API 函数,在前面的实验中我们使用循环来实现延时函数,但是使用循环
来实现的延时函数不准确,误差会很大。虽然使用到延时函数的地方精度要求都不会很严格(要求严格的 话就使用硬件定时器了),但是延时函数肯定是越精确越好,这样延时函数就可以使用在某些对时序要求 严格的场合。下面我们来学习一下如何使用硬件定时器来实现高精度延时。
GPT 是 i.MX6U 芯片内置的通用定时器模块,它是一个 32 位向上递增计数器,支持输入捕获、输出比较、中断生成等功能,可以工作在重新启动或自由运行两种模式,是精确延时的理想硬件基础。
我们将 GPT 的时钟源配置为 ipg_clk(66MHz) ,并通过 12 位分频器设置 66 分频 ,得到 1MHz 的计数频率,即计数器每计 1 个数对应 1 微秒,为高精度延时提供时间基准。
GPT也有两种工作模式:
重新启动(restart)模式:在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后 重新从0X00000000 开始向上计数,只有比较通道 1 才有此模式!向比较通道 1 的比较寄存器写入任何数据都 会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会复位计数器。
自由运行(free-run)模式:当在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并 不会复位计数器,而是继续计数,直到计数值为 0XFFFFFFFF,然后重新回0X00000000。
我们的最终目标是利用 GPT 硬件定时器实现高精度的微秒和毫秒级延时函数,取代不准确的软件循环延时,使其能够用于 UART 波特率生成、传感器时序控制、通信协议超时处理等对时序敏感的场合,提升系统可靠性。
(1)GPT 控制寄存器 (GPTx_CR)
SWR(bit15):软件复位位。写 1 复位 GPT,复位完成后硬件自动清零。
FRR(bit9):运行模式选择。0=比较通道1工作在重启模式,1=所有通道工作在自由运行模式。
CLKSRC(bit8:6):时钟源选择。1=ipg_clk(我们的选择)。
ENMOD(bit1):使能模式。0=关闭时保留计数值,1=关闭时清零计数器。
EN(bit0):使能位。1=启动定时器,0=停止。





(2)GPT 预分频寄存器 (GPTx_PR)
- PRESCALER(bit11:0):12 位分频值。范围 0~4095,对应 1~4096 分频。我们设 65 实现 66 ()分频(65+1)。


(3)配置代码如下
cs
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "gpt.h"
// 软件复位GPT1,置位SWR(bit15)后等待其自动清零(复位完成)
static inline void gpt1_reset(void)
{
GPT1->CR |= (1 << 15); // SWR(bit15)=1,复位GPT
while((GPT1->CR & (1 << 15))); // 等待SWR自动清零
}
// 初始化GPT1:复位→配置CR/PR→使能定时器
void gpt1_init(void)
{
gpt1_reset(); // 复位GPT1
unsigned int tmp = GPT1->CR;
// 清除中断掩码位IMx(bit20~28)和输出模式位OMx(bit16~19)
tmp &= ~(0x1ff << 20);
tmp &= ~(0xf << 16);
// 配置工作模式和时钟源
tmp |= (1 << 9); // FRR(bit9)=1:所有比较通道自由运行模式
tmp &= ~(0x7 << 6); // 清除CLKSRC(bit8:6)
tmp |= (1 << 6); // CLKSRC(bit8:6)=001,选择ipg_clk(66MHz)时钟源
tmp &= ~(1 << 1); // ENMOD(bit1)=0:关闭时保留计数值
GPT1->CR = tmp; // 写入配置
// 配置预分频器
tmp = GPT1->PR; // 预分频寄存器
tmp &= ~(0xfff << 0);// 清除PRESCALER(bit11:0)
tmp |= (65 << 0); // 分频值=65 → 66分频(65+1),时钟=66MHz/66=1MHz(1us计数)
GPT1->PR = tmp;
GPT1->CR |= (1 << 0); // EN(bit0)=1:使能GPT1定时器
}
// 微秒级延时:通过读取GPT1_CNT差值计算时间(处理32位溢出)
void inline delay_us(unsigned int num)
{
unsigned int counter = 0;
unsigned int cur_couter = 0;
unsigned int old_couter = GPT1->CNT; // 记录初始计数值
while(1)
{
cur_couter = GPT1->CNT;
if(cur_couter == old_couter)
continue;
// 计算计数值增量(处理溢出:cur < old 时说明溢出,加0xffffffff)
if(cur_couter > old_couter)
counter += cur_couter - old_couter;
else
counter += cur_couter + 0xffffffff - old_couter;
if(counter >= num) // 达到目标延时则退出
return;
old_couter = cur_couter;
}
}
// 毫秒级延时:循环调用delay_us(1000)
void delay_ms(unsigned int num)
{
while(num--)
{
delay_us(1000);
}
}