中断是ARM处理器提高工作效率的方法之一。
Ⅰ、形象理解中断:
假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。母亲怎么才能知道小孩醒?
过一会打开一次房门,看婴儿是否睡醒,让后接着看书
一直等到婴儿发出声音以后再过去查看,期间都在读书
查询
while(1)
{
1 read book(读书)
2 open door(开门)
if(睡)
return(read book)
else
照顾小孩
}
中断
while(1)
{
read book
中断服务程序()//声音中断
{
处理照顾小孩
}
}
两种方式对比:很明显,第一种会累死妈妈滴。
这种是比较形象化的比喻,真正在ARM处理器中是如何处理的呢?
有数据(孩子声音)
保护现场(放下手头工作)
调用中断函数(打开房门查看)
处理数据(照顾孩子)
恢复现场(继续工作)
这里存在几个问题:
ARM处理是怎样知道有数据的,谁来告诉它
如何保存正在运行的程序
在哪里调用中断函数
如何恢复程序
问题解决:
中断控制器,通过设置中断控制器,监听中断源,例如串口的数据接收中断源
保存寄存器,例如程序返回寄存器,后面通过代码比较好懂
硬件规定异常向量地址,软件设置异常向量表,发生中断时,跳转到处理函数
恢复寄存器
Ⅱ、为了更好、更安全的运行程序,ARM分出了多种工作模式:
用户模式(usr),也称普通模式
系统模式(sys),也称兴奋模式
未定义模式(und),指令不能识别
管理模式(svc)
终止模式(abt)
中断模式
快速中断模式
Ⅲ、再论保护现场
在这些模式下都有一堆自己一堆寄存器,进入其他模式之前都会先保存r0到r14的寄存器到栈中,加粗的代表是这个模式下的专属寄存器。
Ⅳ、中断模式与快速中断模式的区别
ARM有irq和fiq中断通道,这两个通道可以通过CPSR寄存器I位和F位设置使能。
当触发irq中断时,如果此时再来一个irq,此时ARM是不会鸟你的。ARM核心就像老板,老板本来在做事,然后来了一个客户,秘书打断它,让客户进去。而此时再来一个客户,要么秘书不断去敲门问,要么客户走人。老板第一个客户没有会见完,不会理你。
但是有一种情况例外,当ARM处在IRQ模式,这个时候fiq pin来了一个中断信号,fiq pin是什么?快速中断,好比公安局的来查刑事案件,才不管老板是不是在会见客户,直接打断,进入到fiq模式,跳到相应的fiq的异常向量表处去执行代码。那如果当ARM处理FIQ模式,fiq pin又来中断信号,也就是又一批公安来了,那没戏,都是执法人员,你打不断我。如果此时irq pin来了呢?来了也不理,正在办案,还敢来妨碍公务。
所以得出一个结论: IRQ模式只能被FIQ模式打断,FIQ模式下谁也打不断。
中断概念介绍到此,下面将结合三步骤和前面提到的几个问题做详细编程!!!
Ⅴ、芯片手册
由于中断是没有线连接的,所以略过原理图。
1、程序状态寄存器(PSR)
前面提到CPU可以工作在多种模式,CPU也可以处理中断,这些都是可以在CPSR中设置的。
看一下PSR可以设置CPU哪些东西:
T位为1,表示CPU工作于Thumb指令集(一条指令16位)
F位为1,表示CPU不接收任何FIQ中断请求
I位为1,表示CPU不接收任何IRQ中断请求
中间保留位
V是CPU的计算溢出位
C是CPU的计算进位
Z是CPU的计算0位
N是CPU的计算正位,逻辑运算
可以通过设置低4位让CPU进入不同模式,不过处于用户模式是无法设置这4个位的
为什么有CPSR和SPSR?
SPSR是保存程序状态寄存器,CPSR是当前程序状态寄存器,当发生异常时或者中断时,SPSR会保存CPSR的值。
ARM920t给出异常进入和退出的操作,基本上前面都提到了。
2、中断控制器
上面是CPU的中断处理,但是我们还有很多的外设啊,比如按键中断,这些需要中断控制器来完成。
图为中断的流程:最终进入CPU处理的中断需要经过多个屏蔽寄存器的筛选,这些寄存器就是“小秘书”
3、中断向量表
按键中断后,中断处理函数如何调用(中断向量表)
4、外设中断
这里以按键为例子:按键接在GPF0处,只要配置GPF0为中断引脚
除此之外,按键还要配置上升沿触发还是下降沿触发
设置完成后要使能中断吧,对应中断使能即可,这是中断第一屏蔽关
Ⅵ、中断编程
这里以按键中断作为例子,按键触发中断,点亮LED灯。
前面提到了这么多,编程是否有关呢?
答案是确定的,可以想象一下中断的过程:
按键触发中断(中断引脚)
经过屏蔽寄存器(不屏蔽)
进入模式选择(快中断、普通中断)
CPU总中断
编程需要解决的问题:
按键中断后,中断处理函数如何调用(中断向量表)
如何得知是对应按键触发中断的(中断源寄存器)
中断之后在哪里保存现场,又是如何恢复现场(保存寄存器)
设置中断向量表
这些地址都是硬件规定的,编程只需要在对应位置上放上跳转指令即可
_start:
b reset /* vector 0 : reset */
ldr pc, und_addr /* vector 4 : und */
ldr pc, swi_addr /* vector 8 : swi */
b halt /* vector 0x0c : prefetch aboot */
b halt /* vector 0x10 : data abort */
b halt /* vector 0x14 : reserved */
ldr pc, irq_addr /* vector 0x18 : irq */
b halt /* vector 0x1c : fiq */
und_addr:
.word do_und
swi_addr:
.word do_swi
irq_addr:
.word do_irq
设置按键中断源、中断控制器屏蔽中断源
/* 配置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被配置为中断引脚 */
/* 设置中断触发方式: 双边沿触发 */
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));
保护现场,恢复现场
do_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是异常处理完后的返回地址, 也要保存 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
/* 处理irq异常 */
bl handle_irq_c
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr_irq的值恢复到cpsr里 */
中断处理函数
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< 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; }
设计资源 培训 开发板 精华推荐
- 使用 TC7116 模数转换器获得 2V 满量程推荐组件值的典型应用
- MIKROE-3822,基于 PIC16F1779 8 位 MCU 和 SFH 7060 心率和脉搏血氧监测传感器的心率 9 点击板
- 使用 Analog Devices 的 LT124XCS8 的参考设计
- 基于STM32F103C8T6的单片机学习板+835834A
- 使用 NXP Semiconductors 的 TFA9879 的参考设计
- 使用 ACPL-339J 的 SiC MOSFET 栅极驱动光耦合器参考设计
- 使用 Infineon Technologies AG 的 OM7640SM 的参考设计
- LTM4609 的典型应用 - 36VIN、34VOUT 高效降压-升压型 DC/DC 模块稳压器
- LT3007ETS8-5 低压差线性稳压器的典型应用电路,用于最后喘息电路
- ADL5902-EVALZ,基于 ADL5902 50 MHz 至 9 GHz、65 dB TruPwr 检测器的评估板