新手自学嵌入式 | 学习日记: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 小时前
西藏安全员 A 证备考真题题库 强化重点知识刷题提升通过率
学习·安全·考试·题库·考证
Be for thing1 小时前
PyTorch基础|Tensor张量详解,张量创建、数据类型与常用操作
pytorch·学习·1024程序员节
前进的李工2 小时前
Agent 学习笔记 02:Harness 与工作流怎么理解
笔记·学习·agent·工作流·harness
m4Rk_2 小时前
【论文阅读】Agent记忆机制(114):MemoryTax——当 Agent 能自主搜索历史,专门的记忆系统还划算吗?
论文阅读·人工智能·学习·开源·github
程序员梅雨4 小时前
【从零开始学习数据仓库 | 第一篇】:维度建模理论
大数据·数据仓库·学习·阿里云·数据分析
keyipatience5 小时前
Git学习(4)Git分布式优势与实战配置全解析
git·学习
comyccccc8 小时前
STM32F407VET6核心板的部分电路绘制学习笔记
笔记·stm32·学习
叼烟扛炮8 小时前
Linux 学习笔记(三十六):并发进阶——读写锁 vs 自旋锁,到底怎么选?
linux·运维·服务器·笔记·学习
是隼人8 小时前
buuctf-pwn [极客大挑战 2019]Not Bad(orw+jmp)题解(学习过程持续更新)
学习·安全·pwn入门·ctf入门
女神下凡9 小时前
芯参谋(77):裸机(无 OS)存储驱动开发指南
arm开发·单片机·嵌入式硬件·设计规范·1024程序员节