时钟树修改:基于I.MX6ULL

前言

基于 100ask_imx6ull_pro 开发板

使用 100ASK_IMX6ULL_Flashing_tool 工具进行程序烧写

参考资料为《i.MX 6ULL Applications Processor Reference Manual, Rev. 1, 11/2017》

默认情况下 iMX6ULL 的主频为 396MHz,我们想要让 iMX6ULL 运行时候达到最大性能,可将主频上调至最大 528MHz

本文将修改时钟树来提升主频,并通过 LED 亮灭快慢来观察主频的变化

基础知识

BYPASS Clock(旁路时钟) 是指在不使用外部晶体的情况下,直接从外部引入时钟信号,绕过芯片内部的时钟驱动组件,用作替代主时钟信号的备用时钟。

旁路时钟通常用于以下几种情况:

  1. PLL 故障或失效:当锁相环(PLL)发生故障或不稳定时,系统可以切换到旁路时钟,以继续提供稳定的时钟信号,确保系统不因时钟信号丢失或不稳定而崩溃或停止运行。
  2. 系统测试或调试:在某些测试或调试场景中,工程师可能需要绕过 PLL,直接使用外部时钟信号。旁路时钟信号使得对系统进行测试或调试更加方便。
  3. 启动或复位过程:在系统启动或复位时,PLL 需要一段时间来锁定稳定的输出频率。在此期间,系统可以使用旁路时钟信号,以确保启动或复位过程的顺利进行。

时钟树分析

由下图可以看出 I/MX6ULL 时钟的整体结构

请添加图片描述

CCM_ANALOG

CCM 的模拟时钟电路部分,接收外部晶振时钟输入,输出倍频后的 PLLs,以及 PFDs

PLL 可以调节倍频系数

PFD 由相应的 PLL 进一步分频得来,imx6ull 只有 PLL2&PLL3 下面有 PFD

请添加图片描述

CCM_CLK_SWITCHER

接收来自 CCM_ANALOG 模块的 PLL 及 PFD 时钟输出,以及 PLL 的旁路时钟, 并为 CCM_CLK_ROOT_GEN 子模块生成选择后的时钟输出

请添加图片描述

CCM_CLK_ROOT_GEN

接收来自 CCM_CLK_SWITCHER 模块筛选后的 PLL 或 PFD 时钟

进一步进行选择、分频等操作之后产生并输出根时钟

根时钟将会作内核或外设的时钟源

请添加图片描述

修改 ARM 主频

请添加图片描述
请添加图片描述

要修改 ARM_CLK_ROOT 为 528MHZ,那么可以设置 CACRR 的 ARM_PODF 位为 2 分频, PLL1=1056MHz

1. 设置 CCM_CACRR:ARM_PODF

设置pll1_sw_clk->arm_clk_root分频系数为2

请添加图片描述

由于 PLL1 = pll1_sw_clk,当修改 PLL1 时,需要将 pll1_sw_clk 从 pll1_main_clk 切换到 step_clk,待 PLL1 修改稳定后再切换回来

step_clk 我们选择直接接收 外部晶振的输出 osc_clk

请添加图片描述

2. 设置 CCM_CCSR:step_sel

设置step_clk时钟来源为osc_clk

请添加图片描述

3. 设置 CCM_CCSR:pll1_sw_clk_sel

设置pll1_sw_clk时钟来源为step_clk

请添加图片描述

切换为 24MHz 作为时钟来源后,就可以修改 PLL1 的倍频系数了。由于 PLL1 属于模拟电路,所有在模拟(ANALOG)相关的寄存器设置

需要注意的是在 i.MX 6ULL 应用处理器的参考手册中,关于 CCM_ANALOG_PLL_ARM 有四种不同的寄存器。这四个寄存器分别是:

  1. CCM_ANALOG_PLL_ARM(原始寄存器)
  2. CCM_ANALOG_PLL_ARM_SET(设置寄存器)
  3. CCM_ANALOG_PLL_ARM_CLR(清除寄存器)
  4. CCM_ANALOG_PLL_ARM_TOG(切换寄存器)

这四种寄存器的存在是为了方便对 PLL(Phase-Locked Loop,锁相环)进行更灵活的控制和操作。每个寄存器的功能如下:

  1. CCM_ANALOG_PLL_ARM: 这是标准的控制寄存器,用于配置和读取 PLL 的状态。通过该寄存器,可以直接写入新的值来配置 PLL。
  2. CCM_ANALOG_PLL_ARM_SET: 这个寄存器允许你在不影响其他位的情况下,设置寄存器中的特定位(即,将特定位置 1)。这在需要更改寄存器的部分内容时非常有用,而无需修改整个寄存器的值。
  3. CCM_ANALOG_PLL_ARM_CLR: 与 SET 寄存器相反,CLR 寄存器用于清除寄存器中的特定位(即,将特定位置 0)。这提供了一种方便的方法来清除某些配置,而不干扰其他位。
  4. CCM_ANALOG_PLL_ARM_TOG: 这个寄存器用于切换寄存器中的特定位(即,将特定位从 0 变为 1,或者从 1 变为 0)。这可以用于快速切换状态,而无需单独设置或清除某个位。

