ARM嵌入式学习(十二)--- IMX6ULL定时器使用

目录

一、定时器选择

二、EPIT(增强型周期中断定时器)

1.EPIT介绍:

2.EPIT的配置:

注意:

三、GPT(通用目的定时器)

1.GPT介绍

[输入捕获(Input Capture):](#输入捕获(Input Capture):)

注意:

2.GPT配置

延时函数:

注意:

四、总结

一、定时器选择

本篇将用到EPIT(增强型周期中断定时器)GPT(通用目的定时器)

简要介绍一下它俩的区别:

特性对比 EPIT (增强型周期中断定时器) GPT (通用目的定时器)
核心定位 专注于高精度周期中断/延时 通用的多功能定时器,适用于多种复杂场景
计数方向 向下计数(从设定值减到0) 向上计数(从0加到设定值或最大值)
输入捕获 不支持 支持。可以捕获外部输入信号的跳变,用于精确测量PWM波的频率、占空比或时间差
输出比较 1个通道,比较匹配时可触发中断或翻转IO 3个输出比较通道,可以同时控制3个不同的定时任务或PWM输出
工作模式 Set-and-Forget(自动重载)和Free-Running Restart(单次)和Free-Running(自由运行)
其他功能 溢出中断输入捕获中断

简单来说,如果你只需要一个精准的、周期性的中断定时器,EPIT就足够了,它专精于此。但如果你需要测量外部信号的脉宽、生成复杂的PWM波形,或者同时处理多个定时任务,那么GPT是唯一的选择。

二、EPIT(增强型周期中断定时器)

1.EPIT介绍:

从参考手册我们可以看到EPIT的内部结构

模式 行为描述
Set-and-Forget 模式 计数器减到 0 后,自动从 LR(加载寄存器) 重新加载初始值,循环往复,产生周期性中断。这是最常用的模式。
Free-Running 模式 计数器减到 0 后,重新从 0xFFFFFFFF 开始向下计数,不从 LR 加载值。

2.EPIT的配置:

我们用EPIT设计一个1s计时为例子,整体流程为:

  1. 使能EPIT模块的时钟(通过CCGR,这里我们已经打开了全部时钟)。

  2. 配置EPIT_CR:选择时钟源(PERCLK,即ipg_clk),设置分频因子(这里设置66分频,即1MHz),设置模式(set-and-forget模式,RLD=1),使能中断(OCIEN),ENMOD,设置计数值加载(IOVW)。

  3. 设置EPIT_LR加载值为计数值(1000 000)设置EPIT_CMPR比较值(0)

  4. 使能EPIT中断(GIC配置)。

  5. 编写中断服务程序(在中断中翻转蜂鸣器GPIO)。

  6. 启动定时器(EN)。

  7. 清除中断标志位EPIT_SR

查看EPITx_CR寄存器、LR寄存器和CMPR寄存器手册,配置对应的bit位,代码为:

复制代码
oid epit1_init(irq_handler_t handler)
{
    unsigned int tmp = EPIT1->CR;   

    tmp &= ~(0x3 << 24);    
    tmp |= (0x1 << 24);     //选择时钟 66MHZ   

    tmp |= (1 << 17);      //设置计数值加载

    tmp &= ~(0xfff << 4);
    tmp |= (65 << 4);   //66分频

    tmp |= (1 << 3);   //设置模式
    tmp |= (1 << 2);   //使能中断
    tmp |= (1 << 1);   //ENMOD置为1则从0xffffffff开始计数,置为0则时从当前值开始计数

    EPIT1->CR = tmp;

    EPIT1->LR = 1000000; //1s
   // EPIT1->CNR = 1000000;   //CNT初值的配置,手册中为只读就不配置了,如果不是则需要配置
    EPIT1->CMPR = 0;
    request_irq(EPIT1_IRQn, handler);

    EPIT1->CR |= (1 << 0);
}

注意:

1.当ENMOD为1时,启动定时器,那么就从加载寄存器或者0xFFFFFFFF开始计数;如果ENMOD=0,启动定时器后就从当前值开始计数。

2.EPIT1->CNR 寄存器时CNT初值的配置,参考手册中为只读就不配置了,如果是其他开发板不是只读则需要配置

最后到我们之前文章写的总中断函数中加上EPIT的中断(这里中断号为:EPIT1_IRQn):

注意:SR中断标志位清除是往里面写1

然后写一下EPIT的中断函数,这里写的是反转蜂鸣器:

最后main.c中调用我们写好的epit1_init函数,编译一下上电即可听到蜂鸣器按1s的周期工作

三、GPT(通用目的定时器)

1.GPT介绍

简单来说就是比EPIT多了两个输入捕获,以及两个输出比较

输出比较的用法和EPIT差不多,这里介绍一下输入捕获:

输入捕获(Input Capture):

  • GPT_CAPTURE1 / GPT_CAPTURE2:两个捕获输入引脚(图中 IM1、IM2 为输入中断标志)。

  • 当外部信号(如边沿)到来时,当前计数值 会被锁存到 Timer Input Reg 1/2,同时产生相应的中断标志(IF1、IF2)。

  • 通过选择同步或非同步采样,可以滤除毛刺。

  • 常用于测量脉冲宽度、频率或作为外部事件的时间戳。

模式 行为描述
Restart Mode 向上计数器从 0 开始递增,当计数值与比较通道 1 的比较值匹配时,计数器立即清零并重新从 0 开始计数,同时触发相应中断或输出事件。此模式适用于生成固定周期的定时中断或 PWM。
Free-run Mode 向上计数器从 0 一直递增到 0xFFFFFFFF,溢出后自动回绕到 0 并继续计数,循环往复。比较匹配事件不会复位计数器。此模式适用于测量时间间隔(如高精度延时、输入捕获)。

注意:

Restart模式,只有与比较通道 1 的比较值匹配时,才会清零,所以这个模式下一般COMPARE2 或 COMPARE3 的值都是小于COMPARE1的,这样就可以产生复杂的PWM波了

2.GPT配置

我们用GPT设计一个延时函数1us/1ms为例子,整体流程为:

  1. 使能GPT模块的时钟(通过CCGR,这里我们已经打开了全部时钟)。

  2. 复位GPT定时器(CR_SWR)

  3. 配置GPTx_CR:选择时钟源(PERCLK),设置模式(Free-run Mode,FRR=1),ENMOD,设置输入捕获(IM1-3)、设置输出比较(OM1-3)。

  4. 设置GPT_PR分频寄存器(66分频)

  5. 启动定时器(EN)。

查看参考手册上对应的寄存器配置即可:

复制代码
static inline void gpt1_reset(void)
{
  GPT1->CR |= (1 << 15);   //写1复位
  while ((GPT1->CR & (1 << 15))) //复位好后硬件会自动清零,我们一直持续判断是否为0
    ;
}

void gpt1_init(void)
{
  gpt1_reset();
  unsigned int tmp = GPT1->CR;
  tmp &= ~(0x1ff << 20);
  tmp &= ~(0x1ff << 20);

  tmp |= (1 << 9);
  tmp &= ~(0x7 << 6);
  tmp |= (1 << 6);
  tmp &= ~(1 << 1);
  GPT1->CR = tmp;

  tmp = GPT1->PR;
  tmp &= ~(0xfff << 0);
  tmp |= (65 << 0);
  GPT1->PR = tmp;

  GPT1->CR |= (1 << 0);  // start
}

注意:写延时函数没有用到中断。

延时函数:

复制代码
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;

    if (cur_couter > old_couter)
      counter += cur_couter - old_couter;//当前值大于之前的值就相减加到counter总计时中

    else if (cur_couter < old_couter)
      counter += cur_couter + 0xffffffff - old_couter;//如果当前值已经计数了一个周期小于了之前的值
    if (counter >= num)
      return; //计时到与设置的值相匹配,即到达指定时间

    old_couter = cur_couter; //这样赋值是为了counter一直累加1us(GPT定时器的频率就是1us一计数)
  }
}

