NucleusPlus中断处理(基于ARM处理器)

1、中断优先级

1.1、中断优先级表

NucleusPlus中断优先级类似一个数组,数组的索引代表优先级,数组元素的值代表该优先级对应的中断号,0的优先级最高,优先级汇编语言表如下(仅是按中断号排序,移动中断号的位置即可调整中断的优先级):

INT_IRQ_Priority:
    .word       IRQ_EINT0
    .word       IRQ_EINT1
    .word       IRQ_EINT2
    .word       IRQ_EINT3
    .word       IRQ_EINT4_7
    .word       IRQ_EINT8_23
    .word       IRQ_INT_CAM
    .word       IRQ_nBATT_FLT
    .word       IRQ_INT_TICK
    .word       IRQ_INT_WDT_AC97
    .word       IRQ_INT_TIMER0
    .word       IRQ_INT_TIMER1
    .word       IRQ_INT_TIMER2
    .word       IRQ_INT_TIMER3
    .word       IRQ_INT_TIMER4
    .word       IRQ_INT_UART2
    .word       IRQ_INT_LCD
IRQ_PRIORITY_END:

1.2、中断向量表

中断向量表按中断号排序,依次存放中断处理函数的入口地址,汇编语言定义如下(s3c2440):

INT_IRQ_Vectors:
    .long     INT_Interrupt_Shell             // Vector 0
    .long     INT_Interrupt_Shell             // Vector 1
    .long     INT_Interrupt_Shell             // Vector 2
    .long     INT_Interrupt_Shell             // Vector 3
    .long     INT_Interrupt_Shell             // Vector 4
    .long     INT_Interrupt_Shell             // Vector 5
    .long     INT_Interrupt_Shell             // Vector 6
    .long     INT_Interrupt_Shell             // Vector 7
    .long     INT_Interrupt_Shell             // Vector 8
    .long     INT_Interrupt_Shell             // Vector 9
    .long     INT_Interrupt_Shell             // Vector 10
    .long     INT_Interrupt_Shell             // Vector 11
    .long     INT_Interrupt_Shell             // Vector 12
    .long     INT_Interrupt_Shell             // Vector 13
    .long     INT_Timer_Interrupt             // Vector 14
    .long     INT_Interrupt_Shell             // Vector 15
    .long     INT_Interrupt_Shell             // Vector 16
    .long     INT_Interrupt_Shell             // Vector 17
    .long     INT_Interrupt_Shell             // Vector 18
    .long     INT_Interrupt_Shell             // Vector 19
    .long     INT_Interrupt_Shell             // Vector 20
    .long     INT_Interrupt_Shell             // Vector 21
    .long     INT_Interrupt_Shell             // Vector 22
    .long     INT_Interrupt_Shell             // Vector 23
    .long     INT_Interrupt_Shell             // Vector 24
    .long     INT_Interrupt_Shell             // Vector 25
    .long     INT_Interrupt_Shell             // Vector 26
    .long     INT_Interrupt_Shell             // Vector 27
    .long     INT_Interrupt_Shell             // Vector 28
    .long     INT_Interrupt_Shell             // Vector 29
    .long     INT_Interrupt_Shell             // Vector 30
    .long     INT_Interrupt_Shell             // Vector 31
    .long     INT_Interrupt_Shell             // Vector 32

在此代码中仅用了INT_Timer_Interrupt用来实现操作系统调度定时器,其他中断默认调用INT_Interrupt_Shell。

2、中断入口查找

2.1、中断向量

硬件中断向量如下,IRQ处理函数为INT_Interrupt。

.global _start
_start:
        ldr pc, ResetAddr
        ldr pc, _undefined_instruction
        ldr pc, _software_interrupt
        ldr pc, _prefetch_abort
        ldr pc, _data_abort
        ldr pc, _not_used
        ldr pc, _irq // IRQ中断向量地址
        ldr pc, _fiq

ResetAddr:
        .word reset
_undefined_instruction:
        .word undefined_instruction
_software_interrupt:
        .word software_interrupt
_prefetch_abort:
        .word prefetch_abort
_data_abort:
        .word data_abort
_not_used:
        .word not_used
_irq:
        .word INT_Interrupt // IRQ中断处理函数
_fiq:
        .word fiq
_pad:
        .word 0x12345678 /* now 16*4=64 */

