• LV.12 D18 中断处理 学习笔记


    一、ARM的异常处理机制及工程代码结构

    1.1异常概念

            处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生     这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件     异常事件处理完成之后再返回到被异常打断的点继续执行程序。

    1.2异常处理机制

            不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制.

    1.3ARM异常源

    概念:导致异常产生的事件称为异常源

    ARM异常源

    FIQ                                快速中断请求引脚有效            

    IRQ                                外部中断请求引脚有效    

    Reset                             复位电平有效    

    Software Interrupt          执行swi指令    

    Data Abort                      数据终止    

    Prefetch Abort                指令预取终止    

    Undefined Instruction    遇到不能处理的指令

    1.4异常模式

            在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切 换成对应的异常模式

    1.5ARM异常响应

    ARM产生异常后的动作(自动完成)
            1.拷贝CPSR中的内容到对应异常模式下的SPSR_

            2.修改CPSR的值

                    2.1.修改中断禁止位禁止相应的中断

                    2.2.修改模式位进入相应的异常模式   

                    2.3.修改状态位进入ARM状态

            3.保存返回地址到对应异常模式下的LR_

            4.设置PC为相应的异常向量(异常向量表对应的地址)

    1.6异常向量表

    异常向量表
        > 异常向量表的本质是内存中的一段代码

        > 表中为每个异常源分配了四个字节的存储空间

        > 遇到异常后处理器自动将PC修改为对应的地址

        > 因为异常向量表空间有限一般我们不会再这里写异常处理程序,而是在对应的位置写一条跳

          转指令使其跳转到指定的异常处理程序的入口

        注:ARM的异常向量表的基地址默认在0x00地址但可以通过配置协处理器来修改其地址 
        

    1.7异常返回

    ARM异常返回的动作(自己编写)

            1.将SPSR_的值复制给CPSR使处理器恢复之前的状态

            2.将LR_的值复制给PC使程序跳转回被打断的地址继续执行 

    1.8 IRQ异常举例

     注:整个过程CPSR保存的永远是当前程序运行状态,SPSR只是异常时对原来的CPSR进行备份

    二、工程模板代码结构分析 

    common:老师写好的库函数文件,里面实现了很多功能,比如把所有的寄存器封装,比如手搓了一个printf。

    interface.c:  我们自己要实现的c源文件

    start:  启动文件,任何芯片一上电执行的第一个程序一定是汇编程序,要初始化栈,初始化芯片,将异常向量表基地址位置改变,打开FIQ、IRQ然后跳转到C程序。

    makefile:  编译规则

    map.lds:   链接脚本,这个工程模板内有很多的C文件S文件H文件,他们编译链接后只生成一个.bin文件写入开发板,哪个文件放入哪个位置,我们写好的文件在内存中的位置都由它决定
     

    三、中断处理框架搭建

    LR保存的是被打断的下一条指令的地址(指的是汇编指令,一条c语言可能会编译成很多条汇编指令)

    在遇到IRQ时会跳转到以_start:为基地址偏移0x18。

     

    然后我们在 b main 后面来写这个中断服务程序,因为b main之前的代码都是启动代码,一开始就会执行。而 irq_handler是异常处理程序,芯片刚启动时我们不希望它执行,我们希望它遇到异常的时候再执行。

    但是我们不能直接在这里写,因为IRQ模式下有很多寄存器都是和USER模式共用的如果在这里写,可定会用到一些寄存器,这样就会覆盖掉寄存器中本来的内容,返回主程序就不能正确返回这个状态了

    所以我们需要先压栈保护现场

    这个是时候就可以写了吗,还是不能,我们来复习一下LR寄存器 

    R14(LR,Link Register)
        链接寄存器,一般有以下两种用途:

        > 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址程序需要返回时将LR的值复制到PC即可实现。

        > 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回。

    原理

        当执行跳转指令或产生异常时,LR寄存器中不会凭空产生一个返回地址。其原理是当执行跳转指令或产生异常时,处理器内部会将PC寄存器中的值拷贝到LR寄存器中,然后再将LR寄存器中的值自减4。

    BL

        当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是BL指令下一条指令的地址。

    该时刻PC=N+8 LR=N+4

     

     IRQ中断

        当执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保存返回地址,而是执行完成后才会保存,但执行完成后PC的值又会自动增4,所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址。

    该时刻PC=N+12 LR=N+8

     

    因为产生IRQ异常后自动保存到LR寄存器中的返回地址是被IRQ打断的指令下一条在下一条指令,所以需要我们人为的修复一下。

    那么为什么不直接让他返回一个正确的呢,因为ARM是精简指令集,要想直接返回正确的必须要再加一个电路,这样会增加硬件成本,所以不如软件修复一条指令就解决了。

    由于这是一个非叶子函数,在这段程序中可能还会有跳转,所以我们干脆把LR也压栈保护一下。
     

     异常处理程序既可以用汇编写,也可以通过混合编程用C语言写,但前面修改LR和压栈两条指令,只能用汇编写。

    四、中断处理程序编程

    interface.c

    1. #include "exynos_4412.h"
    2. //异常处理程序
    3. void do_irq(void)
    4. {
    5. printf("Key2 pressed\n");
    6. }
    7. void Delay(unsigned int Time)
    8. {
    9. while(Time--);
    10. }
    11. int main()
    12. {
    13. /*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*/
    14. /*将GPX1_1设置成中断功能*/
    15. GPX1.CON = GPX1.CON | (0xF << 4);
    16. /*设置GPX1_1中断触发方式:下降沿触发*/
    17. EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
    18. /*使能GPX1_1的中断功能*/
    19. EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));
    20. /*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*/
    21. /*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
    22. ICDDCR = ICDDCR | 1;
    23. /*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/
    24. ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
    25. /*选择CPU0来处理57号中断*/
    26. ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);
    27. /*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
    28. CPU0.ICCICR = CPU0.ICCICR | 1;
    29. GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
    30. while(1)
    31. {
    32. /*点亮LED2*/
    33. GPX2.DAT = GPX2.DAT | (1 << 7);
    34. /*延时*/
    35. Delay(1000000);
    36. /*熄灭LED2*/
    37. GPX2.DAT = GPX2.DAT & (~(1 << 7));
    38. /*延时*/
    39. Delay(1000000);
    40. }
    41. return 0;
    42. }

     start.S

    1. .text
    2. .global _start
    3. _start:
    4. /*
    5. * Vector table
    6. */
    7. b reset
    8. b .
    9. b .
    10. b .
    11. b .
    12. b .
    13. //从异常向量表再跳转到IRQ的异常处理程序
    14. b irq_handler
    15. b .
    16. reset:
    17. /*
    18. * Set vector address in CP15 VBAR register
    19. */
    20. ldr r0, =_start
    21. mcr p15, 0, r0, c12, c0, 0 @Set VBAR
    22. /*
    23. * Set the cpu to SVC32 mode, Disable FIQ/IRQ
    24. */
    25. mrs r0, cpsr
    26. bic r0, r0, #0x1f
    27. orr r0, r0, #0xd3
    28. msr cpsr ,r0
    29. /*
    30. * Defines access permissions for each coprocessor
    31. */
    32. mov r0, #0xfffffff
    33. mcr p15, 0, r0, c1, c0, 2
    34. /*
    35. * Invalidate L1 I/D
    36. */
    37. mov r0, #0 @Set up for MCR
    38. mcr p15, 0, r0, c8, c7, 0 @Invalidate TLBs
    39. mcr p15, 0, r0, c7, c5, 0 @Invalidate icache
    40. /*
    41. * Set the FPEXC EN bit to enable the FPU
    42. */
    43. mov r3, #0x40000000
    44. fmxr FPEXC, r3
    45. /*
    46. * Disable MMU stuff and caches
    47. */
    48. mrc p15, 0, r0, c1, c0, 0
    49. bic r0, r0, #0x00002000 @Clear bits 13 (--V-)
    50. bic r0, r0, #0x00000007 @Clear bits 2:0 (-CAM)
    51. orr r0, r0, #0x00001000 @Set bit 12 (---I) Icache
    52. orr r0, r0, #0x00000002 @Set bit 1 (--A-) Align
    53. orr r0, r0, #0x00000800 @Set bit 11 (Z---) BTB
    54. mcr p15, 0, r0, c1, c0, 0
    55. /*
    56. * Initialize stacks
    57. */
    58. init_stack:
    59. /*svc mode stack*/
    60. msr cpsr, #0xd3
    61. ldr sp, _stack_svc_end
    62. /*undef mode stack*/
    63. msr cpsr, #0xdb
    64. ldr sp, _stack_und_end
    65. /*abort mode stack*/
    66. msr cpsr,#0xd7
    67. ldr sp,_stack_abt_end
    68. /*irq mode stack*/
    69. msr cpsr,#0xd2
    70. ldr sp, _stack_irq_end
    71. /*fiq mode stack*/
    72. msr cpsr,#0xd1
    73. ldr sp, _stack_fiq_end
    74. /*user mode stack, enable FIQ/IRQ*/
    75. msr cpsr,#0x10
    76. ldr sp, _stack_usr_end
    77. /*Call main*/
    78. b main
    79. //IRQ的异常处理程序
    80. irq_handler:
    81. //因为产生IRQ异常后自动保存到LR中的返回地址是被IRQ打断指令的
    82. //下一条再下一条指令的地址,所以我们需要人为的去修复一下
    83. sub lr, lr, #4
    84. //因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
    85. //所以在处理异常之前需要先将之前USER模式下寄存器的值压栈保护
    86. stmfd sp!,{r0-r12}
    87. //处理异常
    88. bl do_irq
    89. //异常返回
    90. //1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
    91. //2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前的状态
    92. //3.将栈中LR寄存器中的值出栈给PC,实现程序的返回
    93. ldmfd sp!,{r0-r12,pc}^
    94. _stack_svc_end:
    95. .word stack_svc + 512
    96. _stack_und_end:
    97. .word stack_und + 512
    98. _stack_abt_end:
    99. .word stack_abt + 512
    100. _stack_irq_end:
    101. .word stack_irq + 512
    102. _stack_fiq_end:
    103. .word stack_fiq + 512
    104. _stack_usr_end:
    105. .word stack_usr + 512
    106. .data
    107. stack_svc:
    108. .space 512
    109. stack_und:
    110. .space 512
    111. stack_abt:
    112. .space 512
    113. stack_irq:
    114. .space 512
    115. stack_fiq:
    116. .space 512
    117. stack_usr:
    118. .space 512

     只按了一次按键,就会一直打印Key2 pressed。

    中断挂起寄存器, EXT_INT41_PEND[1]对应GPX1_1引脚,会自动置1,置1就会把这个中断挂。当你处理完中断,返回到main函数,此时EXT_INT41_PEND[1]依旧是1,不会自动清零,所以还会给中断控制器发送中断信号,中断控制器还会将中断信号转发给CPU,还会触发中断。所以我们需要在CPU处理完中断后,把挂起位清零。

    中断挂起寄存器比较特殊,写1才会清零,写0则保持不变。

    我们在异常处理程序中对中断挂起寄存器的第一位进行修改,则按一次按键,只会产生一次中断。

    但是有个新问题所有的IRQ异常都会跳到这里,那么我们需要区分一下,但是CPU不知道是谁发来的,所以需要询问中断控制器。

    ICCIAR寄存器,后面[31:10]位与本次实验无关,我们只看[9:0]位,中断控制器把几号中断转给CPU,就会往这个寄存器的[9:0]位写几。所以我们可以让CPU在处理中断之前先读取这个寄存器的值,来写不同的中断处理程序。

    1. #include "exynos_4412.h"
    2. //异常处理程序
    3. void do_irq(void)
    4. {
    5. unsigned int IrqNum = 0;
    6. /*从中断控制器中获取当前中断的中断号*/
    7. IrqNum = CPU0.ICCIAR & 0x3FF;
    8. switch(IrqNum)
    9. {
    10. case 0:
    11. //0号中断的处理程序
    12. break;
    13. case 1:
    14. //1号中断的处理程序
    15. break;
    16. /*
    17. * ......
    18. */
    19. case 57:
    20. printf("Key2 pressed\n");
    21. /*清除GPIO控制器中的中断挂起位*/
    22. EXT_INT41_PEND = (1 << 1);
    23. break;
    24. /*
    25. * ......
    26. */
    27. case 159:
    28. //159号中断的处理程序
    29. break;
    30. default:
    31. break;
    32. }
    33. }
    34. void Delay(unsigned int Time)
    35. {
    36. while(Time--);
    37. }
    38. int main()
    39. {
    40. /*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*/
    41. /*将GPX1_1设置成中断功能*/
    42. GPX1.CON = GPX1.CON | (0xF << 4);
    43. /*设置GPX1_1中断触发方式:下降沿触发*/
    44. EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
    45. /*使能GPX1_1的中断功能*/
    46. EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));
    47. /*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*/
    48. /*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
    49. ICDDCR = ICDDCR | 1;
    50. /*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/
    51. ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
    52. /*选择CPU0来处理57号中断*/
    53. ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);
    54. /*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
    55. CPU0.ICCICR = CPU0.ICCICR | 1;
    56. GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
    57. while(1)
    58. {
    59. /*点亮LED2*/
    60. GPX2.DAT = GPX2.DAT | (1 << 7);
    61. /*延时*/
    62. Delay(1000000);
    63. /*熄灭LED2*/
    64. GPX2.DAT = GPX2.DAT & (~(1 << 7));
    65. /*延时*/
    66. Delay(1000000);
    67. }
    68. return 0;
    69. }

    此时,我们又发现按键只有第一次有效,之后再按按键就没有反应。

    因为中断控制器不知道CPU0已经处理完中断处理程序了,所以并没有将新的中断信号发送给CPU0。

     ICCEOIR寄存器,本次实验只看[9:0]位,CPU处理完中断后,会把中断号写入该寄存器。

    将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断

    1. #include "exynos_4412.h"
    2. //异常处理程序
    3. void do_irq(void)
    4. {
    5. unsigned int IrqNum = 0;
    6. /*从中断控制器中获取当前中断的中断号*/
    7. IrqNum = CPU0.ICCIAR & 0x3FF;
    8. switch(IrqNum)
    9. {
    10. case 0:
    11. //0号中断的处理程序
    12. break;
    13. case 1:
    14. //1号中断的处理程序
    15. break;
    16. /*
    17. * ......
    18. */
    19. case 57:
    20. printf("Key2 pressed\n");
    21. /*清除GPIO控制器中的中断挂起位*/
    22. EXT_INT41_PEND = (1 << 1);
    23. /*将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断*/
    24. CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | 57;
    25. break;
    26. /*
    27. * ......
    28. */
    29. case 159:
    30. //159号中断的处理程序
    31. break;
    32. default:
    33. break;
    34. }
    35. }
    36. void Delay(unsigned int Time)
    37. {
    38. while(Time--);
    39. }
    40. int main()
    41. {
    42. /*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*/
    43. /*将GPX1_1设置成中断功能*/
    44. GPX1.CON = GPX1.CON | (0xF << 4);
    45. /*设置GPX1_1中断触发方式:下降沿触发*/
    46. EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
    47. /*使能GPX1_1的中断功能*/
    48. EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));
    49. /*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*/
    50. /*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
    51. ICDDCR = ICDDCR | 1;
    52. /*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/
    53. ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
    54. /*选择CPU0来处理57号中断*/
    55. ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);
    56. /*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
    57. CPU0.ICCICR = CPU0.ICCICR | 1;
    58. GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
    59. while(1)
    60. {
    61. /*点亮LED2*/
    62. GPX2.DAT = GPX2.DAT | (1 << 7);
    63. /*延时*/
    64. Delay(1000000);
    65. /*熄灭LED2*/
    66. GPX2.DAT = GPX2.DAT & (~(1 << 7));
    67. /*延时*/
    68. Delay(1000000);
    69. }
    70. return 0;
    71. }

    这次代码我们需要实现按一次按键就产生一次中断,但个人原因,没有实现,也没找出问题,后续再解决。

    五、中断编程补充

    中断和轮询:轮询是CPU主动去查看硬件有没有异常产生,而中断是硬件主动通知CPU。中断的效率更高一些,用的较多。

    真正开发时有操作系统,我们其实只需要写中断程序,然后打开对应中断就可以

    FIQ为什么比IRQ快:

    1)FIQ的优先级比IRQ高

    2)FIQ可以打断IRQ

    3)FIQ在异常向量表的最末,别的中断处理程序需要跳转,而FIQ可以直接往后写

    4)FIQ有四组直接独有的寄存器,如果只需要r8-r12的话,他不需要压栈保护现场。但是如果用到了r0-r7还是要压栈保护现场的。


    作业

    1.使用中断的方式检测Key3按键的状态,实现按一次按键,LED2点亮,再次按下,LED2熄灭

     

     

      

      EINT[10]的中断号是58。

     本次实验依旧采用下降沿触发方式,GPX1_2对应EXT_INT41_CON[2]。

    EXT_INT41_MASK[1]对应GPX1_1的开和关。0x0打开中断,0x1关闭中断。我们把它打开。

    我们把ICDDCR寄存器写1 ,监控所有的外部中断,并将挂起的中断转发到CPU的接口

    ICCDCR寄存器相当于GIC(中断控制器)的总开关。

     ICDISER_CPU寄存器的作用:寄存器接收到中断信号,通过配置该寄存器对应的位,控制该中断信号发送或不发送给CPU。 

     下面这个寄存器是ICDIPTR_CPU,它的作用是为每一个中断选择处理他的CPU。 

    哪一位置1,中断信号就发给哪个CPU处理。但4412是一个四核的CPU,所以高四位是没有用的。

     

    一共需要40个寄存器来管理这160个中断归属于哪一个CPU处理

    想把58号中断交给CPU0处理,则把偏移地址为0x838的寄存器的[23:16]写为00000001即可。

     ICCICR_CPUn寄存器是中断控制器和CPU之间的接口,他就像一个开关,用哪个CPU就要打开哪个。

    EXT_INT41_PEND[2]对应GPX1_2引脚,会自动置1,置1就会把这个中断挂。当你处理完中断,返回到main函数,此时EXT_INT41_PEND[2]依旧是1,不会自动清零,所以还会给中断控制器发送中断信号,中断控制器还会将中断信号转发给CPU,还会触发中断。所以我们需要在CPU处理完中断后,把挂起位清零。

    中断挂起寄存器比较特殊,写1才会清零,写0则保持不变。

    ICCIAR寄存器,后面[31:10]位与本次实验无关,我们只看[9:0]位,中断控制器把几号中断转给CPU,就会往这个寄存器的[9:0]位写几。所以我们可以让CPU在处理中断之前先读取这个寄存器的值,来写不同的中断处理程序。

     ICCEOIR寄存器,本次实验只看[9:0]位,CPU处理完中断后,会把中断号写入该寄存器。

    将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断

    1. #include "exynos_4412.h"
    2. unsigned int flag = 1;
    3. //异常处理程序
    4. void do_irq(void)
    5. {
    6. unsigned int IrqNum;
    7. /*从中断控制器中获取当前中断的中断号*/
    8. IrqNum = CPU0.ICCIAR & 0x3FF;
    9. switch(IrqNum)
    10. {
    11. case 0:
    12. //0号中断的处理程序
    13. break;
    14. case 1:
    15. //1号中断的处理程序
    16. break;
    17. /*
    18. * ......
    19. */
    20. case 58:
    21. printf("Key3 pressed\n");
    22. if(flag == 1)
    23. {
    24. GPX2.DAT = GPX2.DAT | (1 << 7);
    25. flag = 0;
    26. }else
    27. {
    28. GPX2.DAT = GPX2.DAT & (~(1 << 7));
    29. flag = 1;
    30. }
    31. /*清除GPIO控制器中的中断挂起位*/
    32. EXT_INT41_PEND = (1 << 2);
    33. /*将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断*/
    34. CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | 58;
    35. break;
    36. /*
    37. * ......
    38. */
    39. case 159:
    40. //159号中断的处理程序
    41. break;
    42. default:
    43. break;
    44. }
    45. }
    46. void Delay(unsigned int Time)
    47. {
    48. while(Time--);
    49. }
    50. int main()
    51. {
    52. /*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*/
    53. /*将GPX1_2设置成中断功能*/
    54. GPX1.CON = GPX1.CON | (0xF << 8);
    55. /*设置GPX1_2中断触发方式:下降沿触发*/
    56. EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
    57. /*使能GPX1_2的中断功能*/
    58. EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));
    59. /*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*/
    60. /*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
    61. ICDDCR = ICDDCR | 1;
    62. /*在中断控制器中使能58号中断,使中断控制器在接收到58号中断后,能将其进一步转发到CPU接口*/
    63. ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
    64. /*选择CPU0来处理58号中断*/
    65. ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x1 << 16);
    66. /*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
    67. CPU0.ICCICR = CPU0.ICCICR | 1;
    68. GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);//LED2
    69. while(1)
    70. {
    71. /*点亮LED2*/
    72. GPX2.DAT = GPX2.DAT | (1 << 7);
    73. /*延时*/
    74. Delay(1000000);
    75. /*熄灭LED2*/
    76. GPX2.DAT = GPX2.DAT & (~(1 << 7));
    77. /*延时*/
    78. Delay(1000000);
    79. }
    80. return 0;
    81. }

  • 相关阅读:
    8.1 矢量图层符号化整体说明
    MVCC与BufferPool缓存机制
    预测多基因扰动的转录结果
    (一)Win10安装MindSpore平台
    竹云荣膺2023十大数字化转型创新企业
    linux系统下,mysql增加用户
    企业为什么选择SDWAN代替MPLS?
    ShellCode_Loader - Msf&CobaltStrike免杀ShellCode加载器&加密工具
    【我的创作纪念日】使用pix2pixgan实现barts2020数据集的处理(完整版本)
    vue中的 render 和 h() 详解
  • 原文地址:https://blog.csdn.net/djjyy/article/details/134256575