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

NVIC_SYSPRI14 EQU 0xE000ED22
NVIC_PENDSV_PRI EQU 0xFF
LDR R0, =NVIC_SYSPRI14 LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
2 如何觸發(fā)PendSV異常?

NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
3 編寫(xiě)PendSV異常handler
這里用PendSV_Handler來(lái)觸發(fā)LED點(diǎn)亮,以此證明PendSV異常觸發(fā)的設(shè)置是正確的。
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,說(shuō)明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)容及順序如下:
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語(yǔ)言表達(dá)是*(--R0)={R4-R11},R0中值先自減1,然后將R4-R11的值保存到該值所指向的地址中,即PSP中。STMDB Rd!,{寄存器列表} 連續(xù)存儲(chǔ)多個(gè)字到Rd中的地址值所指地址處。每次存儲(chǔ)前,Rd先自減一次。若是ISR是從從task0進(jìn)來(lái),那么此時(shí)task0的堆棧中已經(jīng)保存了該任務(wù)的寄存器參數(shù)。保存完成后,當(dāng)前任務(wù)堆棧中的內(nèi)容如下(假設(shè)是task0)
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
中斷返回。完整代碼:
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é)果如圖:
-
led
+關(guān)注
關(guān)注
242文章
23842瀏覽量
673989 -
寄存器
+關(guān)注
關(guān)注
31文章
5434瀏覽量
124456 -
中斷
+關(guān)注
關(guān)注
5文章
905瀏覽量
42794 -
Cortex-M3
+關(guān)注
關(guān)注
9文章
276瀏覽量
60262 -
任務(wù)切換
+關(guān)注
關(guān)注
0文章
4瀏覽量
6922
原文標(biāo)題:進(jìn)入OS前的兩步:PendSV(任務(wù)切換)
文章出處:【微信號(hào):c-stm32,微信公眾號(hào):STM32嵌入式開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
求助,是否可以不用pendSV中斷做任務(wù)切換?
請(qǐng)問(wèn)UCOSIII如何切換到新任務(wù)?
【安富萊】【μCOS-III教程】第5章 任務(wù)切換設(shè)計(jì)
stm32單片機(jī)移植μc/os時(shí),任務(wù)級(jí)切換函數(shù)和中斷級(jí)切換函數(shù)過(guò)程不是一樣的嗎?
PC里VC下移植的uc/os-II任務(wù)切換后切換不回來(lái)
請(qǐng)問(wèn)uc/os任務(wù)切換問(wèn)題該怎么解決?
【設(shè)計(jì)技巧】從單片機(jī)到操作系統(tǒng)(6)-FreeRTOS任務(wù)切換機(jī)制詳解
ucos上下文該怎么切換?
OSCtxSw函數(shù)是怎么觸發(fā)PendSV_Handler函數(shù)的?
SVC和PendSV異常有什么用途?
淺談RTOS中的多任務(wù)切換(基于UC/OS iii)

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

評(píng)論