Board logo

标题: NXP的ARM7带ucos中硬中断与软中断响应详细分析 [打印本页]

作者: yuyang911220    时间: 2017-2-20 16:50     标题: NXP的ARM7带ucos中硬中断与软中断响应详细分析

一.带UCOS系统的软中断响应过程    1
1.第一步:    2
2.第二步:    2
二.带UCOS系统的硬中断响应过程    6





  下面的主要分析LPC系列ARM7的中断响应,以周立公老师带ucos移植程序为分析对象,对其他的ARM中带UCOS的项目也有参考价值。

一.带UCOS系统的软中断响应过程
UCOS操作系统是以任务为单元的执行块,可以理解为linux中线程,任务需要进行切换,轮巡的执行,这种任务切换是通过ARM的软中断来实现的。ARM的软中断是在向量表中就存在,写在startup.s文件中,如:
;interrupt vectors
;中断向量表
Reset
        LDR     PC, ResetAddr
        LDR     PC, UndefinedAddr
        LDR     PC, SWI_Addr
        LDR     PC, PrefetchAddr
        LDR     PC, DataAbortAddr
        DCD     0xb9205f80
        LDR     PC, [PC, #-0xff0]
        LDR     PC, FIQ_Addr

这个向量表是ARM具备的,放在程序的开始位置,具体怎么放置和定位,要在编译时候设置分散定位表(这里不仔细讲),ARM复位启动的时候就会自动跑到开始行PC, ResetAddr。第三行LDR    PC, SWI_Addr为软中断响应行。附带说明,这个向量表程序在编译后的地址(程序在运行时可以看到汇编程序显示的每行地址,也就是常说的中断向量表中所有数据32位累加和为0)累加和为0,所以可以看到程序中为什么有DCD     0xb9205f80这行,数字是手动可以调整的。
  软中断和硬中断如果从程序角度分析,其实是一样的,都是响应的一种中断,响应开始要求环境和其他变量入桟保护起来,然后调用到中断响应的C程序中(就是自己写中断响应程序),执行完中断响应程序后,再进行堆栈的出桟,恢复之前保护的环境与变量,再掉转程序到开始进入中断前的位置,这样程序接着运行。其实这个过程,跟51一样,软中断与硬中断也一样。那软中断和硬中断到底有什么不同,软中断是可以人为控制的,硬中断是ARM芯片里面控制的,自动反应,如串口中断,是串口收到数据后,串口自动产生中断。软中断是通过手动调用程序后产生的中断。以我的理解,不知道是否还有其他区别。
  现在我们具体可以分析软中断响应过程了:
1.第一步:
在UCOS中,当要进行任务切换,直接手动调用OS_TASK_SW()函数的调用,调用之后,相当执行了os_cpu.h文件中的
__swi(0x00) void OS_TASK_SW(void);            /*  任务级任务切换函数          */
这是ADS编译器可以认识的软中断执行程序。这样系统会响应软中断,程序会跳到上面讲过的startup.s文件中断向量表中LDR     PC, SWI_Addr,
2.第二步:
在startup.s文件中有SWI_Addr            DCD     SoftwareInterrupt,相当再跳到SoftwareInterrupt函数,这个函数在OS_cpu_a.s文件中:
;软件中断
SoftwareInterrupt
        LDR     SP, StackSvc            ; 重新设置堆栈指针
        STMFD   SP!, {R0-R3, R12, LR}
        MOV     R1, SP                  ; R1指向参数存储位置

        MRS     R3, SPSR
        TST     R3, #T_bit              ; 中断前是否是Thumb状态
        LDRNEH  R0, [LR,#-2]            ; 是: 取得Thumb状态SWI号
        BICNE   R0, R0, #0xff00
        LDREQ   R0, [LR,#-4]            ; 否: 取得arm状态SWI号
        BICEQ   R0, R0, #0xFF000000
                                        ; r0 = SWI号,R1指向参数存储位置
        CMP     R0, #1
        LDRLO   PC, =OSIntCtxSw
        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换

        BL      SWI_Exception
        
        LDMFD   SP!, {R0-R3, R12, PC}^
        
StackSvc           DCD     (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

这个函数,是自己写的,可以进行改动。这其中就是上面说过的环境的一些入桟,出桟,还有查询是否有更高优先级任务的响应。软中断响应时因为有不同的中断,如关OS_ENTER_CRITICAL,开OS_EXIT_CRITICAL等很多软中断,所以程序执行上面中的SWI_Exception执行自己的外部中断C响应程序,里面实现具体的软中断:
void SWI_Exception(int SWI_Num, int *Regs)
{
    OS_TCB   *ptcb;
   
    switch(SWI_Num)
    {
        //case 0x00:      /* 任务切换函数OS_TASK_SW,参考os_cpu_s.s文件     */
        //    break;
        //case 0x01:      /* 启动任务函数OSStartHighRdy,参考os_cpu_s.s文件 */
        //    break;
        case 0x02:       /* 关中断函数OS_ENTER_CRITICAL(),参考os_cpu.h文件 */
            __asm
            {
                MRS     R0, SPSR
                ORR     R0, R0, #NoInt
                MSR     SPSR_c, R0
            }
            OsEnterSum++;
            break;
        case 0x03:       /* 开中断函数OS_EXIT_CRITICAL(),参考os_cpu.h文件 */
            if (--OsEnterSum == 0)
            {
                __asm
                {
                    MRS     R0, SPSR
                    BIC     R0, R0, #NoInt
                    MSR     SPSR_c, R0
                }
            }
            break;
#if OS_SELF_EN > 0
        case 0x40:
                                        /* 返回指定系统服务函数的地址       */
                                        /* 函数地址存于数组_OSFunctionAddr中*/
                                        /* 数组_OSFunctionAddr需要另外定义  */
                                        /* Regs[0] 为第一个参数,也是返回值 */
                                        /* Regs[1] 为第二个参数             */
                                        /* Regs[2] 为第三个参数             */
                                        /* Regs[3] 为第四个参数             */
                                        /* 仅有一个参数为系统服务函数的索引 */
            Regs[0] =  _OSFunctionAddr[Regs[0]];
            break;
        case 0x41:
                                        /* 返回指定用户的服务函数的地址     */
                                        /* 函数地址存于数组_UsrFunctionAddr中*/
                                        /* 数组_UsrFunctionAddr需要另外定义 */
                                        /* Regs[0] 为第一个参数,也是返回值 */
                                        /* Regs[1] 为第二个参数             */
                                        /* Regs[2] 为第三个参数             */
                                        /* Regs[3] 为第四个参数             */
                                        /* 仅有一个参数为用户服务函数的索引 */
            Regs[0] =  _UsrFunctionAddr[Regs[0]];
            break;
        case 0x42:                      /* 中断开始处理 */
            OSIntNesting++;
            break;
        case 0x43:                      /*  判断中断是否需要切换             */
            if (OSTCBHighRdy == OSTCBCur)
            {
                Regs[0] = 0;
            }
            else
            {
                Regs[0] = 1;
            }
            break;
#endif
        case 0x80:                      /* 任务切换到系统模式 */
            __asm
            {
                MRS     R0, SPSR
                BIC     R0, R0, #0x1f
                ORR     R0, R0, #SYS32Mode   
                MSR     SPSR_c, R0
            }
            break;
        case 0x81:                      /* 任务切换到用户模式 */
            __asm
            {
                MRS     R0, SPSR
                BIC     R0, R0, #0x1f
                ORR     R0, R0, #USR32Mode   
                MSR     SPSR_c, R0
            }
            break;
        case 0x82:                      /* 任务是ARM代码 */
            if (Regs[0] <= OS_LOWEST_PRIO)
            {
                ptcb = OSTCBPrioTbl[Regs[0]];
                if (ptcb != NULL)
                {
                    ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);
                }
            }
            break;
        case 0x83:                      /* 任务是THUMB代码 */
            if (Regs[0] <= OS_LOWEST_PRIO)
            {
                ptcb = OSTCBPrioTbl[Regs[0]];
                if (ptcb != NULL)
                {
                    ptcb -> OSTCBStkPtr[1] |= (1 << 5);
                }
            }
            break;
        default:
            break;
    }
}




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0