i.MX6ULL GPT 通用定时器 中文笔记整理日期2026-06-26参考NXP i.MX6ULL Applications Processor Reference Manual (Chapter 30: General Purpose Timer)前置知识GPIO笔记0623-i.MX6ULL-GPIO中文笔记.md核心目标用GPT实现高精度延时us级/ms级一、GPT是什么一句话GPT 一个计数器。给时钟脉冲就 1数到某个值就触发事件。i.MX6ULL 有GPT1和GPT2两个通用定时器每个 GPT 是32位向上计数器0 → 0xFFFFFFFF → 0 → ...有12位预分频器分频系数 1~4096有3路输出比较通道、2路输入捕获通道本笔记先聚焦延时不展开捕获/比较输出与GPIO的关系GPIOGPT控制引脚输出什么控制什么时候输出配寄存器控制电平高低配寄存器控制计数和定时基础IO方向数据寄存器进阶时钟源分频计数两者合起来就是嵌入式最基础的定时控制。二、核心概念2.1 延时公式最重要先记住延时时间 计数值 × 时钟周期 ​ 其中时钟周期 1 / 时钟频率实际配置思路选时钟源 → 得到输入频率设预分频 → 得到计数器频率计数器频率确定后每个计数值对应的时间就固定了常用配置本笔记采用时钟源ipg_clk 66MHz 预分频66分频PR寄存器写65 → 计数器频率 66MHz / 66 1MHz → 每计数1次 1μs → 延时1000μs → 计数值 1000为什么这样配因为 1MHz 下每个计数正好是 1μs计算最直观。2.2 两种运行模式模式设置行为适用场景Restart重启FRR0计数到 OCR1 值后自动清零重计周期性定时中断Free-Run自由运行FRR1一直计数到 0xFFFFFFFF 才回滚延时函数本笔记用这个做延时时选 Free-Run计数器一直在跑随时可以读当前值来算经过了多少时间。2.3 时钟源选择CLKSRC值时钟源频率备注000关闭-001ipg_clk66MHz本笔记使用010ipg_clk_highfreq~132MHz011外部时钟 GPT_CLK外部需接外部时钟引脚100ipg_clk_32k32kHz低功耗场景101ipg_clk_24M24MHz晶振直供不受系统分频影响注意24MHz 时钟CLKSRC101适合需要精确时基的场景因为它不受系统时钟配置影响。但本笔记先用 ipg_clk66MHz 配合 66分频得到 1MHz计算最简单。三、寄存器地址3.1 GPT基地址定时器基地址GPT10x02098000GPT20x020E80003.2 寄存器偏移所有寄存器都是32位相邻寄存器间隔4字节寄存器偏移读/写作用GPTx_CR0x00R/W控制寄存器开关、时钟源、模式、复位GPTx_PR0x04R/W预分频寄存器GPTx_SR0x08R/W状态寄存器标志位写1清除GPTx_IR0x0CR/W中断使能寄存器GPTx_OCR10x10R/W输出比较寄存器1GPTx_OCR20x14R/W输出比较寄存器2GPTx_OCR30x18R/W输出比较寄存器3GPTx_ICR10x1CR输入捕获寄存器1GPTx_ICR20x20R输入捕获寄存器2GPTx_CNT0x24R当前计数值只读3.3 C语言地址宏定义/* GPT1 寄存器地址定义 */ #define GPT1_BASE 0x02098000 ​ #define GPT1_CR (*(volatile unsigned int *)0x02098000) #define GPT1_PR (*(volatile unsigned int *)0x02098004) #define GPT1_SR (*(volatile unsigned int *)0x02098008) #define GPT1_IR (*(volatile unsigned int *)0x0209800C) #define GPT1_OCR1 (*(volatile unsigned int *)0x02098010) #define GPT1_OCR2 (*(volatile unsigned int *)0x02098014) #define GPT1_OCR3 (*(volatile unsigned int *)0x02098018) #define GPT1_ICR1 (*(volatile unsigned int *)0x0209801C) #define GPT1_ICR2 (*(volatile unsigned int *)0x02098020) #define GPT1_CNT (*(volatile unsigned int *)0x02098024) ​ /* GPT2 寄存器地址定义 */ #define GPT2_BASE 0x020E8000 ​ #define GPT2_CR (*(volatile unsigned int *)0x020E8000) #define GPT2_PR (*(volatile unsigned int *)0x020E8004) #define GPT2_SR (*(volatile unsigned int *)0x020E8008) #define GPT2_IR (*(volatile unsigned int *)0x020E800C) #define GPT2_OCR1 (*(volatile unsigned int *)0x020E8010) #define GPT2_OCR2 (*(volatile unsigned int *)0x020E8014) #define GPT2_OCR3 (*(volatile unsigned int *)0x020E8018) #define GPT2_ICR1 (*(volatile unsigned int *)0x020E801C) #define GPT2_ICR2 (*(volatile unsigned int *)0x020E8020)如果用了 NXP 官方 MCIMX6Y2.h 头文件可以直接用GPT1-CR这种结构体写法上面这些宏就不需要了。四、寄存器详解4.1 控制寄存器 GPTx_CR偏移 0x00这是最核心的寄存器配 GPT 就是配它。位名称含义bit0EN使能1启动计数0停止bit1ENMOD使能模式0停止时保持计数值1停止时清零计数值bit2DBGEN调试模式使能bit3WAITEN等待模式使能bit4DOZEN休眠模式使能bit5STOPEN停止模式使能bit8:6CLKSRC时钟源选择001ipg_clk, 10124MHzbit9FRR模式0Restart1Free-Runbit10EN_24M使能24MHz晶振输入选24MHz时钟源时才需要bit15SWR软件复位写1复位GPT完成后自动清0做延时只需关注加粗的 5 个位。配置示例Free-Run ipg_clk 66MHz// CLKSRC001(0b001), FRR1, EN1 // 二进制0 0000 0000 0010 0000 0101 0x0205 GPT1_CR (1 6) | (1 9) | (1 0); // CLKSRC001 FRR1 EN14.2 预分频寄存器 GPTx_PR偏移 0x04位名称含义bit11:0PRESCALER分频值0~4095实际分频系数 值1关键分频系数 寄存器值 1PR寄存器值实际分频系数输入66MHz → 计数频率每计数时间0166MHz15.26ns65661MHz1μs← 本笔记用这个23999240002.75kHz363.6μsGPT1_PR 65; // 66分频66MHz ÷ 66 1MHz → 每计数1次 1μs4.3 状态寄存器 GPTx_SR偏移 0x08位名称含义清除方式bit0OF1输出比较1标志写1清除bit1OF2输出比较2标志写1清除bit2OF3输出比较3标志写1清除bit3IF1输入捕获1标志写1清除bit4IF2输入捕获2标志写1清除bit5ROV回滚溢出标志写1清除​​​​​​​// 清除所有标志位写1清除不是写0 GPT1_SR 0x03F; // bit5~bit0 全部写14.4 中断使能寄存器 GPTx_IR偏移 0x0C位名称含义bit0OF1IE输出比较1中断使能bit1OF2IE输出比较2中断使能bit2OF3IE输出比较3中断使能bit5ROVIE回滚中断使能做轮询延时不需要配中断用中断方式时再设置对应位。4.5 输出比较寄存器 GPTx_OCR1/2/3偏移 0x10/0x14/0x1832位寄存器存放比较值。计数值 OCR值时触发比较事件。做 Free-Run 延时时把 OCR1 设为最大值让计数器一直跑到溢出不影响延时计算。GPT1_OCR1 0xFFFFFFFF; // 最大比较值Free-Run模式不会因此复位4.6 计数寄存器 GPTx_CNT偏移 0x24只读。读它就能知道当前计数器的值。延时函数的核心就是读两次 CNT算差值。五、C语言实现延时函数5.1 初始化/* * GPT1初始化 * 配置为 Free-Run 模式ipg_clk(66MHz) 时钟源66分频 → 1MHz(1μs/计数) */ void gpt1_init(void) { /* 第1步关闭GPT */ GPT1_CR 0; ​ /* 第2步软件复位 */ GPT1_CR (1 15); // SWR1触发复位 while (GPT1_CR (1 15)); // 等待复位完成SWR自动清0 ​ /* 第3步配置时钟源和模式 */ GPT1_CR (1 6) // CLKSRC001 → ipg_clk(66MHz) | (1 9); // FRR1 → Free-Run模式 ​ /* 第4步配置预分频 */ GPT1_PR 65; // 66分频66MHz/66 1MHz → 每计数1次1μs ​ /* 第5步设置OCR1为最大值Free-Run模式不需要比较中断 */ GPT1_OCR1 0xFFFFFFFF; ​ /* 第6步清除所有状态标志 */ GPT1_SR 0x03F; ​ /* 第7步使能GPT开始计数 */ GPT1_CR | (1 0); // EN1 }初始化流程总结关闭 → 2. 复位 → 3. 选时钟模式 → 4. 设分频 → 5. 设OCR → 6. 清标志 → 7. 启动5.2 微秒延时函数/* * 微秒(μs)级延时 * 原理读两次CNT算差值。处理溢出情况。 */ void delay_us(unsigned int us) { unsigned int old_cnt, new_cnt, elapsed 0; ​ old_cnt GPT1_CNT; // 记录起始计数值 ​ while (1) { new_cnt GPT1_CNT; // 读当前计数值 ​ if (new_cnt old_cnt) { // 未溢出直接累加差值 elapsed (new_cnt - old_cnt); } else { // 溢出了计数器从0xFFFFFFFF回滚到0 elapsed (0xFFFFFFFF - old_cnt new_cnt); } ​ if (elapsed us) return; // 已达到目标延时 ​ old_cnt new_cnt; // 更新基准值 } }溢出处理是难点画个图就清楚了正常情况未溢出 old_cnt ──────→ new_cnt elapsed new_cnt - old_cnt ​ 溢出情况 old_cnt ──→ 0xFFFFFFFF ──→ 0 ──→ new_cnt elapsed (0xFFFFFFFF - old_cnt) new_cnt5.3 毫秒延时函数/* * 毫秒(ms)级延时 * 简单实现循环调用delay_us(1000) */ void delay_ms(unsigned int ms) { while (ms--) { delay_us(1000); } }5.4 使用裸地址的完整版本不依赖MCIMX6Y2.h/* 寄存器地址宏 */ #define GPT1_CR (*(volatile unsigned int *)0x02098000) #define GPT1_PR (*(volatile unsigned int *)0x02098004) #define GPT1_SR (*(volatile unsigned int *)0x02098008) #define GPT1_OCR1 (*(volatile unsigned int *)0x02098010) #define GPT1_CNT (*(volatile unsigned int *)0x02098024) ​ /* 初始化 */ void gpt1_init(void) { GPT1_CR 0; GPT1_CR (1 15); // 复位 while (GPT1_CR (1 15)); // 等待 GPT1_CR (1 6) | (1 9); // ipg_clk Free-Run GPT1_PR 65; // 66分频→1MHz GPT1_OCR1 0xFFFFFFFF; GPT1_SR 0x03F; // 清标志 GPT1_CR | (1 0); // 启动 } ​ /* us延时 */ void delay_us(unsigned int us) { unsigned int old_cnt, new_cnt, elapsed 0; old_cnt GPT1_CNT; while (1) { new_cnt GPT1_CNT; if (new_cnt old_cnt) elapsed (new_cnt - old_cnt); else elapsed (0xFFFFFFFF - old_cnt new_cnt); if (elapsed us) return; old_cnt new_cnt; } } ​ /* ms延时 */ void delay_ms(unsigned int ms) { while (ms--) delay_us(1000); }六、延时能力计算单次最大延时计数器32位最大值 0xFFFFFFFF 4,294,967,295 计数频率 1MHz每计数1次 1μs ​ 单次最大延时 0xFFFFFFFF μs 4,294,967,295 μs 4,294,967 ms 4,294.967 秒 ≈ 71.58 分钟也就是说一次 delay_us 最多能延时约71分钟完全够用。不同分频配置对比时钟源PR值计数频率每计数时间单次最大延时ipg_clk 66MHz651MHz1μs~71.6分钟ipg_clk 66MHz659910kHz100μs~119.3小时24MHz 晶振231MHz1μs~71.6分钟ipg_clk_32k311kHz1ms~49.7天七、常见问题Q1为什么要先复位GPT确保所有寄存器回到默认值避免之前的配置干扰。就像GPIO初始化前先复位一样。Q2PR写65为什么是66分频因为实际分频系数 寄存器值 1。和STM32的预分频器一样。写0 1分频不分频写65 66分频Q3delay_us里为什么每次循环都要更新old_cnt因为每次循环只计算一小段经过的时间累加起来。如果不更新就只能算一次差值精度差。Q4Free-Run和Restart有什么区别Restart计数到OCR1值后自动清零适合每隔固定时间触发一次中断Free-Run一直计数到最大值才回滚适合随时读取当前时间来计算延时Q5选ipg_clk还是24MHz晶振ipg_clk(66MHz)配66分频得1MHz计算简单但依赖系统时钟配置24MHz晶振配23分频得1MHz更稳定不受系统时钟影响但需要设CLKSRC101初学建议用ipg_clk等做精确时序如1-Wire协议时再换24MHz晶振。Q6用结构体写法怎么做// 如果用NXP官方MCIMX6Y2.h GPT1-CR 0; GPT1-CR (1 15); while (GPT1-CR (1 15)); GPT1-CR (1 6) | (1 9); GPT1-PR 65; GPT1-OCR[0] 0xFFFFFFFF; // 注意数组写法 OCR[0]OCR1 GPT1-SR 0x03F; GPT1-CR | (1 0);八、知识地图和GPIO对比概念GPIOGPT核心功能控制IO电平精确计时最核心寄存器DR数据、GDIR方向CR控制、CNT计数配置关键复用PAD方向数据时钟源分频模式启动基地址GPIO10x0209C000GPT10x02098000寄存器数量8个10个复位方式无专门复位CR的SWR位(bit15)状态查询读PSR读SR标志位写1清