一、时钟(clock)

1.1 51单片机时钟系统和ARM时钟系统差异

① STC89C51的时钟体系极其简单,只要外部接个11.0592MHz/12MHz晶振,给内核提供一个单一的运行时钟信号,就可以直接驱动内核、串口、定时器所有外设,几乎没有额外的时钟管理分支。

② ARM时钟系统是指CCM时钟控制模块,它的核心作用是把外部24MHz的低速晶振,通过多级PLL锁相环倍频、多路独立分频器,分别生成内核主频、UART/SPI/I2C外设时钟、USB/EMMC高速接口时钟,是用来控制芯片各个模块运行速度的“动力调度系统”。

1.2 I.MX6ULL时钟系统介绍

① 时钟树:IMX6ULL有着一套复杂的时钟系统。能分离出多个不同的时钟出来,分别加以处理,产生出多路不同频率时钟以提供给不同的外设使用,这个概念被称之为时钟树。

② 时钟树相关概念:

(1) PLL(Phase-Locked Loop,锁相环(倍频)) 是芯片内部的一个关键电路模块,用来生成稳定的高频时钟信号。它可以把一个低频的参考时钟(如 24MHz 晶振)倍频到更高的频率(如 800MHz、1GHz),供 CPU、总线、外设使用。N为倍频因子


(2) Prescaler:预分频器(降频),是一个用于降低信号频率的硬件模块,将高频时钟信号分频为较低频率的信号,供定时器、计数器等外设使用。


(3)PFD:Phase Fractional Divders,相位分数分频器(倍频/降频), 是 PLL(锁相环)内部最核心的模块之一,用于比较输入参考时钟和反馈时钟之间的相位差和频率差,并输出控制信号来调整振荡器频率。既可以倍频也可以降频。

N/M > 1: 倍频;N/M < 1: 降频

③ I.MX6U 系列标准的工作频率为 528MHz, I.MX6U 的所有外设时钟源都是从这 7 路 PLL 和有些 PLL 的PFD 而来的。
时钟树如下:

④ 配置时钟:

  • 配置ARM时钟为 528MHZ,使得CortexA7内核工作在528MHZ频率下;
  • 配置PLL2和PLL3下的8个PFD时钟;
  • 配置3个根时钟,为后续外设工作提供时钟频率;

1.3 时钟配置

① 对于IMX6U的主频(ARM内核)的设置步骤如下:
(1) 设置寄存器 CCSR 的 STEP_SEL 位,设置 step_clk 的时钟源为 24M 的晶振。
(2) 设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,设置 pll1_sw_clk 的时钟源为step_clk=24MHz,通过这一步我们就将 I.MX6U 的主频先设置为 24MHz,直接来自于外部的24M 晶振。
(3)设置寄存器 CCM_ANALOG_PLL_ARMn,将 pll1_main_clk(PLL1)设置为 1056MHz。
(4)设置寄存器 CCM_CACRR 的 ARM_PODF 为 2 分频, pll1_sw_clk就1056/2=528MHz。
(5)设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,重新将 pll1_sw_clk 的时钟源切换回pll1_main_clk,切换回来以后 I.MX6U 的内核主频就等于 528MHz。

void clock_init(void)
{
    unsigned int t = 0;
    //1.ARM时钟
    //step_clock:osc_clk
    CCM->CCSR &= ~(1 << 8);
    //step_clk
    CCM->CCSR |= (1 << 2);

    //PLL1倍频因子
    t = CCM_ANALOG->PLL_ARM;
    t &= ~(3 << 14); //选择24MHZ
    t |= (1 << 13);  //使能时钟输出
    t &= ~(0x7F << 0);  //先把DIV位清0
    t |= (88 << 0);   //然后在写入倍数
    CCM_ANALOG->PLL_ARM = t;

    //设置2分频
    CCM->CACRR |= (1 << 0);
    //将PLL输出改为PLL1_mian_clk
    CCM->CCSR &= ~(1 << 2);

    //2.设置PLL2:PFD0~PFD3
    //3.设置PLL3:PFD0 PFD1 PFD2 PFD3
    //4.设置三个根时钟
    //使能外设时钟
    enable_clocks();
} 

