您尚未登录。

楼主 #1 2018-05-17 09:18:27

xinxiaoci
会员
注册时间: 2018-04-18
已发帖子: 71
积分: 71

中断异常--外部中断配置

《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》



中断框图
1.jpg

Request sources : 中断请求源 发起中断信号
SUBSRCPND :子中断源挂起标记
SUBMASK:子中断屏蔽寄存器  相当于中断开关
SRCPND:中断源挂起标记 有些中断源单独占据一位,有些中断源需要经过子中断源,子中断源汇总后占据SRCPND上的一位。
MASK:中断屏蔽寄存器 功能类似于SUBMASK ,它是总的一个开关
Priority: 优先级配置  暂时不用 保持默认值,与兴趣可以自己研究
INTPND: 发生的中断 通过优先级筛选后的中断

MODE:中断模式,IRQ、FIQ

CPSR: bit7 IRQ 总开关

-----------------------------------------------------
下面介绍 外部中断需要配置的寄存器
GPFCON : IO工作模式配置为中断模式  P292
EXTINT0:配置外部中断的边沿触发方式 P301
EINTMASK:外部中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的SUBMASK  一级开关
INTMSK:中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的MASK 二级开关
CPSR: bit7 打开中断总开关

修改start.S文件 在中断向量表指定的位置添加IRQ入口及对应的现场保存,栈指针设置,中断函数调用,恢复现场,具体可以看我的上一遍文章。

通过这些配置,就可以触发中断。
然后在中断函数中通过分析对应的寄存器,来判断是什么触发的中断,然后再调用特定的函数。

EINTPEND: 外部中断挂起标志,写1清除中断;用于指示哪些外部引脚触发的中断(1个或多个),属于上图中的 SUBSRCPND
SRCPND:中断挂起标志,写1清除中断;用于指示哪些类型的中断(1个或多个);属于上图中的 SRCPND
INTPND:当前发生的中断,写1清除标志,经过中断优先级筛选出来的中断(唯一);用于判断当前发生的是什么中断
INTOFFSET:当前发生中断的偏移,是INTPND对应位的下标,用于快速判断中断源,不用手动清除,清除INTPND时,INTOFFSET也会清除;

下面是代码
----------------------------------------------------------------
start.S

.text
.global _start

_start:
	/* 中断向量表 对应工作模式 参考 P82  */
	b reset  						/* vector 0x00 : reset svc  复位 管理模式*/
	ldr	pc, _undefined_instruction 	/* vector 0x04 : und 无定义指令模式 */
	ldr	pc, _software_interrupt  	/* vector 0x08 : swi svc 软中断 管理模式 */
	b reset 	/* vector 0x0C : abt(prefetch)  终止模式 预取终止 */
	b reset  	/* vector 0x10 : abt(data)  终止模式 数据终止 */
	b reset 	/* vector 0x14 : Reserved 保留 */
	ldr	pc, _irq  	/* vector 0x18 : IRQ 中断模式 */
	b reset 	/* vector 0x1C : FIQ 快速中断模式 */
	
/* 将函数的入口放置在前面,如果不定义默认放置在.text 段的
 * 末尾 ,start.S 汇编后超过4k 在NAND启动时CPU有可能无法
 * 读取到入口
 */
 
_undefined_instruction:	
	.word undefined_instruction	/* 保存入口地址。运行时地址,会跳转到SDRAM执行 */	
_software_interrupt:
	.word software_interrupt
_irq:
	.word irq
	
undefined_instruction:
	/* 执行到这里之前:
	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_und保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
	 * 4. 跳到0x04的地方执行程序 
	 */

	/* sp_und未设置, 先设置它 */
	ldr sp, =0x34000000
	
	/* 保存现场 */
	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr是异常处理完后的返回地址, 也要保存 */
	stmdb sp!, {r0-r12, lr}  
	
	
	/* 处理und异常 */
	mrs r0, cpsr			// 获取程序状态寄存器 传递给 printState
	bl printState 
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
	
