title: "I.MX6ULL 时钟与电源管理" tags: [embedded-linux, clock, power-management, imx6ull, arm] created: 2026-09-17 updated: 2026-09-17
I.MX6ULL 的系统时钟来源于两部分外部晶振:
| 晶振 | 频率 | 用途 |
|---|---|---|
| 32.768 KHz | RTC 时钟源 | 实时时钟、看门狗低功耗定时器 |
| 24 MHz | 系统主时钟源 | ARM 内核、所有外设、PLL 输入 |
24 MHz 晶振是整个时钟树的根,所有 PLL 均由此倍频产生。内部 Boot ROM 默认将主频设置为 396 MHz,但 I.MX6ULL 标准工作频率为 528 MHz,部分型号可跑 696 MHz。
NXP 将外设时钟源分为 7 组,全部由 24 MHz 晶振 PLL 倍频而来:
graph TD
OSC["24 MHz 晶振"] --> PLL1["PLL1 (ARM_PLL)<br/>可编程 54~108 倍频<br/>最高 1.3 GHz"]
OSC --> PLL2["PLL2 (528_PLL / System_PLL)<br/>固定 22 倍频 = 528 MHz"]
OSC --> PLL3["PLL3 (USB1_PLL)<br/>固定 20 倍频 = 480 MHz"]
OSC --> PLL5["PLL5 (VIDEO_PLL)<br/>可编程 650~1300 MHz"]
OSC --> PLL4["PLL4 (AUDIO_PLL)<br/>可编程 650~1300 MHz"]
OSC --> PLL6["PLL6 (ENET_PLL)<br/>固定 20+5/6 倍频 = 500 MHz"]
OSC --> PLL7["PLL7 (USB2_PLL)<br/>固定 20 倍频 = 480 MHz"]
PLL2 --> PFD2_0["PLL2_PFD0 (352 MHz)"]
PLL2 --> PFD2_1["PLL2_PFD1 (594 MHz)"]
PLL2 --> PFD2_2["PLL2_PFD2 (396 MHz)"]
PLL2 --> PFD2_3["PLL2_PFD3 (297 MHz)"]
PLL3 --> PFD3_0["PLL3_PFD0 (720 MHz)"]
PLL3 --> PFD3_1["PLL3_PFD1 (540 MHz)"]
PLL3 --> PFD3_2["PLL3_PFD2 (508 MHz)"]
PLL3 --> PFD3_3["PLL3_PFD3 (455 MHz)"]
| PLL | 名称 | 倍频方式 | 输出频率 | 主要用途 |
|---|---|---|---|---|
| PLL1 | ARM_PLL | 可编程 (54~108) | 最高 1.3 GHz | ARM 内核主频 |
| PLL2 | System_PLL | 固定 ×22 | 528 MHz | 系统总线、DDR、NAND/NOR |
| PLL3 | USB1_PLL | 固定 ×20 | 480 MHz | USB1 PHY + 4 路 PFD |
| PLL4 | AUDIO_PLL | 可编程 | 650~1300 MHz | 音频外设 (ESAI 等) |
| PLL5 | VIDEO_PLL | 可编程 | 650~1300 MHz | 显示外设 (LCD 等) |
| PLL6 | ENET_PLL | 固定 ×(20+5/6) | 500 MHz | 以太网时钟 (25/50/100/125 MHz) |
| PLL7 | USB2_PLL | 固定 ×20 | 480 MHz | USB2 PHY |
I.MX6ULL 的时钟树分为三部分:
graph LR
subgraph CLOCK_SWITCHER["时钟源 (CLOCK SWITCHER)"]
PLL1["PLL1 1056MHz"]
PLL2["PLL2 528MHz"]
PLL3["PLL3 480MHz"]
PLL4["PLL4 650MHz"]
PLL5["PLL5 650MHz"]
PLL6["PLL6 500MHz"]
PLL7["PLL7 480MHz"]
PFD["8路 PFD"]
end
subgraph ROOT_GEN["时钟根生成器 (CLOCK ROOT GENERATOR)"]
SEL["时钟源选择器"]
DIV["分频器 (PODF)"]
end
subgraph SYS_CLK["系统时钟 (SYSTEM CLOCKS)"]
ARM["ARM 内核"]
AHB["AHB 总线"]
IPG["IPG 总线"]
PERCLK["PERCLK"]
UART["UART"]
SPI["SPI"]
I2C["I2C"]
LCD["LCD"]
end
PLL1 --> SEL
PLL2 --> SEL
PLL3 --> SEL
PLL4 --> SEL
PLL5 --> SEL
PFD --> SEL
SEL --> DIV
DIV --> ARM
DIV --> AHB
DIV --> IPG
DIV --> PERCLK
DIV --> UART
DIV --> SPI
DIV --> I2C
DIV --> LCD
外设时钟配置三步曲(以外设 ESAI 为例):
CCM->CSCMR2 的 ESAI_CLK_SEL 位,从 PLL4/PLL5/PLL3_PFD2/pll3_sw_clk 中选一路CCM_CS1CDR 的 ESAI_CLK_PRED 位,1~8 分频CCM_CS1CDR 的 ESAI_CLK_PODF 位,1~8 分频例:PLL4=650 MHz → 前级 2 分频 = 325 MHz → 二级 8 分频 = 40.625 MHz
I.MX6ULL 使用 PODF (Prescale Output Divider) 进行分频,PODF 值与分频比的映射关系:
| PODF 值 | 分频比 |
|---|---|
| 0 | ÷1 |
| 1 | ÷2 |
| 2 | ÷4 |
| 3 | ÷8 |
关键分频器及其寄存器:
| 分频器 | 寄存器 | 位域 | 范围 | 目标频率 |
|---|---|---|---|---|
| ARM_PODF | CCM_CACRR[2:0] | 0~7 | 1~8 分频 | 内核主频 |
| AHB_PODF | CCM_CBCDR[12:10] | 0~7 | 1~8 分频 | AHB 总线 ≤132 MHz |
| IPG_PODF | CCM_CBCDR[9:8] | 0~3 | 1~4 分频 | IPG 总线 ≤66 MHz |
| PERCLK_PODF | CCM_CSCMR1[2:0] | 0~7 | 1~8 分频 | PERCLK ≤66 MHz |
I.MX6ULL 通过 7 组 CCGR 寄存器 (CCM->CCGR0 ~ CCM->CCGR6) 控制外设时钟的使能与关闭。每个外设时钟有 2 位控制:
| CCGR 值 | 含义 |
|---|---|
| 00 | 关闭 (off) |
| 01 | 部分使能 (defined by CCM clock gate) |
| 10 | 保留 |
| 11 | 运行时始终使能 (on during run) |
/* 使能所有外设时钟 (开发阶段) */
void clk_enable(void)
{
CCM->CCGR0 = 0xFFFFFFFF;
CCM->CCGR1 = 0xFFFFFFFF;
CCM->CCGR2 = 0xFFFFFFFF;
CCM->CCGR3 = 0xFFFFFFFF;
CCM->CCGR4 = 0xFFFFFFFF;
CCM->CCGR5 = 0xFFFFFFFF;
CCM->CCGR6 = 0xFFFFFFFF;
}
注意:量产代码应按需使能时钟,未使用的外设应关闭时钟以降低功耗。
修改以下分频器或选择器时会触发与 MMDC (Memory Controller) 的握手:
mmdc_podfperiph_clk_selperiph2_clk_selarm_podfahb_podf握手信号在 CCM_CDHIPR 寄存器中,相应位为 1 表示握手未完成,为 0 表示完成。修改 arm_podf 和 ahb_podf 时需先关闭时钟输出,修改完成后再打开,否则可能导致无时钟输出。
| 位域 | 名称 | 功能 |
|---|---|---|
| [2] | PLL1_SW_CLK_SEL | pll1_sw_clk 源选择:0=pll1_main_clk, 1=step_clk |
| [8] | STEP_SEL | step_clk 源选择:0=osc_clk(24MHz), 1=secondary_clk |
| 位域 | 名称 | 功能 |
|---|---|---|
| [2:0] | ARM_PODF | ARM 内核时钟分频:0~7 对应 1~8 分频 |
| 位域 | 名称 | 功能 |
|---|---|---|
| [25] | PERIPH_CLK_SEL | periph_clk 源:0=pre_periph_clk, 1=periph_clk2_clock |
| [19] | PERIPH2_CLK_SEL | periph2_clk 源:0=PLL2, 1=periph2_clk2_clk |
| [12:10] | AHB_PODF | AHB 分频:0~7 对应 1~8 |
| [9:8] | IPG_PODF | IPG 分频:0~3 对应 1~4 |
| [7:0] | AXI_PODF | AXI 分频:0~7 对应 1~8 |
| 位域 | 名称 | 功能 |
|---|---|---|
| [19:18] | PRE_PERIPH_CLK_SEL | pre_periph_clk 源:00=PLL2, 01=PLL2_PFD2, 10=PLL2_PFD0, 11=PLL2_PFD2/2 |
| [27:26] | PRE_PERIPH2_CLK_SEL | pre_periph2 源:00=PLL2, 01=PLL2_PFD2, 10=PLL2_PFD0, 11=PLL4 |
| 位域 | 名称 | 功能 |
|---|---|---|
| [6] | PERCLK_CK_SEL | PERCLK 源:0=IPG_CLK, 1=OSC_CLK |
| [2:0] | PERCLK_PODF | PERCLK 分频:0~7 对应 1~8 |
| 位域 | 名称 | 功能 |
|---|---|---|
| [13] | ENABLE | PLL1 输出使能:1=使能, 0=关闭 |
| [6:0] | DIV_SELECT | 倍频因子:54~108,Fout = Fin × div_select / 2.0 |
| 位域 | 名称 | 功能 |
|---|---|---|
| [5:0] | PFD0_FRAC | PLL2_PFD0 分频因子:528×18/PFD0_FRAC |
| [6] | PFD0_CLKGATE | PFD0 门控:1=关闭, 0=使能 |
| [13:8] | PFD1_FRAC | PLL2_PFD1 分频因子 |
| [14] | PFD1_CLKGATE | PFD1 门控 |
| [21:16] | PFD2_FRAC | PLL2_PFD2 分频因子 |
| [22] | PFD2_CLKGATE | PFD2 门控 |
| [29:24] | PFD3_FRAC | PLL2_PFD3 分频因子 |
| [30] | PFD3_CLKGATE | PFD3 门控 |
结构与 PFD_528n 相同,频率公式为 480 × 18 / PFDx_FRAC。
| PFD | 推荐频率 | FRAC 值 | 计算公式 |
|---|---|---|---|
| PLL2_PFD0 | 352 MHz | 27 | 528×18/27 |
| PLL2_PFD1 | 594 MHz | 16 | 528×18/16 |
| PLL2_PFD2 | 396 MHz | 24 | 528×18/24 |
| PLL2_PFD3 | 297 MHz | 32 | 528×18/32 |
| PLL3_PFD0 | 720 MHz | 12 | 480×18/12 |
| PLL3_PFD1 | 540 MHz | 16 | 480×18/16 |
| PLL3_PFD2 | 508 MHz | 17 | 480×18/17 |
| PLL3_PFD3 | 455 MHz | 19 | 480×18/19 |
void imx6u_clkinit(void)
{
unsigned int reg = 0;
/* ========================================
* 步骤 1:设置 ARM 内核时钟为 528 MHz
* ======================================== */
/* 1.1 判断当前 pll1_sw_clk 的时钟源
* pll1_sw_clk 有两个来源:pll1_main_clk 和 step_clk
* 要修改 PLL1 频率,必须先切到 step_clk (24MHz)
*/
if ((((CCM->CCSR) >> 2) & 0x1) == 0)
{
/* 当前使用 pll1_main_clk,需要切换 */
CCM->CCSR &= ~(1 << 8); /* step_clk 源 = 24MHz OSC */
CCM->CCSR |= (1 << 2); /* pll1_sw_clk = step_clk */
}
/* 1.2 设置 PLL1 = 1056 MHz
* 公式:Fout = Fin × div_select / 2.0
* 1056 = 24 × div_select / 2.0 → div_select = 88
*/
CCM_ANALOG->PLL_ARM = (1 << 13) | ((88 << 0) & 0x7F);
/* bit13=1 使能时钟输出, bit[6:0]=88 倍频因子 */
/* 1.3 将 pll1_sw_clk 切换回 pll1_main_clk */
CCM->CCSR &= ~(1 << 2);
/* 1.4 设置 ARM_PODF = 2 分频
* 内核主频 = 1056 / 2 = 528 MHz
* 注意:此处实际有隐式 2 分频,文档中明确标注
*/
CCM->CACRR = 1; /* ARM_PODF=1 表示 2 分频 */
/* ========================================
* 步骤 2:设置 PLL2 (SYS PLL) 各 PFD
* ======================================== */
reg = CCM_ANALOG->PFD_528;
reg &= ~(0x3F3F3F3F); /* 清除所有 PFD_FRAC 位 */
reg |= 32 << 24; /* PLL2_PFD3 = 528×18/32 = 297 MHz */
reg |= 24 << 16; /* PLL2_PFD2 = 528×18/24 = 396 MHz */
reg |= 16 << 8; /* PLL2_PFD1 = 528×18/16 = 594 MHz */
reg |= 27 << 0; /* PLL2_PFD0 = 528×18/27 = 352 MHz */
CCM_ANALOG->PFD_528 = reg;
/* ========================================
* 步骤 3:设置 PLL3 (USB1) 各 PFD
* ======================================== */
reg = CCM_ANALOG->PFD_480;
reg &= ~(0x3F3F3F3F);
reg |= 19 << 24; /* PLL3_PFD3 = 480×18/19 = 454.7 MHz */
reg |= 17 << 16; /* PLL3_PFD2 = 480×18/17 = 508.2 MHz */
reg |= 16 << 8; /* PLL3_PFD1 = 480×18/16 = 540 MHz */
reg |= 12 << 0; /* PLL3_PFD0 = 480×18/12 = 720 MHz */
CCM_ANALOG->PFD_480 = reg;
/* ========================================
* 步骤 4:设置 AHB 时钟 (最大 132 MHz)
* ======================================== */
/* pre_periph_clk = PLL2_PFD2 = 396 MHz */
CCM->CBCMR &= ~(3 << 18); /* 清除 PRE_PERIPH_CLK_SEL */
CCM->CBCMR |= (1 << 18); /* 选择 PLL2_PFD2 */
/* periph_clk = pre_periph_clk = 396 MHz */
CCM->CBCDR &= ~(1 << 25); /* PERIPH_CLK_SEL = 0 */
while (CCM->CDHIPR & (1 << 5)); /* 等待握手完成 */
/* AHB_PODF = 3 分频 → AHB_CLK_ROOT = 396/3 = 132 MHz
* 注意:内部 Boot ROM 已设置为 3 分频
* 修改 AHB_PODF 需先关闭 AHB_CLK_ROOT 输出
*/
/* ========================================
* 步骤 5:设置 IPG 时钟 (最大 66 MHz)
* ======================================== */
CCM->CBCDR &= ~(3 << 8); /* IPG_PODF 清零 */
CCM->CBCDR |= 1 << 8; /* IPG_PODF = 1 → 2 分频
* IPG_CLK_ROOT = 132/2 = 66 MHz */
/* ========================================
* 步骤 6:设置 PERCLK 时钟
* ======================================== */
CCM->CSCMR1 &= ~(1 << 6); /* PERCLK 源 = IPG_CLK */
CCM->CSCMR1 &= ~(7 << 0); /* PERCLK_PODF = 1 分频
* PERCLK = 66 MHz */
}
int main(void)
{
unsigned int led_state = OFF;
imx6u_clkinit(); /* 初始化系统时钟,必须最先调用 */
clk_enable(); /* 使能所有外设时钟 */
led_init();
beep_init();
key_init();
/* 时钟配置后 LED 闪烁频率加快:
* 396 MHz → 528 MHz,代码执行速度提升 33%
*/
while (1)
{
led_state = !led_state;
led_switch(LED0, led_state);
delay(500);
}
}
flowchart TD
A["开始"] --> B["切换 pll1_sw_clk → step_clk (24MHz)"]
B --> C["设置 PLL1 = 1056 MHz<br/>CCM_ANALOG_PLL_ARMn"]
C --> D["切回 pll1_sw_clk = pll1_main_clk"]
D --> E["设置 ARM_PODF = 2<br/>内核 = 1056/2 = 528 MHz"]
E --> F["设置 PLL2 PFD0~3"]
F --> G["设置 PLL3 PFD0~3"]
G --> H["设置 AHB = 132 MHz"]
H --> I["设置 IPG = 66 MHz"]
I --> J["设置 PERCLK = 66 MHz"]
J --> K["完成"]
I.MX6ULL 的电源管理由 CCM (Clock Controller Module) 和 PMU (Power Management Unit) 协同完成。主要电源域:
| 电源域 | 供电范围 | 说明 |
|---|---|---|
| ARM core | 0.9V~1.3V | ARM 内核逻辑供电 |
| SOC | 1.1V~1.3V | 片上外设逻辑供电 |
| NVCC_xxx | 1.8V/3.3V | I/O 引脚供电 (可独立控制) |
| RTC | 3.3V | RTC 域独立供电 |
I.MX6ULL 支持多种运行模式:
| 模式 | CPU 状态 | 功耗 | 唤醒源 | 典型应用 |
|---|---|---|---|---|
| Run | 全速运行 | 最高 | N/A | 正常工作 |
| Wait | 时钟停止 | 中 | 中断 | 等待事件 |
| Stop | PLL 关闭 | 低 | 中断/GPIO | 深度休眠 |
| VLLS | SRAM 保持 | 极低 | RESET/WAKEUP | 电池备份 |
DVFS 允许在运行时动态调整 CPU 频率和电压:
graph LR
A["应用需求"] --> B{"负载检测"}
B -->|"高负载"| C["提高频率<br/>增加电压"]
B -->|"低负载"| D["降低频率<br/>降低电压"]
C --> E["PLL1 配置<br/>CCM_ANALOG_PLL_ARMn"]
D --> E
E --> F["电压调节器<br/>外部 PMIC"]
DVFS 调节流程:
| 特性 | I.MX6ULL | STM32F4 | RK3568 |
|---|---|---|---|
| 主频 | 528 MHz | 168 MHz | 1.8 GHz |
| 外部晶振 | 24 MHz | 8~25 MHz | 24 MHz |
| PLL 数量 | 7 路 | 3 路 (PLLM/PLLN/PLLQ) | 4 路 (APLL/DPLL/CPLL/NPLL) |
| PLL 类型 | 固定 + 可编程混合 | 通用 PLL (M/N/P/Q) | 独立 APLL + 通用 PLL |
| 分频方式 | PODF + PFD | 分频因子 (M/N/P/Q/R) | 分频器 + 门控 |
| PFD 机制 | 有 (8路) | 无 | 无 |
| 时钟门控 | CCGR (7组×32位) | AHBxENR/APBxENR | CRU gating |
| DVFS 支持 | 有 (软件实现) | 无 (固定电压) | 硬件 DVFS |
| 时钟树复杂度 | 高 (三层选择) | 中 | 高 |
| 总线时钟 | AHB(132M)/IPG(66M) | AHB(168M)/APB1(42M)/APB2(84M) | AHB(150M)/APB(100M) |
PLL 配置方式:
PFD 机制 (I.MX6ULL 特有):
时钟门控粒度:
答:
答:
PLL1 的输出 (pll1_main_clk) 直接连接到 ARM 内核时钟选择器。如果在 PLL1 输出不稳定时 ARM 内核仍在使用它,会导致:
切换到 step_clk (24 MHz) 后,CPU 在安全的低频下等待 PLL1 稳定,再切回 PLL1,确保时钟切换全程可控。
答:
PFD 频率公式为 Fpfd = Fpll × 18 / FRAC,其中 FRAC 必须是整数。
这是 PFD 分数分频机制的固有局限:分频因子必须为整数,所以只能近似目标频率。396 MHz 与 400 MHz 偏差仅 1%,对绝大多数外设无影响。NXP 在官方推荐值中已注明"实际为 396 MHz"。
答:
AHB 总线连接着 MMDC (内存控制器)、GIC (中断控制器) 等关键部件。修改分频值时:
不关闭的后果:
实际上 I.MX6ULL 内部 Boot ROM 已将 AHB_PODF 设置为 3 分频,很多场景下无需修改。
答:
| 维度 | I.MX6ULL PFD | STM32 PLL |
|---|---|---|
| 本质 | 分数分频器 (÷(FRAC/18)) | 整数倍频器 (×M/N) |
| 输入 | PLL 固定输出 | 外部晶振或 HSI |
| 输出特点 | 一组固定比例的多频点 | 单一可配置频点 |
| 延迟 | 极低 (组合逻辑) | 高 (PLL 重锁) |
| 使用场景 | 多外设共享同一 PLL 的不同频点 | 单外设独立配置 |
PFD 设计思想:一个 PLL 输出通过 PFD 矩阵一次性生成多个衍生频率,避免为每个外设单独配置 PLL,降低时钟切换延迟和功耗。STM32 的 PLL 每次修改都需要重新锁定,不适合频繁切换频率的场景。