[Linux]异常与中断
[Linux]异常与中断
艾恩凝
2021/4/1
INTRODUCTION
这个部分手册内容也不是很多,大体看了看,主要分为两种模式一种是ARM模式另一种是thumb,对于现在的硬件水平来说thumb可以直接考虑不看,毕竟现在不是内存不够用的时代,能跑linux的性能不会太差。arm4字节,thumb2字节。
关于异常和中断,说白了就是异常,中断也算是异常的一种,就像你在路上正常走着,突然来了个姑娘,你的目光从看路转向了姑娘,这个时候就是异常,或者中断,当处理完了,也就是看完了姑娘之后,你又继续走路,这就是一个完美的中断,程序正常运行来了个突发事故,先处理这个,然后接着继续正常的程序。
详细介绍
上图介绍了这芯片异常的几种模式,用户模式,系统模式,未定义,管理模式,终止模式,中断模式,快中断模式。
上图是对应的寄存器,黑色三角形的是另外单独的寄存器。
这个图则说明了程序状态寄存器的格式,总共32位,中间是保留位,其他都注明了每一位代表的意思。
这个图对应了上一张图后5位,代表了7种不同的模式
上面对应了各种模式地址。
裸板中断LED
1/* 执行到这里之前:
2 * 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址
3 * 2. SPSR_irq保存有被中断模式的CPSR
4 * 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
5 * 4. 跳到0x18的地方执行程序
6 */
7
8 /* sp_irq未设置, 先设置它 */
9 ldr sp, =0x33d00000
10
11 /* 保存现场 */
12 /* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
13 /* lr-4是异常处理完后的返回地址, 也要保存 */
14 sub lr, lr, #4
15 stmdb sp!, {r0-r12, lr}
16
17 /* 处理irq异常 */
18 bl handle_irq_c
19
20 /* 恢复现场 */
21 ldmia sp!, {r0-r12, pc}^ /* ^会把spsr_irq的值恢复到cpsr里 */
中断汇编代码
1
2/* SRCPND 用来显示哪个中断产生了, 需要清除对应位
3 * bit0-eint0
4 * bit2-eint2
5 * bit5-eint8_23
6 */
7
8/* INTMSK 用来屏蔽中断, 1-masked
9 * bit0-eint0
10 * bit2-eint2
11 * bit5-eint8_23
12 */
13
14/* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位
15 * bit0-eint0
16 * bit2-eint2
17 * bit5-eint8_23
18 */
19
20/* INTOFFSET : 用来显示INTPND中哪一位被设置为1
21 */
22
23/* 初始化中断控制器 */
24void interrupt_init(void)
25{
26 INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
27}
28
29/* 初始化按键, 设为中断源 */
30void key_eint_init(void)
31{
32 /* 配置GPIO为中断引脚 */
33 GPFCON &= ~((3<<0) | (3<<4));
34 GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置为中断引脚 */
35
36 GPGCON &= ~((3<<6) | (3<<22));
37 GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置为中断引脚 */
38
39
40 /* 设置中断触发方式: 双边沿触发 */
41 EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */
42 EXTINT1 |= (7<<12); /* S4 */
43 EXTINT2 |= (7<<12); /* S5 */
44
45 /* 设置EINTMASK使能eint11,19 */
46 EINTMASK &= ~((1<<11) | (1<<19));
47}
48
49/* 读EINTPEND分辨率哪个EINT产生(eint4~23)
50 * 清除中断时, 写EINTPEND的相应位
51 */
52
53
54void key_eint_irq(int irq)
55{
56 unsigned int val = EINTPEND;
57 unsigned int val1 = GPFDAT;
58 unsigned int val2 = GPGDAT;
59
60 if (irq == 0) /* eint0 : s2 控制 D12 */
61 {
62 if (val1 & (1<<0)) /* s2 --> gpf6 */
63 {
64 /* 松开 */
65 GPFDAT |= (1<<6);
66 }
67 else
68 {
69 /* 按下 */
70 GPFDAT &= ~(1<<6);
71 }
72
73 }
74 else if (irq == 2) /* eint2 : s3 控制 D11 */
75 {
76 if (val1 & (1<<2)) /* s3 --> gpf5 */
77 {
78 /* 松开 */
79 GPFDAT |= (1<<5);
80 }
81 else
82 {
83 /* 按下 */
84 GPFDAT &= ~(1<<5);
85 }
86
87 }
88 else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
89 {
90 if (val & (1<<11)) /* eint11 */
91 {
92 if (val2 & (1<<3)) /* s4 --> gpf4 */
93 {
94 /* 松开 */
95 GPFDAT |= (1<<4);
96 }
97 else
98 {
99 /* 按下 */
100 GPFDAT &= ~(1<<4);
101 }
102 }
103 else if (val & (1<<19)) /* eint19 */
104 {
105 if (val2 & (1<<11))
106 {
107 /* 松开 */
108 /* 熄灭所有LED */
109 GPFDAT |= ((1<<4) | (1<<5) | (1<<6));
110 }
111 else
112 {
113 /* 按下: 点亮所有LED */
114 GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));
115 }
116 }
117 }
118
119 EINTPEND = val;
120}
121
122
123void handle_irq_c(void)
124{
125 /* 分辨中断源 */
126 int bit = INTOFFSET;
127
128 /* 调用对应的处理函数 */
129 if (bit == 0 || bit == 2 || bit == 5) /* eint0,2,eint8_23 */
130 {
131 key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */
132 }
133
134 /* 清中断 : 从源头开始清 */
135 SRCPND = (1<<bit);
136 INTPND = (1<<bit);
137}
138
139
CONCLUSION
等着定时器最后熟悉完,裸板就算是基本结束了,正式进入linux
吾心信其可行,
则移山填海之难,
终有成功之日!
——孙文
则移山填海之难,
终有成功之日!
——孙文
评论
0 评论