1.8.8.S5PV210中断处理的编程实践

朱老师笔记


1.8.8.1、上节中代码中的小问题

主要集中在start.S中,原因就是不常写,格式忽略。解决方案就是编译时发现再解决。
1.8.8.2、中断控制器初始化
主要工作有:第一阶段绑定异常向量表到异常处理程序;禁止所有中断源;选择所有中断类型为IRQ;清理VICnADDR寄存器为0.
1.8.8.3、中断的使能与禁止
思路是先根据中断号判断这个中断属于VIC几,然后在用中断源减去这个VIC的偏移量,得到这个中断号在本VIC中的偏移量,然后1<<x位,写入相应的VIC的INTENABLE/INTENCLEAR寄存器即可。


1.8.8.4、绑定自己实现的isr到VICnVECTADDR
(1)搞清楚2个寄存器的区别:VICnVECTADDR和VICnADDR
(1)VICVECTADDR寄存器一共有4×32个,每个中断源都有一个VECTADDR寄存器,我们应该将自己为这个中断源写的isr地址丢到这个中断源对应的VECTADDR寄存器中即可。


1.8.8.5、真正的中断处理程序如何获取isr
(1)当发生中断时,硬件会自动把相应中断源的isr地址从VICnVECTADDR寄存器中推入VICnADDR寄存器中,所以我们第二阶段的第二阶段isr_handler中,只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。


总结:第4步绑定isr地址到VICnVECTADDR和第5步中断发生时第二阶段的第二阶段如何获取isr地址,这两步是相关的。这两个的结合技术,就是我们一直在说的210的硬件自动寻找isr的机制。


整个中断的流程梳理:
整个中断的工作分为2部分:
第一部分是我们为中断响应而做的预备工作:
1. 初始化中断控制器
2. 绑定写好的isr到中断控制器
3. 相应中断的所有条件使能
第二部分是当硬件产生中断后如何自动执行isr:
1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口
2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR
寄存器中取isr来执行即可。

4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。


《朱老师物联网大讲堂》学习笔记

学习地址:www.zhulaoshi.org


东西有点多啊,怎么梳理下呢~


从start.S开始吧,

[plain]  view plain  copy
  1. IRQ_handle;  
  2.     ldr sp, =IRQ_STACK    
  3.     sub lr, lr, #4    
  4.     stmfd   sp!,    {r0-r12,lr}   
  5.     bl irq_handler    
  6.     ldmfd sp!, {r0-r12, pc}^  
bl irq_handler这句指令前面的部分是设置环境,保存环境,后面的部分是恢复现场。


那么irq_handler是什么呢?然后就要进入int.c了,

在void irq_handler(void)中,我们将会执行我们的isr,

这里就要涉及到中断处理的过程.


下面是朱老师课上写的代码加注释,还有一个int.h文件,就不贴了吧,是寄存器的宏定义,还有中断号的宏定义。