software_interrupt:
	/* 执行到这里之前:
	 * 1. lr_swi保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_swi保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为10011, 进入到swi模式
	 * 4. 跳到0x08的地方执行程序 
	 */

	/* sp_swi未设置, 先设置它 */
	ldr sp, =0x33f00000
	
	/* 保存现场 */
	/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr是异常处理完后的返回地址, 也要保存 */
	stmdb sp!, {r0-r12, lr}  
	
	
	/* 处理swi异常 */
	
	/* C函数调用时会用到 r4 - r11 所以在函数调用时会被保存,函
	 * 数返回时恢复,所以我们可以用r4来获取lr的值  参考第8章内
	 * 容 ATPCS 寄存器使用规则 
	 */
	mov r4, lr	// 将lr 返回地址 保存到 r4 	 sub r0, r4, #4 获取触发SWI异常的指令 ,根据指令获取传递进来的软中断号
	
	mrs r0, cpsr			// 获取程序状态寄存器 传递给 printState
	bl printState 
	
	/* 获取软中断号 */
	sub r0, r4, #4
	bl getSWIVal
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
	
irq:
	/* 执行到这里之前:
	 * 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_irq保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
	 * 4. 跳到0x18的地方执行程序 
	 */

	/* sp_irq未设置, 先设置它 */
	ldr sp, =0x33d00000

	/* 保存现场 */
	/* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr-4是异常处理完后的返回地址, 也要保存  参考 P80 */
	sub lr, lr, #4
	stmdb sp!, {r0-r12, lr} 
	
	mrs r0, cpsr			// 获取程序状态寄存器 传递给 printState
	bl printState 
	
	/* 处理irq异常 */
	bl handle_irq_c
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr_irq的值恢复到cpsr里 */

	
	
/* 上面打印字符串长度有可能不是4字节对齐,所以这里要强
 * 制4字节对齐,否则程序可能会出错
 */
 
.align 4
	
reset:
	/* 关闭看门狗 */
	ldr r0,=0x53000000
	ldr r1,=0
	str r1,[r0]
	/* PLL配置---------------------------- */
	/* 设置MPLL,FCLK:HCLK:PCLK=400M:100M:50M */
	
	/* 设置lock time 详见手册P255
	 * LCOKTIME(0x4c000000)=0xFFFFFFFF 
	 */
	ldr r0,=0x4c000000
	ldr r1,=0xFFFFFFFF
	str r1,[r0]
	
	/* 设置HCLK、PCLK 相对于FCLK的分频系数 详见手册P260
	 * CLKDIVN(0x4c000014)=0x05 tFCLK:tHCLK:tPCLK=1:4:8  
	 */
	ldr r0,=0x4c000014
	ldr r1,=0x05
	str r1,[r0]
	
	/* 设置CPU工作于异步模式 
	 * 详见手册P244
	 */
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0
	
	/* 配置MPLLCON 来设置FCLK频率 详见手册P255~256
	 * 设置MPLLCON(0x4c000004)=(92<<12)|(1<<4)|(1<<0) 
	 * m = MDIV+8 = 92+8 =100
	 * p = PDIV+2 =1+2 =3
	 * s = SDIV = 1
	 * FCLK = 2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400MHz
	 */
	ldr r0,=0x4c000004
	ldr r1,=(92<<12)|(1<<4)|(1<<0)
	str r1,[r0]
	
	/* 一旦设置PLL,就会锁定 lock time 直到PLL输出稳定 */
	/* PLL配置--END-------------------------- */
	
	
	/* 设置内存:sp 栈 */
	
	/* 分辨是nor/nand启动
	 * 写0到0地址,再读出来;如果读出来是0,表示
	 * 操作是内部RAM,是nand启动;否则就是nor启动
	 * 因为nor flash 不向内存那样能直接读写
	 */
	mov r1,#0
	ldr r0,[r1]	/* 备份0地址数据 */
	str r1,[r1]	/* 将0写入0地址 */
	ldr r2,[r1]	/* 读取0地址数据到r2 */
	cmp r1,r2	/* 判断r1 r2是否相等 */
	ldr sp,=0x40000000+4096	/* 假设nor启动 */
	moveq sp,#4096	/* 如果r1==r2 nand启动 */
	streq r0,[r1]	/* 恢复原来的值 */
	
	/*  整个程序重定位 */
	bl sdram_init				/* 初始化SDRAM 否则下面无法搬运数据 */
	bl copy2sdram				/* .text .rodata .data 段数据拷贝 */
	bl clean_bss				/* .bss 段清除 */
	
	
	/* 复位之后, cpu处于svc模式
	 * 现在, 切换到usr模式
	 */
	mrs r0, cpsr      /* 读出cpsr */
	bic r0, r0, #0xf  /* 修改M4-M0为0b10000, 进入usr模式 */
	bic r0, r0, #(1<<7)  /* 清除I位, 使能中断  开总中断 */
	msr cpsr, r0

	/* 设置 sp_usr */
	ldr sp, =0x33e00000
	
	
	
	
	bl uart0_init			/* 故障代码中调用了串口打印,所以先初始化串口 */

