ARM 实操 流水灯 按键控制 day53
五:异常处理
ARM 有两级外部中断 FIQ,IRQ.
可是大多数的基于ARM 的系统有有>2个的中断源,因此需要一个中断控制器
ps
:通常中断处理程序总是应该包含清除中断源的代码
为什么:外设或事件(比如定时器溢出、GPIO 变化、串口接收)会设置一个中断挂起标志位(pending flag)。CPU 检测到这个标志后,进入中断服务程序(ISR)。如果这个标志位不被清除,那么:ISR 执行结束时,CPU 发现中断源还在挂起 → 马上再次进入 ISR。程序就可能死在中断里,永远不回到主程序。
其中保存返回地址到LR ,软中断pc会默认跳到8
看图的右边偏移量对应就是每种异常pc默认回到的地址
返回时,需要手动:恢复spsr
一:异常规范函数
preserve8area reset, code, readonlycode32entry
;这里就是异常向量表 预设b start ; resetnop ; undefb deal_swi ; swinop ; prefetch abortnop ; data abortnop ; reservednop ; irqnop ; fiqdeal_swi ;预设stmfd sp!, {r4-r12, lr} sub r0,lr,#4 ;获取软中断的地址,是中断返回下一个 - 4ldr r1,[r0] ;把r0内存空间的数据放入r1,[]类似于*运算,看地址拿数据,这样swi #5,的机器码5就能取出来bic rl, r1,#(0xff << 24) ;清高8位ldmfd sp!, {r4-r12, pc}^ ;^异常处理专用的带模式切换 startldr sp, =0x40001000 mrs r0, cpsrbic r0, r0, #0x1forr r0, r0, #0x10msr cpsr_c, r0 ldr sp, =0x40000C00 swi #5 ;软中断 范围:0~0xffffff 6个fmov r0, #1mov r1, #2import c_add bl c_add nop b startexport asm_add
asm_addstmfd sp!, {r4-r12, lr} add r0, r0, r1ldmfd 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) ;寄存器一开始放在r0import c_deal_swibl c_deal_swildmfd 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 地址的值
preserve8area reset, code, readonlycode32entryb start ; resetnop ; undefb deal_swi ; swinop ; prefetch abortnop ; data abortnop ; reservednop ; irqnop ; fiqdeal_swistmfd sp!, {r4-r12, lr} ;保护现场sub r0, lr, #4ldr r1, [r0]bic r0, r1, #(0xff << 24)import c_deal_swibl c_deal_swildmfd sp!, {r4-r12, pc}^ ;恢复现场startldr sp, =0x40001000 ;初始化SVC模式的栈mrs r0, cpsrbic r0, r0, #0x1forr r0, r0, #0x10msr cpsr_c, r0 ;设置工作模式位userldr sp, =0x40000C00 ;初始化user模式的栈;swi #5 ;软中断指令; 函数的调用规则; 前四个参数使用r0-r3传递,剩余参数使用栈传递; 返回值存放在r0中mov r0, #1mov r1, #2import main ;声明一个外部符号供本文件使用bl main ;带链接返回的跳转 自动保存返回地址到lr nop ;空转1个机器周期b startend
#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); // µãÁÁµ±Ç°LEDDelay_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;
}