请添加图片描述

由计算公式我们需要设置 DIV_SELECT 为请添加图片描述
请添加图片描述

4. 设置 CCM_ANALOG_PLL_ARM:DIV_SELECT

将 DIV_SELECT 位设为 0101_1000(88)

5. 等待 CCM_ANALOG_PLL_ARM:LOCK

等待 PLL LOCK 位为 1 ,PLL 锁环输出稳定下来才能使用

6. 设置 CCM_CCSR:pll1_sw_clk_sel

设 0,选择 pll1_sw_clk 的时钟来源为 pll1_main_clk

此时主频应该就修改完成了

参考代码

下面的参考代码我分别将主频设为了 81mhz-648mhz,这样观察起来更加明显

main.c

#include "include/led.h"
#include "include/clock.h"

uint32_t count = 10;

int main()
{
   led_init();
   set_arm_clk_root_81mhz();

   while (count--)
   {
      led_toggle();
      delay(1);
   }

   set_arm_clk_root_648mhz();

   while (1)
   {
      led_toggle();
      delay(1);
   }

   return 0;
}

clock.h

#ifndef CLOCK_H // 如果未定义 CLOCK_H
#define CLOCK_H // 定义 CLOCK_H

#define uint32_t unsigned int

// CCM Arm Clock Root Register (CACRR)
#define CCM_CACRR (volatile unsigned long *)0x020C4010
#define CCM_CCSR (volatile unsigned long *)0x020C400C
#define CCM_ANALOG_PLL_ARM (volatile unsigned long *)0x020C8000

// 设置pll1_sw_clk->arm_clk_root分频系数为2
void set_arm_podf_2div(void);
// 设置设置step_clk时钟来源为osc_clk
void set_step_sel_osc(void);
// 设置pll1_sw_clk时钟来源: 0则pll1_main_clk 1则osc_clk
void set_pll1_sw_clk_sel(uint32_t value);
// 设置pll1_div_sel分频系数为8,则pll1_main_clk为96MHz
void set_pll1_div_sel_8(void);
// 设置pll1_div_sel分频系数为108,则pll1_main_clk1296MHz
void set_pll1_div_sel_108(void);
// 获取pll1_lock状态, 0则未锁定 1则锁定
uint32_t get_pll1_lock_status(void);

void set_arm_clk_root_81mhz(void);
void set_arm_clk_root_648mhz(void);

#endif // CLOCK_H

/*
24MHz OSC --> ARM_CLK_ROOT 路径:
切换前: OSC -> PLL1 -> pll1_main_clk -> CCSR:pll1_sw_clk_sel -> pll1_sw_clk -> CACRR[ARM_PODF] -> ARM_CLK_ROOT
切换中: OSC -> osc_clk -> CCSR:step_sel -> step_clk -> CCSR:pll1_sw_clk_sel -> pll1_sw_clk -> CACRR[ARM_PODF] -> ARM_CLK_ROOT
切换后: OSC -> PLL1 -> pll1_main_clk -> CCSR:pll1_sw_clk_sel -> pll1_sw_clk -> CACRR[ARM_PODF] -> ARM_CLK_ROOT
*/

clock.c

#include "../include/clock.h"

void set_arm_podf_2div(void)
{
    *(CCM_CACRR) = 0x00000001;
}

void set_step_sel(uint32_t value)
{
    uint32_t reg_value = *(CCM_CCSR) & ~((0xFF) << 8); // 清除旧值
    *(CCM_CCSR) = reg_value | (value << 8);            // 设置新值
}

void set_pll1_sw_clk_sel(uint32_t value)
{
    uint32_t reg_value = *(CCM_CCSR) & ~((0x3) << 2); // 清除旧值
    *(CCM_CCSR) = reg_value | (value << 2);           // 设置新值
}

void set_pll1_div_sel_8(void)
{
    uint32_t reg_value = *(CCM_ANALOG_PLL_ARM) & ~0x7F; // 清除低7位
    *(CCM_ANALOG_PLL_ARM) = reg_value | 8;              // 设置新值8
}

void set_pll1_div_sel_108(void)
{
    uint32_t reg_value = *(CCM_ANALOG_PLL_ARM) & ~0x7F; // 清除低7位
    *(CCM_ANALOG_PLL_ARM) = reg_value | 108;            // 设置新值108
}

uint32_t get_pll1_lock_status(void)
{
    unsigned long value = *CCM_ANALOG_PLL_ARM;
    unsigned long highest_bit = (value >> 31) & 0x1;
    return highest_bit;
}

void set_arm_clk_root_81mhz(void)
{
    set_arm_podf_2div();
    set_step_sel(0);
    set_pll1_sw_clk_sel(1);
    set_pll1_div_sel_8();
    while (get_pll1_lock_status() == 0)
        ;
    set_pll1_sw_clk_sel(0);
}

void set_arm_clk_root_648mhz(void)
{
    set_arm_podf_2div();
    set_step_sel(0);
    set_pll1_sw_clk_sel(1);
    set_pll1_div_sel_108();
    while (get_pll1_lock_status() == 0)
        ;
    set_pll1_sw_clk_sel(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值