ARM 实操 流水灯 按键控制 day53

发布于:2025-08-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

五:异常处理

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;
}

网站公告

今日签到

点亮在社区的每一天
去签到