新手自学嵌入式 | 学习日记:ARM内核启动代码、时钟系统(PLL/Prescaler/PFD)与IMX6ULL时钟配置

一、 ARM内核启动代码与cps指令

在ARMv6及以上的指令集中,引入了cps指令,用于更灵活地切换处理器工作模式和中断使能。

1. cps指令基础
  • 切换模式 :CPS #<mode>

    • irq模式:10010 -> 对应指令 cps #0x12

    • system模式:11111 -> 对应指令 cps #0x1F

  • 使能/禁用中断 :CPS <effect> <iflags>

    • 使能IRQ中断:cpsie i(清除CPSR中的I位)
2. ARM内核启动代码主要做了哪些工作?

ARM内核上电后的启动代码(通常为汇编文件,如start.S)是系统运行的第一步。结合图示,它主要完成了以下7项工作:

  1. 设置异常向量表:定义复位、未定义指令、软中断、IRQ等异常的处理入口。

  2. 异常向量表地址重映射 :将向量表重映射到 0x87800000(IMX6ULL的DDR3链接地址)。

  3. 为不同工作模式开辟栈:特别是为IRQ模式和System模式分配栈空间(结合之前学的满减栈知识)。

  4. 使能IRQ中断:允许CPU响应中断。

  5. 清零BSS段:将未初始化的全局变量区域清零,保证C语言环境干净。

  6. 打开I-cache:开启指令缓存,提高指令读取速度。

  7. 引导内核跳转到C语言main函数执行:完成汇编阶段的初始化,正式进入C语言世界。


二、 时钟系统核心模块(PLL/Prescaler/PFD)

时钟是SoC的脉搏。在理解IMX6ULL的时钟配置前,需要先搞清几个核心概念。

1. 晶振(时钟源)
  • 无源晶振:需要外部电路驱动,成本低,常见的频率有12MHz、11.0592MHz。

  • 有源晶振:内部自带振荡电路,需要供电,输出更稳定,成本高。

2. PLL、Prescaler、PFD作用?
  • PLL (Phase Locked Loop,锁相环) :倍频 。将低频晶振(如24MHz)倍频到CPU所需的高频(如528MHz、1.2GHz)。公式:24MHz * N。

  • Prescaler (预分频器) :分频 。将高频时钟降频,供低速外设使用。公式:24MHz / M。

  • PFD (Phase Fractional Divider,相位分数分频器) :倍频/分频 。可以通过配置N/M的值,实现非整数倍的频率调整。公式:24MHz * (N/M)。当N/M > 1时为倍频,N/M < 1时为降频。

预分频器细节:

  • 2位预分频器:00 (1分频)、01 (2分频)、10 (3分频)、11 (4分频)。

  • 3位预分频器:支持1分频到8分频。


三、 IMX6ULL时钟配置实战

1. IMX6ULL时钟主要做了哪些配置?

根据图示,IMX6ULL的时钟配置主要分为三步:

  1. 配置ARM内核时钟:设置PLL1,将ARM内核时钟配置为528MHz。

  2. 配置PLL2和PLL3的PFD:配置PLL2下的4个PFD和PLL3下的4个PFD,为各个外设提供基础的时钟源。

  3. 配置三个根时钟:配置AHB、PERCLK、IPG三个根时钟,分配到具体的外设总线。

2. 配置的ARM内核时钟频率是多少?

528MHz。这是Cortex-A7内核的工作频率。

3. 配置的AHB、PERCLK、IPG三个根时钟的频率分别是多少?
  • AHB:132MHz

  • PERCLK:66MHz

  • IPG:66MHz

4. IMX6ULL中有几个PLL、几个PFD?
  • PLL :IMX6ULL共有 7个PLL(包括ARM_PLL、PLL2、PLL3、USB1_PLL、USB2_PLL、ENET_PLL、AUDIO_PLL、VIDEO_PLL)。

  • PFD :PLL2下有4个PFD,PLL3下有4个PFD,共8个PFD。这些PFD为不同外设提供了丰富的时钟频率组合。


四、 中断优先级与OCP原则

1. 中断优先级
  • 规则:编号越小,优先级越高。

  • GIC V2:支持1020个中断,256个优先级(0 - 255)。

  • IMX6ULL:支持160个中断,32个优先级(0 - 31)。

  • 抢占优先级(组优先级)与子优先级:图示展示了优先级的位分配,通过设置抢占优先级和子优先级,可以决定高优先级中断是否能打断正在执行的低优先级中断。

2. OCP原则(开闭原则)
  • 对于代码修改是关闭的:已经写好并测试稳定的底层驱动/核心代码,尽量避免直接修改其内部逻辑。

  • 对于代码扩展是开放的:应该通过提供接口、回调函数、或添加新模块的方式来扩展功能,而不是修改原有的代码。这在嵌入式框架设计中尤为重要。


今日总结与反思

今天的内容跨度很大,既有底层的汇编启动代码,又有宏观的时钟树架构。

  1. 启动代码 是裸机开发的"地基",理清那7个步骤后,再看start.S就不再觉得像天书了。

  2. 时钟配置是后续所有外设(UART、GPIO、定时器)能正常工作的前提。IMX6ULL的时钟树非常复杂,借助PLL、PFD、Prescaler的公式,能更清晰地计算出每个外设最终的时钟频率。

  3. OCP原则提醒我们,在写驱动代码时要考虑可扩展性,不要轻易破坏已有的稳定代码。

相关推荐
丁希希哇1 小时前
强化学习与偏好学习基础:PPO,DPO,GRPO
人工智能·学习·机器学习·大语言模型
破壁者-燕1 小时前
MLE 基础学习 Transformer
深度学习·学习·transformer
JWASX2 小时前
【agent 开发】agent 开发学习 - LangChain(1)
python·学习·agent
星恒随风2 小时前
Linux进程基础(二):进程调度、上下文切换、O(1)调度器、环境变量与虚拟地址空间
linux·笔记·学习·状态模式
牧羊人.3332 小时前
动手学深度学习 06|学习率调整
人工智能·深度学习·学习
Tronlong创龙2 小时前
IoTGateway可以让网关开发速度快一倍?
arm开发·嵌入式开发·硬件开发·工业控制·工业开发板
遇印记2 小时前
数据通信初步
运维·服务器·网络·windows·学习
陈年老古董2 小时前
Hive 文件格式与查询学习笔记
hive·笔记·学习
承渊政道3 小时前
Linux网络学习【UDP Socket编程实战:网络命令与客户端访问Linux验证】
linux·网络·学习·ubuntu·编程实战·udp socket