久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4138|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

μc/os<->STM32完整

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:104287 發(fā)表于 2016-1-31 01:01 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
       P80:μc/osⅡ時鐘
  硬件定時器中斷(使用了STM32中的Systick中斷)每產(chǎn)生一次,μc/osⅡ時鐘就會進(jìn)入一次系統(tǒng)中斷服務(wù)程序(OSTickISR()),系統(tǒng)中斷服務(wù)程序通過調(diào)用OSTimeTick()來完成系統(tǒng)每個時鐘節(jié)拍所要完成的工作(包括遍歷每個任務(wù)控制塊將其延時參數(shù)減1等)。
P87:鉤子函數(shù)
鉤子函數(shù)的使用(以O(shè)STimerTickHook為例):
Step1:發(fā)生硬件時鐘中斷時會調(diào)用右圖的鉤子函數(shù)(在os_cpu.c文件中)。但是調(diào)用的該函數(shù)實現(xiàn)的條件是:
需要到os_cfg.h中把這兩個宏#define成>0。
P88:OSTimeDly
在OSTimeDly中完成OSTCBCur->OSTCBDly(任務(wù)延時寄存器)的寫入并進(jìn)行一次任務(wù)切換:
·μc/osⅡ的時鐘OSTimeTick()是怎么與STM32的SysTick關(guān)聯(lián)起來的?
戰(zhàn)艦開發(fā)板配套程序中在main()中有delay_init()(delay.c下)函數(shù),其原代碼如下:
void delay_init()         
{
#ifdef OS_CRITICAL_METHOD         //如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.
        u32 reload;
#endif
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //選擇外部時鐘  HCLK/8
        fac_us=SystemCoreClock/8000000;        //為系統(tǒng)時鐘的1/8  
         
#ifdef OS_CRITICAL_METHOD         //如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.
        reload=SystemCoreClock/8000000;                //每秒鐘的計數(shù)次數(shù) 單位為K           
        reload*=1000000/OS_TICKS_PER_SEC;//根據(jù)OS_TICKS_PER_SEC設(shè)定溢出時間
                                                        //reload為24位寄存器,最大值:16777216,在72M下,約合1.86s左右
        fac_ms=1000/OS_TICKS_PER_SEC;//代表ucos可以延時的最少單位           
        SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;           //開啟SYSTICK中斷
        SysTick->LOAD=reload;         //每1/OS_TICKS_PER_SEC秒中斷一次        
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;           //開啟SYSTICK   
#else
        fac_ms=(u16)fac_us*1000;//非ucos下,代表每個ms需要的systick時鐘數(shù)   
#endif
}        
可見delay_init()開啟了STM32的SYSTICK中斷,下面繼續(xù)找SYSTICK的中斷服務(wù)程序(同樣也在delay.c),代碼如下:
void SysTick_Handler(void)
{                                   
OSIntEnter();                //進(jìn)入中斷,其作僅僅是將判斷中斷層數(shù)是否達(dá)到255否則OSIntNesting++
OSTimeTick();       //調(diào)用ucos的時鐘服務(wù)程序               
OSIntExit();        //觸發(fā)任務(wù)切換軟中斷
}
發(fā)現(xiàn)OSTimeTick(); 在        SYSTICK的中斷服務(wù)程序被調(diào)用,現(xiàn)在μc/osⅡ的時鐘OSTimeTick()就與STM32的SysTick關(guān)聯(lián)了起來。
OSIntExit (void)的作用除了執(zhí)行了OSIntNesting--之外       還進(jìn)行了一次中斷級任務(wù)調(diào)度OSIntCtxSw()       。
·OSIntCtxSw()切換任務(wù)的原理:
Step1:SIntCtxSw()觸發(fā)了一次軟件中斷,代碼如下
;/**************************************************************************************
;* 函數(shù)名稱: OSIntCtxSw
;* 功能描述: 中斷級任務(wù)切換(其實是進(jìn)行了一次軟件中斷)
;* 參    數(shù): None
;* 返 回 值: None
;***************************************************************************************/
OSIntCtxSw
                PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL ;觸發(fā)PendSV異常 (causes context switch)
                                                                        ;NVIC_INT_CTRL就是軟件中斷控制寄存器
        LDR     R5, =NVIC_PENDSVSET  ;NVIC_PENDSVSET是觸發(fā)軟件中斷的值.
        STR     R5, [R4]     ;將R5中的字?jǐn)?shù)據(jù)寫入以R4為地址的存儲器中就發(fā)生了PendSV中斷
                POP     {R4, R5}
        BX      LR
        NOP
