五:异常处理
ARM 有两级外部中断 FIQ,IRQ.
可是大多数的基于ARM 的系统有有>2个的中断源,因此需要一个中断控制器
ps
:通常中断处理程序总是应该包含清除中断源的代码
为什么:
外设或事件(比如定时器溢出、GPIO 变化、串口接收)会设置一个中断挂起标志位(pending flag)。
CPU 检测到这个标志后,进入中断服务程序(ISR)。如果这个标志位不被清除,那么:ISR 执行结束时,CPU 发现中断源还在挂起 → 马上再次进入 ISR。程序就可能死在中断里,永远不回到主程序。
其中保存返回地址到LR ,软中断pc会默认跳到8
看图的右边偏移量对应就是每种异常pc默认回到的地址
返回时,需要手动:恢复spsr
一:异常规范函数
preserve8
area reset, code, readonly
code32
entry
;这里就是异常向量表 预设
b start ; reset
nop ; undef
b deal_swi ; swi
nop ; prefetch abort
nop ; data abort
nop ; reserved
nop ; irq
nop ; fiq
deal_swi ;预设
stmfd sp!, {r4-r12, lr}
sub r0,lr,#4 ;获取软中断的地址,是中断返回下一个 - 4
ldr r1,[r0] ;把r0内存空间的数据放入r1,[]类似于*运算,看地址拿数据,这样swi #5,的机器码5就能取出来
bic rl, r1,#(0xff << 24) ;清高8位
ldmfd sp!, {r4-r12, pc}^ ;^异常处理专用的带模式切换
start
ldr sp, =0x40001000
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40000C00
swi #5 ;软中断 范围:0~0xffffff 6个f
mov r0, #1
mov r1, #2
import c_add
bl c_add
nop
b start
export asm_add
asm_add
stmfd sp!, {r4-r12, lr}
add r0, r0, r1
ldmfd sp!, {r4-r12, pc}
end
1.1实现switch
,借助C程序
void c_deal_swi(unsigned char swi_num)
{
switch(swi_num)
{
case 1: break;
case 5:break;
default:break;
}
}
deal_swi
stmfd sp!, {r4-r12, lr}
sub r0,lr,#4
ldr r1,[r0]
bic r0, r1,#(0xff << 24) ;寄存器一开始放在r0
import c_deal_swi
bl c_deal_swi
ldmfd sp!, {r4-r12, pc}^
六:输入/输出
一:端口控制描述
端口配置寄存器(GPACON至GPJCON)/ 配直引脚功能
S3C2440A 中,大多数端白为复用引脚。因此要决定每个引脚选择哪项功能。PnCON(引脚控制寄存器)决定了每个引脚使用哪项功能。如果在掉电模式中PE0至PE7用于唤醒信号,这些端口必须配置为输入模式。
端口数据寄存器(GPADAT至GPJDAT)/读(采集)写(控制)数据(电平)
如果端口配置为输出端口,可以写入数据到PnDAT的相应位。如果端口配置为输入端口,可以从PnDAT的相应位读取数据。
二:点亮LED1
//C编程给地址赋值
int a = 10; // a 的地址假设为 0x1000
int *p = &a; // p 的值 = 0x1000
unsigned int b = (unsigned int)p; // b = 0x1000(p 的地址值被复制给 b)
*(unsigned int*)b = 200; // 通过 b 修改 0x1000 地址的值
preserve8
area reset, code, readonly
code32
entry
b start ; reset
nop ; undef
b deal_swi ; swi
nop ; prefetch abort
nop ; data abort
nop ; reserved
nop ; irq
nop ; fiq
deal_swi
stmfd sp!, {r4-r12, lr} ;保护现场
sub r0, lr, #4
ldr r1, [r0]
bic r0, r1, #(0xff << 24)
import c_deal_swi
bl c_deal_swi
ldmfd sp!, {r4-r12, pc}^ ;恢复现场
start
ldr sp, =0x40001000 ;初始化SVC模式的栈
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x10
msr cpsr_c, r0 ;设置工作模式位user
ldr sp, =0x40000C00 ;初始化user模式的栈
;swi #5 ;软中断指令
; 函数的调用规则
; 前四个参数使用r0-r3传递,剩余参数使用栈传递
; 返回值存放在r0中
mov r0, #1
mov r1, #2
import main ;声明一个外部符号供本文件使用
bl main ;带链接返回的跳转 自动保存返回地址到lr
nop ;空转1个机器周期
b start
end
#define GPBCON (*(volatile unsigned long * )0x56000010UL)
#define GPBDAT (*(volatile unsigned long * )0x56000014UL)
int main(void)
{
// 1. 配置GPB5为输出模式(假设bit10-11控制GPB5)
GPBCON = (GPBCON & ~(0x03 << 10)) | (0x01 << 10);
// 2. 持续输出低电平(仅影响GPB5)
while(1)
{
GPBDAT &= ~(1 << 5); // GPB5=0
// 如果需要高电平:GPBDAT |= (1 << 5);
}
return 0;
}
一:volatile
易失性修饰符(写透)
每次读取值的时候,都操作的是内存,
这样就会在缓冲区操作后,立马写入内存的值,写在内存
二:移位
配置1bit一步完成配置多个bit两步完成先清0再置1配置GPB5引脚功能为输出
三:代码
#ifndef __LED_H
#define __LED_H
#define GPBCON (*(volatile unsigned long * )0x56000010UL)
#define GPBDAT (*(volatile unsigned long * )0x56000014UL)
void led_init(void);
void led_on(unsigned int num);
void led_off(unsigned int num);
void Delay_ms(unsigned int num);
#endif
#include "led.h"
void Delay_ms(unsigned int num)
{
unsigned char i, j;
while(num--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void led_init(void)
{
GPBCON = (GPBCON & ~(0x03 << 10)) | (0x01 << 10);
GPBCON = (GPBCON & ~(0x03 << 12)) | (0x01 << 12);
GPBCON = (GPBCON & ~(0x03 << 14)) | (0x01 << 14);
GPBCON = (GPBCON & ~(0x03 << 16)) | (0x01 << 16);
GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
}
void led_on(unsigned int num)
{
GPBDAT &= ~(1 << (num+4));
}
void led_off(unsigned int num)
{
GPBDAT |= (1 << (num+4)); //¹ØµÆ
}
#include "led.h"
int main(void)
{
unsigned char i;
led_init();
while(1)
{
for(i=1; i<=4; i++)
{
led_on(i); // µãÁÁµ±Ç°LED
Delay_ms(200); // ±£³ÖµãÁÁ
led_off(i); // ¹Ø±Õµ±Ç°LED
}
}
return 0;
}
三:按键k1控制LED1
#ifndef __KEY1_H
#define __KEY1_H
#define GPGCON (*(volatile unsigned long * )0x56000060UL)
#define GPGDAT (*(volatile unsigned long * )0x56000064UL)
void key_init(void);
unsigned char key_on(void);
#endif
#include "key1.h"
void key_init(void)
{
GPGCON = GPGCON & ~0x03;
}
unsigned char key_on(void)
{
return (GPGDAT & 0x01) ? 0 : 1;
}
int main(void)
{
led_init();
key_init();
while(1)
{
if(1 == key_on())
{
led_on(1);
}else
{
led_off(1);
}
}
return 0;
}