② PLL2(System PLL、528 PLL)设置:

//2.设置PLL2:PFD0~PFD3
    t = CCM_ANALOG->PFD_528;
    //PFD位清零
    t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
    //使能PFD0 ~ PFD3时钟
    t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31));
    //设置PFD值
    t |= (27 << 0) | (16 << 8) | (24 << 16) | (32 << 24);
    CCM_ANALOG->PFD_528 = t;

③ PLL3(USB PLL、480PLL)设置:

 //3.设置PLL3:PFD0 PFD1 PFD2 PFD3
    t = CCM_ANALOG->PFD_480;
    //PFD位清零
    t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
    //使能PFD0 ~ PFD3时钟
    t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31));
    //设置PFD时钟
    t |= (12 << 0) | (16 << 8) | (17 << 16) | (19 << 24);
    CCM_ANALOG->PFD_480 = t;

④ 3个根时钟设置:将 AHB_CLK_ROOT、 IPG_CLK_ROOT 和 PERCLK_CLK_ROOT 分 别 设 置 为 132MHz 、 66MHz 、66MHz。

//4.设置三个根时钟
    t = CCM->CBCMR;
    t &= ~(3 << 18); //先清零
    t |= (1 << 18);  //在置1
    CCM->CBCMR = t;

    t = CCM->CBCDR;
    t &= ~(1 << 25);  //选择PLL2_clk
    t &= ~((7 << 10) | (3 << 8));  //清0
    t |= (2 << 10) | (1 << 8);   //设置3分频,2分频
    CCM->CBCDR = t;

    t = CCM->CSCMR1;
    t &= ~(1 << 6);
    t &= ~(0x3F << 0);
    CCM->CSCMR1 = t;

二、定时器(EPIT)

2.1 EPIT定时器功能介绍

① EPIT(Enhanced Periodic Interrupt Timer):增强型周期性中断定时器,专用于实现周期性中断定时功能。I.MX6U 的 EPIT 定时器功能单一,仅支持周期性中断定时,不具备输入捕获或 PWM 输出等功能——这些功能由 I.MX6U 的其他外设实现。
② EPIT定时器特点:

  • 时钟源可选的 32 位向下(递减)计数器;
  • 12 位预分频器,用于对输入时钟频率进行分频;
  • 当计数值和比较值相等的时候产生中断。

③ EPIT定时器结构图:

(1)这是个多路选择器,用来选择 EPIT 定时器的时钟源, EPIT 共有 3 个时钟源可选择, ipg_clk、ipg_clk_32k 和 ipg_clk_highfreq, ipg_clk我们之前已经配置好了——66MHz;
(2)这是一个 12 位的分频器,负责对时钟源进行分频, 12 位对应的值是 0~4095,对应着1~4096 分频;
(3)经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:计数寄存器(EPIT_CNR)、加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这三个寄存器都是 32 位的。EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到0, EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数;
(4) 比较器,比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件;
(5) EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号;
(6)产生比较中断,也就是定时中断。

④ EPIT 定时器有两种工作模式: set-and-forget 和 free-running

  • set-and-forget 模式:此模式下,在此模式下 EPIT 的计数器从加载寄存器 EPITx_LR 中获取初始值,不能直接向计数器寄存器写入数据。不管什么时候,只要计数器计数到 0,那么就会从加载寄存器 EPITx_LR 中重新加载数据到计数器中,周而复始。
  • free-running 模式:此模式下,当计数器计数到 0 以后会重新从0XFFFFFFFF 开始计数,并不是从加载寄存器 EPITx_LR 中获取数据。

2.2 实现1S LED翻转

