一步步学习操作系统(1)——参照ucos,在STM32上实现一个简单的多任务(“精简版”)
相同的代码,另外还提供了讲解更详细的“啰里啰嗦版”,以供参考。
这里,我们将实现一个简单的多任务OS。其任务调度的机理如下:
1、 main函数创建任务,任务的数量上限由OS_MAX_TASKS宏指定(默认为16个);
2、所有任务轮流执行,每个任务执行相同的时间;
3、PendSV中断用来完成任务的切换与任务信息保存的工作;
4、SysTick中断规定次数后(由TASK_TIME_SLICE宏指定),触发PendSV中断,从而实现任务切换;
5、在OSStart之前,会创建一个“Idle Task”任务,当没有任务需要执行时,该任务独占CPU。
这里的代码创建的Task1与Task2都是与具体硬件相关的LED开关任务,注意对照着匹配自己的硬件,或使用USART任务来代替这些任务。
总共5个文件,给出如下:
1、main.c:
1 #include "include.h" 2 3 //#define LED1TURN() (GPIOA->ODR ^= 1<<8) 4 //#define LED2TURN() (GPIOD->ODR ^= 1<<2) 5 6 extern OS_TCB OSTCBTbl[OS_MAX_TASKS]; // (OS Task Control Block Table) 7 extern OS_STK TASK_IDLE_STK[TASK_STACK_SIZE]; //("TaskIdle" Stack) 8 extern OS_TCB *OSTCBCur; // Pointer to the current running task(OS Task Control Block Current) 9 extern OS_TCB *OSTCBNext; // Pointer to the next running task(OS Task Control Block Next) 10 extern INT8U OSTaskNext; // Index of the next task 11 extern INT32U TaskTickLeft; // Refer to the time ticks left for the current task 12 extern INT32U TimeMS; // For system time record 13 extern INT32U TaskTimeSlice; // For system time record 14 15 OS_STK Task1Stk[TASK_STACK_SIZE]; 16 OS_STK Task2Stk[TASK_STACK_SIZE]; 17 OS_STK Task3Stk[TASK_STACK_SIZE]; 18 19 void Task1(void *p_arg); 20 void Task2(void *p_arg); 21 void Task3(void *p_arg); 22 //void LedInit(void); 23 24 int main(void) 25 { 26 27 28 OSInit(); 29 OSTaskCreate(Task1, (void*)0, (OS_STK*)&Task1Stk[TASK_STACK_SIZE-1]); 30 OSTaskCreate(Task2, (void*)0, (OS_STK*)&Task2Stk[TASK_STACK_SIZE-1]); 31 OSTaskCreate(Task3, (void*)0, (OS_STK*)&Task3Stk[TASK_STACK_SIZE-1]); 32 SysTickInit(5); 33 LedInit(); 34 USART1_Configuration(8, 9600); 35 36 //while(1); 37 OSStart(); 38 39 // while(1); 40 //while(1){ 41 // GPIOA->ODR &= ~(1<<8); 42 // GPIOD->ODR |= 1<<2; 43 //} 44 } 45 46 /*void LedInit(void) 47 { 48 RCC->APB2ENR |= 1<<2; 49 RCC->APB2ENR |= 1<<5; 50 //GPIOE->CRH&=0X0000FFFF; 51 //GPIOE->CRH|=0X33330000; 52 53 GPIOA->CRH &= 0xfffffff0; 54 GPIOA->CRH |= 0x00000003; 55 //GPIOA->ODR &= 0xfffffeff; 56 GPIOA->ODR |= 1<<8; 57 58 GPIOD->CRL &= 0xfffff0ff; 59 GPIOD->CRL |= 0x00000300; 60 //GPIOD->ODR &= 0xfffffffd; 61 GPIOD->ODR |= 1<<2; 62 63 //LED1TURN(); 64 LED2TURN(); 65 66 }*/ 67 68 void Task1(void *p_arg) 69 { 70 while(1) { 71 delayMs(100); 72 LED1TURN(); 73 } 74 } 75 void Task2(void *p_arg) 76 { 77 while(1) { 78 delayMs(200); 79 LED2TURN(); 80 } 81 } 82 83 void Task3(void *p_arg) 84 { 85 while(1) { 86 USART1->DR = 'B'; 87 while((USART1->SR & 0x40) == 0); 88 USART1->DR = ' '; 89 while((USART1->SR & 0x40) == 0); 90 USART1->DR = ' '; 91 while((USART1->SR & 0x40) == 0); 92 delayMs(50); 93 } 94 }
2、include.h:
1 #ifndef INCLUDE_H 2 #define INCLUDE_H 3 4 #include <stdlib.h> 5 6 #include "myos.h" 7 8 #include "stm32f10x.h" 9 10 #endif
3、myos.h:
1 #ifndef MYOS_H 2 #define MYOS_H 3 #include "stm32f10x.h" 4 5 /**********CPU DEPENDENT************/ 6 #define TASK_TIME_SLICE 5 // 5ms for every task to run every time 7 8 typedef unsigned char INT8U; // Unsigned 8 bit quantity 9 typedef unsigned short INT16U; // Unsigned 16 bit quantity 10 typedef unsigned int INT32U; // Unsigned 32 bit quantity 11 12 typedef unsigned int OS_STK; // Each stack entry is 32-bit wide(OS Stack) 13 14 // assembling functions 15 void OS_ENTER_CRITICAL(void); // Enter Critical area, that is to disable interruptions 16 void OS_EXIT_CRITICAL(void); // Exit Critical area, that is to enable interruptions 17 void OSCtxSw(void); // Task Switching Function(OS Context Switch) 18 void OSStart(void); 19 20 OS_STK* OSTaskStkInit(void (*task)(void *p_arg), // task function 21 void *p_arg, // (pointer of arguments) 22 OS_STK *p_tos); // (pointer to the top of stack) 23 /**********CPU INDEPENDENT************/ 24 25 #define OS_MAX_TASKS 16 26 27 #define TASK_STATE_CREATING 0 28 #define TASK_STATE_RUNNING 1 29 #define TASK_STATE_PAUSING 2 30 31 #define TASK_STACK_SIZE 64 32 33 #define LED1TURN() (GPIOA->ODR ^= 1<<8) 34 #define LED2TURN() (GPIOD->ODR ^= 1<<2) 35 36 37 typedef struct os_tcb { 38 OS_STK *OSTCBStkPtr; // (OS Task Control Block Stack Pointer) 39 INT8U OSTCBStat; // (OS Task Control Block Status) 40 } OS_TCB; // (OS Task Control Block) 41 42 void OSInit(void); // (OS Initialization) 43 void LedInit(void); 44 void USART1_Configuration(u32 pclk2, u32 bound); 45 void OS_TaskIdle(void *p_arg); 46 void OSInitTaskIdle(void); // (OS Initialization of "TaskIdle") 47 void OSTaskCreate(void (*task)(void *p_arg), // task function 48 void *p_arg, // (pointer of arguments) 49 OS_STK *p_tos); // (pointer to the top of stack) 50 void OSTCBSet(OS_TCB *p_tcb, OS_STK *p_tos, INT8U task_state); 51 52 53 void SysTickInit(INT8U Nms); // (System Tick Initialization) 54 void SysTick_Handler(void); // The interrupt function 55 56 INT32U GetTime(void); 57 void delayMs(volatile INT32U ms); // The argument can't be too large 58 59 #endif
4、myos.c:
1 #include "myos.h" 2 #include "stm32f10x.h" 3 4 5 OS_TCB OSTCBTbl[OS_MAX_TASKS]; // (OS Task Control Block Table) 6 OS_STK TASK_IDLE_STK[TASK_STACK_SIZE]; //("TaskIdle" Stack) 7 OS_TCB *OSTCBCur; // Pointer to the current running task(OS Task Control Block Current) 8 OS_TCB *OSTCBNext; // Pointer to the next running task(OS Task Control Block Next) 9 INT8U OSTaskNext; // Index of the next task 10 INT32U TaskTickLeft; // Refer to the time ticks left for the current task 11 INT32U TimeMS; 12 INT32U TaskTimeSlice; 13 char * Systick_priority = (char *)0xe000ed23; 14 // Initialize the stack of a task, it is of much relationship with the specific CPU 15 OS_STK* OSTaskStkInit(void (*task)(void *p_arg), 16 void *p_arg, 17 OS_STK *p_tos) 18 { 19 OS_STK *stk; 20 stk = p_tos; 21 22 *(stk) = (INT32U)0x01000000L; // xPSR 23 *(--stk) = (INT32U)task; // Entry Point 24 25 // Don't be serious with the value below. They are of random 26 *(--stk) = (INT32U)0xFFFFFFFEL; // R14 (LR) 27 *(--stk) = (INT32U)0x12121212L; // R12 28 *(--stk) = (INT32U)0x03030303L; // R3 29 *(--stk) = (INT32U)0x02020202L; // R2 30 *(--stk) = (INT32U)0x01010101L; // R1 31 32 // pointer of the argument 33 *(--stk) = (INT32U)p_arg; // R0 34 35 // Don't be serious with the value below. They are of random 36 *(--stk) = (INT32U)0x11111111L; // R11 37 *(--stk) = (INT32U)0x10101010L; // R10 38 *(--stk) = (INT32U)0x09090909L; // R9 39 *(--stk) = (INT32U)0x08080808L; // R8 40 *(--stk) = (INT32U)0x07070707L; // R7 41 *(--stk) = (INT32U)0x06060606L; // R6 42 *(--stk) = (INT32U)0x05050505L; // R5 43 *(--stk) = (INT32U)0x04040404L; // R4 44 return stk; 45 } 46 47 // Only to initialize the Task Control Block Table 48 void OSInit(void) 49 { 50 INT8U i; 51 OS_ENTER_CRITICAL(); 52 for(i = 0; i < OS_MAX_TASKS; i++) { 53 OSTCBTbl[i].OSTCBStkPtr = (OS_STK*)0; 54 OSTCBTbl[i].OSTCBStat = TASK_STATE_CREATING; 55 } 56 OSInitTaskIdle(); 57 OSTCBCur = &OSTCBTbl[0]; 58 OSTCBNext = &OSTCBTbl[0]; 59 OS_EXIT_CRITICAL(); 60 } 61 62 void OSInitTaskIdle(void) 63 { 64 OS_ENTER_CRITICAL(); 65 OSTCBTbl[0].OSTCBStkPtr = OSTaskStkInit(OS_TaskIdle, (void *)0, (OS_STK*)&TASK_IDLE_STK[TASK_STACK_SIZE - 1]); 66 OSTCBTbl[0].OSTCBStat = TASK_STATE_RUNNING; 67 OS_EXIT_CRITICAL(); 68 } 69 70 void OSTaskCreate(void (*task)(void *p_arg), 71 void *p_arg, 72 OS_STK *p_tos) 73 { 74 OS_STK * tmp; 75 INT8U i = 1; 76 OS_ENTER_CRITICAL(); 77 while(OSTCBTbl[i].OSTCBStkPtr != (OS_STK*)0) { 78 i++; 79 } 80 tmp = OSTaskStkInit(task, p_arg, p_tos); 81 OSTCBSet(&OSTCBTbl[i], tmp, TASK_STATE_CREATING); 82 OS_EXIT_CRITICAL(); 83 } 84 85 void OSTCBSet(OS_TCB *p_tcb, OS_STK *p_tos, INT8U task_state) 86 { 87 p_tcb->OSTCBStkPtr = p_tos; 88 p_tcb->OSTCBStat = task_state; 89 } 90 91 void OS_TaskIdle(void *p_arg) 92 { 93 p_arg = p_arg; // No use of p_arg, only for avoiding "warning" here. 94 for(;;) { 95 // OS_ENTER_CRITICAL(); 96 // Nothing to do 97 // OS_EXIT_CRITICAL(); 98 } 99 } 100 101 // void SysTick_Handler(void) 102 // { 103 // // OS_ENTER_CRITICAL(); 104 // // OS_EXIT_CRITICAL(); 105 // } 106 void SysTick_Handler(void) 107 { 108 OS_ENTER_CRITICAL(); 109 if((--TaskTimeSlice) == 0){ 110 TaskTimeSlice = TASK_TIME_SLICE; 111 OSTCBCur = OSTCBNext; 112 OSCtxSw(); 113 OSTaskNext++; 114 while(OSTCBTbl[OSTaskNext].OSTCBStkPtr == (OS_STK*)0) { 115 OSTaskNext++; 116 if(OSTaskNext >= OS_MAX_TASKS) { 117 OSTaskNext = 0; 118 } 119 } 120 OSTCBNext = &OSTCBTbl[OSTaskNext]; 121 TaskTimeSlice = TASK_TIME_SLICE; 122 } 123 TimeMS++; 124 OS_EXIT_CRITICAL(); 125 } 126 127 void SysTickInit(INT8U Nms) 128 { 129 130 OS_ENTER_CRITICAL(); 131 132 TimeMS = 0; 133 TaskTimeSlice = TASK_TIME_SLICE; 134 135 SysTick->LOAD = 1000 * Nms - 1; 136 *Systick_priority = 0x00; 137 SysTick->VAL = 0; 138 SysTick->CTRL = 0x3; 139 OS_EXIT_CRITICAL(); 140 } 141 142 INT32U GetTime(void) 143 { 144 return TimeMS; 145 } 146 147 void delayMs(volatile INT32U ms) 148 { 149 INT32U tmp; 150 tmp = GetTime() + ms; 151 while(1){ 152 if(tmp < GetTime()) break; 153 } 154 } 155 156 void LedInit(void) 157 { 158 RCC->APB2ENR |= 1<<2; 159 RCC->APB2ENR |= 1<<5; 160 //GPIOE->CRH&=0X0000FFFF; 161 //GPIOE->CRH|=0X33330000; 162 163 GPIOA->CRH &= 0xfffffff0; 164 GPIOA->CRH |= 0x00000003; 165 //GPIOA->ODR &= 0xfffffeff; 166 GPIOA->ODR |= 1<<8; 167 168 GPIOD->CRL &= 0xfffff0ff; 169 GPIOD->CRL |= 0x00000300; 170 //GPIOD->ODR &= 0xfffffffd; 171 GPIOD->ODR |= 1<<2; 172 173 //LED1TURN(); 174 LED2TURN(); 175 176 } 177 178 void USART1_Configuration(u32 pclk2, u32 bound) 179 { 180 float tmp; 181 u16 mantissa; 182 u16 fraction; 183 tmp = (float)(pclk2 * 1000000)/(bound * 16); 184 mantissa = tmp; 185 fraction = (tmp - mantissa) * 16; 186 mantissa <<= 4; 187 mantissa += fraction; 188 RCC->APB2ENR |= 1 << 2; 189 RCC->APB2ENR |= 1 << 14; 190 GPIOA->CRH &= 0xFFFFF00F; 191 GPIOA->CRH |= 0x000008b0; 192 RCC->APB2RSTR |= 1 << 14; 193 RCC->APB2RSTR &= ~(1<<14); 194 USART1->BRR = mantissa; 195 USART1->CR1 |= 0x2008; // enable usart1, TE 196 USART1->CR2 &= ~(0x3000); // stop 1 bit 197 }
5、os_cpu_a.asm:
1 IMPORT OSTCBCur 2 IMPORT OSTCBNext 3 4 EXPORT OS_ENTER_CRITICAL 5 EXPORT OS_EXIT_CRITICAL 6 EXPORT OSStart 7 EXPORT PendSV_Handler 8 EXPORT OSCtxSw 9 10 NVIC_INT_CTRL EQU 0xE000ED04 ; Address of NVIC Interruptions Control Register 11 NVIC_PENDSVSET EQU 0x10000000 ; Enable PendSV 12 NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14). 13 NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest). 14 15 PRESERVE8 ; align 8 16 17 AREA |.text|, CODE, READONLY 18 THUMB 19 20 ;/******************OS_ENTER_CRITICAL************/ 21 OS_ENTER_CRITICAL 22 CPSID I ; Enable interruptions(Change Processor States: Interrupts Disable) 23 BX LR ; Return 24 25 ;/******************OS_EXIT_CRITICAL************/ 26 OS_EXIT_CRITICAL 27 CPSIE I ; Disable interruptions 28 BX LR ; Return 29 30 ;/******************OSStart************/ 31 OSStart 32 ; disable interruptions 33 CPSID I ; OS_ENTER_CRITICAL(); 34 ; initialize PendSV 35 ; Set the PendSV exception priority 36 LDR R0, =NVIC_SYSPRI14 ; R0 = NVIC_SYSPRI14; 37 LDR R1, =NVIC_PENDSV_PRI ; R1 = NVIC_PENDSV_PRI; 38 STRB R1, [R0] ; *R0 = R1; 39 40 ; initialize PSP as 0 41 ; MOV R4, #0 42 LDR R4, =0x0 ; R4 = 0; 43 MSR PSP, R4 ; PSP = R4; 44 45 ; trigger PendSV 46 LDR R4, =NVIC_INT_CTRL ; R4 = NVIC_INT_CTRL; 47 LDR R5, =NVIC_PENDSVSET ; R5 = NVIC_PENDSVSET; 48 STR R5, [R4] ; *R4 = R5; 49 50 ; enable interruptions 51 CPSIE I ; OS_EXIT_CRITICAL(); 52 53 ; should never get here 54 ; a endless loop 55 OSStartHang 56 B OSStartHang 57 58 ;/******************PendSV_Handler************/ 59 PendSV_Handler 60 CPSID I ; OS_ENTER_CRITICAL(); 61 ; judge if PSP is 0 which means the task is first invoked 62 MRS R0, PSP ; R0 = PSP; 63 CBZ R0, PendSV_Handler_NoSave ; if(R0 == 0) goto PendSV_Handler_NoSave; 64 65 ; R12, R3, R2, R1 66 SUB R0, R0, #0x20 ; R0 = R0 - 0x20; 67 68 ; store R4 69 STR R4 , [R0] ; *R0 = R4; 70 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 71 ; store R5 72 STR R5 , [R0] ; *R0 = R5; 73 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 74 ; store R6 75 STR R6 , [R0] ; *R0 = R6; 76 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 77 ; store R7 78 STR R7 , [R0] ; *R0 = R7; 79 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 80 ; store R8 81 STR R8 , [R0] ; *R0 = R8; 82 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 83 ; store R9 84 STR R9, [R0] ; *R0 = R4; 85 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 86 ; store R10 87 STR R10, [R0] ; *R0 = R10; 88 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 89 ; store R11 90 STR R11, [R0] ; *R0 = R11; 91 ADD R0, R0, #0x4 ; R0 = R0 + 0x4; 92 93 SUB R0, R0, #0x20 ; R0 = R0 - 0x20; 94 95 ; easy method 96 ;SUB R0, R0, #0x20 97 ;STM R0, {R4-R11} 98 99 LDR R1, =OSTCBCur ; R1 = OSTCBCur; 100 LDR R1, [R1] ; R1 = *R1;(R1 = OSTCBCur->OSTCBStkPtr) 101 STR R0, [R1] ; *R1 = R0;(*(OSTCBCur->OSTCBStkPrt) = R0) 102 103 PendSV_Handler_NoSave 104 LDR R0, =OSTCBCur ; R0 = OSTCBCur; 105 LDR R1, =OSTCBNext ; R1 = OSTCBNext; 106 LDR R2, [R1] ; R2 = OSTCBNext->OSTCBStkPtr; 107 STR R2, [R0] ; *R0 = R2;(OSTCBCur->OSTCBStkPtr = OSTCBNext->OSTCBStkPtr) 108 109 LDR R0, [R2] ; R0 = *R2;(R0 = OSTCBNext->OSTCBStkPtr) 110 ; LDM R0, {R4-R11} 111 ; load R4 112 LDR R4, [R0] ; R4 = *R0;(R4 = *(OSTCBNext->OSTCBStkPtr)) 113 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 114 ; load R5 115 LDR R5, [R0] ; R5 = *R0;(R5 = *(OSTCBNext->OSTCBStkPtr)) 116 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 117 ; load R6 118 LDR R6, [R0] ; R6 = *R0;(R6 = *(OSTCBNext->OSTCBStkPtr)) 119 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 120 ; load R7 121 LDR R7 , [R0] ; R7 = *R0;(R7 = *(OSTCBNext->OSTCBStkPtr)) 122 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 123 ; load R8 124 LDR R8 , [R0] ; R8 = *R0;(R8 = *(OSTCBNext->OSTCBStkPtr)) 125 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 126 ; load R9 127 LDR R9 , [R0] ; R9 = *R0;(R9 = *(OSTCBNext->OSTCBStkPtr)) 128 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 129 ; load R10 130 LDR R10 , [R0] ; R10 = *R0;(R10 = *(OSTCBNext->OSTCBStkPtr)) 131 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 132 ; load R11 133 LDR R11 , [R0] ; R11 = *R0;(R11 = *(OSTCBNext->OSTCBStkPtr)) 134 ADD R0, R0, #0x4 ; R0 = R0 + 0x4;(OSTCBNext->OSTCBStkPtr++) 135 136 MSR PSP, R0 ; PSP = R0;(PSP = OSTCBNext->OSTCBStkPtr) 137 ; P42 138 ; P139 (key word: EXC_RETURN) 139 ; use PSP 140 ORR LR, LR, #0x04 ; LR = LR | 0x04; 141 CPSIE I ; OS_EXIT_CRITICAL(); 142 BX LR ; return; 143 144 OSCtxSw ;OS context switch 145 PUSH {R4, R5} 146 LDR R4, =NVIC_INT_CTRL ; R4 = NVIC_INT_CTRL 147 LDR R5, =NVIC_PENDSVSET ; R5 = NVIC_PENDSVSET 148 STR R5, [R4] ; *R4 = R5 149 POP {R4, R5} 150 BX LR ; return; 151 152 align 4 153 end