文章目录
- 一. 时钟相关概念解析
- 二. 时钟体系简介
- 1. 2440 开发板 时钟体系
- (1) 2440 开发板时钟体系介绍 ( 12MHz 晶振 | 2 PLL : ① MPLL ② UPLL | MPLL -> ① FCLK [ ARM 核 ] ② HCLK [ AHB 总线 ] ③ PCLK [ APB 总线 ] | UPLL -> UCLK [ USB 总线 ] )
- (2) 6410 开发板时钟体系介绍 ( 12MHz 晶振频率 | 3 PLL : ① APLL ② MPLL ③ EPLL | APLL -> ACLK [ ARM 核 ] | MPLL -> ① HCLK [ AHB 总线 ] ② PCLK [ APB 总线 ] | EPLL -> SCLK [ USB 总线 ] )
- (3) S5PV210 开发板时钟体系介绍 ( 24MHz 晶振频率 | 4 PLL ① APLL ② MPLL ③ EPLL ④ VPLL )
- 三. S3C6410 初始化时钟
本博客的参考文章及相关资料下载 :
- 1.本博客代码及参考手册下载 :https://download.csdn.net/download/han1202012/10606616
一. 时钟相关概念解析
1. 相关概念术语
( 1 ) 时钟脉冲信号 ( 概念 : 电压幅度 时间间隔 形成脉冲 | 作用 : 时序逻辑基础 间隔固定 根据脉冲数量可计算出时间 )
时钟脉冲信号 :
- 1.概念 : 按照 一定的电压幅度 和 一定的时间间隔 , 连续发出的 脉冲信号, 就是 时钟 脉冲信号;
- 2.重要作用 : 时钟脉冲信号 是 时序逻辑的 基础, 脉冲信号的 间隔是固定的, 可以 根据脉冲信号的个数 及 间隔 计算出对应的时间 ;
- 3.应用场景 : 芯片 中的 晶体管 工作状态 都是由 0 1 组成, 即 开和关, 控制这些开关操作都是 按照 时钟信号 的 节奏 进行 的;
- 4.说明示例 : 现在的主流 CPU i7 8700K 主频是 3.7GHz, 其中 1 GHz = 10^3 MHz = 10^6 KHz = 10^9 Hz 即 10亿 Hz, 每秒钟有 37亿 次时钟脉冲信号; 也就是 经常说的 每秒钟 运算 37 亿次; 当前的超级计算机可以到达 每秒 2亿亿次;
( 2 ) 时钟脉冲频率 ( 概念 : 单位时间内产生的脉冲个数 )
时钟脉冲频率 :
- 1.概念 : 单位时间内 产生 的 时钟脉冲个数 就是 时钟脉冲频率;
- 2.举例 : 1秒中 产生 1次, 就是 1Hz, 1秒钟产生 100 次就是 100Hz, 上面举例的 i78700K CPU, 一秒钟产生 37亿次, 就是 3.7GHz 的时钟脉冲频率;
(3) 时钟源 ( 产生来源 : ① 晶振 ② 锁相环 PLL | 晶振 : 晶体震荡电路 | PLL 锁相环 : 晶振 + PLL 合成 | 晶振 与 PLL 对比 优缺点 )
时钟源 : 时钟脉冲信号 是由 晶振 或 锁相环 PLL 产生;
- 1.晶振 : 全称 晶体振荡器, 是由 石英晶体 制作的, 石英晶体 ① 按照一定的方位角 切割, 并 ② 在其内部添加电子元件组成振荡电路;
- ① 震荡特性 : 如果 给晶振通电, 就会产生机械震荡, 其 频率由 制作的材质, 切割角度 等决定;
- ② 物理特性 : 石英非常 稳定, 其控制的 震荡频率也很稳定, 其 频率可以根据集合尺寸精确控制;
- ③ 晶振优势 : 晶振具有 A. 结构简单, B. 噪音低, C. 可提供精确定制的频率, 等优点;
- ④ 晶振缺陷 : A. 生产成本高 , B. 交货的周期很长;
- 2.PLL(锁相环) : 锁相环 比 晶振 更复杂, PLL 需要一个外部晶振作为输入, PLL 可以对外部晶振产生的频率进行 加倍 或 分频 操作, 即 提高 或 降低 频率;
- ① 使用场景 : 简单系统一般采用 石英晶振, 复杂的系统采用 晶振 + PLL 合成 提供时钟源;
- ② 降低成本 : 如果需要特定的时钟频率, 可以使用 PLL + 低频晶振 代替高频的晶振 , 这样成本比较低;
- ③ 多时钟频率系统 : 如果 在一个设备上需要多个时钟频率系统, 可以使用 PLL + 晶振 合成提供时钟源, PLL 将 晶振频率 进行 加倍 或 分频 即可得到不同频率的时钟源;
- ④ 与晶振对比 : PLL + 晶振 比 纯晶振 成本要低, 并且提供更加灵活多变的时钟频率 ;
二. 时钟体系简介
时钟体系学习步骤 : 对不不同的时钟体系, 需要按照下面的学习步骤学习即可;
① 晶振频率 ;
② PLL 个数 及 种类 ;
③ PLL 产生的时钟类型;
④ PLL 产生的时钟作用;
1. 2440 开发板 时钟体系
参考手册 : S3C2440.pdf , 章节 : 7 CLOCK & POWER MANAGEMENT , Page 235;
(1) 2440 开发板时钟体系介绍 ( 12MHz 晶振 | 2 PLL : ① MPLL ② UPLL | MPLL -> ① FCLK [ ARM 核 ] ② HCLK [ AHB 总线 ] ③ PCLK [ APB 总线 ] | UPLL -> UCLK [ USB 总线 ] )
2440 开发板时钟体系介绍 :
- 1.晶振频率 : 12MHz;
- 2.PLL (锁相环) : 有 2 个 PLL, 分别是 MPLL 和 UPLL;
- 3.产生时钟个数 : ① MPLL 锁相环 产生出 FCLK , HCLK, PCLK 三个时钟; ② UPLL 锁相环 产生出 UCLK 时钟;
- 4.各个时钟作用 :
- ① FCLK 时钟作用 : 处理器 中使用, ARM 核使用的时钟是 FCLK, 该时钟是 MPLL 产生的;
- ② HCLK 时钟作用 : AHB 总线中使用, 如 LCD, DMA 控制, 该时钟是 MPLL 产生的;
- ③ PCLK 时钟作用 : APB 总线中使用, 如 Uart 串口, GPIO, 该时钟是 MPLL 产生的;
- ④ UCLK 时钟作用 : USB 总线中使用, 该时钟是 UPLL 产生的;
- 5.2440 时钟体系图示 : 该图在 S3C2440.pdf 文档中的 7 CLOCK & POWER MANAGEMENT 章节, 237 页 ;
(2) 6410 开发板时钟体系介绍 ( 12MHz 晶振频率 | 3 PLL : ① APLL ② MPLL ③ EPLL | APLL -> ACLK [ ARM 核 ] | MPLL -> ① HCLK [ AHB 总线 ] ② PCLK [ APB 总线 ] | EPLL -> SCLK [ USB 总线 ] )
参考手册 : S3C6410X.pdf , 章节 : 3.3.4.1 Clock selection between PLLs and input reference clock , Page 124;
6410 开发板时钟体系介绍 :
- 1.晶振频率 : 12MHz;
- 2.PLL (锁相环) : 有 3 个 PLL, 分别是 APLL , MPLL 和 EPLL;
- 3.产生时钟个数 : ① APLL 锁相环 产生出 ACLK 一个时钟; ② MPLL 锁相环 产生出 HCLK, PCLK 时钟; ③ EPLL 产生 SCLK 时钟;
- 4.各个时钟作用 :
- ① ACLK 时钟作用 : 处理器中使用, ARM核中使用 ACLK 时钟, 该时钟是 APLL 锁相环产生;
- ② HCLK 时钟作用 : AHB 总线中使用, 如 LCD, DMA ; 该时钟是 MPLL 产生的;
- ③ PCLK 时钟作用 : APB 总线中使用, 如 Uart 串口, GPIO 中使用, 该时钟是 MPLL 产生的;
- ④ SCLK 时钟作用 : USB 总线中使用, 该时钟是 EPLL 锁相环产生的;
- 5.6410时钟体系图示 : 该图在 S3C6410X.pdf 文档中的 3.3.4.1 Clock selection between PLLs and input reference clock 章节, 124 页 ;
(3) S5PV210 开发板时钟体系介绍 ( 24MHz 晶振频率 | 4 PLL ① APLL ② MPLL ③ EPLL ④ VPLL )
参考手册 : S5PV210_UM_REV1.1.pdf , 章节 : 3.4 CLOCK GENERATION , Page 361;
S5PV210 开发板时钟体系介绍 :
- 1.晶振频率 : 24MHz;
- 2.PLL (锁相环) : 有 4 个 PLL, 分别是 APLL , MPLL , EPLL 和 VPLL;
- 3.S5PV210 的 时钟体系分类 : 分为 以下 三类 ;
- ① 主系统时钟体系 : MSYS , 由 APLL 锁相环产生, 产生的时钟有 ARMCLK, HCLK_MSYS, PCLK_MSYS , 应用在 ARM 核 中;
- ② 显示相关时钟体系 : DSYS , 由 MPLL 锁相环产生, 产生的时钟有 HCLK_DSYS, PCLK_DSYS, 主要应用在显示相关的部件中 ;
- ③ 外围设备时钟体系 : PSYS , 由 EPLL 锁相环产生, 产生的时钟有 HCLK_PSYS, CLK_DPM, 主要用于外设, 如 Uart 串口 等;
- ④ VPLL 锁相环产生的时钟 : VPLL 产生的时钟 主要用于视频处理;
- 4.S5PV210时钟体系图示 : 该图在 S5PV210_UM_REV1.1.pdf 文档中的3.4 CLOCK GENERATION, 361 页 ;
三. S3C6410 初始化时钟
1. S3C 6410 时钟初始化流程简介
(1) CPU 频率变化过程 ( ① 上电后 12MHz | ② 配置 PLL | ③ 处于 Lock Time 频率 0Hz | ④ 正常 PLL 频率 )
CPU 上电后 从 低频率 到 高频率的变化过程 :
- 1.上电后的工作频率 : 上电后 ARM 核的工作频率就是晶振频率, 即 12MHz, 这个速度非常慢;
- 2.配置锁相环 : 配置锁相环, 使 频率 增加;
- 3.Lock Time 时间 : 配置 PLL 锁相环后, 会出现一段不工作的时间, 此时 CPU 频率为 0Hz, 这段时间叫 Lock Time;
- 4.正常频率 : 在 Lock Time 之后, 就会进入正式的 锁相环 工作频率, 此时的频率是 晶振频率 经过 锁相环 处理后的 高频率;
- 5.图示 : 下图是处理器上电后各种参数的变化, 横轴是时间轴, 没有纵轴, 各个参数在上电后的每个时间段的表现;
整个时钟初始化流程是需要软件帮助进行的, 因此这些步骤需要开发者进行开发, 这也是该博客的主要内容;
(2) 时钟初始化的四个步骤 ( ① 配置 Lock Time | ② 设置分频系数 | ③ 设置 APLL MPLL 频率 | ④ 设置 CPU 工作模式 -> 异步工作模式 )
时钟初始化流程 :
- 1.配置 Lock Time : 配置 PLL 锁相环后会有一段 CPU 频率为 0 的时间, 这段时间处理器不工作, 这段时间就是 Lock Time;
- 2.设置分频系数 : 通过 为 不同的时钟设置不同的分频系数, 根据这个分频系数, 来确定每个时钟的频率;
- 3.设置 APLL MPLL 频率 : 设置一个时钟的频率, 可以根据分频系数计算出其它所有时钟的频率了;
- 4.设置 CPU 工作模式 : 如果 FCLK 与 HCLK 的频率不同, 那么 CPU 需要设置为 异步工作模式, FCLK 是 ARM 核的时钟, HCLK 是 总线时钟, 如果两个时钟不一致, 需要将 CPU 设置为 异步工作模式;
- ① 常用设置 : 一般情况下 设置 分频系数的时候, 不会给 FCLK 与 HCLK 设置相同的分频系数, 因此, 该步骤大部分情况下都要执行;
2. S3C 6410 时钟初始化 汇编代码编写
参考手册 : ARM芯片 手册 S3C6410X.pdf ( 基于 6410 开发板 ARM 11 )
- 1.手册对应章节 : 3 SYSTEM CONTROLLER;
- 2.S3C6410X.pdf手册下载地址 :
(1) 配置 Lock Time
配置 Lock Time :
- 1.文档位置 : S3C6410X.pdf 手册, Page 141, 3.4.2.1 PLL Control Registers 章节;
- 2.默认设置不变 ( 推荐 ) : 一般情况下, 使用开发板的默认设置即可, 如果有特殊定制需求才修改该 PLL_LOCK 寄存器的值;
(2) 设置分频系数
设置分频系数 :
- 1.相关文档位置 : S3C6410X.pdf 手册, Page 125, 3.3.4.2 ARM and AXI/AHB/APB bus clock generation 章节;
- 2.APLL 锁相环 : 12 MHz 的晶振频率, 经过 APLL 锁相环 产生 输出时钟, 该输出时钟 经过 DIVARM 分频, 产生 ARMCLK, 这是 ARM 核使用的时钟;
- 3.MPLL 锁相环 : 12MHz 晶振频率时钟, 经过 MPLL 锁相环 产生的 时钟, 该时钟 经过 DIVHCLKX2 分频后, 产生 HCLKx2 时钟, 同时 DIVHCLKX2 分频后的时钟 又 经过不同的 分频 产生 HCLK, PCLK, CLKJPEG, CLKSECUR 时钟;
- ① HCLK 时钟 : HCLKx2 时钟 经过 DIVHCLK 分频后, 产生 HCLK 时钟;
- ② PCLK 时钟 : HCLKx2 时钟 经过 DIVPCLK 分频后, 产生 PCLK 时钟;
- ③ CLKJPEG 时钟 : HCLKx2 时钟 经过 DIVCLKJPEG 分频后, 产生 CLKJPEG 时钟;
- ④ CLKSECUR 时钟 : HCLKx2 时钟 经过 DIVCLKSECUR 分频后, 产生 CLKSECUR 时钟;
- 4.分频参数参考 : 不同的时钟分频器的分频参数列表 , 来源 S3C6410X.pdf 手册, Page 126, 3.3.4.2 ARM and AXI/AHB/APB bus clock generation 章节;
- 5.时钟分频公式 : 文档位置 S3C6410X.pdf 手册, Page 126, 3.4.2.3 Clock divider control register 章节;
- ① DIVARM 分频公式 : ARMCLK = DOUTAPLL / (ARM_RATIO + 1) , DOUTAPLL 是 APLL 锁相环输出的时钟, ARM_RATIO 是设置的分频系数;
- ② DIVHCLKX2 分频公式 : HCLKX2 = HCLKX2IN / (HCLKX2_RATIO + 1), HCLKX2IN 是 MPLL 锁相环的输出时钟频率, HCLKX2_RATIO 是设置的分频系数;
- ③ DIVHCLK 分频公式 : HCLK = HCLKX2 / (HCLK_RATIO + 1), HCLKX2 是 DIVHCLKX2 分频后的时钟频率, HCLK_RATIO 是设置的分频系数;
- ④ DIVPCLK 分频公式 : PCLK = HCLKX2 / (PCLK_RATIO + 1), HCLKX2 是 DIVHCLKX2 分频后的时钟频率, PCLK_RATIO 是设置的分频系数;
- 6.PLL 锁相环输出频率 :
- ① APLL 锁相环输出频率 : 533 MHz ;
- ② MPLL 锁相环输出频率 : 533 MHz ;
- 7.具体参考参数设置 : 从 6410 开发板中的 u-boot 源码中查找相关的时钟 分频系数 ;
- ① DIVARM 分频参数 : 0 , APLL 输出频率为 533MHz, 根据公式计算 ARMCLK 时钟频率为 533MHz;
- ② DIVHCLKX2 分频参数 : 1 , MPLL 输出频率 533MHz, 根据公式计算 HCLKX2 时钟为 266 MHz;
- ③ DIVPCLK 分频参数 : 3 , HCLKX2 时钟为 266 MHz, PCLK 频率为 66MHz;
- ④ DIVHCLK 分频参数 : 1 , HCLKX2 时钟为 266 MHz, HCLK 频率为 133MHz;
设置分频系数代码编写 :
- 1.定义分频控制寄存器( Clock divider control register ) 地址 : 之前说的 分频参数 都是通过 CLK_DIV0 寄存器设置的, 将 CLK_DIV0 的地址定义成常量,
#define CLK_DIV0 0x7E00F020
;
- 2.定义分频参数的值 : 参考 CLK_DIV0 值的表格 设置 CLK_DIV0 寄存器的实际值;
- ① 定义 ARM_RATIO 分频参数 : 这个参数设置成 0, CLK_DIV0 寄存器的 [3:0] 位 设置该参数, 该参数单独为 0x0 << 0;
- ② 定义 HCLKX2_RATIO 分频参数 : 这个参数设置成 1, CLK_DIV0 寄存器的 [11:9] 位 设置该参数, 值为 0x1 << 9;
- ③ 定义 HCLK_RATIO 分频参数 : 这个参数设置成 1, CLK_DIV0 寄存器的 [8] 位 设置该参数, 值为 0x1 << 8;
- ④ 定义 PCLK_RATIO 分频参数 : 这个参数设置成 3, CLK_DIV0 寄存器的 [15:12] 位 设置该参数, 值为 0x3 << 12;
- ⑤ 设置的值 : 将上面四个值汇总起来为 (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ;
- ⑥ 代码 :
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) )
;
- 3.装载 CLK_DIV0 地址到通用寄存器中 :
ldr r0, =CLK_DIV0
; - 4.装载 CLK_DIV0 的寄存器值到通用寄存器中 :
ldr r1, =CLK_VAL
; - 5.设置 CLK_DIV0 寄存器的值 :
str r1, [r0]
, 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中 ; - 6.截止到当前的汇编代码展示 :
#define CLK_DIV0 0x7E00F020 @ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
init_clock :
ldr r0, =CLK_DIV0 @ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
ldr r1, =CLK_VAL @ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
mov pc, lr
(3) 设置 CPU 异步工作模式
设置 CPU 异步工作模式 :
- 1.相关文档位置 : S3C6410X.pdf 手册, Page 169, 3.4.2.14 Others control register 章节;
- 2.Others control register 寄存器 [7] : 第 7 位 [7], 设置为 0 时工作在 异步模式下, 设置为 1 时 工作在 同步模式 下;
- 3.Others control register 寄存器 [6] : 第 6 位 用于选择 锁相环输出源, 设置为 0 选择 MPLL 锁相环 输出时钟, 设置为 1 选择 APLL 锁相环输出时钟, 注意 只有在 CPU 同步模式下才设置为1;
汇编代码编写 :
- 1.定义 OTHERS 寄存器地址 :
#define OTHERS 0x7E00F900
; - 2.将 OTHERS 寄存器地址存储到 r0 寄存器中 :
ldr r0, =OTHERS
; - 3.读取 OTHERS 寄存器的值 :
ldr r1, [r0]
, 即 将 r0 寄存器存储的地址指向的内存中的值 装载到 r1 通用寄存器中; - 4.设置寄存器值 : 将 r1 寄存器中存储的 OTHERS 寄存器的 6 和 7 位 清零, 设置 CPU 异步工作模式, 同时设置 MPLL 锁相环时钟输出,
bic r1, r1, #0xc0
; - 5.设置 OTHERS 寄存器值 :
str r1, [r0]
, 将 r1 通用寄存器中的值 存储到 r0 寄存器中保存的地址指向的内存中, 即 OTHERS 寄存器; - 6.截止到当前的汇编代码展示 :
#define CLK_DIV0 0x7E00F020 @ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的
#define OTHERS 0x7E00F900 @ 定义 OTHERS 寄存器地址, 用于设置 CPU 异步工作模式
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
init_clock :
ldr r0, =CLK_DIV0 @ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
ldr r1, =CLK_VAL @ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
ldr r0, =OTHERS @ 将 OTHERS 寄存器地址存到 r0 通用寄存器中
ldr r1, [r0] @ 将 r0 寄存器存储的地址指向的寄存器中的值读取到 r1 通用寄存器中
bic r1, r1, #0xc0 @ 将 r1 寄存器中的值的 第 6 位 和 第 7 位 设置成 0
str r1, [r0] @ 将 r1 寄存器中的值 写出到 r0 寄存器存储的地址指向的内存位置 即 OTHERS 寄存器
mov pc, lr
(4) 设置 APLL 和 MPLL 时钟频率
设置 APLL 和 MPLL 时钟频率 :
- **1.相关文档位置 : **S3C6410X.pdf 手册, Page 141, 3.4.2.1 PLL Control Registers 章节;
- 2.APLL 和 MPLL 控制寄存器 : 下面两张表格 分别说明了 两个 PLL 控制寄存器对应的地址, 以及寄存器每一位设置的值;
- 3.PLL 输出频率公式 : FOUT = MDIV X FIN / (PDIV X 2^SDIV), 该公式在 S3C6410X.pdf 文档 142 页, 由公式可以得到 PLL 输出频率由 MDIV , PDIV, SDIV 三个参数决定, 文档中给出了一个固定的表格示例, 这里我们选择 第 5 行的参数进行设置;
汇编代码编写 :
- 1.定义 APLL_CON 寄存器地址常量 :
#define APLL_CON 0x7E00F00C
; - 2.定义 MPLL_CON 寄存器地址常量 :
#define MPLL_CON 0x7E00F010
; - 3.分析 PLL 控制寄存器要设置的位 : 我们要设置 533MHz 的 PLL 输出频率, APLL 和 MPLL 都输出 533MHz 的频率;
- ① 533MHz 输出确定的参数 : MDIV 设置成 266, PDIV 设置 3, SDIV 设置 1;
- ② 寄存器值设置 :
#define PLL_VAL ( (0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0) )
;- a. 启用 PLL : [31] 设置成 1 , 设置值为 1 << 31;
- b. 设置 MDIV : [25:16] 位设置成 266, 设置值为 266 << 16;
- c. 设置 PDIV : [13:8] 位 设置成 3, 值为 3 << 8 ;
- d.设置 SDIV : [2:0] 位 设置成 1, 值为 1 << 0;
- e. 最终值为 :
(0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0)
;
- ① 533MHz 输出确定的参数 : MDIV 设置成 266, PDIV 设置 3, SDIV 设置 1;
- 4.设置 APLL_CON 寄存器的值 : 首先将 APLL_CON 寄存器地址存储到 r0 寄存器中, 然后将 要设置的值 存储到 r1 寄存器中, 之后 使用 str 指令将 r1 寄存器的值 存储到 r0 寄存器中存储的地址指向的内存中, 即 将 PLL_VAL 值设置给 APLL_CON 寄存器;
ldr r0, =APLL_CON
ldr r1, =PLL_VAL
str r1, [r0]
- 5.设置 MPLL_CON 寄存器的值 : 首先将 MPLL_CON 寄存器地址存储到 r0 寄存器中, 然后将 要设置的值 存储到 r1 寄存器中, 之后 使用 str 指令将 r1 寄存器的值 存储到 r0 寄存器中存储的地址指向的内存中, 即 将 PLL_VAL 值设置给 MPLL_CON 寄存器;
ldr r0, =MPLL_CON
ldr r1, =PLL_VAL
str r1, [r0]
- 6.截止到当前的代码 : 目前已完成 ① 分频参数设置,② CPU 异步模式设置, ③APLL 和 MPLL 时钟频率设置工作;
#define CLK_DIV0 0x7E00F020 @ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的
#define OTHERS 0x7E00F900 @ 定义 OTHERS 寄存器地址, 用于设置 CPU 异步工作模式
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
#define MPLL_CON 0x7E00F010 @ 定义 MPLL_CON 寄存器地址常量
#define APLL_CON 0x7E00F00C @ 定义 APLL_CON 寄存器地址常量
#define PLL_VAL ( (0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0) ) @ 设置 PLL 控制寄存器的值
init_clock :
ldr r0, =CLK_DIV0 @ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
ldr r1, =CLK_VAL @ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
ldr r0, =OTHERS @ 将 OTHERS 寄存器地址存到 r0 通用寄存器中
ldr r1, [r0] @ 将 r0 寄存器存储的地址指向的寄存器中的值读取到 r1 通用寄存器中
bic r1, r1, #0xc0 @ 将 r1 寄存器中的值的 第 6 位 和 第 7 位 设置成 0
str r1, [r0] @ 将 r1 寄存器中的值 写出到 r0 寄存器存储的地址指向的内存位置 即 OTHERS 寄存器
ldr r0, =APLL_CON @ 将 APLL_CON 寄存器地址存到 r0 通用寄存器中
ldr r1, =PLL_VAL @ 将 要设置给 APLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 APLL_CON 寄存器中
ldr r0, =MPLL_CON @ 将 MPLL_CON 寄存器地址存到 r0 通用寄存器中
ldr r1, =PLL_VAL @ 将 要设置给 MPLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 MPLL_CON 寄存器中
mov pc, lr
(5) 设置 时钟源
时钟源设置 :
- 1.相关文档位置 : S3C6410X.pdf 手册, Page 145, 3.4.2.2 Clock source control register 章节;
- 2.CLK_SRC 寄存器 : 控制时钟源;
- ① 控制 APLL 时钟源 : [0] 位 控制 APLL 时钟源, 如果设置为 0 , 使用晶振作为时钟源, 如果设置为 1, 使用 APLL 输出的时钟作为时钟源;
- ② 控制 MPLL 时钟源 : [1] 位 控制 MPLL 时钟源, 如果设置为 0 , 使用晶振作为时钟源, 如果设置为 1, 使用 MPLL 输出的时钟作为时钟源;
- ③ 控制 EPLL 时钟源 : [2] 位 控制 EPLL 时钟源, 如果设置为 0 , 使用晶振作为时钟源, 如果设置为 1, 使用 EPLL 输出的时钟作为时钟源;
汇编代码编写 :
- 1.定义 CLK_SRC 寄存器地址常量 :
#define CLK_SRC 0x7E00F01C
; - 2.设置 CLK_SRC 寄存器的值 : 将 [1:0] 两位设置成 1; 首先将 CLK_SRC 寄存器地址存储到 r0 寄存器中, 然后将 要设置的值 0x3 立即数 存储到 r1 寄存器中, 之后 使用 str 指令将 r1 寄存器的值 存储到 r0 寄存器中存储的地址指向的内存中, 即 将 0x3 值设置给 CLK_SRC 寄存器;
ldr r0, =CLK_SRC
mov r1, #0x3
str r1, [r0]
(6) 代码示例
代码示例 : 截止到当前的代码示例 ① 设置 MVC 模式 ② 外设基地址初始化, ③ 关闭看门狗, ④ 关闭中断, ⑤ 关闭 MMU, ⑥ 初始化时钟, ⑦ 打开 LED 发光二极管;
@****************************
@File:start.S
@
@BootLoader 初始化代码
@****************************
.text @ 宏 指明代码段
.global _start @ 伪指令声明全局开始符号
_start: @ 程序入口标志
b reset @ reset 复位异常
ldr pc, _undefined_instruction @ 未定义异常, 将 _undefined_instruction 值装载到 pc 指针中
ldr pc, _software_interrupt @ 软中断异常
ldr pc, _prefetch_abort @ 预取指令异常
ldr pc, _data_abort @ 数据读取异常
ldr pc, _not_used @ 占用 0x00000014 地址
ldr pc, _irq @ 普通中断异常
ldr pc, _fiq @ 软中断异常
_undefined_instruction: .word undefined_instruction @ _undefined_instruction 标号存放了一个值, 该值是 32 位地址 undefined_instruction, undefined_instruction 是一个地址
_software_interrupt: .word software_interrupt @ 软中断异常
_prefetch_abort: .word prefetch_abort @ 预取指令异常 处理
_data_abort: .word data_abort @ 数据读取异常
_not_used: .word not_used @ 空位处理
_irq: .word irq @ 普通中断处理
_fiq: .word fiq @ 快速中断处理
undefined_instruction: @ undefined_instruction 地址存放要执行的内容
nop
software_interrupt: @ software_interrupt 地址存放要执行的内容
nop
prefetch_abort: @ prefetch_abort 地址存放要执行的内容
nop
data_abort: @ data_abort 地址存放要执行的内容
nop
not_used: @ not_used 地址存放要执行的内容
nop
irq: @ irq 地址存放要执行的内容
nop
fiq: @ fiq 地址存放要执行的内容
nop
reset: @ reset 地址存放要执行的内容
bl set_svc @ 跳转到 set_svc 标号处执行
bl set_serial_port @ 设置外设基地址端口初始化
bl disable_watchdog @ 跳转到 disable_watchdog 标号执行, 关闭看门狗
bl disable_interrupt @ 跳转到 disable_interrupt 标号执行, 关闭中断
bl disable_mmu @ 跳转到 disable_mmu 标号执行, 关闭 MMU
init_clock @ 跳转到 init_clock 标号, 执行时钟初始化操作
bl light_led @ 打开开发板上的 LED 发光二极管
set_svc:
mrs r0, cpsr @ 将 CPSR 寄存器中的值 导出到 R0 寄存器中
bic r0, r0, #0x1f @ 将 R0 寄存器中的值 与 #0x1f 立即数 进行与操作, 并将结果保存到 R0 寄存器中, 实际是将寄存器的 0 ~ 4 位 置 0
orr r0, r0, #0xd3 @ 将 R0 寄存器中的值 与 #0xd3 立即数 进行或操作, 并将结果保存到 R0 寄存器中, 实际是设置 0 ~ 4 位 寄存器值 的处理器工作模式代码
msr cpsr, r0 @ 将 R0 寄存器中的值 保存到 CPSR 寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码
#define pWTCON 0x7e004000 @ 定义看门狗控制寄存器 地址 ( 6410开发板 )
disable_watchdog:
ldr r0, =pWTCON @ 先将控制寄存器地址保存到通用寄存器中
mov r1, #0x0 @ 准备一个 0 值, 看门狗控制寄存器都设置为0 , 即看门狗也关闭了
str r1, [r0] @ 将 0 值 设置到 看门狗控制寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码
disable_interrupt:
mvn r1,#0x0 @ 将 0x0 按位取反, 获取 全 1 的数据, 设置到 R1 寄存器中
ldr r0,=0x71200014 @ 设置第一个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中
str r1,[r0] @ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中
ldr r0,=0x71300014 @ 设置第二个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中
str r1,[r0] @ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码
disable_mmu :
mcr p15,0,r0,c7,c7,0 @ 设置 I-Cache 和 D-Cache 失效
mrc p15,0,r0,c1,c0,0 @ 将 c1 寄存器中的值 读取到 R0 通用寄存器中
bic r0, r0, #0x00000007 @ 使用 bic 位清除指令, 将 R0 寄存器中的 第 0, 1, 2 三位 设置成0, 代表 关闭 MMU 和 D-Cache
mcr p15,0,r0,c1,c0,0 @ 将 R0 寄存器中的值写回到 C1 寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码
set_serial_port :
ldr r0, =0x70000000 @ 将基地址装载到 r0 寄存器中, 该基地址 在 arm 核 手册中定义
orr r0, r0, #0x13 @ 设置初始化基地址的范围, 将 r0 中的值 与 0x13 立即数 进行或操作, 将结果存放到 r0 中
mcr p15, 0, r0, c15, c2, 4 @ 将 r0 中的值设置给 c15 协处理器
mov pc, lr
#define CLK_DIV0 0x7E00F020 @ 定义 CLK_DIV0 寄存器地址, 时钟的分频参数都是通过该寄存器进行设置的
#define OTHERS 0x7E00F900 @ 定义 OTHERS 寄存器地址, 用于设置 CPU 异步工作模式
#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 设置 CLK_DIV0 寄存器的值, 即 各个时钟分频器的参数
#define MPLL_CON 0x7E00F010 @ 定义 MPLL_CON 寄存器地址常量
#define APLL_CON 0x7E00F00C @ 定义 APLL_CON 寄存器地址常量
#define PLL_VAL ( (0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0) ) @ 设置 PLL 控制寄存器的值
#define CLK_SRC 0x7E00F01C @ 定义 CLK_SRC 时钟源控制寄存器的地址常量
init_clock :
ldr r0, =CLK_DIV0 @ 将 CLK_DIV0 的地址装载到 r0 通用寄存器中
ldr r1, =CLK_VAL @ 将 要设置给 CLK_DIV0 寄存器的值 CLK_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中
ldr r0, =OTHERS @ 将 OTHERS 寄存器地址存到 r0 通用寄存器中
ldr r1, [r0] @ 将 r0 寄存器存储的地址指向的寄存器中的值读取到 r1 通用寄存器中
bic r1, r1, #0xc0 @ 将 r1 寄存器中的值的 第 6 位 和 第 7 位 设置成 0
str r1, [r0] @ 将 r1 寄存器中的值 写出到 r0 寄存器存储的地址指向的内存位置 即 OTHERS 寄存器
ldr r0, =APLL_CON @ 将 APLL_CON 寄存器地址存到 r0 通用寄存器中
ldr r1, =PLL_VAL @ 将 要设置给 APLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 APLL_CON 寄存器中
ldr r0, =MPLL_CON @ 将 MPLL_CON 寄存器地址存到 r0 通用寄存器中
ldr r1, =PLL_VAL @ 将 要设置给 MPLL_CON 寄存器的值 PLL_VAL 立即数 装载到 r1 通用寄存器中;
str r1, [r0] @ 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中, 即 将 PLL_VAL 的值 设置到 MPLL_CON 寄存器中
ldr r0, =CLK_SRC @ 将 CLK_SRC 寄存器地址设置到 r0 通用寄存器中
mov r1, #0x3 @ 将 0x3 立即数设置给 r1 寄存器
str r1, [r0] @ 将 r1 中存储的立即数设置给 r0 寄存器存储的地址指向的内存中, 即 CLK_SRC 寄存器中
mov pc, lr
#define GPBCON 0x7F008820
#define GPBDAT 0x7F008824
light_led :
ldr r0, =GPBCON @ 将 0x7F008820 GPM 控制寄存器的地址 0x7F008820 装载到 r0 寄存器中
ldr r1, =0x1111 @ 设置 GPM 控制寄存器的行为 为 Output 输出, 即每个对应引脚的设置为 0b0001 值
str r1, [r0] @ 将 r1 中的值 存储到 r0 指向的 GPBCON 0x7F008820 地址的内存中
ldr r0, =GPBDAT @ 将 GPBDAT 0x7F008824 地址值 装载到 r0 寄存器中
ldr r1, =0b110000 @ 计算 GPM 数据寄存器中的值, 设置 0 为 低电平, 设置 1 为高电平, 这里设置 0 ~ 3 位为低电平, 其它为高电平
str r1, [r0] @ 将 r1 中的值 存储到 r0 指向的 GPBDAT 0x7F008824 地址的内存中
mov pc, lr
3. 链接器脚本
u-boot.lds 链接器脚本 代码解析 :
- 1.指明输出格式 ( 处理器架构 ) : 使用
OUTPUT_ARCH(架构名称)
指明***输出格式, 即处理器的架构***, 这里是 arm 架构的,OUTPUT_ARCH(arm)
; - 2.指明输出程序的入口 : 设置编译输出的程序入口位置, 语法为
ENTRY(入口位置)
, 在上面的 Start.S 中设置的程序入口是_start
, 代码为ENTRY(_start)
; - 3.设置代码段 : 使用
.text :
设置代码段; - 4.设置数据段 : 使用
.data :
设置数据段; - 5.设置 BSS 段 : 使用
.bss :
设置 BSS 段;- ( 1 ) 记录 BSS 段的起始地址 :
bss_start = .;
; - ( 2 ) 记录 BSS 段的结束地址 :
bss_end = .;
;
- ( 1 ) 记录 BSS 段的起始地址 :
- 6.对齐 : 每个段都需要设置内存的对齐格式, 使用
. = ALIGN(4);
设置四字节对齐即可; - 7.代码示例 :
OUTPUT_ARCH(arm) /*指明处理器结构*/
ENTRY(_start) /*指明程序入口 在 _start 标号处*/
SECTIONS {
. = 0x50008000; /*整个程序链接的起始位置, 根据开发板确定, 不同开发板地址不一致*/
. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
.text : /*代码段*/
{
start.o (.text) /*start.S 转化来的代码段*/
*(.text) /*其它代码段*/
}
. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
.data : /*数据段*/
{
*(.data)
}
. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
bss_start = .; /*记录 bss 段起始位置*/
.bss : /*bss 段*/
{
*(.bss)
}
bss_end = .; /*记录 bss 段结束位置*/
}
4. Makefile 编译脚本
makefile 文件编写 :
- 1.通用规则 ( 汇编文件编译规则 ) : 汇编文件 编译 成同名的 .o 文件, 文件名称相同, 后缀不同,
%.o : %.S
, 产生过程是arm-linux-gcc -g -c $^
, 其中^
标识是所有的依赖文件, 在该规则下 start.S 会被变异成 start.o ; - 2.通用规则 ( C 文件编译规则 ) : C 代码编译成同名的 .o 文件,
%.o : %.c
, 产生过程是arm-linux-gcc -g -c $^
; - 3.设置最终目标 : 使用
all:
设置最终编译目标;- ( 1 ) 依赖文件 : 产生最终目标需要依赖 start.o 文件, 使用
all: start.o
表示最终目标需要依赖该文件; - ( 2 ) 链接过程 :
arm-linux-ld -Tu-boot.lds -o u-boot.elf $^
, 需要使用链接器脚本进行连接, ①链接工具是 arm-linux-ld 工具, ②使用-Tu-boot.lds
设置链接器脚本 是刚写的 u-boot.lds 链接器脚本, ③输出文件是 u-boot.elf 这是个中间文件, ④ 依赖文件是$^
代表所有的依赖; - ( 3 ) 转换成可执行二进制文件 :
arm-linux-objcopy -O binary u-boot.elf u-boot.bin
, 使用-O binary
设置输出二进制文件, 依赖文件是u-boot.elf
, 输出的可执行二进制文件 即 结果是u-boot.bin
;
- ( 1 ) 依赖文件 : 产生最终目标需要依赖 start.o 文件, 使用
- 4.makefile 文件内容 :
all: start.o
arm-linux-ld -Tu-boot.lds -o u-boot.elf $^
arm-linux-objcopy -O binary u-boot.elf u-boot.bin
%.o : %.S
arm-linux-gcc -g -c $^
%.o : %.c
arm-linux-gcc -g -c $^
.PHONY: clean
clean:
rm *.o *.elf *.bin
5. 编译输出可执行文件
编译过程 :
- 1.文件准备 : 将 汇编代码 ( start.S ) 链接器脚本 ( gboot.lds ) makefile 文件 拷贝到编译目录 ;
- 2.执行编译命令 :
make
; - 3.编译结果 : 可以看到 生成了 编译目标文件 start.o, 链接文件 u-boot.elf, 可执行的二进制文件 u-boot.bin ;
6. 烧写代码到开发板并执行
( 1 ) OK6410 开发板启动切换方式
OK6410 开发板启动切换方式 : 通过控制 开发板右侧的 8个开关来设置启动来源;
- 1.sd 卡启动 : (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1;
- 2.nand flash 启动 : (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1;
- 3.nor flash 启动 : (1~8) 位置 : x, x, x, 1, 0, 1, 0, x;
( 2 ) 制作 SD 卡启盘 并 准备程序
制作 SD 卡启动盘 :
- 1.找到开发板的烧写工具 : OK6410-A 开发板的烧写工具 在开发光盘 A 的 OK6410-A-1G用户光盘(A)-20160812\Linux-3.0.1\Linux烧写工具 目录下, 开发板光盘资料下载地址 ;
- 2.设置 SD_Writer.exe 属性 ( win10系统需要进行的设置 ) : 右键点击属性, 在兼容性一栏, 设置 以 Windows 7 兼容模式运行, 并设置 以管理员身份运行此程序 ;
- 3.先格式化 SD 卡 : 注意这里要使用 SD_Writer 中的 format 功能进行格式化 , 按照下面的步骤, 一步一步点击确定执行 ;
- 4.选择要烧写的文件 : 这里选择 OK6410-A-1G用户光盘(A)-20160812\Linux-3.0.1\Linux烧写工具\mmc_ram256.bin 文件;
- 5.烧写文件到 SD 卡中 : 直接点击 Program 按钮, 就将启动程序烧写到了 SD 卡中;
- 6.准备 LED 灯程序 : 将编译出的 gboot.bin 文件名修改成 u-boot.bin, 必须修改成该文件名, 否则无法烧写上去;
- 7.将程序拷贝到 SD 卡中 : 将程序直接拷贝到 SD 卡中即可;
参考资料 : OK6410烧写裸板程序方法
这是之前写过的博客, 仅作为参考;
( 3 ) SecureCRT 连接开发板并烧写程序
SecureCRT 连接开发板并烧写程序 步骤 :
- 1.硬件连接操作 : 使用 USB 转 串口工具 将电脑 与 开发板链接, USB 插在电脑端, 串口端插在 开发板上, 插上电源适配器, 但是不要打开电源开关;
- 2.开发板设置 : 将开发板右侧的开关设置成 SD 卡启动, 即 (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1; 该步骤很重要;
- 2.查询串口端口号 : 在设备管理器中查看串口端口号, 这里可以看到是 COM9;
- 3.SecureCRT 连接串口 : 打开 SecureCRT 软件, 点击快速连接, 然后在弹出的对话框中按照下面进行配置, ① 首先要选择 Serial 协议, ② 然后选择端口, 这个端口从设备管理器中查看, ③ 波特率选择 115200, ④ 取消 RTS/CTS 选项;
- 4.打开开发板 ( 很重要 ) : 选中 SecureCRT 软件, 然后按住空格键不放, 这个操作很重要, 打开开发板开关, ① 先按住空格键, ②再打开开关;
- 5.首先格式化 Nand Flash : 选择 [1] 选项, 格式化 Nand Flash;
- 6.选择从 SD 卡中烧写 : 选择 [2] Burn image from SD card 选项, 从 SD 卡中向开发板烧写程序;
- 7.选择烧写 u-boot : 选择 [2] Flash u-boot, 烧写 u-boot, 会从 SD 卡中查找 u-boot.bin 文件, 然后烧写到 nand flash 中, 如果 SD 卡中 没有 u-boot.bin 会报错;
- 8.设置从 Nand Flash 启动 : 设置开发板上的启动开关, (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1; 此时 四个 LED 全亮;
- 9.效果展示 : 设置的 GPBDAT 寄存器值为 0b110000, 四个 LED 灯都亮起来;
- 10.修改 LED 灯显示参数后显示结果 : 设置 GPBDAT 寄存器中值为 0b110101 是 第一个 和 第三个 LED 亮起来;
7. 将程序烧写到开发板上运行 ( 仅做参考 )
烧写程序并运行 :
- 1.参考步骤 : 烧写代码到开发板并执行
- 2.本次运行结果 : 设置的 GPBDAT 寄存器值为 0b110000, 四个 LED 灯都亮起来;
- 3.修改 LED 灯显示参数后显示结果 : 设置 GPBDAT 寄存器中值为 0b110101 是 第一个 和 第三个 LED 亮起来;