und_code:
	.word 0xeeadc0de	/*  未定义指令  P86 指令集格式 原代码 0xdeadc0de 有误 参考:http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=20299&highlight=und%D2%EC%B3%A3  */ 
	
	/* 获取当前状态的CPSR */
	mrs r0, cpsr			// 获取程序状态寄存器 传递给 printState
	bl printState			// 打印当前状态信息

	swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */
	
.align 4
	
	/* 调用main函数 */
	ldr pc, =main  /* 绝对跳转, 跳到SDRAM 运行 */

	/* 死循环 */
halt:
	b halt

interrupt.c

#include "interrupt.h"

/* 初始化按键, 设为中断源 */
void key_eint_init(void)
{
	/* 配置GPIO为中断引脚 */
	GPFCON &= ~((3<<0) | (3<<4));
	GPFCON |= ((2<<0) | (2<<4));   /* S2,S3被配置为中断引脚 */

	GPGCON &= ~((3<<6) | (3<<22));
	GPGCON |= ((2<<6) | (2<<22));   /* S4,S5被配置为中断引脚 */
	
	/* 设置GPFCON让GPF4/5/6配置为输出引脚 */
	GPFCON &= ~((3<<8) | (3<<10) | (3<<12));
	GPFCON |=  ((1<<8) | (1<<10) | (1<<12));
	
	/* 设置中断触发方式: 双边沿触发 */
	EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */
	EXTINT1 |= (7<<12);             /* S4 */
	EXTINT2 |= (7<<12);             /* S5 */

	/* 设置EINTMASK使能eint11,19 */
	EINTMASK &= ~((1<<11) | (1<<19));
	
	INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
}

/* 读EINTPEND分辨率哪个EINT产生(eint4~23)
 * 清除中断时, 写EINTPEND的相应位
 */


void key_eint_irq(int irq)
{
	unsigned int val = EINTPEND;
	unsigned int val1 = GPFDAT;
	unsigned int val2 = GPGDAT;
	
	if (irq == 0) /* eint0 : s2 控制 D12 */
	{
		if (val1 & (1<<0)) /* s2 --> gpf6 */
		{
			/* 松开 */
			GPFDAT |= (1<<6);
		}
		else
		{
			/* 按下 */
			GPFDAT &= ~(1<<6);
		}
		
	}
	else if (irq == 2) /* eint2 : s3 控制 D11 */
	{
		if (val1 & (1<<2)) /* s3 --> gpf5 */
		{
			/* 松开 */
			GPFDAT |= (1<<5);
		}
		else
		{
			/* 按下 */
			GPFDAT &= ~(1<<5);
		}
		
	}
	else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
	{
		if (val & (1<<11)) /* eint11 */
		{
			if (val2 & (1<<3)) /* s4 --> gpf4 */
			{
				/* 松开 */
				GPFDAT |= (1<<4);
			}
			else
			{
				/* 按下 */
				GPFDAT &= ~(1<<4);
			}
		}
		else if (val & (1<<19)) /* eint19 */
		{
			if (val2 & (1<<11))
			{
				/* 松开 */
				/* 熄灭所有LED */
				GPFDAT |= ((1<<4) | (1<<5) | (1<<6));
			}
			else
			{
				/* 按下: 点亮所有LED */
				GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));
			}
		}
	}

	EINTPEND = val;
}


void handle_irq_c(void)
{
	/* 分辨中断源 */
	int bit = INTOFFSET;

	/* 调用对应的处理函数 */
	if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,eint8_23 */
	{
		key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */
	}

	/* 清中断 : 从源头开始清 */
	SRCPND = (1<<bit);
	INTPND = (1<<bit);	
}

最近编辑记录 xinxiaoci (2018-05-17 09:20:01)

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn