ARM 实操 流水灯 按键控制 day53

五:异常处理

ARM 有两级外部中断 FIQ,IRQ.

可是大多数的基于ARM 的系统有有>2个的中断源,因此需要一个中断控制器

ps:通常中断处理程序总是应该包含清除中断源的代码

c 复制代码
为什么:
	外设或事件(比如定时器溢出、GPIO 变化、串口接收)会设置一个中断挂起标志位(pending flag)。
	CPU 检测到这个标志后,进入中断服务程序(ISR)。如果这个标志位不被清除,那么:ISR 执行结束时,CPU 发现中断源还在挂起 → 马上再次进入 ISR。程序就可能死在中断里,永远不回到主程序。

其中保存返回地址到LR ,软中断pc会默认跳到8

看图的右边偏移量对应就是每种异常pc默认回到的地址

返回时,需要手动:恢复spsr

一:异常规范函数

assembly 复制代码
	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程序
c 复制代码
void c_deal_swi(unsigned char swi_num)
{
    switch(swi_num)
    {
        case 1: break;
        case 5:break;
        default:break;
    }
}
assembly 复制代码
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 复制代码
//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 地址的值
assembly 复制代码
	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 
c 复制代码
#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引脚功能为输出

三:代码
c 复制代码
#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));		//¹ØµÆ	

}
c 复制代码
#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

c 复制代码
#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;	
}
c 复制代码
int main(void)
{
	led_init();
    key_init();
    while(1)
    {
	  if(1 == key_on())
	  {
	  	led_on(1);
	  }else
	  {
	  	led_off(1);
	  }

    }
    return 0;
}
相关推荐
214实验室1 小时前
STM32串口打印使用printf乱码问题
stm32·单片机·嵌入式硬件
沐欣工作室_lvyiyi1 小时前
基于单片机的电厂烟道粉尘浓度检测系统(论文+源码)
单片机·嵌入式硬件·毕业设计
wanhengidc2 小时前
巨椰 云手机 云游戏稳定运行
运维·服务器·arm开发·游戏·云计算
林政硕(Cohen0415)3 小时前
ARM Qt 字体过小的问题
arm开发·qt
d111111111d4 小时前
什么是内存对齐?在STM32上面如何通过编辑器指令来实现内存对齐。
笔记·stm32·单片机·嵌入式硬件·学习·编辑器
bai5459364 小时前
STM32 CuberIDE 中断
stm32·单片机·嵌入式硬件
小叶子来了啊4 小时前
5Arduino 程序结构
单片机·嵌入式硬件
小叶子来了啊5 小时前
1Arduino 简介
单片机·嵌入式硬件
渡我白衣6 小时前
计算机组成原理(7):定点数的编码表示
汇编·人工智能·嵌入式硬件·网络协议·机器学习·硬件工程
雾岛听风眠6 小时前
电路板维修
单片机·嵌入式硬件