学习笔记——时钟系统与定时器

时钟系统与定时器

一、基本概念定义

1. 核心术语解析

  • 定时器 (Timer):通过对已知频率的时钟信号进行计数,实现时间测量、延时控制或事件计数功能的硬件模块或软件机制。

  • 时钟 (Clock):在电子系统中产生稳定周期性振荡信号的电路或组件,为数字电路提供同步时序基准。

  • 实时时钟 (RTC):独立供电的时间保持模块,在主系统电源关闭时继续提供精确的日历和时间信息。

2. 频率单位说明

  • 频率计算:1 MHz = 1,000 × 1,000 = 1,000,000 Hz

  • 存储计算:1 MByte = 1,024 × 1,024 = 1,048,576 Byte

二、时钟硬件架构

1. 时钟源:晶体振荡器

工作原理

复制代码
石英晶体 → 切割成音叉形状 → 施加电压 → 压电效应 → 稳定机械振荡 → 电信号输出
频率特性:24 MHz(i.MX6ULL基准频率)
稳定性:±10 ppm(百万分之十)

2. 锁相环电路 (PLL)

功能:频率合成与倍频

复制代码
输入频率 F_in → 鉴相器 → 环路滤波器 → 压控振荡器 → 输出频率 F_out
数学模型:F_out = F_in × (N/D)
其中:N为倍频因子,D为分频因子

3. 预分频器 (Prescaler)

功能:整数分频

复制代码
F_out = F_in / M
M为整数分频系数(1-64)

4. 相位分数分频器 (PFD)

功能:分数分频,实现非整数分频比

复制代码
F_out = F_in × (N/M)
可精确控制输出相位

三、i.MX6ULL时钟系统架构

1. PLL配置总览

复制代码
外部晶振 (24MHz)
    ↓
├── PLL1 (ARM PLL)    - Cortex-A7内核时钟,可配置至1056MHz
├── PLL2 (528 PLL)    - 系统PLL,固定528MHz
├── PLL3 (480 PLL)    - USB1 PLL,固定480MHz
├── PLL4 (Audio PLL)  - 音频设备
├── PLL5 (Video PLL)  - 视频设备
├── PLL6 (Ethernet PLL)- 以太网设备
└── PLL7 (USB2 PLL)   - USB2设备

2. PFD配置

每个主PLL包含4个PFD通道:

复制代码
PLL2 (528MHz) PFD通道:
    PFD0: 352MHz
    PFD1: 594MHz
    PFD2: 396MHz
    PFD3: 297MHz

PLL3 (480MHz) PFD通道:
    PFD0: 720MHz
    PFD1: 540MHz
    PFD2: 508.2MHz
    PFD3: 454.7MHz

3. 时钟树关键节点

复制代码
ARM内核时钟路径:
24MHz晶振 → PLL1倍频 → 二分频 → Cortex-A7内核 (1056MHz)

系统总线时钟:
24MHz → PLL2 (528MHz) → 各PFD通道 → 多路选择器 → 分频器 → 外设时钟

四、时钟配置代码实现

1. ARM PLL配置流程

复制代码
void configure_arm_pll(void)
{
    /* 步骤1:切换到安全时钟源 */
    // CBCMR[18:19] PRE_PERIPH_CLK_SEL = 00 (osc_clk)
    CCM->CBCMR &= ~(3 << 18);
    
    // CBCDR[25] PERIPH_CLK_SEL = 0 (选择step_clk)
    CCM->CBCDR &= ~(1 << 25);
    
    /* 步骤2:旁路PLL,使用24MHz直接工作 */
    // CBCDR[25] PERIPH_CLK_SEL = 1 (选择pll1_sw_clk)
    CCM->CBCDR |= (1 << 25);
    
    /* 步骤3:配置PLL1参数(关键安全步骤)*/
    // 先设置输出分频,避免过高频率
    CCM_ANALOG->PLL_ARM |= (1 << 19);    // 使能二分频
    
    // 配置倍频因子:N=88
    CCM_ANALOG->PLL_ARM &= ~0x7F;        // 清除DIV_SELECT
    CCM_ANALOG->PLL_ARM |= (88 << 0);    // DIV_SELECT=88
    
    // 计算:24MHz × 88 = 2112MHz,二分频后=1056MHz
    
    /* 步骤4:等待PLL锁定 */
    while(!(CCM_ANALOG->PLL_ARM & (1 << 31)));  // 检查LOCK位
    
    /* 步骤5:切换回PLL输出 */
    CCM->CBCDR &= ~(1 << 25);             // PERIPH_CLK_SEL=0
}

2. 系统时钟配置

复制代码
// AHB_CLK_ROOT配置为132MHz
void configure_ahb_clk(void)
{
    // 选择时钟源:PLL2 PFD2 (396MHz)
    CCM->CBCMR |= (1 << 18);        // PRE_PERIPH_CLK_SEL=01
    CCM->CBCDR &= ~(1 << 25);       // PERIPH_CLK_SEL=0
    
    // 设置AHB分频:396MHz ÷ 3 = 132MHz
    CCM->CBCDR &= ~(7 << 10);       // 清除AHB_PODF
    CCM->CBCDR |= (2 << 10);        // AHB_PODF=010 (3分频)
}

