在整个ARM体系结构中,为了处理外部中断,依次学习了MMU,模式跳转,异常,GIC,看门狗程序,这些都是为了处理外部中断
具体如下:
处理外部中断有五个步骤:
30 //step 1: cpu cpsr
31 __asm__ __volatile__(
32 "mrs r0, cpsr
"
33 "bic r0, r0, #0x80
"//设置CPSR的I位,将IRQ位打开
34 "msr cpsr, r0
"
35 ::: "r0"
36 );
37
38 //setp1:GIC
39 ICCICR_CPU0 = 1;
40 ICCPMR_CPU0 = 0xff;
41
前面的代码不解释,和以前的差不多
外部中断号为:64
42 //step2:设置中断号
43 ICDDCR = 1;//总开关
44 ICDIPR16_CPU0 = (0x0 << 0);//ICD一共40个寄存器,第16个寄存器,第0位
45 ICDIPTR16_CPU0 = (0x1 << 0);//优先级,最高
46 ICDISER2_CPU0 = (1 << 0);//一共五个,第2个使能中断
47
第二步为设置中断号:
详细看代码注释:寄存器参考手册第594页
48 //step 3: set gpio
49 GPX3CON = (0xf << 8);//是GPXCON为外部中断功能
查看到使用外部中断功能的对应外部寄存器:EXT_INT43:
51 //step 4: extern
52 EXT_INT43CON = (2 << 8);//设置上升沿触发
53 EXT_INT43MASK = 0;//使能中断
设置外部中断控制器:寄存器参考手册第366页
55 //step 5: source
56 外部中断源这里设置由外部按键K触发:
代码如下
1 #include"regs.h" 2 3 int (*printf)(char *, ...) = 0xc3e114d8; 4 int(*delay)(int)=0xc3e25f90; 5 6 void init_ttb(unsigned long *addr); 7 void enable_mmu(void); 8 unsigned long data_abort_init(); 9 void memcopy(unsigned long* dest,unsigned long* source,int len); 10 void do_irq(); 11 void pwm_on(void); 12 void pwm_off(void); 13 void led_on(void); 14 void led_on(void); 15 16 int main() 17 { 18 *(unsigned long *)0x66000000 = do_irq; 19 20 //发生异常时会进入异常模式跳转到0000 0004地址处理异常事件 21 unsigned long source_addr=data_abort_init(); 22 //异常事件处理函数 23 printf("swi_souce addr is %x ",source_addr); 24 //将异常处理地址的值放到0x60000004 25 memcopy(0x60000000,source_addr,0x1000); 26 27 enable_mmu(); 28 //内存映射将0x00000004映射到0x6000000004 29 30 //step 1: cpu cpsr 31 __asm__ __volatile__( 32 "mrs r0, cpsr " 33 "bic r0, r0, #0x80 "//设置CPSR的I位,将IRQ位打开 34 "msr cpsr, r0 " 35 ::: "r0" 36 ); 37 38 //setp1:GIC 39 ICCICR_CPU0 = 1; 40 ICCPMR_CPU0 = 0xff; 41 42 //step1:设置中断号 43 ICDDCR = 1; 44 ICDIPR16_CPU0 = (0x0 << 0);//ICD第16个寄存器,第0位 45 ICDIPTR16_CPU0 = (0x1 << 0);//优先级,最高 46 ICDISER2_CPU0 = (1 << 0);//一共五个,第2个使能中断 47 48 //step 3: set gpio 49 GPX3CON = (0xf << 8);//是GPXCON为外部中断功能 50 51 //step 4: extern 52 EXT_INT43CON = (2 << 8);//设置上升沿触发 53 EXT_INT43MASK = 0;//使能中断 54 55 //step 5: source 56 // 57 printf("welcome back! "); 58 } 59 60 void pwm_on(void) 61 { 62 GPD0CON &= ~0xffff; 63 GPD0CON |= 0x1;//配置寄存器为2 64 GPD0DAT |= 0x1;//date=0xf 65 } 66 67 void pwm_off(void) 68 { 69 GPD0CON &= ~0xffff; 70 GPD0CON |= 0x0; 71 // GPD0DAT &=0x0 ;//date=0xf 72 73 } 74 void led_off(void) 75 { 76 GPM4CON &= ~0xffff;//清零 77 GPM4CON |= 0x0000;//0---3位清零 78 GPM4DAT |= 0x0;//date=0xf关闭置一 79 } 80 void led_on(void) 81 { 82 GPM4CON &= ~0xffff; 83 GPM4CON |= 0x1111;//配置寄存器3-0-----3-3全为1111,全为输出模式 84 GPM4DAT &= ~0xf;//打开置0-4位为0000 85 } 86 87 void do_irq() 88 { 89 printf("key 1 down "); 90 static int flag=1; 91 if(flag) 92 { 93 printf("wtc_on "); 94 led_on(); 95 pwm_on(); 96 flag=0; 97 } 98 else if(flag == 0) 99 { 100 printf("wtc_off "); 101 led_off(); 102 pwm_off(); 103 flag=1; 104 } 105 EXT_INT43PEND = (1 << 2);//清中断 106 } 107 108 void memcopy(unsigned long* dest, unsigned long* source,int len) 109 { 110 int i=0;; 111 for(i=0;i<len;i++) 112 dest[i]=source[i]; 113 } 114 115 unsigned long data_abort_init() 116 { 117 unsigned long source; 118 __asm__ __volatile__( 119 "ldr %0, =voliate_start " 120 : "=r" (source) 121 ); 122 return source; 123 124 } 125 126 __asm__( 127 128 "voliate_start: " 129 //跳转目录 130 " b reset " 131 " b undefined " 132 " b swi " 133 " b pre_abt " 134 " b data_abt " 135 " .word 0 "//占位符号,一个位占4个字节 136 " b irq " 137 " b fiq " 138 " " 139 140 //跳转要分三部: 141 //1:将PC保存到新模式下的lr中; 142 //2:将CPSR保存在SPSR中 143 //3:初始化SP 144 //前两步由硬件完成,而第三部需要手动完成 145 "reset: " 146 147 "undefined: " 148 "mov sp, #0x66000000 "//初始化SP 149 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 150 //打印一句话 151 "ldr r0, =und_string " 152 "ldr r2, show " 153 "blx r2 " 154 //跳回来分两部 155 //1:将CPSR保存在SPSR中 156 //2:将PC保存到新模式下的lr中; 157 "mov sp, #0x66000000 "// 158 "ldmea sp, {r0-r12, pc}^ "// 159 160 "swi: " 161 162 "pre_abt: " 163 "data_abt: " 164 "sub lr, lr, #4 " 165 "mov sp, #0x66000000 "//初始化SP 166 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 167 //打印一句话 168 "ldr r0, =data_string " 169 "ldr r2, show " 170 "blx r2 " 171 //跳回来分两部 172 //1:将CPSR保存在SPSR中 173 //2:将PC保存到新模式下的lr中; 174 "mov sp, #0x66000000 "// 175 "ldmea sp, {r0-r12, pc}^ "// 176 177 "irq: " 178 "sub lr, lr, #4 " 179 "mov sp, #0x66000000 "//初始化SP 180 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 181 //打印一句话 182 "mov r2, #0x66000000 " 183 "ldr r1, [r2] " 184 "blx r1 " 185 186 "mov sp, #0x66000000 "// 187 "ldmea sp, {r0-r12, pc}^ "// 188 "fiq: " 189 "show: " 190 ".word 0xc3e114d8 " 191 192 "und_string: " 193 ".asciz "This is UND!\n" " 194 "data_string: " 195 ".asciz "This DATA_ABORT!\n" " 196 "irq_string: " 197 ".asciz "This IRQ!\n" " 198 199 ); 200 201 void init_ttb(unsigned long *addr) 202 { 203 unsigned long va = 0;//定义虚拟地址 204 unsigned long pa = 0;//定义物理地址 205 206 //40000000-------80000000 ==== 40000000------80000000 207 for(va=0x40000000; va<=0x80000000; va+=0x100000){ 208 pa = va; 209 addr[va >> 20] = pa | 2; 210 //|2的目的是将0-2位置为10此时将是小页模式4K 211 } 212 213 //00000000-------10000000 ==== 60000000------70000000 214 for(va=0x00000000; va<=0x10000000; va+=0x100000){ 215 pa = va+0x60000000; 216 addr[va >> 20] = pa | 2; 217 } 218 219 //10000000-------14000000 ==== 10000000------14000000 220 for(va=0x10000000; va<=0x14000000; va+=0x100000){ 221 pa = va; 222 addr[va >> 20] = pa | 2; 223 } 224 225 //30000000-------40000000 ==== 50000000------60000000 226 for(va=0x30000000; va<0x40000000; va+=0x100000){ 227 pa = va + 0x20000000; 228 addr[va >> 20] = pa | 2; 229 } 230 } 231 232 void enable_mmu(void) 233 234 { 235 unsigned long addr = 0x70000000; 236 init_ttb(addr); 237 //step:初始化页表 238 239 unsigned long mmu = 1 | (1 << 1) | (1 << 8); 240 //将MMU的第0,1,8位置1 241 __asm__ __volatile__( 242 "mov r0, #3 " 243 "MCR p15, 0, r0, c3, c0, 0 "//manager 244 "MCR p15, 0, %0, c2, c0, 0 "//addr 245 "MCR p15, 0, %1, c1, c0, 0 "// enable mmu 246 : 247 : "r" (addr), "r" (mmu) 248 : "r0" 249 ); 250 printf("MMU is enable! "); 251 } 252 253 254
需要注意的是:在do_irq()函数中有一个清中断的操作,否则,将会告诉中断控制器处理好了中断,以免一直触发外部中断:
87 void do_irq()
88 {
89 printf("key 1 down
");
90 static int flag=1;
91 if(flag)
92 {
93 printf("wtc_on
");
94 led_on();
95 pwm_on();
96 flag=0;
97 }
98 else if(flag == 0)
99 {
100 printf("wtc_off
");
101 led_off();
102 pwm_off();
103 flag=1;
104 }
105
EXT_INT43PEND = (1 << 2);//清中断
106 }
当程序运行成功了之后,按下按键,LED和蜂鸣器就会工作,再按下按键,LED和蜂鸣器就会停止!
接下来,将以前的中断问题综合起来:写了一个用外部中断来控制LED灯闪烁的例子
流程图是:
主要是do_rirq()函数:代码如下:
87 void do_irq() 88 { 89 printf("key 1 down "); 90 static int flag=1; 91 if(flag) 92 { 93 printf("wtc_on "); 94 led_on(); 95 pwm_on(); 96 flag=0; 97 } 98 else if(flag == 0) 99 { 100 printf("wtc_off "); 101 led_off(); 102 pwm_off(); 103 flag=1; 104 } 105 EXT_INT43PEND = (1 << 2);//清中断 106 }
接下来是整个程序的代码:
1 #include"regs.h" 2 3 int (*printf)(char *, ...) = 0xc3e114d8; 4 int(*delay)(int)=0xc3e25f90; 5 6 void init_ttb(unsigned long *addr); 7 void enable_mmu(void); 8 unsigned long data_abort_init(); 9 void memcopy(unsigned long* dest,unsigned long* source,int len); 10 void do_irq(); 11 void wtc_on(); 12 void wtc_off(); 13 void pwm_on(void); 14 void pwm_off(void); 15 void led_on(void); 16 void led_on(void); 17 18 19 20 int main() 21 { 22 *(unsigned long *)0x66000000 = do_irq; 23 24 //发生异常时会进入异常模式跳转到0000 0004地址处理异常事件 25 unsigned long source_addr=data_abort_init(); 26 //异常事件处理函数 27 printf("swi_souce addr is %x ",source_addr); 28 //将异常处理地址的值放到0x60000004 29 memcopy(0x60000000,source_addr,0x1000); 30 31 enable_mmu(); 32 //内存映射将0x00000004映射到0x6000000004 33 34 //step 1: cpu cpsr 35 __asm__ __volatile__( 36 "mrs r0, cpsr " 37 "bic r0, r0, #0x80 "//设置CPSR的I位,将IRQ位打开 38 "msr cpsr, r0 " 39 ::: "r0" 40 ); 41 42 //setp1:GIC 43 ICCICR_CPU0 = 1; 44 ICCPMR_CPU0 = 0xff; 45 46 //64 47 ICDDCR = 1; 48 ICDIPR16_CPU0 = (0x0 << 0);//ICD第16个寄存器,第0位 49 ICDIPTR16_CPU0 = (0x1 << 0);//优先级,最高 50 ICDISER2_CPU0 = (1 << 0);//一共五个,第2个使能中断 51 52 //75 53 ICDIPR18_CPU0 = (0x0 << 24); 54 //ICDIPTR0_CPU0 = 1; 55 ICDIPTR18_CPU0 = (0x1 << 24); 56 //ICDISER0_CPU0 = (1 << 0); 57 ICDISER2_CPU0 = (1 << 11); 58 59 //step 3: set gpio 60 GPX3CON = (0xf << 8);//是GPXCON为外部中断功能 61 62 //step 4: extern 63 EXT_INT43CON = (2 << 8);//设置上升沿触发 64 EXT_INT43MASK = 0;//使能中断 65 66 /////////////////////////狗 67 68 //step 5: sourcevoid pwm_on(void) 69 70 71 72 printf("welcome back! "); 73 } 74 75 void pwm_on(void) 76 { 77 GPD0CON &= ~0xffff; 78 GPD0CON |= 0x1;//配置寄存器为2 79 GPD0DAT |= 0x1;//date=0xf 80 } 81 82 void pwm_off(void) 83 { 84 GPD0CON &= ~0xffff; 85 GPD0CON |= 0x0; 86 // GPD0DAT &=0x0 ;//date=0xf 87 88 } 89 void led_off(void) 90 { 91 GPM4CON &= ~0xffff;//清零 92 GPM4CON |= 0x0000;//0---3位清零 93 GPM4DAT |= 0x0;//date=0xf关闭置一 94 } 95 void led_on(void) 96 { 97 GPM4CON &= ~0xffff; 98 GPM4CON |= 0x1111;//配置寄存器3-0-----3-3全为1111,全为输出模式 99 GPM4DAT &= ~0xf;//打开置0-4位为0000 100 } 101 102 void wtc_on() 103 { 104 //step 3: interrupt source watchdog 105 WTCON = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (20 << 8); 106 WTCNT = 0x8000; 107 WTDAT = 0x2000; 108 } 109 110 void wtc_off() 111 { 112 WTCON = 0; 113 } 114 115 void do_irq() 116 { 117 // pwm_on(); 118 // led_on(); 119 // delay(6000000); 120 // pwm_off(); 121 // led_off(); 122 123 unsigned long data = ICCIAR_CPU0; 124 unsigned long irq_id = data & 0x3ff; 125 unsigned long cpu_id = (data >> 10) & 0x7; 126 ICCEOIR_CPU0 = irq_id | (cpu_id << 10); 127 printf("irq is %d, cpu is %d ", irq_id, cpu_id); 128 if(irq_id==64)//如果按键中断 129 { 130 if(EXT_INT43PEND & (1 << 2)) 131 { 132 EXT_INT43PEND = (1 << 2);//清中断 133 printf("key 1 down "); 134 static int flag=1; 135 if(flag) 136 { 137 printf("wtc_on "); 138 wtc_on(); 139 flag=0; 140 } 141 else if(flag == 0) 142 { 143 printf("wtc_off "); 144 wtc_off(); 145 led_off(); 146 pwm_off(); 147 flag=1; 148 } 149 } 150 } 151 if(irq_id==75)//如果DOG中断 152 { 153 printf("dog dog dog "); 154 static int flag=1; 155 if(flag) 156 { 157 led_on(); 158 pwm_on(); 159 flag=0; 160 } 161 else 162 { 163 led_off(); 164 pwm_off(); 165 flag=1; 166 } 167 WTCLRINT = 100;//清狗中断 168 } 169 170 } 171 172 void memcopy(unsigned long* dest, unsigned long* source,int len) 173 { 174 int i=0;; 175 for(i=0;i<len;i++) 176 dest[i]=source[i]; 177 } 178 179 unsigned long data_abort_init() 180 { 181 unsigned long source; 182 __asm__ __volatile__( 183 "ldr %0, =voliate_start " 184 : "=r" (source) 185 ); 186 return source; 187 188 } 189 190 __asm__( 191 192 "voliate_start: " 193 //跳转目录 194 " b reset " 195 " b undefined " 196 " b swi " 197 " b pre_abt " 198 " b data_abt " 199 " .word 0 "//占位符号,一个位占4个字节 200 " b irq " 201 " b fiq " 202 " " 203 204 //跳转要分三部: 205 //1:将PC保存到新模式下的lr中; 206 //2:将CPSR保存在SPSR中 207 //3:初始化SP 208 //前两步由硬件完成,而第三部需要手动完成 209 "reset: " 210 211 "undefined: " 212 "mov sp, #0x66000000 "//初始化SP 213 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 214 //打印一句话 215 "ldr r0, =und_string " 216 "ldr r2, show " 217 "blx r2 " 218 //跳回来分两部 219 //1:将CPSR保存在SPSR中 220 //2:将PC保存到新模式下的lr中; 221 "mov sp, #0x66000000 "// 222 "ldmea sp, {r0-r12, pc}^ "// 223 224 "swi: " 225 226 "pre_abt: " 227 "data_abt: " 228 "sub lr, lr, #4 " 229 "mov sp, #0x66000000 "//初始化SP 230 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 231 //打印一句话 232 "ldr r0, =data_string " 233 "ldr r2, show " 234 "blx r2 " 235 //跳回来分两部 236 //1:将CPSR保存在SPSR中 237 //2:将PC保存到新模式下的lr中; 238 "mov sp, #0x66000000 "// 239 "ldmea sp, {r0-r12, pc}^ "// 240 241 "irq: " 242 "sub lr, lr, #4 " 243 "mov sp, #0x66000000 "//初始化SP 244 "stmfd sp!, {r0-r12, lr} "//初始化sp,入栈保护寄存器 245 //打印一句话 246 "mov r2, #0x66000000 " 247 "ldr r1, [r2] " 248 "blx r1 " 249 250 // "ldr r0, =irq_string " 251 // "ldr r2, show " 252 // "blx r2 " 253 //跳回来分两部 254 //1:将CPSR保存在SPSR中 255 //2:将PC保存到新模式下的lr中; 256 "mov sp, #0x66000000 "// 257 "ldmea sp, {r0-r12, pc}^ "// 258 "fiq: " 259 "show: " 260 ".word 0xc3e114d8 " 261 262 "und_string: " 263 ".asciz "This is UND!\n" " 264 "data_string: " 265 ".asciz "This DATA_ABORT!\n" " 266 "irq_string: " 267 ".asciz "This IRQ!\n" " 268 269 ); 270 271 void init_ttb(unsigned long *addr) 272 { 273 unsigned long va = 0;//定义虚拟地址 274 unsigned long pa = 0;//定义物理地址 275 276 //40000000-------80000000 ==== 40000000------80000000 277 for(va=0x40000000; va<=0x80000000; va+=0x100000){ 278 pa = va; 279 addr[va >> 20] = pa | 2; 280 //|2的目的是将0-2位置为10此时将是小页模式4K 281 } 282 283 //00000000-------10000000 ==== 60000000------70000000 284 for(va=0x00000000; va<=0x10000000; va+=0x100000){ 285 pa = va+0x60000000; 286 addr[va >> 20] = pa | 2; 287 } 288 289 //10000000-------14000000 ==== 10000000------14000000 290 for(va=0x10000000; va<=0x14000000; va+=0x100000){ 291 pa = va; 292 addr[va >> 20] = pa | 2; 293 } 294 295 //30000000-------40000000 ==== 50000000------60000000 296 for(va=0x30000000; va<0x40000000; va+=0x100000){ 297 pa = va + 0x20000000; 298 addr[va >> 20] = pa | 2; 299 } 300 } 301 302 void enable_mmu(void) 303 304 { 305 unsigned long addr = 0x70000000; 306 init_ttb(addr); 307 //step:初始化页表 308 309 unsigned long mmu = 1 | (1 << 1) | (1 << 8); 310 //将MMU的第0,1,8位置1 311 __asm__ __volatile__( 312 "mov r0, #3 " 313 "MCR p15, 0, r0, c3, c0, 0 "//manager 314 "MCR p15, 0, %0, c2, c0, 0 "//addr 315 "MCR p15, 0, %1, c1, c0, 0 "// enable mmu 316 : 317 : "r" (addr), "r" (mmu) 318 : "r0" 319 ); 320 printf("MMU is enable! "); 321 } 322
运行成功:
发现当按下按键是触发外部中断,LED和蜂鸣器工作,当再次按下按键的时候,停止工作!看门狗在这里的作用就是
不断的使LED和蜂鸣器闪烁和鸣叫。
到这里,ARM体系结构到一段落