本博客的参考文章及相关资料下载 :
时钟脉冲信号 :
时钟脉冲频率 :
时钟源 : 时钟脉冲信号 是由 晶振 或 锁相环 PLL 产生;
时钟体系学习步骤 : 对不不同的时钟体系, 需要按照下面的学习步骤学习即可;
① 晶振频率 ;
② PLL 个数 及 种类 ;
③ PLL 产生的时钟类型;
④ PLL 产生的时钟作用;
参考手册 : S3C2440.pdf , 章节 : 7 CLOCK & POWER MANAGEMENT , Page 235;
2440 开发板时钟体系介绍 :
参考手册 : S3C6410X.pdf , 章节 : 3.3.4.1 Clock selection between PLLs and input reference clock , Page 124;
6410 开发板时钟体系介绍 :
参考手册 : S5PV210_UM_REV1.1.pdf , 章节 : 3.4 CLOCK GENERATION , Page 361;
S5PV210 开发板时钟体系介绍 :
CPU 上电后 从 低频率 到 高频率的变化过程 :
整个时钟初始化流程是需要软件帮助进行的, 因此这些步骤需要开发者进行开发, 这也是该博客的主要内容;
时钟初始化流程 :
参考手册 : ARM芯片 手册 S3C6410X.pdf ( 基于 6410 开发板 ARM 11 )
配置 Lock Time :
设置分频系数 :
设置分频系数代码编写 :
#define CLK_DIV0 0x7E00F020
;#define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) )
;ldr r0, =CLK_DIV0
;ldr r1, =CLK_VAL
;str r1, [r0]
, 将 r1 寄存器中的内容 存储到 r0 存储的地址 指向的内存中 ;#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
设置 CPU 异步工作模式 :
汇编代码编写 :
#define OTHERS 0x7E00F900
;ldr r0, =OTHERS
;ldr r1, [r0]
, 即 将 r0 寄存器存储的地址指向的内存中的值 装载到 r1 通用寄存器中;bic r1, r1, #0xc0
;str r1, [r0]
, 将 r1 通用寄存器中的值 存储到 r0 寄存器中保存的地址指向的内存中, 即 OTHERS 寄存器;#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
设置 APLL 和 MPLL 时钟频率 :
汇编代码编写 :
#define APLL_CON 0x7E00F00C
;#define MPLL_CON 0x7E00F010
;ldr r0, =APLL_CON
ldr r1, =PLL_VAL
str r1, [r0]
ldr r0, =MPLL_CON
ldr r1, =PLL_VAL
str r1, [r0]
#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
时钟源设置 :
汇编代码编写 :
#define CLK_SRC 0x7E00F01C
;ldr r0, =CLK_SRC
mov r1, #0x3
str r1, [r0]
代码示例 : 截止到当前的代码示例 ① 设置 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
u-boot.lds 链接器脚本 代码解析 :
OUTPUT_ARCH(架构名称)
指明***输出格式, 即处理器的架构***, 这里是 arm 架构的, OUTPUT_ARCH(arm)
;ENTRY(入口位置)
, 在上面的 Start.S 中设置的程序入口是 _start
, 代码为 ENTRY(_start)
;.text :
设置代码段;.data :
设置数据段;.bss :
设置 BSS 段;
bss_start = .;
;bss_end = .;
;. = ALIGN(4);
设置四字节对齐即可;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 段结束位置*/
}
makefile 文件编写 :
%.o : %.S
, 产生过程是 arm-linux-gcc -g -c $^
, 其中 ^
标识是所有的依赖文件, 在该规则下 start.S 会被变异成 start.o ;%.o : %.c
, 产生过程是 arm-linux-gcc -g -c $^
;all:
设置最终编译目标;
all: start.o
表示最终目标需要依赖该文件;arm-linux-ld -Tu-boot.lds -o u-boot.elf $^
, 需要使用链接器脚本进行连接, ①链接工具是 arm-linux-ld 工具, ②使用 -Tu-boot.lds
设置链接器脚本 是刚写的 u-boot.lds 链接器脚本, ③输出文件是 u-boot.elf 这是个中间文件, ④ 依赖文件是 $^
代表所有的依赖;arm-linux-objcopy -O binary u-boot.elf u-boot.bin
, 使用 -O binary
设置输出二进制文件, 依赖文件是 u-boot.elf
, 输出的可执行二进制文件 即 结果是 u-boot.bin
;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
编译过程 :
make
;OK6410 开发板启动切换方式 : 通过控制 开发板右侧的 8个开关来设置启动来源;
制作 SD 卡启动盘 :
参考资料 : OK6410烧写裸板程序方法
这是之前写过的博客, 仅作为参考;
SecureCRT 连接开发板并烧写程序 步骤 :
烧写程序并运行 :