.global _end_vect
_end_vect:
        .balignl 16,0xdeadbeef

2.2、查找中断处理函数

    .globl  INT_Interrupt
INT_Interrupt:
    /* This Code is used to correctly handle interrupts and
        is necessary due to the nature of the ARM7 architecture  */
    STMDB   sp!, {r0-r4}

    SUB     lr,lr,#4

    LDR     r3, =0x4a000010             // INTPND(Interrupt request status)
    LDR     r2, [r3, #0]
    LDR     r3, =INT_IRQ_Priority

IRQ_VECTOR_LOOP:
    LDR     r0, [r3,#0]                 // Load first vector to be checked from priority table
    MOV     r1, #1                      // Build mask
    MOV     r1, r1, LSL r0              // Use vector number to set mask to correct bit position
    
    TST     r1, r2                      // Test if pending bit is set
    BNE     IRQ_VECTOR_FOUND            // If bit is set, branch to found section...
    
    ADD     r3, r3, #4                  // Move to next word in the priority table
    LDR     r0, =IRQ_PRIORITY_END       // Load the end address for the priority table
    CMP     r0, r3                      // Make sure not at the end of the table (shouldn't happen!)
    BNE     IRQ_VECTOR_LOOP             // Continue to loop if not at the end of the table

    // No bits in pending register set, restore context and exit interrupt servicing
    ADD     sp,sp,#4                    // Adjust sp above IRQ enable value
    LDMIA   sp!,{r0-r4}                 // Restore r0-r4
    STMDB   sp!,{lr}
    LDMIA   sp!,{pc}^
    MOV     pc,lr                       // return to the point of the exception

IRQ_VECTOR_FOUND:
    LDR     r3,=INT_IRQ_Vectors         // Get IRQ vector table address
    MOV     r2, r0, LSL #2              // Multiply vector by 4 to get offset into table
    ADD     r3, r3, r2                  // Adjust vector table address to correct offset
    LDR     r2, [r3,#0]                 // Load branch address from vector table

    MOV     PC, r2                      // Jump to correct branch location based on vector table

    /* END: INT_Interrupt */

第5行 保存通用寄存器r0-r4到中断栈里面;

第7行 修正中断返回地址;

第9~10行 读取s3c2440中断挂起寄存器的值(正等待处理的中断,每bit代表一个中断,bit0代表中断号0,bit1代表中断号1),r2 = INTPND;

第11行 获取中断优先级表INT_IRQ_Priority的地址,r3 = &INT_IRQ_Priority;

第14行 获取r3指向的优先级的中断号(r3用于从前往后遍历中断优先级表),r0 = 当前优先级的中断号;

第15~16行 计算当前优先级中断号对应的INTPND bit位,例如中断3发生了中断请求,那么INTPND[3]应为1(1<<3);

第18行 判断当前优先级中断对应的INTPND bit位是否设置(是否有中断请求),r1 & r2并且设置标记位;

第19行 根据前一结果进行跳转,r1 & r2结果为0,表示中断对应的bit位为0(没有中断请求),否则为1(有中断请求),跳转到IRQ_VECTOR_FOUND处理中断;

第21~24行 r3指针移动一个优先级,并判断r3是否已经移动到中断优先级表的末尾,没有的话,跳转到IRQ_VECTOR_LOOP接着判断下一优先级的中断是否产生;

第27~30行 恢复中断,"STMDB   sp!,{lr}"主要是用于下一条指令"LDMIA   sp!,{pc}^","LDMIA   sp!,{pc}^"带有pc寄存器,执行时将恢复spsr到cpsr;

第34~37行 获取中断号r0对应的中断函数地址,注释比较详细就不介绍每一行代码。

第39行 跳转到中断号对应的中断处理函数,即调用中断处理函数。

3、中断处理

3.1、定时器中断处理函数(INT_Timer_Interrupt)

       .globl  INT_Timer_Interrupt
INT_Timer_Interrupt:
    ldr     r1, =0x4a000000     // Interrupt Controller Address
    ldr     r2, [r1, #0x14]     // r2 = INTOFFSET
    mov     r3, #1
    lsl     r2, r3, r2          // r2 = (1 << INTOFFSET)
    ldr     r3, [r1, #0]        // r3 = SRCPND
    orr     r3, r3, r2
    str     r3, [r1, #0]        // SRCPND |= (1 << INTOFFSET)
    str     r3, [r1, #0x10]     // INTPND |= (1 << INTOFFSET)

    mov     r4,lr               // Put IRQ return address into r4

    bl      TCT_Interrupt_Context_Save

    bl      TMT_Timer_Interrupt              // Call the timer interrupt
                                                 //   processing.
    b       TCT_Interrupt_Context_Restore
    /* End of INT_Timer_Interrupt */

第3~10行 用于清除s3c2440定时器中断,清除中断方式参考s3c2440手册;

第12行 保存中断返回地址lr(pc),后面的bl指令会修改lr,需要先保存,另外函数内部默认不保存r0-r3的值,函数内部直接修改r0-r3,因此lr不能保存在r0-r3的寄存器里面;

第14行 保存中断上下文;

第16行 调用操作系统定时器处理函数;(任务时间片计数、timer计时...)

第18行 恢复中断上下文。

3.2、中断上下文保存(TCT_Interrupt_Context_Save)

//VOID  TCT_Interrupt_Context_Save(INT vector)
//{
        .globl  TCT_Interrupt_Context_Save
TCT_Interrupt_Context_Save:

    /* This routine is designed to handle ARM60/THUMB IRQ interrupts.  The IRQ
       stack is used as a temporary area.  Actual context is saved either
       on the interrupted thread's stack or the system stack- both of which
       are in the Supervisor (SVC) mode.  Note:  upon entry to this routine
       r0-r3 are saved on the current stack and r3 contains the original
       (interrupt return address) lr value.  The current lr contains the
       ISR return address.  */

    /* Determine if this is a nested interrupt.  */
        LDR     r1,Int_Count                // Pickup address of interrupt count
        LDR     r2,[r1, #0]                 // Pickup interrupt counter
        ADD     r2,r2,#1                    // Add 1 to interrupt counter
        STR     r2,[r1, #0]                 // Store new interrupt counter value
        CMP     r2,#1                       // Is it nested?
        BEQ     TCT_Not_Nested_Save         // No

/* Nested interrupt.  Save complete context on the current stack.  */
TCT_Nested_Save:


       /* 1.  Save another register on the exception stack so we have enough to work with */
        STMDB   sp!,{r5}

//       STMFD   sp!,{r0-r1}
//       LDR     R0,=NestX
//       ldr     r1,[r0]
//       add     r1,r1,#1
//       str     r1,[r0]
//       LDMFD   sp!,{r0-r1}

       /* 2.  Save the necessary exception registers into r1-r3 */
        MOV     r1,sp                       // Put the exception sp into r1
        MOV     r2,lr                       // Move the return address for the caller
                                            //  of this function into r2
        MRS     r3,spsr                     // Put the exception spsr into r3

       /* 3.  Adjust the exception stack pointer for future exceptions */
        ADD     sp,sp,#24                   // sp will point to enable reg value when done

       /* 4.  Switch CPU modes to save context on system stack */
        MRS     r5,CPSR                     // Pickup the current CPSR
        BIC     r5,r5,#MODE_MASK            // Clear the mode bits
        ORR     r5,r5,#SUP_MODE             // Change to supervisor mode (SVD)
        MSR     CPSR_cxsf,r5                // Switch modes (IRQ->SVC)

       /* 5.  Store the SVC sp into r5 so the sp can be saved as is. */
        MOV     r5,sp

       /* 6.  Save the exception return address on the stack (PC). */
        STMDB   r5!,{r4}

       /* 7.  Save r6-r14 on stack */
        STMDB   r5!,{r6-r14}

       /* 8.  Switch back to using sp now that the original sp has been saved. */
        MOV     sp,r5

       /* 9.  Get r5 and exception enable registers off of exception stack and
              save r5 (stored in r4) back to the system stack. */
//        LDMIA   r1!,{r4-r5}
        LDMIA   r1!,{r4}

        STMDB   sp!,{r4}
//        MOV     r4,r5                       ; Put exception enable value into r4

       /* 10. Get the rest of the registers off the exception stack and
              save them onto the system stack. */
        LDMIA   r1!,{r5-r8,r11}             // Get r0-r4 off exception stack
        STMDB   sp!,{r5-r8,r11}             // Put r0-r4 on system stack

       /* 11. Store the exception enable value back on the exception stack. */
//        STMDB   r1!,{r4}

       /* 12. Save the SPSR on the system stack (CPSR) */
        STMDB   sp!,{r3}


       /* 13. Re-enable interrupts */
        MRS     r1,CPSR
        BIC     r1,r1,#(IBIT|FBIT)
        MSR     CPSR_cxsf,r1


        BX      r2                          // Return to calling ISR
//    }
//    else
//    {
TCT_Not_Nested_Save:

       /* Determine if a thread was interrupted.  */
//       if (TCD_Current_Thread)
//       {

        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r1,[r1, #0]                 // Pickup the current thread pointer
        CMP     r1,#0                       // Is it NU_NULL?
        BEQ     TCT_Idle_Context_Save       // If no, no real save is necessary


            /* Yes, a thread was interrupted.  Save complete context on the
               thread's stack.  */

       /* 1.  Save another register on the exception stack so we have enough to work with */
        STMDB   sp!,{r5}

       /* 2.  Save the necessary exception registers into r1-r3 */
        MOV     r1,sp                       // Put the exception sp into r1
        MOV     r2,lr                       // Move the return address for the caller
                                            //  of this function into r2
        MRS     r3,spsr                     // Put the exception spsr into r3

       /* 3.  Adjust the exception stack pointer for future exceptions */
        ADD     sp,sp,#24                   // sp will point to enable reg value when done

       /* 4.  Switch CPU modes to save context on system stack */
        MRS     r5,CPSR                     // Pickup the current CPSR
        BIC     r5,r5,#MODE_MASK            // Clear the mode bits
        ORR     r5,r5,#SUP_MODE             // Change to supervisor mode (SVD)
        MSR     CPSR_cxsf,r5                // Switch modes (IRQ->SVC)

       /* 5.  Store the SVC sp into r5 so the sp can be saved as is. */
        MOV     r5,sp

       /* 6.  Save the exception return address on the stack (PC). */
        STMDB   r5!,{r4}

       /* 7.  Save r6-r14 on stack */
        STMDB   r5!,{r6-r14}

       /* 8.  Switch back to using sp now that the original sp has been saved. */
        MOV     sp,r5

       /* 9.  Get r5 and exception enable registers off of exception stack and
              save r5 (stored in r4) back to the system stack. */
//        LDMIA   r1!,{r4-r5}
        LDMIA   r1!,{r4}
        STMDB   sp!,{r4}
//        MOV     r4,r5                       ; Put exception enable value into r4

       /* 10. Get the rest of the registers off the exception stack and
              save them onto the system stack. */
        LDMIA   r1!,{r5-r8,r11}             // Get r0-r4 off exception stack
        STMDB   sp!,{r5-r8,r11}             // Put r0-r4 on system stack

       /* 11. Store the exception enable value back on the exception stack. */
//        STMDB   r1!,{r4}

       /* 12. Save the SPSR on the system stack (CPSR) */
        STMDB   sp!,{r3}

       /* 13. Save stack type to the task stack (1=interrupt stack) */
        MOV     r1,#1                       // Interrupt stack type
        STMDB   sp!,{r1}


        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r3,[r1, #0]                 // Pickup current thread pointer
        STR     sp,[r3, #0x2c]               // Save stack pointer

            /* Switch to the system stack.  */
//            REG_Stack_Ptr =  TCD_System_Stack;

        LDR     r1,System_Stack             // Pickup address of stack pointer
        LDR     r3,System_Limit             // Pickup address of stack limit ptr
        LDR     sp,[r1, #0]                 // Switch to system stack
        LDR     r10,[r3, #0]                // Setup system stack limit




       /* Re-enable interrupts */
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        MRS     r1,CPSR
        BIC     r1,r1,#(IBIT|FBIT)             //;;;;;;;now don't open the interrupt
        MSR     CPSR_cxsf,r1
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

            /* Return to caller ISR.  */
        BX      r2                          // Return to caller ISR

//       }

第15~20行 主要对中断嵌套计数器TCD_Interrupt_Count加1,并判断中断嵌套是否发生(TCD_Interrupt_Count不为1表示此次是中断嵌套,即之前正在执行中断处理函数过程中,发生了新的中断并且中断允许,之前的中断处理被打断),中断没有嵌套则跳转到TCT_Not_Nested_Save执行;

第27行 r5入栈(INT_Interrupt已经保存了r0-r4),此时栈里面的内容为{r5, r0-r4} (低地址到高地址);

第37~40行 IRQ栈保存到r1寄存器,TCT_Interrupt_Context_Save返回地址lr保存到r2寄存器,中断前的cpsr保存到r3(这几个寄存器都是专用的,模式切换后就不能直接访问了);

第43行 恢复IRQ的栈指针(中断上下文的r5,r0-r4保存在IRQ栈里面,共4 * 6 = 24个字节空间),至此IRQ栈已经恢复到中断前;

第46~49行 切换到SVC模式

第52行 SVC的栈指针保存到r5寄存器(中断处理程序最终是在SVC模式下执行的),对于中断嵌套,此处的sp是前一中断处理函数的sp,紧跟的代码不直接修改sp,通过r5间接操作SVC栈,第58行会把中断前的sp入栈;

第55行 INT_Timer_Interrupt函数保存的r4(lr中断返回地址)入栈,SVC栈里面的内容为{lr(pc)};

第58行 r6-r14入栈,SVC栈里面的内容为{r6-r14, lr(pc)} (低地址到高地址)

第61行 更新栈sp指针(sp已经保存了,此后可以通过sp直接操作SVC栈);

第66~68行 r5出IRQ栈入SVC栈(r1指向IRQ保存中断上下文栈的地址),SVC栈里面的内容为{r5,r6-r14, lr(pc)} (低地址到高地址);

第73~74 r0-r4出IRQ栈入SVC栈,SVC栈里面的内容为{r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第80行 第40行保存的cpsr入栈,SVC栈里面的内容为{cpsr,r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第84~89行 使能中断并返回TCT_Interrupt_Context_Save上一级函数,至此已经保存完了嵌套中断的上下文,第89行之后的为非中断嵌套上下文保存;

第99~102行 判断中断前允许的任务是否为Idle任务,是的话跳转到TCT_Idle_Context_Save保存Idle任务的上下文,第108行起保存被中断的任务的上下文;

第109行 r5入IRQ栈,SVC栈里面的内容为{r5,r0-r4} (低地址到高地址);

第112~154行 模式切换,中断上下文保存到SVC栈(此时的栈正好是被中断任务的栈),出入栈代码与中断嵌套出入栈代码一样;

第157~158行 1入任务栈栈顶,任务栈里面的内容为{1,cpsr,r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第161~163行 任务栈保存到TCD_Current_Thread->tc_stack_pointer里面(与中断嵌套不一样,中断恢复是一层层恢复栈,任务栈是下次调度时恢复,中断处理之后,当前任务可能被换出cpu,需要知道任务的上下文保存在哪里,因此将任务栈保存在任务控制块的一个变量里面);

第168~171行 设置栈、栈帧指针(保存完中断上下文后的代码运行在System_Stack里面,这有个好处,中断进入时都设置一下,中断退出的时候不用记得恢复栈,下次进入又会重新设置,因此不会造成栈内存泄漏);

第176~184行 使能中断并返回TCT_Interrupt_Context_Save上一级函数接着调用中断处理函数;

3.3、Idle任务上下文保存

Idle任务其实并不存在的,以单任务为例,任务进入睡眠状态,首先任务保存自己的上下文,然后调用TCT_Control_To_System,使用新的栈不断检测是否有任务需要运行,中断返回时仍没有任务需要运行,又会通过其他路径重新设置任务栈,每次Idle都重新执行,因此Idle任务栈的保存TCT_Idle_Context_Save仅仅恢复IRQ栈及使能中断。

TCT_Idle_Context_Save:

        MOV     r2,lr                       // Save lr in r2
//        LDR     r3,[sp]                     ; Get exception enable value from stack
        ADD     sp,sp,#20                   // Adjust exception sp for future interrupts
//        STR     r3,[sp]                     ; Put exception enable value back on stack


        MRS     r1,CPSR                     // Pickup current CPSR
        BIC     r1,r1,#MODE_MASK            // Clear the current mode
        BIC     r1,r1,#(IBIT|FBIT)     // Re-enable interrupts
        ORR     r1,r1,#SUP_MODE             // Prepare to switch to supervisor
                                            //   mode (SVC)
        MSR     CPSR_cxsf,r1                // Switch to supervisor mode (SVC)


        BX      r2                          // Return to caller ISR
//    }
//}

3.4、中断上下文恢复

//VOID  TCT_Interrupt_Context_Restore(void)
//{
        .globl  TCT_Interrupt_Context_Restore
TCT_Interrupt_Context_Restore:

    /* It is assumed that anything pushed on the stack by ISRs has been
       removed upon entry into this routine.  */

    /* Decrement and check for nested interrupt conditions.  */
//    if (--TCD_Interrupt_Count)
//    {

        LDR     r1,Int_Count                // Pickup address of interrupt count
        LDR     r2,[r1, #0]                 // Pickup interrupt counter
        SUB     r2,r2,#1                    // Decrement interrupt counter
        STR     r2,[r1, #0]                 // Store interrupt counter
        CMP     r2,#0
        BEQ     TCT_Not_Nested_Restore

        /* Restore previous context.  */

        LDR     r1,[sp], #4                 // Pickup the saved CPSR
        MSR     SPSR_cxsf,r1                // Place into saved SPSR
        LDMIA   sp,{r0-r15}^                // Return to the point of interrupt

//    }
//    else
//    {

TCT_Not_Nested_Restore:

        /* Determine if a thread is active.  */
//        if (TCD_Current_Thread)
//        {

        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r0,[r1, #0]                 // Pickup current thread pointer
        CMP     r0,#0                       // Determine if a thread is active
        BEQ     TCT_Idle_Context_Restore    // If not, idle system restore

            /* Clear the current thread pointer.  */
//            TCD_Current_Thread =  NU_NULL;

        MOV     r2,#0                       // Build NU_NULL value
        STR     r2,[r1, #0]                 // Set current thread ptr to NU_NULL

            /* Determine if a time slice is active.  If so, the remaining
               time left on the time slice must be saved in the task's
               control block.  */
//            if (TMD_Time_Slice_State == 0)
//            {

        LDR     r3,Slice_State              // Pickup time slice state address
        LDR     r1,[r3, #0]                 // Pickup time slice state
        CMP     r1,#0                       // Determine if time slice active
        BNE     TCT_Idle_Context_Restore    // If not, skip time slice reset

                /* Pickup the remaining portion of the time slice and save it
                   in the task's control block.  */
//                REG_Thread_Ptr -> tc_cur_time_slice =  TMD_Time_Slice;
//                TMD_Time_Slice_State =  1;

        LDR     r2,Time_Slice               // Pickup address of time slice left
        MOV     r1,#1                       // Build disable time slice value
        LDR     r2,[r2, #0]                 // Pickup remaining time slice
        STR     r1,[r3, #0]                 // Disable time slice
        STR     r2,[r0, #0x20]               // Store remaining time slice

//            }
//        }
TCT_Idle_Context_Restore:

        /* Reset the system stack pointer.  */
        LDR     r1,System_Stack             // Pickup address of stack pointer
        LDR     r2,System_Limit             // Pickup address of stack limit ptr
        LDR     sp,[r1, #0]                 // Switch to system stack
        LDR     r10,[r2, #0]                // Setup system stack limit

        /* Return to scheduler.  */

        B       TCT_Schedule                // Return to scheduling loop

//    }
//}

第13~18行 中断嵌套计数器TCD_Interrupt_Count减1并判断是否是中断嵌套,非中断嵌套跳转到TCT_Not_Nested_Restore恢复任务上下文(此次被中断的是任务);

第22~24行 取出中断前的cpsr,恢复被嵌套的中断上下文,接着处理未处理完的中断代码;

第36~39行 获取TCD_Current_Thread,判断中断时是否有任务在运行,没有任务在运行则跳转到TCT_Idle_Context_Restore;

第44~45行 设置TCD_Current_Thread = NULL;

第53~56行 检查时间片状态是否激活,激活情况,每个时钟中断会对任务时间片减1,没有激活,那么当前任务的时间片不用处理;

第63~67行 去激活时间片状态,保存当前任务已经运行的时间片,下次再次调度该任务时,时间片继续按当前时间执行,否则如果每次都从一个完整时间片运行,那么经常被中断的任务拥有的执行时间可能就会很长;(去激活时间片后到保存时间片前可能被中断,去激活时间片后,任务的时间片不再更新)

第73~81行 设置System栈、栈帧指针,跳转到TCT_Schedule执行任务调度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值