[plain]  view plain  copy
  1. #include "int.h"  
  2. #include "stdio.h"  
  3.   
  4.   
  5. void reset_exception(void)  
  6. {  
  7.     printf("reset_exception.\n");  
  8. }  
  9.   
  10. void undef_exception(void)  
  11. {  
  12.     printf("undef_exception.\n");  
  13. }  
  14.   
  15. void sotf_int_exception(void)  
  16. {  
  17.     printf("sotf_int_exception.\n");  
  18. }  
  19.   
  20. void prefetch_exception(void)  
  21. {  
  22.     printf("prefetch_exception.\n");  
  23. }  
  24.   
  25. void data_exception(void)  
  26. {  
  27.     printf("data_exception.\n");  
  28. }  
  29.   
  30. // 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;  
  31. // 清除VICnADDR为0  
  32. void system_init_exception(void)  
  33. {  
  34.     // 第一阶段处理,绑定异常向量表  
  35.     r_exception_reset = (unsigned int)reset_exception;  
  36.     r_exception_undef = (unsigned int)undef_exception;  
  37.     r_exception_sotf_int = (unsigned int)sotf_int_exception;  
  38.     r_exception_prefetch = (unsigned int)prefetch_exception;  
  39.     r_exception_data = (unsigned int)data_exception;  
  40.     r_exception_irq = (unsigned int)IRQ_handle;  
  41.     r_exception_fiq = (unsigned int)IRQ_handle;  
  42.       
  43.     // 初始化中断控制器的基本寄存器  
  44.     intc_init();  
  45. }  
  46.   
  47. // 清除需要处理的中断的中断处理函数的地址  
  48. void intc_clearvectaddr(void)  
  49. {  
  50.     // VICxADDR:当前正在处理的中断的中断处理函数的地址  
  51.     VIC0ADDR = 0;  
  52.     VIC1ADDR = 0;  
  53.     VIC2ADDR = 0;  
  54.     VIC3ADDR = 0;  
  55. }  
  56.   
  57. // 初始化中断控制器  
  58. void intc_init(void)  
  59. {  
  60.     // 禁止所有中断  
  61.     // 为什么在中断初始化之初要禁止所有中断?  
  62.     // 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr  
  63.     // 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码  
  64.     // 则程序很可能跑飞,所以不用的中断一定要关掉。  
  65.     // 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须  
  66.     // 给这个中断提供相应的isr并绑定好。  
  67.     VIC0INTENCLEAR = 0xffffffff;  
  68.     VIC1INTENCLEAR = 0xffffffff;  
  69.     VIC2INTENCLEAR = 0xffffffff;  
  70.     VIC3INTENCLEAR = 0xffffffff;  
  71.   
  72.     // 选择中断类型为IRQ  
  73.     VIC0INTSELECT = 0x0;  
  74.     VIC1INTSELECT = 0x0;  
  75.     VIC2INTSELECT = 0x0;  
  76.     VIC3INTSELECT = 0x0;  
  77.   
  78.     // 清VICxADDR  
  79.     intc_clearvectaddr();  
  80. }  
  81.   
  82.   
  83. // 绑定我们写的isr到VICnVECTADDR寄存器  
  84. // 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理  
  85. // 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。  
  86. // 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr  
  87.   
  88. // VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个  
  89. // 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候  
  90. // 只需要首地址+偏移量即可。  
  91. void intc_setvectaddr(unsigned long intnum, void (*handler)(void))  
  92. {  
  93.     //VIC0  
  94.     if(intnum<32)  
  95.     {  
  96.         *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;  
  97.     }  
  98.     //VIC1  
  99.     else if(intnum<64)  
  100.     {  
  101.         *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;  
  102.     }  
  103.     //VIC2  
  104.     else if(intnum<96)  
  105.     {  
  106.         *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;  
  107.     }  
  108.     //VIC3  
  109.     else  
  110.     {  
  111.         *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;  
  112.     }  
  113.     return;  
  114. }  
  115.   
  116.   
  117. // 使能中断  
  118. // 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号  
  119. void intc_enable(unsigned long intnum)  
  120. {  
  121.     unsigned long temp;  
  122.     // 确定intnum在哪个寄存器的哪一位  
  123.     // <32就是0~31,必然在VIC0  
  124.     if(intnum<32)  
  125.     {  
  126.         temp = VIC0INTENABLE;  
  127.         temp |= (1<<intnum);      // 如果是第一种设计则必须位操作,第二种设计可以  
  128.                                     // 直接写。  
  129.         VIC0INTENABLE = temp;  
  130.     }  
  131.     else if(intnum<64)  
  132.     {  
  133.         temp = VIC1INTENABLE;  
  134.         temp |= (1<<(intnum-32));  
  135.         VIC1INTENABLE = temp;  
  136.     }  
  137.     else if(intnum<96)  
  138.     {  
  139.         temp = VIC2INTENABLE;  
  140.         temp |= (1<<(intnum-64));  
  141.         VIC2INTENABLE = temp;  
  142.     }  
  143.     else if(intnum<NUM_ALL)  
  144.     {  
  145.         temp = VIC3INTENABLE;  
  146.         temp |= (1<<(intnum-96));  
  147.         VIC3INTENABLE = temp;  
  148.     }  
  149.     // NUM_ALL : enable all interrupt  
  150.     else  
  151.     {  
  152.         VIC0INTENABLE = 0xFFFFFFFF;  
  153.         VIC1INTENABLE = 0xFFFFFFFF;  
  154.         VIC2INTENABLE = 0xFFFFFFFF;  
  155.         VIC3INTENABLE = 0xFFFFFFFF;  
  156.     }  
  157.   
  158. }  
  159.   
  160. // 禁止中断  
  161. // 通过传参的intnum来禁止某个具体的中断源,中断号在int.h中定义,是物理中断号  
  162. void intc_disable(unsigned long intnum)  
  163. {  
  164.     unsigned long temp;  
  165.   
  166.     if(intnum<32)  
  167.     {  
  168.         temp = VIC0INTENCLEAR;  
  169.         temp |= (1<<intnum);  
  170.         VIC0INTENCLEAR = temp;  
  171.     }  
  172.     else if(intnum<64)  
  173.     {  
  174.         temp = VIC1INTENCLEAR;  
  175.         temp |= (1<<(intnum-32));  
  176.         VIC1INTENCLEAR = temp;  
  177.     }  
  178.     else if(intnum<96)  
  179.     {  
  180.         temp = VIC2INTENCLEAR;  
  181.         temp |= (1<<(intnum-64));  
  182.         VIC2INTENCLEAR = temp;  
  183.     }  
  184.     else if(intnum<NUM_ALL)  
  185.     {  
  186.         temp = VIC3INTENCLEAR;  
  187.         temp |= (1<<(intnum-96));  
  188.         VIC3INTENCLEAR = temp;  
  189.     }  
  190.     // NUM_ALL : disable all interrupt  
  191.     else  
  192.     {  
  193.         VIC0INTENCLEAR = 0xFFFFFFFF;  
  194.         VIC1INTENCLEAR = 0xFFFFFFFF;  
  195.         VIC2INTENCLEAR = 0xFFFFFFFF;  
  196.         VIC3INTENCLEAR = 0xFFFFFFFF;  
  197.     }  
  198.   
  199.     return;  
  200. }  
  201.   
  202.   
  203. // 通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,来得知哪个VIC发生中断了  
  204. unsigned long intc_getvicirqstatus(unsigned long ucontroller)  
  205. {  
  206.     if(ucontroller == 0)  
  207.         return  VIC0IRQSTATUS;  
  208.     else if(ucontroller == 1)  
  209.         return  VIC1IRQSTATUS;  
  210.     else if(ucontroller == 2)  
  211.         return  VIC2IRQSTATUS;  
  212.     else if(ucontroller == 3)  
  213.         return  VIC3IRQSTATUS;  
  214.     else  
  215.     {}  
  216.     return 0;  
  217. }  
  218.   
  219.   
  220. // 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场  
  221. void irq_handler(void)  
  222. {  
  223.     printf("irq_handler.\n");  
  224.     // SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断  
  225.     // 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来  
  226.     // 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断  
  227.     // 对应的isr。  
  228.       
  229.       
  230.     // 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须  
  231.     // 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中  
  232.     unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};  
  233.     int i=0;  
  234.     void (*isr)(void) = NULL;  
  235.   
  236.     for(i=0; i<4; i++)  
  237.     {  
  238.         // 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0  
  239.         if(intc_getvicirqstatus(i) != 0)  
  240.         {  
  241.             isr = (void (*)(void)) vicaddr[i];  
  242.             break;  
  243.         }  
  244.     }  
  245.     (*isr)();       // 通过函数指针来调用函数  
  246. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值