Chinaunix首页 | 论坛 | 博客
  • 博客访问: 282368
  • 博文数量: 60
  • 博客积分: 2501
  • 博客等级: 少校
  • 技术积分: 774
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-16 13:27
文章分类

全部博文(60)

文章存档

2011年(1)

2010年(1)

2009年(58)

我的朋友

分类: 嵌入式

2009-07-19 11:11:10

                                        2009-7-19

4UCOSII移植中软中断的理解与应用

再回到我们的ARM7的移植中,网上有篇文章写的已经很好了,在这里摘录下,原文地址:

http://blog.21ic.com/user1/4663/archives/2008/48268.html

原文内容:

ARM的移植代码吧首先定义了一堆软中断的中断号,其中01的中断服务子程序是用汇编编写的,

其他的都是在c语言编写的中断服务子程序SWI_Exception中。

__swi(0x00) void OS_TASK_SW(void); /*  任务级任务切换函数          */

__swi(0x01) void _OSStartHighRdy(void);/*  运行优先级最高的任务        */

__swi(0x02) void OS_ENTER_CRITICAL(void);/*  关中断                      */

__swi(0x03) void OS_EXIT_CRITICAL(void);/*  开中断                      */

__swi(0x40) void *GetOSAddr(int Index);/*  获取系统服务函数入口        */

__swi(0x41) void *GetUsrAddr(int Index);/*  获取自定义服务函数入口      */

__swi(0x42) void OSISRBegin(void);/*  中断开始处理                */

__swi(0x43) int  OSISRNeedSwap(void);/*  判断中断是否需要切换        */

__swi(0x80) void ChangeToSYSMode(void);/*  任务切换到系统模式          */

__swi(0x81) void ChangeToUSRMode(void);/*  任务切换到用户模式          */

__swi(0x82) void TaskIsARM(INT8U prio);/*  任务代码是ARM代码           */

__swi(0x83) void TaskIsTHUMB(INT8U prio);/*  任务代码是THUMB             */

 

比如在程序运行到调用OS_TASK_SW(void)函数时,就产生软件中断,然后就进入中断服务子程序,

按照什么指令走呢?恩,就按照下面这个代码,这个代码是将软件中断异常处理程序挂接到内核

的作用的,是在启动代码中实现的:

LDR   PCSWI_Addr   

SWI_Addr   DCD     SoftwareInterrupt

因此当产生软中断之后PC就跳到了SoftwareInterrupt,这时就算真正进入了软件异常中断处理部

分了,然后就是执行下面的汇编代码SoftwareInterrupt

LDR     SP, StackSvc

/*重新设置堆栈指针*/

STMFD   SP!, {R0-R3, R12, LR}

/*保存 R0,R1,R2,R3,R12,LR(R14),注意为什么只保存这几个

寄存器呢,因为R4-R11存储局部变量,编译器自动保护他们*/

MOV     R1, SP           /*   R1指向参数存储位置   */

MRS     R3, SPSR         /*保存管理模式的状态寄存器*/

TST     R3, #T_bit       /*  中断前是否是Thumb状态 */