// IPG_CLK_ROOT配置为66MHz
void configure_ipg_clk(void)
{
    // AHB时钟 ÷ 2 = 66MHz
    CCM->CBCDR &= ~(3 << 8);        // 清除IPG_PODF
    CCM->CBCDR |= (1 << 8);         // IPG_PODF=01 (2分频)
}

// PERCLK_CLK_ROOT配置为66MHz
void configure_perclk(void)
{
    // 选择IPG时钟作为源
    CCM->CSCMR1 &= ~(1 << 6);       // PERCLK_CLK_SEL=0
    
    // 设置分频系数
    CCM->CSCMR1 &= ~(0x3F << 0);    // 清除PERCLK_PODF
    CCM->CSCMR1 |= (0 << 0);        // PERCLK_PODF=0 (1分频)
}

五、定时器工作原理

1. 51单片机定时器

基本结构

  • Timer0/Timer1:16位定时器/计数器

  • 工作模式

    • 模式0:13位定时器

    • 模式1:16位定时器

    • 模式2:8位自动重装

    • 模式3:双8位定时器

配置示例

复制代码
// Timer0 16位定时器配置
void timer0_init(void)
{
    TMOD &= 0xF0;           // 清除Timer0配置位
    TMOD |= 0x01;           // 模式1:16位定时器
    
    // 计算初值(12MHz晶振,1ms定时)
    // 机器周期 = 1μs,计数1000次=1ms
    TH0 = (65536 - 1000) / 256;
    TL0 = (65536 - 1000) % 256;
    
    ET0 = 1;                // 使能Timer0中断
    TR0 = 1;                // 启动Timer0
}

// 中断服务程序
void timer0_isr() interrupt 1
{
    TH0 = (65536 - 1000) / 256;    // 手动重装初值
    TL0 = (65536 - 1000) % 256;
    // 用户处理代码
}

2. i.MX6ULL EPIT定时器

特性

  • 32位递减计数器

  • 自动重载功能

  • 精确周期性中断

1秒LED闪烁实验

复制代码
// EPIT1初始化
void epit1_init(void)
{
    // 1. 禁用EPIT1
    EPIT1->CR = 0;
    
    // 2. 配置控制寄存器
    EPIT1->CR = (1 << 24) |    // ENMOD:计数器初始加载值
                (1 << 3)  |    // OCIEN:使能比较中断
                (1 << 2)  |    // RLD:使能重载模式
                (0 << 1)  |    // OM:输出模式(比较输出)
                (0 << 0);      // EN:暂不使能
    
    // 3. 设置预分频(66MHz ÷ 66 = 1MHz)
    EPIT1->CR |= (65 << 4);    // PRESCALAR = 65
    
    // 4. 设置加载值(1MHz时钟,1秒计数)
    EPIT1->LR = 1000000;       // 加载寄存器
    
    // 5. 设置比较值
    EPIT1->CMPR = 0;           // 比较寄存器
    
    // 6. 清除中断标志
    EPIT1->SR |= (1 << 0);
    
    // 7. 使能中断
    enable_irq(EPIT1_IRQn);
    
    // 8. 启动定时器
    EPIT1->CR |= (1 << 0);
}

// EPIT1中断服务函数
void epit1_irq_handler(void)
{
    if (EPIT1->SR & (1 << 0)) {
        EPIT1->SR |= (1 << 0);  // 清除中断标志
        gpio_toggle(LED_GPIO);  // 翻转LED
    }
}

3. GPT定时器

特性

  • 32位递增计数器

  • 输入捕获功能

  • 比较输出功能

  • 多种工作模式

精准延时函数实现

复制代码
// GPT1微秒级延时
void gpt_delay_us(uint32_t us)
{
    // 1. 复位GPT1
    GPT1->CR = (1 << 15);        // SWR:软件复位
    
    // 2. 配置GPT1
    GPT1->CR = (0 << 9)  |        // CLKSRC:ipg_clk (66MHz)
               (1 << 6)  |        // ENMOD:使能模式
               (0 << 1)  |        // FRR:自由运行模式
               (0 << 0);          // EN:暂不使能
    
    // 3. 设置预分频(66MHz ÷ 66 = 1MHz)
    GPT1->PR = 65;               // 分频寄存器
    
    // 4. 启动GPT1
    GPT1->CR |= (1 << 0);
    
    // 5. 获取起始计数值
    uint32_t start_time = GPT1->CNT;
    
    // 6. 等待指定时间
    uint32_t target_cnt = us;    // 1MHz时钟,1计数=1μs
    while ((GPT1->CNT - start_time) < target_cnt) {
        // 处理计数器溢出
        if (GPT1->CNT < start_time) {
            target_cnt -= (0xFFFFFFFF - start_time + GPT1->CNT + 1);
            start_time = GPT1->CNT;
        }
    }
    
    // 7. 停止GPT1
    GPT1->CR &= ~(1 << 0);
}

