【嵌入式开发】ARM 关闭中断 ( CPRS 中断控制位 | 中断使能寄存器 | 中断屏蔽寄存器 | 关闭中断 | 汇编代码编写 )
本博客的参考文章及相关资料下载 :
- 1.本博客代码及参考手册下载 : https://download.csdn.net/download/han1202012/10412045
一. 中断控制 ( 基于 S3C6410 开发板 )
本节 基于 S3C 6410 开发板 , 不同的开发板 以及 不同 的芯片 中断控制机制是不同的 ;
1. 关闭中断的两个步骤
(1) 关闭中断步骤
关闭中断步骤 :
- 1.步骤 1 ( 程序状态字寄存器设置 ) : 设置 程序状态字寄存器 ( CPSR ) 中对应的与 中断 相关的位 ;
- 2.步骤 2 ( 中断屏蔽寄存器设置 ) : 设置 中断屏蔽寄存器 , 该寄存器的 使用方法在开发板芯片资料中 有详细介绍;
2. CPRS 寄存器中的中断控制位
(1) CPRS 寄存器简介
程序状态字寄存器 :
- 1.寄存器内容 : 该寄存器 中 包含 ① 状态码标志位 , ② 中断标志位 , ③ 当前处理器工作模式 和 其它一些 ④ 状态 与 ⑤ 控制信息 ;
- 2.CPSR 寄存器 : 全称 Current Program Status Register ( 当前程序状态字寄存器 ), 保存的是当前的程序状态 ;
- 3.SPSR 寄存器 : 全称 Saved Program Status Register ( 程序状态保存寄存器 ), 每个异常都有对应的独立的 SPSR 寄存器 , 当异常发生的时候, 先将 CPSR 寄存器中的值 保存到 SPSR 寄存器中 , 以便 异常处理完毕后 再回到原来断点处 继续运行 ;
-
4.SPSR 寄存器分布 :
用户模式 和 系统模式 没有 对应的 SPSR 寄存器
,
只有 5 种 异常模式才有对应的 SPSR 寄存器
;
- ( 1 ) SPSR 寄存器读写 : 在 用户模式 或 系统模式 读写 SPSR 指令 会出现不可预测的错误或行为 ;
(2) CPRS 寄存器 中断控制 相关 位
CPRS 寄存器中断控制相关位 :
- 1.普通中断控制位 : I 位, 第 [ 7 ] 位 , 如果设置了该位 可以 关闭 普通中断 ;
- 2.快速中断控制位 : F 位, 第 [ 6 ] 位 , 如果设置了该位, 可以 关闭 快速中断 ;
3. 中断使能寄存器
参考手册 : S3C6410X.pdf ( 基于 6410 开发板 )
- 1.手册对应章节 : 12.6.5 章节 VECTORED INTERRUPT CONTROLLERS ;
- 2.S3C6410芯片手册下载地址 : https://download.csdn.net/download/han1202012/10412045
(1) 中断使能寄存器简介
中断使能寄存器 ( VICINTENABLE ) :
-
1.分为两个寄存器 :
中断使能寄存器
分 两个寄存器
控制所有的中断打开功能;
- ( 1 ) 寄存器1 VIC0INTENABLE : 其地址是 0x71200010 ;
- ( 2 ) 寄存器2 VIC1INTENABLE : 其地址是 0x71300010 ;
- 2.寄存器位 : 这两个寄存器中, 每一位都对应着一种中断源 ;
- 3.中断关闭情况 : 在 重启的时候, 所有的中断都要被禁用 ;
-
4.寄存器功能 :
激活中断的请求线路, 允许中断信号能到达 处理器
; 查看对应位的设置, 可以获取某种类型的中断是否可以到达 处理器 ;
- ( 1 ) 读取到 0 值 : 如果读取到的值 为 0, 那么 中断不可用 ;
- ( 1 ) 读取到 1 值 : 如果读取到的值 为 1, 那么 中断可用 ;
-
5.设置寄存器值 :
这两个寄存器
只能设置中断可以使用, 无法设置 屏蔽 中断
, 如果设置 1 那么激活中断, 设置 0 没有任何效果, 如果要屏蔽中断, 那么需要使用 中断屏蔽寄存器 VICINTENCLEAR;
- ( 1 ) 设置 0 值 : 设置 0 值 没有任何效果 ;
- ( 2 ) 设置 1 值 : 设置 1 值 激活中断功能 ;
(2) 中断屏蔽寄存器简介
中断屏蔽寄存器 ( VICINTENCLEAR ) 简介 :
-
1.分为两个寄存器 :
中断 屏蔽 寄存器
分 两个寄存器
控制所有的中断 屏蔽 的功能;
- ( 1 ) 寄存器1 VIC0INTENABLE : 其地址是 0x71200010 ;
- ( 2 ) 寄存器2 VIC1INTENABLE : 其地址是 0x71300010 ;
- 2.寄存器位 : 这两个寄存器中, 每一位都对应着一种中断源 ;
-
3.功能概述 :
清除 中断屏蔽寄存器 ( VICINTENABLE ) 中 对应的位就可以屏蔽对应的中断
;
- ( 1 ) 设置 0 值 : 没有任何效果 ;
- ( 2 ) 设置 1 值 : 屏蔽对应的中断源 ;
二. 关闭中断 代码示例
参考手册 : S3C6410X.pdf ( 基于 6410 开发板 )
- 1.手册对应章节 : 12.6.5 章节 VECTORED INTERRUPT CONTROLLERS ;
- 2.S3C6410芯片手册下载地址 : https://download.csdn.net/download/han1202012/10412045
1. 汇编代码编写
(1) 设置 CPRS 程序状态字寄存器
CPRS 设置 值 分析 : 该寄存器需要考虑两个方面, ① 设置处理器的 SVC 工作模式 , ② 关闭中断 ;
- 1.SVC 模式设置 : SVC 模式需要将 CPRS 的 M [ 4 : 0 ] 位 设置为 指定的 0b10011 值 ;
- 2.普通中断设置 : 关闭 普通中断, 需要将 CPRS 的 I [ 7 ] 位 设置为 1 ;
- 3.快速中断设置 : 关闭 快速中断, 需要将 CPRS 的 F [ 6 ] 位 设置为 1 ;
- 4.最后向 CPRS 寄存器设置的值 : 最终值为 0b11010011 , 其中 0 ~ 5 位 的 0b10011 设置的是 SVC 工作模式, 0b11000000 设置 I 和 F 位 为 1, 这样 同时设置了 处理器的 SVC 工作模式 和 关闭了 普通中断 和 快速中断 ;
(2) 设置 CPRS 程序状态字寄存器 代码逻辑分析
代码 逻辑 分析 : 之前 设置 处理器工作模式时 有涉及到 CPRS 寄存器设置 , 在这里将关闭中断的操作也一并设置了 ;
- 1.设置 CPRS 寄存器 时机 : 进行 处理器工作模式 设置 是在 开发板上电后, 对应的 reset 异常向量处 ;
-
2.设置 指令标号 :
设置一个指令标号, 在标号下定义一组汇编指令,
当需要执行这一组指令的时候, 在跳转到该标号即可
;
-
( 1 ) 定义标号 :
set_svc :
, 在标号下定义一组汇编指令;
-
( 1 ) 定义标号 :
-
3.导出 CPSR 寄存器值 :
使用 MRS 指令, 即
mrs r0 cpsr
将 CPSR 寄存器中的值导出到 R0 寄存器中; -
4.将 R0 中的 M 位 清 0 :
在 R0 中将从 CPSR 中导出的寄存器值 对应的 0 ~ 4 位 清0, 使用
bic r0, r0, #0x1f
, 将 R0 寄存器的值 与 #0x1f 进行 与操作, 即 后5 位都设置成0, 然后将 与 操作的结果保存到 R0 寄存器中 ; -
5.将 R0 中的 M 位 设置 模式代码 :
在下图中,
svc 的模式代码时 0b10011 ( 二进制 ), 即 0x13 ( 十六进制 )
,
同时关闭 普通中断 和 快速中断, 最终设置值为 0b11010011, 即 0xd3
, 使用
orr r0, r0, #0xd3
语句设置, 将 R0 寄存器中的值 与 0x13 进行 或操作, 将 或操作的结果 存放到 R0 寄存器中 ;
-
6.将值写回 CPSR 寄存器 :
使用 MSR 指令
msr cpsr, r0
, 将处理完的 CPSR 寄存器值 设置给 CPSR 寄存器;
(3) 设置 中断屏蔽寄存器 代码逻辑分析
设置 中断屏蔽 寄存器 汇编代码分析 :
- 1.设置 中断屏蔽 寄存器 时机 : 进行 关闭中断 设置 是在 开发板上电后, 对应的 reset 异常向量处 , 因此 在 reset 处 执行相关的 关闭 中断 的代码 ;
-
2.设置 指令标号 :
设置一个指令标号, 在标号下定义一组汇编指令,
当需要执行这一组指令的时候, 在跳转到该标号即可
, 代码
disable_interrupt :
, 然后 在 reset 处, 跳转到 该标号处执行 ,bl disable_interrupt
; -
3.准备 所有位都是 1 数据 :
mvn r1, #0x0
, 将 0 按位取反 设置到 r1 通用寄存器中 ;-
( 1 ) 取反指令 :
MVN 指令, 语法
MVN{条件}{S} <dest>, <op 1>
, 将 操作数 1 的值先按位取反, 在将值设置到 dest 寄存器中 ; - ( 2 ) 指令说明 : dest 必须是寄存器 , 操作数 1 ( op 1 ) 可以是 寄存器, 被移位的寄存器 或 立即数 ;
-
( 1 ) 取反指令 :
MVN 指令, 语法
-
4.设置第一个 中断屏蔽 寄存器 :
① 先将 寄存器 地址装载到 通用寄存器中
, 代码
ldr r0, =0x71200014
, ② 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中 , 代码str r1,[r0]
;-
( 1 ) ldr 指令语法 :
LDR{条件} Rd, <地址>
, 将内存中的数据装载到 寄存器 中, Rd 必须是通用寄存器, -
( 2 ) str 指令语法 :
STR{条件} Rd, <地址>
, 将寄存器中的数据 装载 到内存中; 将 Rd 寄存器的内容 装载到地址中; -
( 3 ) ldr 指令注意点 :
区分
ldr r0, =0x71200014
和ldr r0, 0x71200014
, 前者是将 0x71200014 数值装载到寄存器中 , 后者是将 0x71200014 地址中的内容装载到 r0 寄存器中 ;
-
( 1 ) ldr 指令语法 :
-
5.设置第二个 中断屏蔽寄存器 :
① 先将 寄存器 地址装载到 通用寄存器中
, 代码
ldr r0, =0x71300014
, ② 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中 , 代码str r1,[r0]
;-
( 1 ) ldr 指令语法 :
LDR{条件} Rd, <地址>
, 将内存中的数据装载到 寄存器 中, Rd 必须是通用寄存器, -
( 2 ) str 指令语法 :
STR{条件} Rd, <地址>
, 将寄存器中的数据 装载 到内存中; 将 Rd 寄存器的内容 装载到地址中; -
( 3 ) ldr 指令注意点 :
区分
ldr r0, =0x71200014
和ldr r0, 0x71200014
, 前者是将 0x71300014 数值装载到寄存器中 , 后者是将 0x71300014 地址中的内容装载到 r0 寄存器中 ;
-
( 1 ) ldr 指令语法 :
(4) 完整汇编代码示例
汇编代码示例 : Bootloader 流程 : ① 初始化异常向量表 , ② 设置 svc 模式 , ③ 关闭看门狗, ④ 关闭中断 ;
@****************************
@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 地址存放要执行的内容
software_interrupt: @ software_interrupt 地址存放要执行的内容
prefetch_abort: @ prefetch_abort 地址存放要执行的内容
data_abort: @ data_abort 地址存放要执行的内容
not_used: @ not_used 地址存放要执行的内容
irq: @ irq 地址存放要执行的内容
fiq: @ fiq 地址存放要执行的内容
reset: @ reset 地址存放要执行的内容
bl set_svc @ 跳转到 set_svc 标号处执行
bl disable_watchdog @ 跳转到 disable_watchdog 标号执行, 关闭看门狗
bl disable_interrupt @ 跳转到 disable_interrupt 标号执行, 关闭中断
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 寄存器中
#define pWTCON 0x7e004000 @ 定义看门狗控制寄存器 地址 ( 6410开发板 )
disable_watchdog:
ldr r0, =pWTCON @ 先将控制寄存器地址保存到通用寄存器中
mov r1, #0x0 @ 准备一个 0 值, 看门狗控制寄存器都设置为0 , 即看门狗也关闭了
str r1, [r0] @ 将 0 值 设置到 看门狗控制寄存器中
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 通用寄存器中
2. 链接器脚本
gboot.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 段结束位置*/
}
3. 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 -Tgboot.lds -o gboot.elf ^, 需要使用链接器脚本进行连接, ①链接工具是 arm-linux-ld 工具, ②使用 -Tgboot.lds 设置链接器脚本 是刚写的 gboot.lds 链接器脚本, ③输出文件是 gboot.elf 这是个中间文件, ④ 依赖文件是 ^ 代表所有的依赖; ( 3 ) 转换成可执行二进制文件 : arm-linux-objcopy -O binary gboot.elf gboot.bin, 使用 -O binary 设置输出二进制文件, 依赖文件是 gboot.elf, 输出的可执行二进制文件 即 结果是 gboot.bin ;
- 4.makefile 文件内容 :
all: start.o #依赖于 start.o
arm-linux-ld -Tgboot.lds -o gboot.elf $^ #使用链接器脚本, 将 start.o 转为 gboot.elf
arm-linux-objcopy -O binary gboot.elf gboot.bin #将 gboot.elf 转化为可以直接在板子上执行的 gboot.bin 文件
%.o : %.S #通用规则, 如 start.o 是由 start.S 编译来的, -c 是只编译不链接
arm-linux-gcc -g -c $^