EPIT设置步骤:

  • 设置 EPIT1 的时钟源;(01-Peripheral Clock)
  • 设置当写入数据到加载寄存器时要立即覆盖当前的计数值 (1),后面就不用设置CNR寄存器了。
  • 设置分频值 ;(66分频)
  • 设置工作模式;(1 set-and-forget
  • 使能比较中断;(1)
  • 设置计数器的初始值来源;(1)
  • 设置加载值和比较值;(1000000,0)
  • 设置EPIT1 中断服务函数;
  • 使能 EPIT1 定时器。
//epit.c
#include "epit.h"
#include "led.h"
#include "interrupt.h"
#include "MCIMX6Y2.h"
#include "core_ca7.h"

// epit处理函数
void epit_interrupt_handler(void)
{   
    if ((EPIT1->SR & (1 << 0)) != 0)  //为1表示定时器发生了比较匹配事件(即定时溢出)。
    {
        led_nor();
    }
    EPIT1->SR |= (1 << 0); //写1清除中断标志
}
//epit初始化,定时1s
void epit_init(void)
{
    EPIT1->CR = 0;  //清0
    //66分频,1MHZ=1us=0.000001s,
    EPIT1->CR |= (1 << 24) | (1 << 17) | (65 << 4) | (1 << 3) | (1 << 2) | (1 << 1); 
    EPIT1->LR = 1000000;  //设置加载值
    EPIT1->CMPR = 0;   //设置比较值

    // 注册EPIT1中断
    system_interrupt_register(EPIT1_IRQn, epit_interrupt_handler);

    // 设置EPIT中断优先级
    GIC_SetPriority(EPIT1_IRQn, 1);

    // 使能EPIT中断
    GIC_EnableIRQ(EPIT1_IRQn);

    // 开启EPIT1定时器
    EPIT1->CR |= (1 << 0);
}

三、定时器(GPT)

2.1 GPT定时器功能介绍

① GPT( General Purpose Timer):通用目的定时器, GPT 定时器是一个32 位向上定时器(也就是从 0X00000000 开始向上递增计数), GPT 定时器也可以跟一个值进行比较,当
计数器值和这个值相等的话就发生比较事件,产生比较中断。 GPT 定时器有一个 12 位的分频器,可以对GPT 定时器的时钟源进行分频。

② GPT 定时器特性:

  • 一个可选时钟源的 32 位向上计数器;
  • 两个输入捕获通道,可以设置触发方式;
  • 三个输出比较通道,可以设置输出模式;
  • 可以生成捕获中断、比较中断和溢出中断;
  • 计数器可以运行在重新启动(Restart)或(自由运行)Free-Run 模式。

③ GPT定时器结构图:

        定时器计数器的值可以通过外部引脚上的事件捕获到寄存器中。捕获触发可以编程为上升沿或下降沿。

        GPT_CAPTURE 1和GPT_CAPTURE 2表示GPT的两个输入捕获通道,作用是考察这两个引脚电平变化之间的时间间隔(捕获方波)。sync是同步寄存器

        GPT_COMPARE1、 2、3是GPT的三个比较输出通道,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断(周期性输出方波),也可以修改三个引脚的电平。


④ GPT也有两种工作模式:

  • 重新启动(Restart)模式:在此模式下,当计数值和比较寄存器中的值相等的话 计数值就会清零,然后重新从0X00000000 开始向上计数,只有比较通道 1 才有此模式!向比较通道 1 的比较寄存器写入任何数据都会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会复位计数器。
  • 自由运行(Free-Run)模式:当在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为 0XFFFFFFFF,然后重新回滚到 0X00000000。

2.2 实现1S LED翻转

① GPT设置步骤:

//gpt.c
#include "gpt.h"
#include "MCIMX6Y2.h"
#include "core_ca7.h"

void gpt_init(void)
{
    GPT1->CR = 0;   //清零
    GPT1->CR |= (1 << 15);      // 复位GPT
    while ((GPT1->CR & (1 << 15)) != 0);   // 等待GPT复位完成
    GPT1->CR |= (1 << 9);       // GPT运行在Free-Run模式
    GPT1->CR |= (1 << 6);       // 时钟源选择为ipg_clk
    GPT1->PR = 65;              // 预分频器选择为66分频
    GPT1->CR |= (1 << 0);       // 使能GPT
}

② 延迟函数

//delay.c
#include "delay.h"
#include "MCIMX6Y2.h"
#include "core_ca7.h"

// 微秒延时函数
void delay_us(unsigned int n)
{
    unsigned int counter = 0;
    unsigned int new_counter = 0;
    unsigned int old_counter = 0;
    old_counter = GPT1->CNT;
    while (1)
    {
        new_counter = GPT1->CNT;
        if (new_counter != old_counter)
        {
            if (new_counter > old_counter)
            {
                counter += new_counter - old_counter;
            }
            else
            {
                counter += 0xFFFFFFFF - old_counter + new_counter;
            }
            if (counter >= n)
            {
                return;
            }
            old_counter = new_counter;
        }
    }
}

// 毫秒延时函数
void delay_ms(unsigned int n)
{
    while (n--)
    {
        delay_us(1000);
    }
}

③ main.c函数

#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include  "led.h" 
#include  "beep.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"
#include "gpt.h"
#include "delay.h"

int main(void)
{
    //时钟初始化
    clock_init();
    //GIC初始化
    system_interrupt_init();
    //led初始化
    led_init();
    //蜂鸣器beep初始化
    beep_init();
    //按键初始化
    key_init();
    //EPIT初始化
   // epit_init();
    //GPT初始化
    gpt_init();

    led_off();
    beep_off();

    while(1)
    {
        led_nor();
        delay_ms(1000);

    }
    return 0;
}

四、EPIT与GPT定时器核心差异对比

下表从功能、工作模式、时钟源、中断特性、应用场景等多个维度对比EPIT与GPT定时器的核心差异:

对比维度 EPIT定时器 GPT定时器
功能定位 增强型周期性中断定时器,功能单一,专用于周期性中断定时 通用目的定时器,功能丰富,支持定时、输入捕获、输出比较、PWM等
计数器方向 32位向下(递减)计数器,从设定值递减到0 32位向上(递增)计数器,从0开始递增
工作模式
  • set-and-forget模式:计数到0后从加载寄存器重新加载
  • free-running模式:计数到0后从0xFFFFFFFF重新开始
  • 重新启动(Restart)模式:比较匹配后计数器复位(仅通道1)
  • 自由运行(Free-Run)模式:比较匹配后继续计数到0xFFFFFFFF
时钟源 可选:ipg_clk、ipg_clk_32k、ipg_clk_highfreq 可选:ipg_clk、ipg_clk_highfreq、外部时钟等
分频器 12位预分频器(1~4096分频) 12位分频器
中断类型 主要支持比较中断(计数值与比较值相等时) 支持捕获中断比较中断溢出中断三种
输入/输出通道 无专用输入捕获或输出比较通道
  • 2个输入捕获通道(可检测边沿触发)
  • 3个输出比较通道(可产生PWM、修改引脚电平)
寄存器特点 三个核心寄存器:
- EPIT_CNR(计数寄存器)
- EPIT_LR(加载寄存器)
- EPIT_CMPR(比较寄存器)
寄存器更丰富:
- GPT_CNT(计数寄存器)
- GPT_OCR1/2/3(输出比较寄存器)
- GPT_ICR1/2(输入捕获寄存器)
应用场景
  • 周期性定时任务(如系统心跳、LED闪烁)
  • 简单的时间基准生成
  • 不需要复杂输入/输出功能的定时应用
  • 需要输入捕获的应用(如测量脉冲宽度、频率)
  • 需要输出比较/PWM的应用(如电机控制、呼吸灯)
  • 复杂的定时、计数需求
  • 多通道定时任务
复杂度 相对简单,配置和使用较为直接 相对复杂,功能多,配置选项丰富
代码示例中的分频值 66分频(ipg_clk=66MHz → 1MHz计数频率) 66分频(ipg_clk=66MHz → 1MHz计数频率)
1秒定时实现方式 设置加载值=1000000,比较值=0,66分频后1MHz计数频率,递减到0产生中断 利用66分频后1MHz计数频率,通过读取CNT寄存器差值实现延时函数

总结:EPIT定时器专注于简单、精确的周期性中断定时,适合需要稳定周期触发的应用;GPT定时器作为通用定时器,功能全面,适合需要输入捕获、输出比较、PWM生成等复杂定时/计数场景。选择时可根据具体需求:若只需周期性中断,EPIT更简洁高效;若需要测量外部信号或生成PWM,GPT是更合适的选择。

更多推荐