(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,常见的包括
- ARM PLL:这是ARM核心复合体的PLL时钟。
- System PLL or 528_PLL:从 XTALOSC 产生 24 MHz 的参考频率,输出 528 MHz 的频率
- **USB1 PLLor 480_PLL:**固定480MHz的频率
- Audio PLL:
- Video PLL :
- ENET_PLL:
- 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
计算跨溢出的计数差值
整体逻辑清晰,先初始化定时器为自由运行模式,再利用其计数器值的变化实现精确延时,适用于需要精准时间控制的嵌入式场景(如传感器采样、外设时序控制等)。