Step2:執(zhí)行完了step后會進(jìn)入軟件中斷服務(wù)函數(shù),代碼(在os_cpu_aasm中)如下
;/**************************************************************************************
;* 函數(shù)名稱: OSPendSV
;*
;* 功能描述: 該函數(shù)實際上完成了cpu各寄存器的壓棧和新任務(wù)堆棧向cpu的進(jìn)棧;
;* 參    數(shù): None
;*
;* 返 回 值: None
;***************************************************************************************/
PendSV_Handler  ;軟件中斷服務(wù)函數(shù)
    CPSID   I      ; Prevent interruption during context switch
    MRS     R0, PSP   ; PSP is process stack pointer 如果在用PSP堆棧,則可以忽略保存寄存器,參考CM3權(quán)威中的雙堆棧-白菜注
    CBZ     R0, PendSV_Handler_Nosave         ; Skip register save the first time
    SUBS    R0, R0, #0x20   ; Save remaining regs R4-11 on process stack
    STM     R0, {R4-R11}
    LDR     R1, =OSTCBCur  ; OSTCBCur->OSTCBStkPtr = SP; =OSTCBCur就是取的OSTCBCur
;首地址,即任務(wù)控制塊的堆棧。
    LDR     R1, [R1]
    STR     R0, [R1]   ; R0 is SP of process being switched out
