0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

進(jìn)入OS前的兩步:PendSV(任務(wù)切換)

STM32嵌入式開發(fā) ? 來源:博客園-Weyne ? 2023-02-14 16:01 ? 次閱讀
先了解下如何使用PendSV異常。為何要使用PendSV而不是其他的異常,請(qǐng)參考《cortex-M3權(quán)威指南》。

1 如何設(shè)定PendSV優(yōu)先級(jí)?

5016a00a-a9ab-11ed-bfe3-dac502259ad0.png

		
NVIC_SYSPRI14 EQU 0xE000ED22
NVIC_PENDSV_PRI EQU 0xFF
    LDR R0, =NVIC_SYSPRI14 LDR R1, =NVIC_PENDSV_PRI
    STRB R1, [R0]

2 如何觸發(fā)PendSV異常?

503ae686-a9ab-11ed-bfe3-dac502259ad0.pngICSR第28位寫1,即可將PendSV異常掛起。若是當(dāng)前沒有高優(yōu)先級(jí)中斷產(chǎn)生,那么程序?qū)?huì)進(jìn)入PendSV handler

		
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000


LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]

3 編寫PendSV異常handler

這里用PendSV_Handler來觸發(fā)LED點(diǎn)亮,以此證明PendSV異常觸發(fā)的設(shè)置是正確的。

		
#include "stm32f10x_conf.h"


#define LED0 *((volatile unsigned long *)(0x422101a0)) //PA8


unsigned char flag=0;
void LEDInit(void)
{
    RCC->APB2ENR|=1<<2;
    GPIOA->CRH&=0XFFFFFFF0;
    GPIOA->CRH|=0X00000003;
    GPIOA->ODR|=1<<8;
}


__asm void SetPendSVPro(void)
{
    NVIC_SYSPRI14 EQU 0xE000ED22
    NVIC_PENDSV_PRI EQU 0xFF
    LDR R1, =NVIC_PENDSV_PRI    
    LDR R0, =NVIC_SYSPRI14
    STRB R1, [R0]
    BX LR
}


__asm void TriggerPendSV(void)
{
    NVIC_INT_CTRL EQU 0xE000ED04
    NVIC_PENDSVSET EQU 0x10000000
    LDR R0, =NVIC_INT_CTRL
    LDR R1, =NVIC_PENDSVSET
    STR R1, [R0]
    BX LR
}


int main(void)
{
    SetPendSVPro();
    LEDInit();
    TriggerPendSV();
    while(1);
}