void delay_ms(unsigned int num)
{
  while (num--)
  {
    delay_us(1000);
  }
}

注意:

1. 为什么要处理当前读取的值等于之前的值是因为CPU工作在528MHz下,也就是 说它可以1us执行528条这样的语句,有很大可能会一直读到相同的值,所以这里判断一下使用countine回到循环开头可以提高效率

  1. inline 关键字:用于建议编译器 将函数的代码在调用处直接展开 ,替换掉调用指令。这样,最终生成的机器码中不再有独立的函数体,而是多处展开的代码副本。适用于频繁调用的寄存器访问、简单数学运算、延时函数等

四、总结

GPT和EPIT定时器配置并不难,只要了解他们的结构,会看手册配置对应的寄存器就好

相关推荐
科技林总2 小时前
【系统分析师】13.2 概要设计的主要内容
学习
SUNNY_SHUN2 小时前
ICLR 2026 | Judo: 7B小模型工业缺陷问答超越GPT-4o,用对比学习+强化学习注入领域知识
论文阅读·人工智能·学习·视觉检测·github
嗷嗷哦润橘_3 小时前
图解PD分离分布式架构及端口配置解析
人工智能·学习·pd分离
圣光SG3 小时前
ES6+ 基础学习笔记
笔记·学习·es6
zx_zx_1233 小时前
红黑树的学习
学习
小陈phd3 小时前
多模态大模型学习笔记(二十六)—— 核心技术篇③ | 虚拟人的声音情感:从语音合成到声音克隆
笔记·学习
云边散步3 小时前
godot2D游戏教程系列二(23)
笔记·学习·游戏·音视频·游戏开发
hzb666663 小时前
xd_day32-day40
java·javascript·学习·安全·web安全·tomcat·php
前端小趴菜~时倾4 小时前
自我提升-python爬虫学习:day05-函数与面向对象编程
爬虫·python·学习