·當(dāng)臨界區(qū)遇上單片機中斷
其中OSIntExit對任務(wù)的調(diào)度是在臨界區(qū)里進(jìn)行的。那么進(jìn)入了臨界區(qū)后恰好單片機發(fā)生了中時會不會造成單片機中斷的失效呢?
---此時當(dāng)然是先完成臨界區(qū)的程序然后退出臨界區(qū)時單片機會自動執(zhí)行中斷服務(wù)函數(shù)。因為雖然在臨界區(qū)系統(tǒng)給單片機關(guān)閉了總中斷,但是中斷還是會被單片機接收的,只是在臨界區(qū)時中斷沒被觸發(fā)。
所以在ucos下使用stm32的中斷只要用裸奔的形式配置寄存器,而在進(jìn)入和退出中斷服務(wù)函數(shù)是只需要將中斷層數(shù)+1或-1(以下程序以串口1為例)即可:
/**********************USART1寄存器配置***********************/
void uart_init(u32 bound)
{
    //GPIO端口設(shè)置
        
GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使 能USART1,GPIOA時鐘以及復(fù)用功能時鐘
     
        //USART1_TX   PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //復(fù)用推挽輸出
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX          PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
    GPIO_Init(GPIOA, &GPIO_InitStructure);  
   //Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優(yōu)先級3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子優(yōu)先級3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);        //根據(jù)指定的參數(shù)初始化VIC寄存器
  
   //USART 初始化設(shè)置
        USART_InitStructure.USART_BaudRate = bound;//一般設(shè)置為9600;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收發(fā)模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷
    USART_Cmd(USART1, ENABLE);                    //使能串口
}
/***************/**********************斷服務(wù)函數(shù)*******************************************/
·任務(wù)的掛起與恢復(fù)
/***********************************任務(wù)的掛起與恢復(fù)*******************************************/
掛起:OSTaskSuspend(INT8U prio) 如果是掛起自身則參數(shù)為 OS_PRIO_SELF
恢復(fù):OSTaskResume(INT8U prio)
如若掛起和恢復(fù)成功則返回:OS_NO_ERROR,不成功則返回值見任哲教材P47。
·微秒級延時和毫秒級延時的區(qū)別
微秒級延時和毫秒級延時都是使用了系統(tǒng)時鐘,其中微秒級延時僅僅是用在延時場合,如DS18B20,而毫秒級延時在延時的同時還發(fā)生了任務(wù)調(diào)度。這兩個延時都是使用了系統(tǒng)時鐘是怎么做到的呢?
——SysTick是一個減計數(shù)器,delay_us是用實時查詢定時器SysTick->VAL; 通過對比初始值和定時值之間的差來計算是否達(dá)到定時時長,delay_us只是單純的延時,延時期間不會進(jìn)行任務(wù)調(diào)度。而delay_ms是使用了SysTick中斷,在中斷中會執(zhí)行一次任務(wù)調(diào)度。
·OSTaskStkInit()
說之前還是得先說一下任務(wù)切換,因為初始化任務(wù)堆棧,是為任務(wù)切換服務(wù)的。代碼在正常運行時,一行一行往下執(zhí)行,怎么才能跑到另一個任務(wù)(即函數(shù))執(zhí)行呢?首先大家可以回想一下中斷過程,當(dāng)中斷發(fā)生時,原來函數(shù)執(zhí)行的地方(程序計數(shù)器PC、處理器狀態(tài)寄存器及所有通用寄存器,即當(dāng)前代碼的現(xiàn)場)被保存到棧里面去了,然后開始取中斷向量,跑到中斷函數(shù)里面執(zhí)行。執(zhí)行完了呢,想回到原來函數(shù)執(zhí)行的地方,該怎么辦呢,只要把棧中保存的原來函數(shù)執(zhí)行的信息恢復(fù)即可(把棧中保存的代碼現(xiàn)場重新賦給cpu的各個寄存器),一切就都回去了,好像什么事都沒發(fā)生一樣。這個過程大家應(yīng)該都比較熟悉,任務(wù)切換和這有什么關(guān)系,試想一下,如果有3個函數(shù)foo1(), foo2(), foo3()像是剛被中斷,現(xiàn)場保存到棧里面去了,而中斷返回時做點手腳(調(diào)度程序的作用),想回哪個回哪個,是不是就做了函數(shù)(任務(wù))切換了。看到這里應(yīng)該有點明白OSTaskStkInit()的作用了吧,它被任務(wù)創(chuàng)建函數(shù)調(diào)用,所以要在開始時,在棧中作出該任務(wù)好像剛被中斷一樣的假象。(關(guān)于任務(wù)切換的原理邵老師書中的3.06節(jié)有介紹)。
    那么中斷后棧中是個什么情形呢,中9.1.1有介紹,xPSR,PC,LR,R12,R3-R0被自動保存到棧中的,R11-R4如果需要保存,只能手工保存。因此OSTaskStkInit()的工作就是在任務(wù)自己的棧中保存cpu的所有寄存器。這些值里R1-R12都沒什么意義,這里用相應(yīng)的數(shù)字代號(如R1用0x01010101)主要是方便調(diào)試。
    其他幾個:
    xPSR = 0x01000000L,xPSR T位(第24位)置1,否則第一次執(zhí)行任務(wù)時Fault,
    PC肯定得指向任務(wù)入口,
    R14 = 0xFFFFFFFEL,最低4位為E,是一個非法值,主要目的是不讓使用R14,即任務(wù)是不能返回的。
    R0用于傳遞任務(wù)函數(shù)的參數(shù),因此等于p_arg。   
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *stk;

    (void)opt;                        /* 'opt' is not used, prevent warning */
    stk       = ptos;                 /* Load stack pointer                 */
    /* Registers stacked as if auto-saved on exception */
    *(stk)    = (INT32U)0x01000000L;  /* xPSR        */
    *(--stk)  = (INT32U)task;         /* Entry Point */
    /* R14 (LR) (init value will cause fault if ever used)*/
    *(--stk)  = (INT32U)0xFFFFFFFEL;  
    *(--stk)  = (INT32U)0x12121212L;  /* R12 */
    *(--stk)  = (INT32U)0x03030303L;  /* R3  */
    *(--stk)  = (INT32U)0x02020202L;  /* R2  */
    *(--stk)  = (INT32U)0x01010101L;  /* R1  */
    *(--stk)  = (INT32U)p_arg;        /* R0 : argument  */
    /* Remaining registers saved on process stack */
    *(--stk)  = (INT32U)0x11111111L;  /* R11 */
    *(--stk)  = (INT32U)0x10101010L;  /* R10 */
    *(--stk)  = (INT32U)0x09090909L;  /* R9  */
    *(--stk)  = (INT32U)0x08080808L;  /* R8  */
    *(--stk)  = (INT32U)0x07070707L;  /* R7  */
    *(--stk)  = (INT32U)0x06060606L;  /* R6  */
    *(--stk)  = (INT32U)0x05050505L;  /* R5  */
    *(--stk)  = (INT32U)0x04040404L;  /* R4  */
    return (stk);
}
把OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()注釋掉。
#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010))
#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)0xE000E014))
#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))
#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01C))
#define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    0x00010000   
#define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  0x00000004   
#define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    0x00000002   
#define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   0x00000001  
把上面這些宏定義也注釋掉,因為它們都用于OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()。
os_cpu_a.asm
這個文件包含著必須用匯編寫的代碼。
    EXTERN  OSRunning    ; External references
    EXTERN  OSPrioCur
    EXTERN  OSPrioHighRdy
    EXTERN  OSTCBCur
    EXTERN  OSTCBHighRdy
    EXTERN  OSIntNesting
    EXTERN  OSIntExit
    EXTERN  OSTaskSwHook
    申明這些變量是在其他文件定義的,本文件只做引用(有幾個好像并未引用,不過沒有關(guān)系)。
    EXPORT  OS_CPU_SR_Save   ; Functions declared in this file
    EXPORT  OS_CPU_SR_Restore
    EXPORT  OSStartHighRdy
    EXPORT  OSCtxSw
    EXPORT  OSIntCtxSw
    EXPORT  OS_CPU_PendSVHandler
    申明這些函數(shù)是在本文件中定義的。