void PendSV_Handler(void)
{
    LED0 = 0;
}
上述代碼可以正常點(diǎn)亮LED,說明PendSV異常是正常觸發(fā)了。OK,是時(shí)候挑戰(zhàn)任務(wù)切換了。如何實(shí)現(xiàn)任務(wù)切換?三個(gè)步驟:步驟一:在進(jìn)入中斷前先設(shè)置PSP。

		
curr_task = 0;
設(shè)置任務(wù)0為當(dāng)前任務(wù):

		
__set_PSP((PSP_array[curr_task] + 16*4));
設(shè)置PSP指向task0堆棧的棧頂位置:

		
__set_CONTROL(0x3);
設(shè)置為用戶級(jí),并使用PSP堆棧:

		
__ISB();
指令同步隔離。步驟二:將當(dāng)前寄存器的內(nèi)容保存到當(dāng)前任務(wù)堆棧中。進(jìn)入ISR時(shí),cortex-m3會(huì)自動(dòng)保存八個(gè)寄存器到PSP中,剩下的幾個(gè)需要我們手動(dòng)保存。步驟三:在Handler中將下一個(gè)任務(wù)的堆棧中的內(nèi)容加載到寄存器中,并將PSP指向下一個(gè)任務(wù)的堆棧。這樣就完成了任務(wù)切換。要在PendSV 的ISR中完成這兩個(gè)步驟,我們先需了解下在進(jìn)入PendSV ISR時(shí),cortex-M3做了什么? 1入會(huì)有8個(gè)寄存器自動(dòng)入棧。入棧內(nèi)容及順序如下:504d85c0-a9ab-11ed-bfe3-dac502259ad0.png在步驟一中,我們已經(jīng)設(shè)置了PSP,那這8個(gè)寄存器就會(huì)自動(dòng)入棧到PSP所指地址處。2取向量:找到PendSV ISR的入口地址,這樣就能跳到ISR了。,3更新寄存器內(nèi)容:做完這三步后,程序就進(jìn)入ISR了。進(jìn)入ISR前,我們已經(jīng)完成了步驟一,cortex-M3已經(jīng)幫我們完成了步驟二的一部分,剩下的需要我們手動(dòng)完成。在ISR中添加代碼如下:

		
MRS R0, PSP
保存PSP到R0。為什么是PSP而不是MSP。因?yàn)樵贠S啟動(dòng)的時(shí)候,我們已經(jīng)把SP設(shè)置為PSP了。這樣使得用戶程序使用任務(wù)堆棧,OS使用主堆棧,不會(huì)互相干擾。不會(huì)因?yàn)橛脩舫绦驅(qū)е翺S崩潰。

		
STMDB R0!,{R4-R11}
保存R4-R11到PSP中。C語言表達(dá)是*(--R0)={R4-R11},R0中值先自減1,然后將R4-R11的值保存到該值所指向的地址中,即PSP中。STMDB Rd!,{寄存器列表} 連續(xù)存儲(chǔ)多個(gè)字到Rd中的地址值所指地址處。每次存儲(chǔ)前,Rd先自減一次。若是ISR是從從task0進(jìn)來,那么此時(shí)task0的堆棧中已經(jīng)保存了該任務(wù)的寄存器參數(shù)。保存完成后,當(dāng)前任務(wù)堆棧中的內(nèi)容如下(假設(shè)是task0)50715004-a9ab-11ed-bfe3-dac502259ad0.png左邊表格是預(yù)期值,右邊是keil調(diào)試的實(shí)際值??梢钥闯觯且恢碌?。在任務(wù)初始化時(shí)(步驟一),我們將PSP指向任務(wù)0的棧頂0x20000080。在進(jìn)入PendSV之前,cortex-M3自動(dòng)入棧八個(gè)值,此時(shí)PSP指向了0x20000060。然后我們?cè)俦4鍾4-R11到0x20000040~0x2000005C。這樣很容易看明白,如果需要下次再切換到task0,只需恢復(fù)R4~R11,再將PSP指向0x20000060即可。所以切換到另一個(gè)任務(wù)的代碼:

		
LDR R1,=__cpp(&curr_task)
LDR R3,=__cpp(&PSP_array)
LDR R4,=__cpp(&next_task)
LDR R4,[R4]
獲取下一個(gè)任務(wù)的編號(hào):

		
STR R4,[R1]
Curr_task=next_task
LDR R0,[R3, R4, LSL #2]
獲得任務(wù)堆棧地址,若是task0,那么R0=0x20000040( R0=R3+R4*4)

		
LDMIA R0!,{R4-R11}
恢復(fù)堆棧中的值到R4~R11。R4=*(R0++)。執(zhí)行完后,R0中值變?yōu)?x20000060LDMIA Rd! {寄存器列表} 先將Rd中值所指地址處的值送出寄存器中,Rd再自增1.*

		
MSR PSP, R0
PSP=R0。
BX LR
中斷返回。

完整代碼:


		
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stdio.h"
#include "misc.h"


#define HW32_REG(ADDRESS)  (*((volatile unsigned long  *)(ADDRESS)))
#define LED0 *((volatile unsigned long *)(0x422101a0)) //PA8
void USART1_Init(void);
void task0(void) ; 
unsigned char flag=1;


