基于IMX6ULL的时钟,定时器(EPIT,GPT)

(1)时钟:

1.PLL(锁相环,Phase - Locked Loop):倍频

  • 定义:PLL 是一种反馈控制系统,它能够自动跟踪输入信号的频率和相位,使输出信号的频率和相位与输入信号保持同步 。它主要由鉴相器(PD,Phase Detector)、环路滤波器(LF,Loop Filter)和压控振荡器(VCO,Voltage - Controlled Oscillator)三个基本部件组成。
  • 作用 :在 IMX6ULL 等芯片中,PLL 的主要作用是产生稳定的、高精度的时钟信号。它可以将一个较低频率的参考时钟信号,通过倍频等操作,产生满足芯片内部不同模块(如 CPU 内核、外设接口等)工作需求的多种频率的时钟信号。例如,ARM PLL 可以为 ARM 内核提供合适的工作频率 。

2.Prescaler(预分频器):分频

  • 定义 :Prescaler 是一种数字电路模块,它可以对输入的时钟信号进行分频处理,即将输入时钟信号的频率降低为原来的几分之一 。比如一个 2 分频的预分频器,会使输出时钟信号频率变为输入时钟信号频率的一半。
  • 作用 :在 IMX6ULL 芯片中,预分频器主要用于调整时钟信号的频率,使其满足特定模块的工作需求。通过对 PLL 输出的时钟信号进行分频,可以得到适合不同外设工作的时钟频率,同时也有助于降低功耗,因为一些外设不需要太高的时钟频率。

3.PFD(相位频率检测器,Phase - Frequency Detector):既可以用分频也可以倍频

  • 定义:PFD 是 PLL 中的一个关键部件,它主要用于比较输入信号和反馈信号的相位和频率,并输出一个反映两者差异的信号 。
  • 作用:在 PLL 环路中,PFD 的输出信号会被送到环路滤波器进行处理,然后再去控制压控振荡器(VCO)的输出频率和相位。通过不断比较和调整,PLL 可以使输出信号与输入信号保持同频同相。在 IMX6ULL 中,PFD 与 PLL 配合,实现稳定的时钟信号生成和调整。

4.IMX6ULL 中的 PLL 和 PFD 数量

①PLL 数量IMX6ULL 通常有7个 PLL,常见的包括

  1. ARM PLL:这是ARM核心复合体的PLL时钟。
  2. System PLL or 528_PLL:从 XTALOSC 产生 24 MHz 的参考频率,输出 528 MHz 的频率
  3. **USB1 PLLor 480_PLL:**固定480MHz的频率
  4. Audio PLL:
  5. Video PLL :
  6. ENET_PLL:
  7. USB2_PLL:
  • PFD 数量:IMX6ULL 中存在多个 PFD,例如与 528 PLL 和 480 PLL 相关的 PFD,它们与对应的 PLL 协同工作,为系统提供多种频率选择。具体数量会根据芯片手册的定义和不同的配置模式有所差异,但一般会有多个 PFD 来满足不同的时钟生成和调整需求。

时钟(clock):在电子系统中是一个产生稳定、周期性振荡信号的电路或组件。这个信号像节拍器或心跳一样,为数字电路中的各种操作提供同步时序基准。

实时时钟(RTC real time clock): 是微处理器中的一个功能模块,用于在系统主电源关闭的情况下,继续提供精确的日历和时间信息。

配置ARM PLL:

