ARM架构——时钟系统与定时器详解

目录

一、时钟系统基础

[1.1 基本概念](#1.1 基本概念)

[1.2 时钟系统核心组件](#1.2 时钟系统核心组件)

[1.2.1 时钟源](#1.2.1 时钟源)

[1.2.2 时钟树关键组件](#1.2.2 时钟树关键组件)

二、时钟系统代码实现

[2.1 关键时钟计算](#2.1 关键时钟计算)

[2.2 时钟初始化](#2.2 时钟初始化)

三、定时器模块详解

[3.1 EPIT 定时器](#3.1 EPIT 定时器)

[3.2 GPT定时器](#3.2 GPT定时器)

四、总结


一、时钟系统基础

1.1 基本概念

  • **时钟(clock):**在电子系统中是一个产生稳定、周期性振荡信号的电路或组件。这个信号像节拍器或心跳一样,为数字电路中的各种操作提供同步时序基准。
  • **定时器(EPIT GPT):**是一个通过对已知频率的时钟信号进行计数,来实现定时、延时或事件计数功能的硬件模块或软件机制。
  • **实时时钟(RTC, real time clock):**是微处理器中的一个功能模块,用于在系统主电源关闭的情况下,继续提供精确的日历和时间信息。

1.2 时钟系统核心组件

1.2.1 时钟源

时钟源是整个时钟系统的起点,提供稳定的原始时钟信号。

  • 晶体振荡器:将石英晶体切割成音叉,施加电压产生稳定震荡
  • 通常使用 8MHz 或 24MHz 的晶振作为基础时钟源

1.2.2 时钟树关键组件

ARM架构的时钟系统由多个关键组件构成,形成一个复杂的时钟树:
IMX6ULL时钟树

组件 作用 代码示例
PLL(锁相环) 倍频,将低频时钟提升到更高频率 CCM_ANALOG->PLL_ARM
Prescale(分频器) 分频,将高频时钟降低到所需频率 CCM->CACRR
PFD(相位分数分频器) 输出频率可升可降,提供更灵活的频率选择 CCM_ANALOG->PFD_528
MUX(多路选择器) 选择不同的时钟源 CCM->CBCMRCCM->CSCMR1
CG门(Clock Gating 控制时钟门控,节省功耗 CCM->CCGR0-CCM->CCGR6

二、时钟系统代码实现

2.1 关键时钟计算

IMX6ULL时钟树

  • ARM 内核时钟 :通过PLL倍频提升到高频率。
    • PLL1 倍频系数 88(24MHz×88=2112MHz)→ 二分频 → 1056MHz(可通过 CACRR 寄存器调整分频系数);
  • AHB 总线时钟 :系统总线时钟,用于处理器和外设。
    • PLL2 输出 528MHz → 4 分频 → 132MHz(CBCDR 寄存器的 AHB_PODF 配置);
  • IPG 总线时钟 :外设接口时钟,用于GPIO、UART等外设。
    • AHB 时钟 132MHz → 2 分频 → 66MHz(CBCDR 寄存器的 IPG_PODF 配置);
  • PERCLK时钟 :外设时钟,用于定时器、ADC等。
    • AHB 时钟 132MHz → 2 分频 → 66MHz(CSCMR1 寄存器的 PERCLK_PODF 配置)。

2.2 时钟初始化

cpp 复制代码
void clock_init(void)
{
  // ARM内核时钟切换(避免配置PLL时内核故障)
  CCM->CCSR &= ~(1 << 8);  // 选择osc_clk作为step_clk(24MHz)
  CCM->CCSR |= (1 << 2);   // 让ARM暂时工作在step_clk(24MHz)
  
  // 配置ARM内核分频器(CACRR)
  CCM->CACRR &= ~(7 << 0);
  CCM->CACRR |= (1 << 0);  // 二分频(后续PLL1输出1056MHz,分频后528MHz)
  
  // 配置PLL1(ARM PLL)为1056MHz
  unsigned int t = CCM_ANALOG->PLL_ARM;
  t &= ~(3 << 4);          // 清除原有倍频系数
  t |= (1 << 13);          // 使能PLL
  t &= ~(0x7F << 0);
  t |= (88 << 0);          // 倍频系数88(24×88=2112MHz,二分频后1056MHz)
  CCM_ANALOG->PLL_ARM = t;
  CCM->CCSR &= ~(1 << 2);  // 切换回PLL1输出(pll1_main_clk)
  
  // 配置PLL2(528MHz)的PFD分频器
  t = CCM_ANALOG->PFD_528;
  t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
  // PFD0=27→352M,PFD1=16→594M,PFD2=24→396M,PFD3=32→297M
  t |= ((27 << 0) | (16 << 8) | (24 << 16) | (32 << 24));
  CCM_ANALOG->PFD_528 = t;
  
  // 配置PLL3(480MHz)的PFD分频器
  t = CCM_ANALOG->PFD_480;
  t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
  t |= ((27 << 0) | (16 << 8) | (24 << 16) | (32 << 24));
  CCM_ANALOG->PFD_480 = t;
  
  // 配置AHB_CLK_ROOT(132M)
  t = CCM->CBCMR;
  t &= ~(3 << 18);
  t |= (1 << 18);          // 选择PLL2_PFD2(396M)作为输入
  CCM->CBCMR = t;
  t = CCM->CBCDR;
  t &= ~(1 << 25);
  t &= ~(7 << 10);
  t |= (2 << 10);          // AHB分频系数3(396M/3=132M)
  CCM->CBCDR = t;
  
  // 配置IPG_CLK_ROOT(66M)
  t &= ~(3 << 8);
  t |= (1 << 8);           // IPG分频系数2(132M/2=66M)
  CCM->CBCDR = t;
  
  // 配置PERCLK_CLK_ROOT(66M)
  t = CCM->CSCMR1;
  t &= ~(1 << 6);          // 选择IPG_CLK_ROOT作为输入
  t &= ~(0x3F << 0);       // 不分频
  CCM->CSCMR1 = t;
  
  // 使能所有外设时钟门控(CG)
  clock_cg_init();
}

时钟配置关键注意事项:

  • 时钟切换顺序:配置 PLL1 时,必须先将 ARM 内核切换到 24MHz 的 step_clk,避免倍频过程中内核因时钟突变而故障;
  • 分频系数匹配:外设时钟必须与器件手册要求一致(如 USB 需 480MHz,以太网需特定频率),否则会导致外设工作异常;
  • 时钟门控使能:clock_cg_init() 函数将所有 CCGR 寄存器置为 0xFFFFFFFF,即开启所有外设时钟,实际项目中可按需关闭未使用外设的时钟以降低功耗。

三、定时器模块详解

3.1 EPIT 定时器

Enhanced Periodic Interrupt Timer ------ 增强型周期中断定时器

**应用场景:**周期性任务,如 1 秒翻转一次 LED。

工作原理:

  • 定时器根据配置的时钟源进行计数。
  • 当计数达到 LR (Load Register) 设定的值时,产生中断,并将计数器自动重置为 LR 值。

实验: 1秒中断翻转 LED

使用 PERCLK_CLK_ROOT (66MHz) 作为时钟源。

  • 目标频率:1Hz(即每1秒一次)。
  • 1秒中断的计算:1000 * 1000 = 1MHz(1MHz时钟,100万计数=1秒)
  • 代码实现:
cpp 复制代码
void epit1_init(void)
{
    unsigned int t;
    t = EPIT1->CR;
    t &= ~(3 << 24);    // 清除时钟源选择
    t |= (1 << 24);     // 选择PERCLK_CLK_ROOT(66M)作为时钟源
    t |= (1 << 17);     // 覆盖计数器值
    t &= ~(0xFFF << 4); // 清除预分频值
    t |= (65 << 4);     // 分频系数66(66M/66=1MHz)
    t |= (1 << 3);      // 计数器达到零时,从模数寄存器重新加载(置位-遗忘模式)
    t |= (1 << 2);      // 启用中断
    t |= (1 << 1);      // 从加载值开始计数
    EPIT1->CR = t;
    
    EPIT1->LR = 1000 * 1000;  // 载入值 = 1MHz时钟,1秒
    EPIT1->CMPR = 0;          // 比较寄存器
    EPIT1->CNR = 1000 * 1000; // 当前计数值
    
    // 中断配置
    GIC_EnableIRQ(EPIT1_IRQn);
    GIC_SetPriority(EPIT1_IRQn, 0);
    system_interrupt_register(EPIT1_IRQn, epit_irq_handler);
    
    EPIT1->CR |= (1 << 0); // 使能定时器
}

// 中断服务函数:1s翻转LED和蜂鸣器
void epit_irq_handler(void)
{
  if ((EPIT1->SR & (1 << 0)) != 0)
  {
    led_nor();
    beep_nor();
    EPIT1->SR |= (1 << 0); // 清除中断标志
  }
}

3.2 GPT定时器

General Purpose Timer ------ 通用目的定时器

**应用场景:**高精度延时函数。

**工作原理:**GPT 支持自由运行模式、输入捕获和比较输出。我们利用其自由运行模式读取当前计数值来实现软件延时。

实验: 精准延时

通过读取 GPT1->CNT 寄存器,对比当前值与上一次的值,累加时间差,直到达到所需的微秒数。

  • 代码实现:
cpp 复制代码
void gpt1_init(void)
{
    // 复位定时器
    reset_fun();
    
    unsigned int t;
    t = GPT1->CR;
    t &= ~(7 << 26);  // 输出断开
    t &= ~(3 << 18);  // 捕获已禁用
    t |= (1 << 9);    // 自由运行模式
    t &= ~(7 << 6);   // 清除时钟源选择
    t |= (1 << 6);    // 选择PERCLK_CLK_ROOT(66M)作为时钟源
    t &= ~(1 << 1);   // 失能定时器后保留原来的值
    GPT1->CR = t;
    
    GPT1->PR &= ~(0xFFF << 0); // 清除预分频值
    GPT1->PR |= (65 << 0);     // 分频系数66(66M/66=1MHz)
    GPT1->CNT = 0;             // 清零计数器
    GPT1->CR |= (1 << 0);      // 使能定时器
}

// 微秒级延时函数
void delay_us(unsigned int us)  
{  
    unsigned int count = 0;  
    unsigned int old_count = 0, new_count = 0;  
    
    old_count = GPT1->CNT; // 记录起始时刻  
    while (1)  
    {  
        new_count = GPT1->CNT;  
      
        // 计算溢出情况(例如从0xFFFFFFFF变到0)  
        if (new_count != old_count)  
        {  
            if (new_count > old_count)  
            {  
                count += new_count - old_count;  
            }  
            else  
            {  
                count += 0xFFFFFFFF - old_count + new_count;  
            }  
        }  
      
        if (count >= us)  
        {  
            return;  
        }  
        old_count = new_count;  
    }  
}  

四、总结

ARM架构的时钟系统和定时器模块是嵌入式系统设计的核心组件。理解时钟树的结构和工作原理,对于实现精确的定时控制、优化系统功耗和性能至关重要。

  • 时钟系统:通过PLL、分频器和多路选择器构建复杂的时钟树,为系统提供精确的时序基准
  • 定时器:EPIT提供精确的周期中断,GPT提供灵活的延时功能,两者在嵌入式系统中广泛应用
  • 配置实践:通过寄存器配置,实现时钟频率的精确控制和定时器的精准工作
相关推荐
石去皿1 小时前
【嵌入式就业10】Linux内核深度解析:从启动流程到驱动框架的工业级实践
linux·运维·服务器
954L1 小时前
CentOs7执行yum update出现链接404问题
linux·centos·yum·vault
Trouvaille ~2 小时前
【Linux】应用层协议设计实战(二):Jsoncpp序列化与完整实现
linux·运维·服务器·网络·c++·json·应用层
qq_672592752 小时前
电源芯片为什么发热
单片机·嵌入式硬件
天天爱吃肉82182 小时前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车
EmbedLinX2 小时前
嵌入式之协议解析
linux·网络·c++·笔记·学习
vortex52 小时前
解密UUOC:Shell编程中“无用的cat使用”详解
linux·shell编程
凉、介2 小时前
VMware 三种网络模式(桥接 / NAT / Host-Only)原理与实验解析
c语言·网络·笔记·操作系统·嵌入式·vmware
wangjialelele2 小时前
Linux中的进程管理
java·linux·服务器·c语言·c++·个人开发
国科安芯2 小时前
抗辐照MCU在精密时频系统中的单粒子效应评估与可靠性验证
单片机·嵌入式硬件·架构·制造·安全性测试