uint32_t  curr_task=0;     // 當(dāng)前執(zhí)行任務(wù)
uint32_t  next_task=1;     // 下一個(gè)任務(wù)
uint32_t task0_stack[17];
uint32_t task1_stack[17];
uint32_t  PSP_array[4];


u8 task0_handle=1;
u8 task1_handle=1;


void task0(void) 
{ 
    while(1)
    {
        if(task0_handle==1)
        {
            printf("task0
");
            task0_handle=0;
            task1_handle=1;
        }
    }
}


void task1(void)
{
    while(1)
    {
        if(task1_handle==1)
        {
            printf("task1
");
            task1_handle=0;
            task0_handle=1;
        }
    }
}


void LEDInit(void)
{
    RCC->APB2ENR|=1<<2; 
    GPIOA->CRH&=0XFFFFFFF0;
    GPIOA->CRH|=0X00000003; 
      GPIOA->ODR|=1<<8;
}


__asm void SetPendSVPro(void)
{
NVIC_SYSPRI14   EQU     0xE000ED22
NVIC_PENDSV_PRI EQU           0xFF
    
    LDR     R1, =NVIC_PENDSV_PRI    
    LDR     R0, =NVIC_SYSPRI14    
    STRB    R1, [R0]
    BX      LR
}


__asm void TriggerPendSV(void)
{
NVIC_INT_CTRL   EQU     0xE000ED04                              
NVIC_PENDSVSET  EQU     0x10000000                              


    LDR     R0, =NVIC_INT_CTRL                                 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR
}


int main(void)
{
    USART1_Init();


    SetPendSVPro();
    LEDInit();
    
    printf("OS test
");
    
    PSP_array[0] = ((unsigned int) task0_stack) + (sizeof task0_stack) - 16*4;
    //PSP_array中存儲(chǔ)的為task0_stack數(shù)組的尾地址-16*4,即task0_stack[1023-16]地址
    HW32_REG((PSP_array[0] + (14<<2))) = (unsigned long) task0; /* PC */
    //task0的PC存儲(chǔ)在task0_stack[1023-16]地址  +14<<2中,即task0_stack[1022]中
    HW32_REG((PSP_array[0] + (15<<2))) = 0x01000000;            /* xPSR */
  
    PSP_array[1] = ((unsigned int) task1_stack) + (sizeof task1_stack) - 16*4;
    HW32_REG((PSP_array[1] + (14<<2))) = (unsigned long) task1; /* PC */
    HW32_REG((PSP_array[1] + (15<<2))) = 0x01000000;            /* xPSR */    
    
    /* 任務(wù)0先執(zhí)行 */
    curr_task = 0; 
     
    /* 設(shè)置PSP指向任務(wù)0堆棧的棧頂 */
    __set_PSP((PSP_array[curr_task] + 16*4)); 
    
    SysTick_Config(9000000);
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//72/8=9MHZ     
    /* 使用堆棧指針,非特權(quán)級(jí)狀態(tài) */
    __set_CONTROL(0x3);
    
    /* 改變CONTROL后執(zhí)行ISB (architectural recommendation) */
    __ISB();
    
    /* 啟動(dòng)任務(wù)0 */
    task0();  
    //LED0=0;
    while(1);
}


__asm void PendSV_Handler(void)
{ 
    // 保存當(dāng)前任務(wù)的寄存器內(nèi)容
    MRS    R0, PSP     // 得到PSP  R0 = PSP
                       // xPSR, PC, LR, R12, R0-R3已自動(dòng)保存
    STMDB  R0!,{R4-R11}// 保存R4-R11共8個(gè)寄存器得到當(dāng)前任務(wù)堆棧
    
    // 加載下一個(gè)任務(wù)的內(nèi)容
    LDR    R1,=__cpp(&curr_task)
    LDR    R3,=__cpp(&PSP_array)
    LDR    R4,=__cpp(&next_task)
    LDR    R4,[R4]     // 得到下一個(gè)任務(wù)的ID
    STR    R4,[R1]     // 設(shè)置 curr_task = next_task
    LDR    R0,[R3, R4, LSL #2] // 從PSP_array中獲取PSP的值
    LDMIA  R0!,{R4-R11}// 將任務(wù)堆棧中的數(shù)值加載到R4-R11中
  //ADDS   R0, R0, #0x20
    MSR    PSP, R0     // 設(shè)置PSP指向此任務(wù)
 // ORR     LR, LR, #0x04   
    BX     LR          // 返回
                       // xPSR, PC, LR, R12, R0-R3會(huì)自動(dòng)的恢復(fù)
    ALIGN  4
}


void SysTick_Handler(void)
{
    flag=~flag;
    LED0=flag;
    if(curr_task==0)
        next_task=1;
    else
        next_task=0;
    TriggerPendSV();
}


void USART1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    
    /* config USART1 clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    /* USART1 GPIO config */
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);    
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* USART1 mode config */
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure); 
    USART_Cmd(USART1, ENABLE);
}


int fputc(int ch, FILE *f)
{
  USART_SendData(USART1, (unsigned char) ch);
  while (!(USART1->SR & USART_FLAG_TXE));
 
  return (ch);
}
測(cè)試后結(jié)果如圖:509ecdb8-a9ab-11ed-bfe3-dac502259ad0.png可以看出,兩個(gè)任務(wù)可以切換了。 審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • led
    led
    +關(guān)注

    關(guān)注

    240

    文章

    23065

    瀏覽量

    657071
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5294

    瀏覽量

    119823
  • 中斷
    +關(guān)注

    關(guān)注

    5

    文章

    895

    瀏覽量

    41349
  • Cortex-M3
    +關(guān)注

    關(guān)注

    9

    文章

    269

    瀏覽量

    59406
  • 任務(wù)切換
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    6796

原文標(biāo)題:進(jìn)入OS前的兩步:PendSV(任務(wù)切換)

文章出處:【微信號(hào):c-stm32,微信公眾號(hào):STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    求助,是否可以不用pendSV中斷做任務(wù)切換

    freeRTOS的任務(wù)切換我的理解是:通過SysTick中斷去觸發(fā)pendSV中斷去做任務(wù)切換,因?yàn)?b class='flag-5'>pe
    發(fā)表于 04-16 07:59

    請(qǐng)問UCOSIII如何切換到新任務(wù)

    UCOS/III 是在PendSV中斷里切換任務(wù)的,最后BXLR(見紅色字體),LR是舊任務(wù)返回地址, 所以應(yīng)該跳到舊任務(wù)繼續(xù)執(zhí)行,為什么會(huì)
    發(fā)表于 05-09 06:35

    【安富萊】【μCOS-III教程】第5章 任務(wù)切換設(shè)計(jì)

    大家理解,下面是上圖的執(zhí)行流程:1.任務(wù)A呼叫SVC來請(qǐng)求任務(wù)切換(例如,等待某些工作完成)2.OS接收到請(qǐng)求,做好上下文切換的準(zhǔn)備,并且懸
    發(fā)表于 01-08 17:40

    stm32單片機(jī)移植μc/os時(shí),任務(wù)級(jí)切換函數(shù)和中斷級(jí)切換函數(shù)過程不是一樣的嗎?

    移植μc/os時(shí),任務(wù)級(jí)切換函數(shù)即OSCtrSw,中斷級(jí)任務(wù)切換函數(shù)為OSIntCtrSw,
    發(fā)表于 03-13 07:26

    PC里VC下移植的uc/os-II任務(wù)切換切換不回來

    菜鳥一個(gè),想先在PC上熟悉一下uc/os-II ,但是遇到了些問題了,希望熟悉uc/os-II的高手們幫幫忙吧。任務(wù)切換什么的完全不會(huì),OSTimeDly()函數(shù)貌似在PC上面有很大的
    發(fā)表于 06-10 04:36

    請(qǐng)問uc/os任務(wù)切換問題該怎么解決?

    uc/os任務(wù)切換是通過 堆棧和軟中斷來實(shí)現(xiàn)。假設(shè)有如下的任務(wù)優(yōu)先級(jí)56 PC ->任務(wù)1 PC ->
    發(fā)表于 06-12 04:35

    【設(shè)計(jì)技巧】從單片機(jī)到操作系統(tǒng)(6)-FreeRTOS任務(wù)切換機(jī)制詳解

    上下文切換。過程如圖所示任務(wù)切換的源碼實(shí)現(xiàn)過程差不多了解了,那看看FreeRTOS中怎么實(shí)現(xiàn)吧??!FreeRTOS有種方法觸發(fā)任務(wù)
    發(fā)表于 07-31 08:30

    ucos上下文該怎么切換?

    的值,那么在pendSV處理結(jié)束的時(shí)刻我們看到有條 BXLR 指令, 那豈不是又回到了任務(wù)A的這個(gè)地方呀,怎么能切換到別的任務(wù)上去?。??????---->問題2:參見附件:圖示中上下文
    發(fā)表于 08-26 03:21

    OSCtxSw函數(shù)是怎么觸發(fā)PendSV_Handler函數(shù)的?

    , =NVIC_PENDSVSET STRR1, [R0] BXLR[/mw_shl_code]在ucos中,這個(gè)匯編代碼應(yīng)該是起到切換任務(wù)的作用吧!聽說他是通過調(diào)用PendSV_Handler這個(gè)匯編函數(shù)實(shí)現(xiàn)
    發(fā)表于 09-02 20:27

    SVC和PendSV異常有什么用途?

    等待下一次的OS節(jié)拍中斷產(chǎn)生時(shí)才能再次切換,特別是當(dāng)中斷源與OS節(jié)拍中斷頻率接近時(shí),發(fā)生“共振”導(dǎo)致任務(wù)無法切換
    發(fā)表于 05-12 10:58

    基于兩步映射的輪胎花紋曲面造型方法張勝男

    基于兩步映射的輪胎花紋曲面造型方法_張勝男
    發(fā)表于 03-16 08:00 ?0次下載

    淺談RTOS中的多任務(wù)切換(基于UC/OS iii)

    淺談RTOS中的多任務(wù)切換(基于UC/OS iii)文章目錄淺談RTOS中的多任務(wù)切換(基于UC/OS
    發(fā)表于 11-23 18:06 ?25次下載
    淺談RTOS中的多<b class='flag-5'>任務(wù)</b><b class='flag-5'>切換</b>(基于UC/<b class='flag-5'>OS</b> iii)

    UC/OS-III學(xué)習(xí)——觸發(fā)PendSV中斷

    文檔UC/OS-III學(xué)習(xí)——觸發(fā)PendSV中斷系列文章目錄前言一、關(guān)于PendSV的基礎(chǔ)知識(shí)二、代碼1.c語言2.匯編語言前言PendSV典型使用場(chǎng)合是在上下文
    發(fā)表于 12-01 14:51 ?8次下載
    UC/<b class='flag-5'>OS</b>-III學(xué)習(xí)——觸發(fā)<b class='flag-5'>PendSV</b>中斷

    freeRTOS的任務(wù)

    freeRTOS系統(tǒng)的任務(wù)切換的具體過程最終都是在pendSV中斷服務(wù)函數(shù)里面完成的。
    的頭像 發(fā)表于 02-10 11:02 ?1507次閱讀
    freeRTOS的<b class='flag-5'>任務(wù)</b>

    兩步走 解決開關(guān)電源輸入過壓的煩惱!

    兩步走 解決開關(guān)電源輸入過壓的煩惱!
    的頭像 發(fā)表于 09-27 16:00 ?852次閱讀
    <b class='flag-5'>兩步</b>走 解決開關(guān)電源輸入過壓的煩惱!