NVIC_INT_CTRL   EQU     0xE000ED04   ;中斷控制及狀態(tài)寄存器ICSR的地址
NVIC_SYSPRI14   EQU     0xE000ED22   pendSV優(yōu)先級寄存器的地址
NVIC_PENDSV_PRI EQU           0xFF   pendSV中斷的優(yōu)先級為255(最低)
NVIC_PENDSVSET  EQU     0x10000000   ;位28為1
    定義幾個常量,類似C語言中的#define預(yù)處理指令。
OS_CPU_SR_Save
    MRS     R0, PRIMASK   ;讀取PRIMASK到R0中,R0為返回值
    CPSID   I             PRIMASK=1,關(guān)中斷(NMI和硬fault可以響應(yīng))
    BX      LR            ;返回
OS_CPU_SR_Restore
    MSR     PRIMASK, R0   ;讀取R0到PRIMASK中,R0為參數(shù)
    BX      LR            ;返回
OSStartHighRdy()由OSStart()調(diào)用,用來啟動最高優(yōu)先級任務(wù),當(dāng)然任務(wù)必須在OSStart()前已被創(chuàng)建。
OSStartHighRdy
    ;設(shè)置PendSV中斷的優(yōu)先級 #1
    LDR     R0, =NVIC_SYSPRI14    ;R0 = NVIC_SYSPRI14
    LDR     R1, =NVIC_PENDSV_PRI  ;R1 = NVIC_PENDSV_PRI
    STRB    R1, [R0]              ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
    ;設(shè)置PSP為0 #2
    MOVS    R0, #0                ;R0 = 0
    MSR     PSP, R0               PSP = R0
    ;設(shè)置OSRunning為TRUE
    LDR     R0, =OSRunning        ;R0 = OSRunning
    MOVS    R1, #1                ;R1 = 1
    STRB    R1, [R0]              ;OSRunning = 1

    ;觸發(fā)PendSV中斷 #3
    LDR     R0, =NVIC_INT_CTRL    ;R0 = NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET   ;R1 = NVIC_PENDSVSET
    STR     R1, [R0]              ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
    CPSIE   I                     ;開中斷                  
