从最简单开始制作自己的RTOS嵌入式操作系统
整个内核非常小巧,STM32F030F4P6 4K 内存,跑三个任务,还有将近2K内存,估计还可以跑3-4任务(TASK)应该没问题,而之前测试FreeRTOS,最多只能跑两个任务。这就有意思了,操作系统每1ms来一次中断,这些寄存器不用管,只需要把R4-R11压入栈,然后指向新的TAS环境,从新的TASK栈里,把寄存器回复,于是,实现了任务的调度,每1ms(通常)来切换一下。(都为最低,0xff
前言:2013年,时间有点富裕,就学习,查看CortexM3权威指南,并参照某些操作系统,
实现了最简单的时间片轮转和线程休眠一定时间(osDelayMS())。最近突然有想完善一下,增加了Mutex 和 进程间信息传递类似mailbox(协作式调度,以实现信息传递的快速)。并优化了任务调度。整个内核非常小巧,STM32F030F4P6 4K 内存,跑三个任务,还有将近2K内存,估计还可以跑3-4任务(TASK)应该没问题,而之前测试FreeRTOS,最多只能跑两个任务。闲话少说,开始正题:
基本知识:
cortex -M0,M3,M4 有16个寄存器,R0-R15,xPSR
你些的C代码,编译器会试用这些寄存器来进行各种运算,当有中断来的时候,ARM内核会自动依次将xPSR,PC,LR,R12,R0-R3压入堆栈,这就有意思了,操作系统每1ms来一次中断,这些寄存器不用管,只需要把R4-R11压入栈,然后指向新的TAS环境,从新的TASK栈里,把寄存器回复,于是,实现了任务的调度,每1ms(通常)来切换一下。当然,怎么切换,就是操作系统最核心的算法,那么,我们简单处理,来个时间片轮换调度,说干就干:
cortex里有个著名的sysTick中断,SysTick_Handler()CPU我先先用手头上的STM32F407VET6,
CUBEMX配置,
建立文件cpu.s
实现两个函数
PendSV_Handler (将中断文件里的此函数注释掉)
CPSID I ; Prevent interruption during context switch
; LDR R1,=TCBRunning ; @1 if here use R3,@2 can remove
; LDR R1,[R1] ; R1 == running tcb
LDR R3,=TCBRunning ; @1 if here use R3,@2 can remove
LDR R1,[R3] ; R1 == running tcb
LDR R2,=TCBNext
LDR R2,[R2] ; R2 == next tcb
CMP R1,R2
BEQ exitPendSV
MRS R0, PSP ; Get PSP point (can not use PUSH,in ISR,SP is MSP )
STMDB R0!,{R4-R11} ; Store r4-r11,r0 -= regCnt * 4,r0 is new stack
; top point (addr h->l r11,r10,...,r5,r4)
IF :DEF:M4FPU ;#if CFG_CHIP_TYPE == 3
VSTMDB R0!, {S16-S31} ;// store remaining FPU registers
ENDIF ;#endif // CFG_FPU_ENABLE
STR R0,[R1] ; Save orig PSP
popStk
;LDR R3,=TCBRunning ; @2
STR R2, [R3] ; TCBRunning = TCBNext;
LDR R0, [R2] ; Get SP of task that be switch into.
IF :DEF:M4FPU ;#if CFG_CHIP_TYPE == 3
VLDMIA R0!, {S16-S31} ;// load remaining FPU registers
ENDIF;#endif // CFG_FPU_ENABLE
LDMIA R0!,{R4-R11} ; POP (R4-R11),R0 += regCnt * 4
MSR PSP, R0 ; Mov new stack point to PSP
exitPendSV
LDR R3,=OSSchedLock
MOVS R0, #0x0
STRB R0, [R3]
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
IF :DEF:M4FPU ;#if CFG_CHIP_TYPE == 3
LDR LR,=0xFFFFFFED
ENDIF
; and we can find the defination in com_m4.h
;/* The following EXC_RETURN values are saved the LR on exception entry */
;#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */
;#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */
;#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */
;#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */
;#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */
;#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */
;#endif
CPSIE I
BX LR ; Exit interrupt
ALIGN
END
在 工程选项 asm里定义 M4FPU
在cpu.s里
SwitchContext
; PUSH {R4, R5}
; LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
; LDR R5, =NVIC_PENDSVSET
; STR R5, [R4]
; POP {R4, R5}
; BX LR
; NOP
LDR R0, =0xE000ED04 ; Trigger the PendSV exception (causes context switch)
LDR R1, =0x10000000
STR R1, [R0]
BX LR
ALIGN ; ".align 4\n"// ALIGN \n"
以及上部各内容:
IMPORT TCBRunning
IMPORT TCBNext
IMPORT OSSchedLock
IMPORT OSRunning
EXPORT OS_CPU_SR_Save ; Functions declared in this file
EXPORT OS_CPU_SR_Restore
; EXPORT OSStartFirstTask
; EXPORT SetEnvironment_M3
EXPORT SetFirstRunningTaskStak
EXPORT SwitchContext
EXPORT PendSV_Handler
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制寄存器
NVIC_SYSPRI2 EQU 0xE000ED22 ; 系统优先级寄存器(2)
NVIC_PENDSV_PRI EQU 0xFFFF ; PendSV中断和系统节拍中断
; (都为最低,0xff).
NVIC_PENDSVSET EQU 0x10000000 ; 触发软件中断的值.
PRESERVE8
AREA |.text|, CODE, READONLY
THUMB
OS_CPU_SR_Save
MRS R0, PRIMASK ;读取PRIMASK到R0,R0为返回值
CPSID I ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
BX LR ;返回
SetFirstRunningTaskStak
IF :DEF:M4FPU ; #if CPU_TYPE == CPU_M4_FPU
SUBS R0,#100
ELSE
SUBS R0,#28
ENDIF
MSR PSP, R0 ; Mov new stack point to PSP
BX LR
ALIGN ; ".align 4 \n"//
好了,开始写调度,其余全是C语言,不再是汇编
定义任务管理控制结构
typedef unsigned int OS_STK;
typedef struct TCB
{
OS_STK * stkPtr; /*!< The current point of task. */
U32 delayTick;// sleep sysTick
}OSTCB,*P_OSTCB;
未完待续.....2022.10.09
SysTIck_Handler函数
void SysTick_Handler(void)
{
// unsigned int st;
//st = IRQ_DISABLE_SAVE();
OS_CPU_SR cpu_sr = 0u;
HAL_IncTick();
TimeTick++;
if(OSRunning)
{
OS_ENTER_CRITICAL();
OSSchedLock++;
//TCBRunning = &TCBTbl[taskRuning]; /* Get running task */
DecTaskDelay();
Schedule();
OS_EXIT_CRITICAL();
//SwitchContext();
}
//IRQ_ENABLE_RESTORE(st);
//NVIC_SetPendingIRQ(PendSV_IRQn);
}
休眠处理函数
void DecTaskDelay()// all sleep delayTick --
{
int i;
for(i=0; i<MAX_TASK_COUNT;i++)
{
if(TCBTbl[i].delayTick != NOT_SLEEP)//多了一次,NOT_SLEEP = 0 ?
TCBTbl[i].delayTick--;
}
}
关键的几个变量:
volatile U32 TimeTick = 0;
volatile U8 OSSchedLock = 0; /*!< Task Switch lock. */
volatile U8 OSRunning = 0;
//#define NULL 0
P_OSTCB TCBNext = NULL; /*!< Poniter to task that next scheduled by OS */
P_OSTCB TCBRunning = NULL; /*!< Pointer to TCB that current running task. */
/*!< Table of TCB */
// the last item saved the idle task tcb
OSTCB TCBTbl[MAX_TASK_COUNT+1] = {{0}};
OS_STK stkTaskIdle[SIZE_TASK_IDLE]; // Stack of Blink task 0/
创建 任务函数
int g_taskCount = 0;
int OSCreateTask(void* task,void*pParam,,OS_STK *stack,char* tskname)//,
// int taskIndex)
{
int taskIndex = g_taskCount;
OSTCB * pTCB;
if(taskIndex>=MAX_TASK_COUNT+1)
return 0;
g_taskCount++;
//IRQ_DISABLE_SAVE();
pTCB = &TCBTbl[taskIndex];
pTCB->stkPtr = InitTaskContext((FUNCPtr)task,pParam,pParam2,stack);
pTCB->delayTick = NOT_SLEEP;
}
void OSSleepTick(int tick)
{
OS_CPU_SR cpu_sr = 0u;
U32 startSysTick = TimeTick;
if(OSRunning)
{
OS_ENTER_CRITICAL();
OSSchedLock++;
TCBRunning->delayTick = tick;
Schedule();
OS_EXIT_CRITICAL();
}else
{
DelayTicks(tick);
}
}
初始化堆栈
typedef void (*FUNCPtr)(void*);
OS_STK *InitTaskContext(FUNCPtr task,void*pParam,OS_STK *pstk)//void *param,
{
#if 1
#define SORT_R4_THEN_S16 0
#define DEBUG_REG 1
OS_STK *context;
context = pstk;
#if CPU_TYPE == CPU_M4_FPU
context = context - 18;
#endif
*(context--) = (U32)0x01000000L; /* xPSR */
*(context--) = (U32)task; /* Entry point of task. */
*(context) = (U32)0xFFFFFFFEL;
context = context - 5;
*(context) = (U32)pParam; /* R0: argument */ //(U32)param
context = context - 8;
#if CPU_TYPE == CPU_M4_FPU
context = context - 16;
#endif
return (context); /* Returns location of new stack top. */
}
ok,
main.c
#define SIZE_TASK1 128 // Stack size oftask 1 / 32 could not enough for printf
// function printf need more stack size
#define SIZE_TASK2 128 // Stack size of\task 2 /
OS_STK stkTask1[SIZE_TASK1]; // Stack of Blink task 0 /
OS_STK stkTask2[SIZE_TASK2]; // Stack of Blink task 0/
void bleTask(void *param,void *param2);
int printTask(void *param,void *param2);
OSCreateTask( bleTask,(void*)50,
&stkTask1[SIZE_TASK1-1]);
OSCreateTask( printTask,(void*)200,
&stkTask2[SIZE_TASK2-1]);
MyOSStart();
// enjoy you task
int printTask(void *param)
{
U8 i;
while(1)
{
for(i=0; i<3; i++)
{
HAL_GPIO_WritePin(GPIOE, LED1_Pin, GPIO_PIN_SET);
OSSleepTick(p);
HAL_GPIO_WritePin(GPIOE, LED1_Pin, GPIO_PIN_RESET);
OSSleepTick(p2);
}
OSSleepTick(960);
}
return 1;// return would enter dead loop or delete the task;
}
void bleTask(void *param,)
{
int i;
while (1)
{
for(i=0; i<3; i++)
{
HAL_GPIO_WritePin(GPIOE, LED2_Pin, GPIO_PIN_SET);
OSSleepTick(100);
HAL_GPIO_WritePin(GPIOE, LED2_Pin, GPIO_PIN_RESET);
OSSleepTick(200);
}
}
}
更多推荐
所有评论(0)