LDRNEH  R0, [LR,#-2]     /* 若是,取得Thumb状态SWI*/

BICNE   R0, R0, #0xff00  /*THUMB指令SWI功能号为8 */

LDREQ   R0, [LR,#-4]     /* 为零即ARM指令取得SWI */

BICEQ   R0, R0, #0xFF000000/*ARM指令集中SWI功能号为24位所以高8位清零r0=SWI*/

CMP     R0, #1           /*

LDRLO   PC, =OSIntCtxSw  /*        疑惑ing         */

/*     功能号为0OSIntCtxSw执行中断任务切换函数   */

LDREQ   PC, =__OSStartHighRdy/*SWI1第一次任务切换*/

BL      SWI_Exception    /*否则进入c编写的中断函数 */

LDMFD   SP!, {R0-R3, R12, PC}/*R0-R3R12LR出栈  */

StackSvc

 DCD  (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

 

怎么进入c编写的中断服务子程序SWI_Exception呢?通过下面的申明

 

IMPORT  SWI_Exception               ;软中断异常处理程序

表示将c程序中的该函数挂接到此段汇编代码中,同样的道理

EXPORT  __OSStartHighRdy

EXPORT  OSIntCtxSw                  ;中断退出时的入口

参见startup.s中的IRQ_Handler

EXPORT  SoftwareInterrupt           ;软中断入口上面的申明是将该段汇编代码挂接到外面,

因此在外部可以直接调用函数名

继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSwOSStarHighRdyOSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码OSIntCtxSw

     ;下面为保存任务环境   

;当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。

        LDR     R2, [SP, #20]                       ;获取PC

        LDR     R12, [SP, #16]                      ;获取R12

        MRS     R0, CPSR        MSR     CPSR_c, #(NoInt | SYS32Mode)

        MOV     R1, LR

        STMFD   SP!, {R1-R2}                        ;保存LR,PC

        STMFD   SP!, {R4-R12}                       ;保存R4-R12        MSR     CPSR_c, R0

        LDMFD   SP!, {R4-R7}                        ;获取R0-R3

        ADD     SP, SP, #8                          ;出栈R12,PC

        MSR     CPSR_c, #(NoInt | SYS32Mode)

        STMFD   SP!, {R4-R7}                        ;保存R0-R3

        LDR     R1, =OsEnterSum                     ;获取OsEnterSum

        LDR     R2, [R1]

        STMFD   SP!, {R2, R3}   ;保存CPSR,OsEnterSum  ;保存当前任务堆栈指针到当前任务的TCB

        LDR     R1, =OSTCBCur

        LDR     R1, [R1]

        STR     SP, [R1]        BL      OSTaskSwHook        ;调用钩子函数

                                                    ;OSPrioCur <= OSPrioHighRdy

        LDR     R4, =OSPrioCur

        LDR     R5, =OSPrioHighRdy

        LDRB    R6, [R5]

        STRB    R6, [R4]          ;OSTCBCur <= OSTCBHighRdy

        LDR     R6, =OSTCBHighRdy

        LDR     R6, [R6]

        LDR     R4, =OSTCBCur

        STR     R6, [R4]

OSIntCtxSw_1

                                                    ;获取新任务堆栈指针

        LDR     R4, [R6]

        ADD     SP, R4, #68                         ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

        LDR     LR, [SP, #-8]

        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式

        MOV     SP, R4    ;设置堆栈指针   

    LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum

                                                    ;恢复新任务的OsEnterSum

        LDR     R3, =OsEnterSum

        STR     R4, [R3]

        MSR     SPSR_cxsf, R5                       ;恢复CPSR

        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务

 

__OSStartHighRdy

        MSR     CPSR_c, #(NoInt | SYS32Mode)    ;调整到管理模式

                                                ;告诉uC/OS-II自身已经运行

        LDR     R4, =OSRunning

        MOV     R5, #1

        STRB    R5, [R4]   ;标记多任务运行标记为真        BL      OSTaskSwHook                    ;调用钩子函数,可以运行用户自定义的函数        LDR     R6, =OSTCBHighRdy    ;R6存有最高优先级的就绪任务的控制块地址

        LDR     R6, [R6]

        B       OSIntCtxSw_1     ;转到前面编写的中断返回函数块的任务跳转部分的代码,因为这两个函数都要用到这部分代码,进入这段代码之前高优先级的就绪任务的任务控制快地址存在R6中。        AREA    SWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;管理模式堆栈空间 OSIntCtxSw_1的代码:OSIntCtxSw_1

                                                    ;获取新任务堆栈指针

        LDR     R4, [R6] ;任务控制块的堆栈指针放在R6中,现在放在R4

        ADD     SP, R4, #68                         ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

        LDR     LR, [SP, #-8]

        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式

        MOV     SP, R4           ;设置堆栈指针,R4存有没有改动过的堆栈指针      

 LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum

                                                    ;恢复新任务的OsEnterSum

        LDR     R3, =OsEnterSum

        STR     R4, [R3]

        MSR     SPSR_cxsf, R5                       ;恢复CPSR

        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务,恢复现场,异常处理返回;中断返回指令的寄存器列表其中必须包括PC后的^符号,表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时,CPSR也得到恢复。这里使用的堆栈指针SP是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。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)OSIntCtxSw

                                                    ;下面为保存任务环境

        LDR     R2, [SP, #20]                       ;获取PCLR

        LDR     R12, [SP, #16]                      ;获取R12

        MRS     R0, CPSR        MSR     CPSR_c, #(NoInt | SYS32Mode)

        MOV     R1, LR

        STMFD   SP!, {R1-R2}                        ;保存LR,PC

        STMFD   SP!, {R4-R12}                       ;保存R4-R12        MSR     CPSR_c, R0

        LDMFD   SP!, {R4-R7}                        ;获取R0-R3

        ADD     SP, SP, #8                          ;出栈R12,PC

        MSR     CPSR_c, #(NoInt | SYS32Mode)

        STMFD   SP!, {R4-R7}                        ;保存R0-R3

        LDR     R1, =OsEnterSum                     ;获取OsEnterSum

        LDR     R2, [R1]

        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum                                                    ;保存当前任务堆栈指针到当前任务的TCB

        LDR     R1, =OSTCBCur

        LDR     R1, [R1]

        STR     SP, [R1]        BL      OSTaskSwHook                        ;调用钩子函数

                                                    ;OSPrioCur <= OSPrioHighRdy

        LDR     R4, =OSPrioCur

        LDR     R5, =OSPrioHighRdy

        LDRB    R6, [R5]

        STRB    R6, [R4]             ;OSPrioHighRdy最高优先级的就绪任务传给OSPrioCur

                                                    ;OSTCBCur <= OSTCBHighRdy

        LDR     R6, =OSTCBHighRdy

        LDR     R6, [R6]

        LDR     R4, =OSTCBCur

        STR     R6, [R4]         ;将最高优先级的任务控制块指针传给当前任务控制块指针

 

关于中断和时钟节拍,UCOS-II对于ARM7通用的中断服务程序的汇编与c函数接口如下:MACROMEND伪指令用于宏定义,MACRO标识宏定义的开始,MEND标识宏定义的结束。定义之后在程序中就可以通过宏指令多次调用该段代码MACRO

$IRQ_Label HANDLER $IRQ_Exception_        EXPORT  $IRQ_Label                      ; 输出的标号

        IMPORT  $IRQ_Exception_         ; 引用的外部标号$IRQ_Label

        SUB     LR, LR, #4                      ; 计算返回地址

        STMFD   SP!, {R0-R3, R12, LR}           ; 保存任务环境

        MRS     R3, SPSR                        ; 保存状态

        STMFD   SP, {R3, SP, LR}^               ; 保存用户状态的R3,SP,LR,注意不能回写

                                                ; 如果回写的是用户的SP,所以后面要调整SP

        LDR     R2,  =OSIntNesting              ; OSIntNesting++

        LDRB    R1, [R2]

        ADD     R1, R1, #1

        STRB    R1, [R2]        SUB     SP, SP, #4*3

        MSR     CPSR_c, #(NoInt | SYS32Mode)    ; 切换到系统模式

        CMP     R1, #1

        LDREQ   SP, =StackUsr

        BL      $IRQ_Exception_         ; 调用c语言的中断处理程序        MSR     CPSR_c, #(NoInt | SYS32Mode)    ; 切换到系统模式

        LDR     R2, =OsEnterSum                 ; OsEnterSum,使OSIntExit退出时中断关闭

        MOV     R1, #1

        STR     R1, [R2]        BL      OSIntExit        LDR     R2, =OsEnterSum                 ; 因为中断服务程序要退出,所以OsEnterSum=0

        MOV     R1, #0

        STR     R1, [R2]        MSR     CPSR_c, #(NoInt | IRQ32Mode)    ; 切换回irq模式

        LDMFD   SP, {R3, SP, LR}^               ; 恢复用户状态的R3,SP,LR,注意不能回写

                                                ; 如果回写的是用户的SP,所以后面要调整SP

        LDR     R0, =OSTCBHighRdy

        LDR     R0, [R0]

        LDR     R1, =OSTCBCur

        LDR     R1, [R1]

        CMP     R0, R1        ADD     SP, SP, #4*3                    ;

        MSR     SPSR_cxsf, R3

        LDMEQFD SP!, {R0-R3, R12, PC}^          ; 不进行任务切换

        LDR     PC, =OSIntCtxSw                 ; 进行任务切换

    MEND二:OS_CPU_C.C    个文件中要求用户编写10个简单的C函数,但是只有1个函数是必要的,其余的函数必须声明,但不一定要包含任何代码,大致看了一下作用好像是用来调试之类的。唯一要编写的是OSTaskStkInit()    OSTaskStkInit()函数的功能是初始化任务的栈结构,任务的堆栈结构与CPU的体系结构、编译器有密切的关联。从ARM的结构可以写出如下的栈结构:程序计数器PC,程序链接器LRR12R1R0用于传递第一个参数pdataCPSR/SPSR,关中断计数器(用于计算关中断的次数,这样就实现了中断的嵌套),返回的地址指针是指向的最后一个存入的数据,而不是一个空地址。软件中断异常SWI服务程序C语言部分    void SWI_Exception(int SWI_Num, int *Regs):参数SWI_Num对应前面文件中定义的功能号,其中01号的功能在后面的文件中定义,这里只定义了其他10个功能。    23分别对应关中断和开中断    关中断:MRS     R0, SPSR         //在软件中断的时候直接对程序状态保存寄存器SPSR操作也就是对CPSR的操作

                   ORR     R0, R0, #NoInt    //在汇编语言中对寄存器的对应位置位用ORR,清零用BIC

                   MSR     SPSR_c, R0        //SPSR_c表示的是只改变SPSR的控制段的8位代码,其他三段_f_s_x中标志位在_f段,其他为保留位    开中断:MRS     R0, SPSR       //在开中断中基本与上面相同,只是ORR改成BIC清零

                   BIC     R0, R0, #NoInt

                   MSR     SPSR_c, R    由于需要实现中断嵌套,所以只有当关中断的计数器减为0的时候才能够开中断,而且每次关中断的时候该计数器都应该加1。另外,插入汇编语言时用_asm指令。    80818283分别对应系统模式、用户模式、ARM指令集、THUMB指令集    系统模式:MRS     R0, SPSR

                        BIC     R0, R0, #0x1f     //先将控制模式的低5位清零

                        ORR     R0, R0, #SYS32Mode       //设置成系统模式的1F

                        MSR     SPSR_c, R0    用户模式:MRS     R0, SPSR

                        BIC     R0, R0, #0x1f

                        ORR     R0, R0, #USR32Mode     //设置成用户模式的10

                        MSR     SPSR_c, R0    ARM指令集与THUMB指令集的代码如下:    ptcb = OSTCBPrioTbl[Regs[0]];

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);

                }    ptcb = OSTCBPrioTbl[Regs[0]];

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] |= (1 << 5);

                }    昨天就是看到这里,出现了一个意识到是不能忽悠的地方就是UCOS里面的任务控制块OS_TCB的概念,因此今天的任务就是把这部分看看。。。    大概回忆了一下昨天晚上的工作,开始今天的工作吧

 

一点一点来,什么不会就学什么,都不会就都学。。。没有问题只要你肯努力。。。。。。__OSStartHighRdy

        MSR     CPSR_c, #(NoInt | SYS32Mode)  ;MSR:在ARM中只有MSR能够直接设置状态寄存器CPSRSPSR,可以是立即数或者源寄存器,NoInt是禁止中断,SYS32Mode是系统模式

                                                ;告诉uC/OS-II自身已经运行

        LDR     R4, =OSRunning  ;OSRunning正在运行多任务的标志,=OSRunning是把OSRunning的地址加载到R4R4里存的是一个地址。。。

        MOV     R5, #1

        STRB    R5, [R4]   ;R5存储到R4存的地址的变量即OSRunning中,也就是将OSRunning1        BL      OSTaskSwHook                    ;调用钩子函数,OSTaskSwHook 是用于扩展的,在任务切换的时候执行用户自己定义的功能。        LDR     R6, =OSTCBHighRdy    ;OSTCBHighRdy指向最高优先级任务的控制块TCB的指针!!!将放指针的地址放到R6中。

        LDR     R6, [R6]  ;R6地址处的数据读出即OSTCBHighRdy的地址放到R6

        B       OSIntCtxSw_1   ;跳转到OSIntCtxSw_1         AREA    SWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;管理模式堆栈空间继续昨天没有看完的代码OSIntCtxSw

                                                    ;下面为保存任务环境

        LDR     R2, [SP, #20]                       ;获取PC,放入R2

        LDR     R12, [SP, #16]                      ;获取R12//R12存的什么东西啊???

        MRS     R0, CPSR        MSR     CPSR_c, #(NoInt | SYS32Mode)   ;进入系统模式并禁止中断

        MOV     R1, LR    ;R1LR

        STMFD   SP!, {R1-R2}                        ;保存LR,PC,将R1R2存入SP

        STMFD   SP!, {R4-R12}                       ;保存R4-R12,将R4-12存入SP        MSR     CPSR_c, R0   ;再回到之前的模式

        LDMFD   SP!, {R4-R7}                        ;获取R0-R3

        ADD     SP, SP, #8                          ;出栈R12,PC

        MSR     CPSR_c, #(NoInt | SYS32Mode)

        STMFD   SP!, {R4-R7}                        ;保存R0-R3

        LDR     R1, =OsEnterSum                     ;获取OsEnterSum

        LDR     R2, [R1]

        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum                                                    ;保存当前任务堆栈指针到当前任务的TCB

        LDR     R1, =OSTCBCur

        LDR     R1, [R1]

        STR     SP, [R1]        BL      OSTaskSwHook                        ;调用钩子函数

                                                    ;OSPrioCur <= OSPrioHighRdy

        LDR     R4, =OSPrioCur

        LDR     R5, =OSPrioHighRdy

        LDRB    R6, [R5]

        STRB    R6, [R4]

                                                    ;OSTCBCur <= OSTCBHighRdy

        LDR     R6, =OSTCBHighRdy

        LDR     R6, [R6]

        LDR     R4, =OSTCBCur

        STR     R6, [R4]

OSIntCtxSw_1

                                                    ;获取新任务堆栈指针

        LDR     R4, [R6]      ;OSTCBHighRdy指向最高优先级任务的控制块TCB的指针给R4

        ADD     SP, R4, #68                         ;17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP

        LDR     LR, [SP, #-8]   ;取出LR放到LR

        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式并且保持禁止中断

        MOV     SP, R4                              ;设置堆栈指针        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSumLDMFD数据出栈,放入R4R5

                                                    ;恢复新任务的OsEnterSum

        LDR     R3, =OsEnterSum   ;OsEnterSum的地址存入R3

        STR     R4, [R3]   ;R4的值赋给OsEnterSum

        MSR     SPSR_cxsf, R5                       ;恢复CPSR;在管理模式里是修改SPSR

        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务  ,恢复现场,异常处理返回

 

5OS_STK_GROWTH

ARM处理器核对堆栈的两种生长方式都支持,但ADS编译器只支持一种 #define OS_STK_GROWTH      1

阅读(1700) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~