OSStartHang                       ;死循環(huán),應(yīng)該不會到這里
    B       OSStartHang
#1.PendSV中斷的優(yōu)先級應(yīng)該為最低優(yōu)先級,原因在的7.6節(jié)已有說明。
#2.PSP設(shè)置為0,是告訴具體的任務(wù)切換程序(OS_CPU_PendSVHandler()),這是第一次任務(wù)切換。做過切換后PSP就不會為0了,后面會看到。
#3.往中斷控制及狀態(tài)寄存器ICSR(0xE000ED04)第28位寫1即可產(chǎn)生PendSV中斷。這個8.4.5 其它異常的配置寄存器有說明。
    當(dāng)一個任務(wù)放棄cpu的使用權(quán),就會調(diào)用OS_TASK_SW()宏,而OS_TASK_SW()就是OSCtxSw()。OSCtxSw()應(yīng)該做任務(wù)切換。但是在CM3中,所有任務(wù)切換都被放到PendSV的中斷處理函數(shù)中去做了,因此OSCtxSw()只需簡單的觸發(fā)PendSV中斷即可。OS_TASK_SW()是由OS_Sched()調(diào)用。
void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr = 0;
#endif

    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0) {
        if (OSLockNesting == 0) {
            OS_SchedNew();
            if (OSPrioHighRdy != OSPrioCur) {
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN &gt; 0
                OSTCBHighRdy-&gt;OSTCBCtxSwCtr++;
#endif
                OSCtxSwCtr++;
                OS_TASK_SW();    /* 觸發(fā)PendSV中斷 */
            }
        }
    }
    /* 一旦開中斷,PendSV中斷函數(shù)會執(zhí)行(當(dāng)然要等更高優(yōu)先級中斷處理完) */
    OS_EXIT_CRITICAL();  
}
OSCtxSw
    ;觸發(fā)PendSV中斷
    LDR     R0, =NVIC_INT_CTRL    ;R0 = NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET   ;R1 = NVIC_PENDSVSET
    STR     R1, [R0]              ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
    BX      LR                    ;返回
    當(dāng)一個中斷處理函數(shù)退出時,OSIntExit()會被調(diào)用來決定是否有優(yōu)先級更高的任務(wù)需要執(zhí)行。如果有OSIntExit()對調(diào)用OSIntCtxSw()做任務(wù)切換。

OSIntCtxSw
    ;觸發(fā)PendSV中斷
     LDR     R0, =NVIC_INT_CTRL
   LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR
    看到這里有些同學(xué)可能奇怪怎么OSCtxSw()和OSIntCtxSw()完全一樣,事實上,這兩個函數(shù)的意義是不一樣的,OSCtxSw()做的是任務(wù)之間的切換,如任務(wù)A因為等待某個資源或是做延時切換到任務(wù)B,而OSIntCtxSw()則是中斷退出時,由中斷狀態(tài)切換到另一個任務(wù)。由中斷切換到任務(wù)時,CPU寄存器入棧的工作已經(jīng)做完了,所以無需做第二次了(參考邵老師書的3.10節(jié))。這里只不過由于CM3的特殊機制導(dǎo)致了在這兩個函數(shù)中只要做觸發(fā)PendSV中斷即可,具體切換由PendSV中斷來處理。
    前面已經(jīng)說過真正的任務(wù)切換是在PendSV中斷處理函數(shù)里做的,由于CM3在中斷時會有一半的寄存器自動保存到任務(wù)堆棧里,所以在PendSV中斷處理函數(shù)中只需保存R4-R11并調(diào)節(jié)堆棧指針即可。