1. 配置 ARM 工作频率(主频相关寄存器 CCM->CCSR

  • CCM->CCSR &= ~(1<<8);
    • CCM 模块的 CCSR 寄存器的第 8 位 清零CCSR(Clock Control Switch Register)是时钟控制切换寄存器,该操作通常用于关闭某个与 ARM 主频相关的默认时钟路径或功能。
  • CCM->CCSR |= (1<<2);
    • CCM->CCSR 的第 2 位 置 1。这一步是选择 ARM 内核的时钟源(比如切换到某个 PLL 输出的时钟),为后续设置 ARM 主频做准备。
cs 复制代码
// 配置ARM内核时钟源及PLL参数,设置ARM工作频率
// 参考i.MX6ULL芯片手册中CCM(时钟控制模块)和PLL相关寄存器定义

// 1. 配置时钟控制切换寄存器(CCSR),准备切换ARM时钟源
// CCSR寄存器:用于选择ARM内核时钟源和控制时钟路径
// 第8位:通常用于关闭默认时钟源或使能时钟切换准备
CCM->CCSR &= ~(1<<8);    // 清除CCSR第8位,关闭默认时钟路径或相关功能

// 第2位:ARM_PODF(ARM分频器)时钟源选择位
// 置1时,通常选择PLL_ARM作为ARM时钟源的前级时钟
CCM->CCSR |= (1<<2);     // 设置CCSR第2位,选择PLL_ARM作为ARM时钟源

// 2. 配置ARM分频寄存器(CACRR),设置ARM内核最终分频系数
// CACRR寄存器:控制ARM内核时钟的分频比例(ARM_PODF)
// 低3位(bit0~bit2):表示分频系数,公式为 分频值 = (bit值 + 1)
CCM->CACRR &= ~(7<<0);   // 清除低3位,清除原有分频设置
CCM->CACRR |= (1<<0);    // 设置低3位为0b001,即分频系数为2(1+1)

// 3. 配置ARM锁相环(PLL_ARM),生成高频基准时钟
unsigned int t;           // 临时变量,用于寄存器值的修改(避免直接操作寄存器覆盖其他位)

// 读取当前PLL_ARM寄存器值
t = CCM_ANALOG->PLL_ARM;

// PLL_ARM寄存器bit14~15:PLL工作模式配置位
// 清零这两位,通常用于禁用PLL旁路模式,启用正常锁相环模式
t &= ~(3<<14);            // 清除bit14~15,配置PLL为正常工作模式

// PLL_ARM寄存器bit13:PLL使能/控制位
// 置1时,通常用于使能PLL的倍频功能或锁定控制
t |= (1<<13);             // 设置bit13,使能PLL_ARM的倍频功能

// PLL_ARM寄存器bit0~bit6:倍频系数配置位(NDIV)
// 清零这7位,清除原有倍频设置
t &= ~(0x7F<<0);          // 清除bit0~bit6,准备设置新的倍频系数

// 设置倍频系数为88(bit0~bit6赋值88)
// 公式:PLL输出频率 = 参考时钟频率 * 倍频系数
// 假设参考时钟为24MHz,则此时PLL输出为24MHz * 88 = 2112MHz
t |= (88<<0);             // 设置bit0~bit6为88,配置PLL_ARM倍频系数

// 将修改后的值写回PLL_ARM寄存器,完成PLL配置
CCM_ANALOG->PLL_ARM = t;

// 4. 完成时钟源切换,使新配置生效
// 清除CCSR第2位,确认时钟源切换完成,ARM内核开始使用PLL_ARM经过分频后的时钟
CCM->CCSR &= ~(1<<2);     // 确认切换,ARM时钟源正式切换为PLL_ARM分频后输出

// 最终ARM内核频率计算:
// PLL_ARM输出(2112MHz) ÷ CACRR分频系数(2) = 1056MHz

时钟树:

(2)定时:

1.EPIT(增强型周期中断定时器)

主打高效的周期性定时中断能力。它以递减计数器为核心,可从系统时钟中选择合适源并经分频后驱动计数。工作时,计数器从预设值开始递减,当与比较寄存器值匹配时触发中断,且支持自动重装载功能 ------ 计数到 0 或匹配事件后,能自动将初始值重新载入计数器,无需软件干预即可实现稳定的周期性定时,非常适合需要固定间隔触发任务(如周期性数据采集、定时刷新)的场景,功能专一且响应高效。

cs 复制代码
#include "epit.h"
#include "../imx6ull/MCIMX6Y2.h"
#include "interrupt.h"
#include "led.h"

void epit_irq_handler(void)//定时器EPIT中断
{
    if((EPIT1->SR & (1 << 0)) != 0) //SR位状态寄存器:0比较事件尚未发生;1发生比较事件。在0bit    
    {
        led_nor();
        EPIT1->SR |= (1 << 0); //该位写入一清除位:硬件高电平与1位清零
    }
}

void epit1_init(void)//EPIT初始化
{
    unsigned int t;
    t = EPIT1->CR;//CR:控制寄存器,将当前的EPIT->CR寄存器的值放入t中
    t &= ~(3 << 24);//24,25bit先置0
    t |= (1 << 24);//24,25bit选择时钟源:01设置成外围时钟
    t |= (1 << 17);//17bit:EPIT计数器覆盖启用,0写入加载寄存器不会导致计数器被覆盖,1会
    t &= ~(0xFFF << 4);//4---15bit:逆时针预分频器值,此位字段用于确定在将时钟分频至计数器之前所进行的分频值。0x00:除以一
    t |= (65 << 4);//除以66;
    t |= (1 << 3);//3bit:计数器重新加载控制。0:当计数器达到零时,它会滚动至到OxFFF_FFF(自由运行模式)
                                        //1:当计数器达到零时,它会从模数寄存器重新加载(设置并忘记模式)
    t |= (1 << 2);//输出比较中断使能:      0比较中断禁用状态0比较中断已禁用
                                    //  1比较中断开启状态1比较中断已启用
    t |= (1 << 1);//EPIT使能模式。当ENMOD为1时,启动定时器,那么就从加载寄存器或者0xFFFFFFFF开始计数;
                    //如果ENMOD=0,启动定时器后就从当前值开始计数。

    EPIT1->CR = t;

    EPIT1->LR = 1000*1000;//CR:加载寄存器
    EPIT1->CMPR = 0;//比较寄存器
    EPIT1->CNR = 1000*1000;//计数寄存器
    // 1.使能 EPIT1 中断
    GIC_EnableIRQ(EPIT1_IRQn);
    //2.中断优先级设置
    GIC_SetPriority(EPIT1_IRQn, 0);
    //3.注册中断处理函数
    system_interrupt_register(EPIT1_IRQn, epit_irq_handler);
    //启动 EPIT1 定时器:最后开启
    EPIT1->CR |= (1 << 0); 
}

整体实现了一个周期性中断定时器,当计数器从 1000000 递减到 0 时,会触发中断并翻转 LED 状态,然后自动重新加载计数,形成周期性定时功能。

2.GPT(通用定时器)

则具备更灵活的多功能性。它支持向上、向下及双向计数模式,时钟源选择和分频配置同样灵活。除基础定时功能外,其核心优势在于输入捕获输出控制:输入捕获可检测外部信号边沿(如上升 / 下降沿)并锁存当前计数值,用于测量信号周期、脉宽等;比较功能不仅能触发中断,还可通过配置在外部引脚输出特定波形(如 PWM)。这种多模式、多接口的特性,使其适用于更复杂的场景,如电机调速(PWM 输出)、外部事件计时(捕获功能)等,灵活性远高于 EPIT。

cs 复制代码
#include "gpt.h"
#include "../imx6ull/MCIMX6Y2.h"

/**
 * @brief 重置GPT1定时器
 * 作用:通过设置CR寄存器的软件复位位,将GPT1恢复到初始状态
 */
void reset_gpt1()
{
  // GPT1->CR:GPT1控制寄存器
  // 第15位:软件复位位,置1触发GPT1复位
  GPT1->CR |= (1 << 15);
  
  // 等待复位完成:复位过程中第15位保持1,复位完成后自动清0
  while ((GPT1->CR & (1 << 15)) != 0);
}

/**
 * @brief 初始化GPT1定时器,配置为自由运行模式用于计时
 */
void gpt1_init(void)
{
  reset_gpt1();  // 先复位定时器,确保初始状态一致
  
  unsigned int t;
  t = GPT1->CR;  // 读取当前控制寄存器值,避免直接操作覆盖其他位

  // 配置时钟源(CR寄存器26-28位)
  t &= ~(7 << 26);  // 清除26-28位(时钟源选择位)
  // 此处未设置新值,默认使用外设时钟(具体需参考芯片手册)

  // 配置输出比较模式(CR寄存器18-19位)
  t &= ~(3 << 18);  // 清除18-19位,禁用输出比较功能(仅用于计时)

  // 使能计数器溢出中断(CR寄存器第9位)
  t |= (1 << 9);    // 置1:允许计数器溢出时产生中断

  // 配置计数模式(CR寄存器6-8位)
  t &= ~(7 << 6);   // 清除6-8位
  t |= (1 << 6);    // 设置为6-8位为001,选择"自由运行模式"(计数器从0x00000000递增到0xFFFFFFFF后回0)

  // 禁用暂停模式(CR寄存器第1位)
  t &= ~(1 << 1);   // 清0:禁止在调试模式下暂停计数器

  GPT1->CR = t;     // 写入配置值

  // 配置预分频器(PR寄存器0-11位)
  GPT1->PR &= ~(0xFFF << 0);  // 清除0-11位(预分频值)
  GPT1->PR |= (65 << 0);      // 设置预分频值为65,实际分频系数=65+1=66

  // 启动GPT1定时器(CR寄存器第0位)
  GPT1->CR |= (1 << 0);  // 置1:使能计数器开始计数
}

/**
 * @brief 微秒级延时函数
 * @param us 延时的微秒数
 * 原理:利用GPT1的自由运行计数器计算经过的时间
 */
void delay_us(unsigned int us)
{
    unsigned int counter = 0;          // 累计的计数次数
    unsigned int old_counter = 0, new_counter = 0;  // 用于保存计数器值

    old_counter = GPT1->CNT;  // 读取当前计数器值作为起始点

    while(1)
    {
        new_counter = GPT1->CNT;  // 读取当前计数器值

        // 仅在计数器值变化时更新累计值(避免重复计算)
        if (old_counter != new_counter)
        {
            // 处理计数器溢出(从0xFFFFFFFF回0的情况)
            if (old_counter < new_counter)
            {
                // 未溢出:直接累加差值
                counter += new_counter - old_counter;
            }
            else
            {
                // 已溢出:累加"最大值到old_counter的差值" + "new_counter到0的差值"
                counter += 0xFFFFFFFF - old_counter + new_counter;
            }

            // 累计计数达到目标值时退出循环
            if (counter >= us)
            {
                return;
            }

            old_counter = new_counter;  // 更新起始点
        }
    }
}

/**
 * @brief 毫秒级延时函数
 * @param ms 延时的毫秒数
 * 原理:通过循环调用微秒延时函数实现
 */
void delay_ms(unsigned int ms)
{
    while(ms--)
    {
        delay_us(1000);  // 1毫秒 = 1000微秒
    }
}
    
1. 核心功能

这段代码实现了 i.MX6ULL 芯片中 GPT1(通用定时器 1)的初始化配置,并基于 GPT1 实现了微秒级(delay_us)和毫秒级(delay_ms)的延时功能。

2. 初始化流程(gpt1_init
  • 复位定时器 :通过reset_gpt1函数触发软件复位,确保定时器初始状态一致
  • 配置控制寄存器(CR)
    • 选择时钟源(默认外设时钟)
    • 禁用输出比较功能(仅用于计时)
    • 使能计数器溢出中断(为后续扩展预留)
    • 设置为自由运行模式(计数器从 0 递增到 0xFFFFFFFF 后自动回 0)
    • 禁用调试暂停功能(确保计时不受调试影响)
  • 配置预分频器:设置分频系数为 66(输入时钟 / 66 作为计数时钟)
  • 启动定时器:使能计数器开始工作
3. 延时功能实现
  • delay_us

    • 记录起始时刻的计数器值(old_counter
    • 循环读取当前计数器值(new_counter),计算累计计数次数
    • 处理计数器溢出情况(从 0xFFFFFFFF 到 0 的跳变)
    • 当累计计数达到目标微秒数时退出
  • delay_ms

    • 通过循环调用delay_us(1000)实现毫秒级延时
    • 每次循环减少 1 毫秒计数,直到完成指定延时
4. 关键技术点
  • 自由运行模式:GPT1 计数器持续递增,溢出后自动归零,适合持续计时场景
  • 预分频配置:通过 66 分频降低计数频率,使计数器值与微秒级时间对应
  • 溢出处理 :通过0xFFFFFFFF - old_counter + new_counter计算跨溢出的计数差值

整体逻辑清晰,先初始化定时器为自由运行模式,再利用其计数器值的变化实现精确延时,适用于需要精准时间控制的嵌入式场景(如传感器采样、外设时序控制等)。

相关推荐
happygrilclh3 小时前
stm32L496 flash 分配
stm32·单片机·嵌入式硬件
古译汉书4 小时前
嵌入式铁头山羊STM32-各章节详细笔记-查阅传送门
数据结构·笔记·stm32·单片机·嵌入式硬件·个人开发
自由的好好干活5 小时前
从0开始使用LabVIEW操作数据采集卡-概述和新建新建项目
嵌入式硬件·labview
一枚码农~7 小时前
STM32红外与LED控制实战
单片机·嵌入式硬件
Heavy sea7 小时前
STM32定时器(寄存器与HAL库实现)
stm32·单片机
路过羊圈的狼9 小时前
STM32的HAL库驱动ADS124S08进行PT100温度采集
stm32·嵌入式硬件·mongodb
风已经起了9 小时前
FPGA学习笔记——图像处理之对比度调节(直方图均衡化)
图像处理·笔记·学习·fpga开发·fpga
李永奉10 小时前
51单片机-实现红外遥控模块教程
单片机·嵌入式硬件·51单片机
辛集电子10 小时前
【STM32】位带操作
stm32·单片机·嵌入式硬件