// GPT1毫秒级延时
void gpt_delay_ms(uint32_t ms)
{
    for (uint32_t i = 0; i < ms; i++) {
        gpt_delay_us(1000);
    }
}

六、重点问题详解

问题1:PLL、Prescaler、PFD的作用

模块 功能 数学关系 应用场景
PLL 频率合成与倍频 F_out = F_in × N/D 产生高频系统时钟
Prescaler 整数分频 F_out = F_in / M 降低时钟频率
PFD 分数分频 F_out = F_in × N/M 精确频率控制

问题2:i.MX6ULL PLL和PFD数量

  • PLL总数:7个

    • PLL1:ARM PLL(1056MHz)

    • PLL2:System PLL(528MHz)

    • PLL3:USB1 PLL(480MHz)

    • PLL4:Audio PLL

    • PLL5:Video PLL

    • PLL6:Ethernet PLL

    • PLL7:USB2 PLL

  • PFD总数:28个(每个PLL有4个PFD通道)

问题3:ARM PLL配置流程

  1. 时钟源切换:选择24MHz OSC作为临时时钟源

  2. PLL旁路:绕过PLL,直接使用低频时钟

  3. 参数配置

    • 设置输出分频(安全措施)

    • 配置倍频因子(N=88)

    • 等待PLL锁定

  4. 时钟切换:切回PLL输出时钟

  5. 频率验证:确认时钟稳定工作

问题4:EPIT与GPT工作原理对比

特性 EPIT GPT
计数方向 递减计数 递增计数
重载方式 自动重载 多种模式
主要用途 周期性中断 通用定时
输出模式 比较输出 捕获/比较
时钟源 ipg_clk/ipg_clk_highfreq 多种选择

问题5:时钟树中各模块作用

模块 功能描述 在时钟树中的位置
锁相环PLL 频率提升,将低频时钟倍频到工作频率 时钟树前端
预分频器 整数分频,降低时钟频率 各级时钟通路
相位分数分频器 精确分数分频,实现任意频率比 PLL输出后
多路选择器 时钟源选择,动态切换时钟 各时钟节点
CG门 时钟门控,低功耗控制 各外设时钟入口

七、实践注意事项

1. 时钟配置安全规范

复制代码
// 错误示例:直接修改PLL倍频因子
CCM_ANALOG->PLL_ARM |= (88 << 0);  // 危险!可能造成系统崩溃

// 正确示例:安全的PLL配置流程
// 1. 切换到低频时钟源
// 2. 设置输出分频
// 3. 配置倍频因子
// 4. 等待锁定
// 5. 切换回高频时钟

2. 定时器精度考虑因素

  1. 时钟源精度:晶体振荡器的稳定度

  2. 分频误差:整数分频的量化误差

  3. 中断延迟:中断响应时间

  4. 软件开销:中断服务程序执行时间

3. 调试技巧

复制代码
// 时钟频率测量方法
void measure_clock_frequency(void)
{
    uint32_t start, end;
    
    // 使用GPT测量1秒内计数
    GPT1->CR = 0;
    GPT1->CR = (1 << 6) | (1 << 0);  // ENMOD + EN
    GPT1->PR = 0;                    // 不分频
    
    start = GPT1->CNT;
    gpt_delay_ms(1000);              // 延时1秒
    end = GPT1->CNT;
    
    printf("频率:%lu Hz\n", end - start);
}

八、总结

时钟系统和定时器是嵌入式系统的核心基础组件,理解其工作原理和配置方法对于系统稳定性、性能和功耗控制至关重要。i.MX6ULL提供了灵活的时钟架构和多种定时器,能够满足不同应用场景的需求。配置时钟时应遵循安全规范,定时器使用时要考虑精度和实时性要求。

相关推荐
尽兴-2 小时前
MySQL 8.0高可用集群架构实战深度解析
数据库·mysql·架构·集群·高可用·innodb cluster
诸葛成2 小时前
渗透测试-信息收集
经验分享·笔记·课程设计
CQ_YM2 小时前
ARM--SDK、led、beep与链接脚本
c语言·arm开发·嵌入式硬件·嵌入式
峰顶听歌的鲸鱼2 小时前
Kubernetes管理
运维·笔记·云原生·容器·kubernetes·云计算
xiaobobo33302 小时前
EIDE的最新版本已经默认只支持debug调试STM32单片机了
stm32·单片机·debug·eide
small_planet2 小时前
通过mqtt使用webhook转发消息实现远程查看单片机日志
单片机·运维开发
小魏每天都学习2 小时前
【计算机基础知识学习】
学习
Nan_Shu_6143 小时前
学习: 尚硅谷Java项目之尚庭公寓(2)
学习
好奇龙猫3 小时前
【人工智能学习-AI入试相关题目练习-第九次】
人工智能·学习