PendSV中斷處理函數(shù)偽代碼如下:
OS_CPU_PendSVHandler()
{
        if (PSP != NULL) {
                Save R4-R11 onto task stack;
                OSTCBCur-&gt;OSTCBStkPtr = SP;
        }
        OSTaskSwHook();
        OSPrioCur = OSPrioHighRdy;
        OSTCBCur = OSTCBHighRdy;
        PSP = OSTCBHighRdy-&gt;OSTCBStkPtr;
        Restore R4-R11 from new task stack;
        Return from exception;
}
OS_CPU_PendSVHandler             ;xPSR, PC, LR, R12, R0-R3已自動保存
    CPSID   I                                    ;任務(wù)切換期間需要關(guān)中斷
    MRS     R0, PSP                      ;R0 = PSP
    ;如果PSP == 0,跳到OS_CPU_PendSVHandler_nosave執(zhí)行 #1
    CBZ     R0, OS_CPU_PendSVHandler_nosave
    ;保存R4-R11到任務(wù)堆棧
    SUBS    R0, R0, #0x20                ;R0 -= 0x20                        
    STM     R0, {R4-R11}                 ;保存R4-R11到任務(wù)堆棧
    ;OSTCBCur-&gt;OSTCBStkPtr = SP;
    LDR     R1, =OSTCBCur                ;R1 = &amp;OSTCBCur
    LDR     R1, [R1]                             ;R1 = *R1 (R1 = OSTCBCur)
    STR     R0, [R1]                             ;*R1 = R0 (*OSTCBCur = SP) #2                       
OS_CPU_PendSVHandler_nosave
    ;調(diào)用OSTaskSwHook()
    PUSH    {R14}                                        ;保存R14,因為后面要調(diào)用函數(shù)            
    LDR     R0, =OSTaskSwHook            ;R0 = &amp;OSTaskSwHook
    BLX     R0                                   ;調(diào)用OSTaskSwHook()
    POP     {R14}                                ;恢復(fù)R14
    ;OSPrioCur = OSPrioHighRdy;
    LDR     R0, =OSPrioCur                       ;R0 = &amp;OSPrioCur
    LDR     R1, =OSPrioHighRdy                   ;R1 = &amp;OSPrioHighRdy
    LDRB    R2, [R1]                             ;R2 = *R1 (R2 = OSPrioHighRdy)
    STRB    R2, [R0]                             ;*R0 = R2 (OSPrioCur = OSPrioHighRdy)
    ;OSTCBCur = OSTCBHighRdy;
    LDR     R0, =OSTCBCur                        ;R0 = &amp;OSTCBCur      
    LDR     R1, =OSTCBHighRdy            ;R1 = &amp;OSTCBHighRdy
    LDR     R2, [R1]                             ;R2 = *R1 (R2 = OSTCBHighRdy)
    STR     R2, [R0]                         ;*R0 = R2 (OSTCBCur = OSTCBHighRdy)
    LDR     R0, [R2]                             ;R0 = *R2 (R0 = OSTCBHighRdy), 此時R0是新任務(wù)的SP
                                                         ;SP = OSTCBHighRdy-&gt;OSTCBStkPtr #3   
    LDM     R0, {R4-R11}                 ;從任務(wù)堆棧SP恢復(fù)R4-R11      
    ADDS    R0, R0, #0x20                ;R0 += 0x20
    MSR     PSP, R0                              ;PSP = R0,用新任務(wù)的SP加載PSP
    ORR     LR, LR, #0x04         ;確保LR位2為1,返回后使用進(jìn)程堆棧 #4      
    CPSIE   I                    ;開中斷
    BX      LR                   ;中斷返回                 
    END
#1 如果PSP == 0,說明OSStartHighRdy()啟動后第一次做任務(wù)切換,而任務(wù)剛創(chuàng)建時R4-R11已經(jīng)保存在堆棧中了,所以不需要再保存一次了。
#2 OSTCBStkPtr是任務(wù)控制塊結(jié)構(gòu)體的第一個變量,所以*OSTCBCur = SP(不是很科學(xué))就是OSTCBCur-&gt;OSTCBStkPtr = SP;
#3 和#2類似。
#4 因為在中斷處理函數(shù)中使用的是MSP,所以在返回任務(wù)后必須使用PSP,所以LR位2必須為1。
os_dbg.c
用于系統(tǒng)調(diào)試,可以不管。
·關(guān)于任務(wù)切切換時的知識補充
上文可知,STM32任務(wù)調(diào)度是在軟件中斷服務(wù)函數(shù)中執(zhí)行,首先要將CPU的內(nèi)容保存到任務(wù)堆棧中,匯編代碼如下:
OS_CPU_PendSVHandler             ;xPSR, PC, LR, R12, R0-R3已自動保存
    CPSID   I                                    ;任務(wù)切換期間需要關(guān)中斷
    MRS     R0, PSP                      ;R0 = PSP
    ;如果PSP == 0,跳到OS_CPU_PendSVHandler_nosave執(zhí)行 #1
    CBZ     R0, OS_CPU_PendSVHandler_nosave
    ;保存R4-R11到任務(wù)堆棧
    SUBS    R0, R0, #0x20                ;R0 -= 0x20                        
    STM     R0, {R4-R11}                 ;保存R4-R11到任務(wù)堆棧
    ;OSTCBCur-&gt;OSTCBStkPtr = SP;
    LDR     R1, =OSTCBCur                ;R1 = &amp;OSTCBCur
    LDR     R1, [R1]                             ;R1 = *R1 (R1 = OSTCBCur)
    STR     R0, [R1]                             ;*R1 = R0 (*OSTCBCur = SP) #2   
那么CPU的內(nèi)容是被保存到那個堆棧中呢?
——當(dāng)然是保存在將要切換出CPU的任務(wù)的堆棧中。
那么CPU是怎么做到的呢?
——因為CPU的R13中保存了當(dāng)前正在運任務(wù)的堆棧指針。所以當(dāng)進(jìn)入中斷時CPU就會按照R13指向的地址將CPU的狀態(tài)保存到堆棧中。這就完成了舊任務(wù)的切出。
                  
如右圖9-1,一個任務(wù)堆棧的結(jié)構(gòu)如右圖所示。當(dāng)一個任務(wù)放棄CPU的使用權(quán)時就將CPU的內(nèi)容保存在該任務(wù)對應(yīng)的任務(wù)堆棧中。

   



分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 国产一区二区免费 | 精品欧美一区免费观看α√ | 久久精品亚洲 | 国产视频一区二区 | 国产欧美日韩一区 | 欧美激情一区二区 | 91av导航 | 91久久婷婷 | 亚洲精品在线看 | 日本小电影网站 | 国产精品视频免费观看 | 国产成人一区二区三区 | 美女日批免费视频 | 国产线视频精品免费观看视频 | 亚洲日韩欧美一区二区在线 | 伊人久久综合影院 | 色资源在线 | www久久av| 狠狠色综合网站久久久久久久 | 国产一级片一区二区三区 | 精品免费国产 | 6996成人影院网在线播放 | 国产精品色一区二区三区 | 综合色久 | 亚洲精品视频在线 | 一区二区三区四区毛片 | 日韩在线资源 | 在线一区二区三区 | 亚洲社区在线 | 国产精品一区二区无线 | 国内精品视频 | 国产精品免费一区二区 | 狠狠的干狠狠的操 | 超碰日本 | 欧美精品一区三区 | 国产精品亚洲精品 | 色爱综合网 | 亚洲免费人成在线视频观看 | 日韩欧美字幕 | 欧美精品一区二区三区在线播放 | 国产精品久久久久久久久久久新郎 |