基于STM32的简易RTOS

之前看过一篇卢晓铭写的简易RTOS设计,自己也实践了一下,感觉多任务运行起来毫无压力,在此做份笔记以备他日之需也为他人提供一份参考

要想完成一个RTOS,我们核心任务是需要编写任务调度。

所以,我们需要知道,任务到底什么地方会被调度。

1.        我们开始OSStart();时,肯定需要调度一次任务。这样才能进入第一个最高优先级就绪任务中。

2.        在任务中的OSTimeDly();延时函数中,我们需要进行任务调度。当前任务延时了,肯定就要换一个别的任务来运行呀。

3.        在中断退出时,需要进行任务调度(该处主要指定时器中断),可以理解为每个时钟周期都在进行任务最高优先级别的检验。

 

任务状态的标志,我想我们可以用1bit来代表,0:代表任务挂起或不存在,1:代表任务就绪。

U32 OSRdyTbl;        这是一个32bit的任务就绪表,每一位代表任务的状态.

/*在就绪表中登记任务*/

__inline void OSSetPrioRdy(u8 prio)                                      //__inline是内联函数,用在该处是为了提高效率。关于内联函数的详情和使用,可以百度。

{

        OSRdyTbl|= 1 << prio;

}

 

/*在就绪表中删除任务*/

__inline void OSDelPrioRdy(u8 prio)

{

        OSRdyTbl&= ~(1<

}

 

在每个任务调度前,肯定需要知道当前最高优先级的就绪任务是什么,所以我们需要一个查找函数

/*在就绪表中查找更高级的就绪任务*/

__inline void OSGetHighRdy(void)

{

        u8OSNextTaskPrio = 0;                           /*任务优先级*/

 

        for(OSNextTaskPrio = 0; (OSNextTaskPrio < OS_TASKS) &&(!(OSRdyTbl&(0x01<

        OSPrioHighRdy= OSNextTaskPrio;        //获得最高优先级,OSPrioHighRdy是一个全局变量

}

 

根据分析,我们知道我们的简易RTOS有3个地方会出现任务调度

首先是任务刚开始时.

我们先定义几个全局变量:

1.        OSRunning指示OS是否开始运行

2.        OSPrioCur指示当前任务优先级

3.        OSPrioHighRdy指示当前已就绪的最高优先级任务,由OSGetHighRdy函数更新

void OSStart(void)

{

        if(OSRunning== 0)                     //OSRuning是一个全局变量

        {

                      OSRunning= 1;

                     

        //先不忙讲任务创建 OSTaskCreate(IdleTask, (void *)0,(u32 *)&IDELTASK_STK[31], IdelTask_Prio);             //创建空闲任

 

                      OSGetHighRdy();                                       /*获得最高级的就绪任务*/

                      OSPrioCur= OSPrioHighRdy;                  /*获得最高优先级就绪任务ID*/

                      p_OSTCBCur= &TCB[OSPrioCur];

                      p_OSTCBHighRdy= &TCB[OSPrioHighRdy];

                      OSStartHighRdy();                       //在汇编语句中

        }

}

其次出现任务调度的地方是延时函数OSTimeDly()

在这个函数前,要先介绍一个结构体TCB,这是任务控制怪(Task Control Block)

每一个任务都有一个任务控制块,这个控制块记录着任务的重要信息,由于该处是简易OS设计,所以仅仅有两种

typedef struct TaskCtrBlockHead                 /*任务控制块数据结构*/

{

        u32OSTCBStkPtr;                                                    /*保存任务栈顶地址*/

        u32OSTCBDly;                                                                       /*任务延时时钟*/

}TaskCtrBlock;

#define OS_TASKS 32                                                    //最多任务数

TaskCtrBlock TCB[OS_TASKS];                                       //定义任务TCB,由于最多有32个任务,所以该处定义32个TCB

 

 

void OSTimeDly(u32 ticks)

{

        if(ticks> 0)

        {

                      OS_ENTER_CRITICAL();                                                         //进入临界区

                      OSDelPrioRdy(OSPrioCur);                                   //将任务挂起

                      TCB[OSPrioCur].OSTCBDly= ticks;                      //设置TCB中任务延时节拍数

                      OS_EXIT_CRITICAL();                                               //退出临界区

                      OSSched();                                                                //任务调度

        }

}

/*任务切换*/

void OSSched(void)

{

        OSGetHighRdy();                                                                   //找出任务就绪表中优先级最高的任务

        if(OSPrioHighRdy!=OSPrioCur)              //如果不是当前运行任务,进行任务调度

        {

                      p_OSTCBCur= &TCB[OSPrioCur];                       //以便在汇编中引用,可以理解为用于将现在的任务环境保存在该指针指向的堆栈中

//直接把当前任务的堆栈指针保存到当前任务的TCB(TCB的OSTCBStkPtr)

                      p_OSTCBHighRdy= &TCB[OSPrioHighRdy];     //以便在汇编中引用,可以理解为用于将该指针中的数据释放出来,恢复指定任务环境

//直接把最高优先级就绪任务的TCB(TCB的OSTCBStkPtr)设为当前任务的堆栈指针

                      OSPrioCur= OSPrioHighRdy;                                //更新OSPrio

                      OSCtxSw();                                                                //调度任务,在汇编中引用

        }

}

 

最后是在时钟中断中出现

由于每次时钟中断我们都需要解决任务时钟延时问题,所以我们需要一个函数

/*定时器中断对任务延时处理函数*/

void TicksInterrupt(void)

{

        static u8i;

 

        OSTime++;

        for(i=0;i

        {

                      if(TCB[i].OSTCBDly)

                      {

                                    TCB[i].OSTCBDly--;

                                    if(TCB[i].OSTCBDly==0)             //延时时钟到达

                                    {

                                                  OSSetPrioRdy(i);            //任务重新就绪

                                    }            

                      }

        }

}

 

//系统时钟中断服务函数

void SysTick_Handler(void)

{

        OS_ENTER_CRITICAL();                             //进入临界区

   OSIntNesting++;                         //任务前套数

   OS_EXIT_CRITICAL();                  //退出临界区

   TicksInterrupt();                          //

    OSIntExit();                                   //在中断中处理任务调度

}

void OSIntExit(void)

{

        OS_ENTER_CRITICAL();                             //进入临界区

       

        if(OSIntNesting> 0)

        OSIntNesting--;

        if(OSIntNesting== 0)                               //没有中断嵌套时,才可以进行任务调度

        {

                      OSGetHighRdy();                                                                   /*找出任务优先级最高的就绪任务*/

                      if(OSPrioHighRdy!=OSPrioCur)              /*当前任务并非优先级最高的就绪任务*/

                      {

                                    p_OSTCBCur= &TCB[OSPrioCur];

                                    p_OSTCBHighRdy= &TCB[OSPrioHighRdy];

                                    OSPrioCur= OSPrioHighRdy;

                                    OSIntCtxSw();                                             /*中断级任务调度,注意这里和OSCtxSw不一样,但是作用是一样的*/

                      }

        }

OS_EXIT_CRITICAL();                   //退出临界区

}

 

现在任务调度已经写完了,那么应该要创建任务了吧,这里使用创建任务函数

/*任务创建*/

void OSTaskCreate(void (*Task)(void  *parg), void *parg,u32 *p_Stack, u8 TaskID)

{

        if(TaskID<= OS_TASKS)

        {

            *(p_Stack) = (u32)0x01000000L;                                                        /*  xPSR                        */

            *(--p_Stack) = (u32)Task;                                                          /*  Entry Point of the task  任务入口地址   */

            *(--p_Stack) = (u32)0xFFFFFFFEL;                                                         /*  R14 (LR) (init value will  */

                                                                                   

            *(--p_Stack) = (u32)0x12121212L;                                                     /*  R12                         */

            *(--p_Stack) = (u32)0x03030303L;                                                     /*  R3                          */

            *(--p_Stack) = (u32)0x02020202L;                                                     /*  R2                          */

            *(--p_Stack) = (u32)0x01010101L;                                                     /*  R1                          */

            *(--p_Stack) = (u32)parg;                                                         /*  R0 : argument 输入参数    */

 

            *(--p_Stack) = (u32)0x11111111L;                                                     /*  R11                         */

            *(--p_Stack) = (u32)0x10101010L;                                                     /*  R10                         */

            *(--p_Stack) = (u32)0x09090909L;                                                     /*  R9                          */

            *(--p_Stack) = (u32)0x08080808L;                                                     /*  R8                          */

            *(--p_Stack) = (u32)0x07070707L;                                                     /*  R7                          */

            *(--p_Stack) = (u32)0x06060606L;                                                     /*  R6                          */

            *(--p_Stack) = (u32)0x05050505L;                                                     /*  R5                          */

            *(--p_Stack) = (u32)0x04040404L;                                                     /*  R4                         */

 

                      TCB[TaskID].OSTCBStkPtr= (u32)p_Stack;                                    /*保存堆栈地址*/

                      TCB[TaskID].OSTCBDly= 0;                                                                              /*初始化任务延时*/

                      OSSetPrioRdy(TaskID);                                                                                     /*在任务就绪表中登记*/

        }

}

这里主要是入栈寄存器地址

 

为了保证系统的正常运行,我们需要一个空闲任务,空闲任务可以什么事情都不做,也可以随便做点什么简单的事。

/*系统空闲任务*/

void IdleTask(void *pdata)

{

        u32IdleCount = 0;

        while(1)

        {

                      IdleCount++;

        }

}

 

既然如此,那么系统开始前应该申请一个空闲任务,所以OSStart()函数改为

void OSStart(void)

{

        if(OSRunning== 0)

        {

                      OSRunning= 1;

                     

                      OSTaskCreate(IdleTask,(void *)0, (u32 *)&IDELTASK_STK[31], IdelTask_Prio);             //创建空闲任务

 

                      OSGetHighRdy();                                                                   /*获得最高级的就绪任务*/

                      OSPrioCur= OSPrioHighRdy;                  /*获得最高优先级就绪任务ID*/

                      p_OSTCBCur= &TCB[OSPrioCur];

                      p_OSTCBHighRdy= &TCB[OSPrioHighRdy];

                      OSStartHighRdy();

        }

}

 

以上就是任务调度器的核心.

至于汇编代码,可以直接参考ucosii在STM32上的汇编代码。

 

文件RTOS.c

/*********************** (C) COPYRIGHT 2013 Libraworks *************************
* File Name	    : RTOS.c
* Author		: 卢晓铭 
* Version		: V1.0
* Date			: 01/26/2013
* Description	: LXM-RTOS 任务管理
*******************************************************************************/

#include"RTOS.h"

TaskCtrBlock TCB[OS_TASKS - 1];	/*任务控制块定义*/
TaskCtrBlock *p_OSTCBCur;		/*指向当前任务控制块的指针*/
TaskCtrBlock *p_OSTCBHighRdy;	/*指向最高优先级就绪任务控制块的指针*/
u8 OSPrioCur;					/*当前执行任务*/
u8 OSPrioHighRdy;				/*最高优先级*/
u8 OSRunning;					/*多任务运行标志0:为运行,1:运行*/

u32 OSInterruptSum;				/*进入中断次数*/

u32 OSTime;						/*系统时间(进入时钟中断次数)*/

u32 OSRdyTbl;					/*任务就绪表,0表示挂起,1表示就绪*/

u32 OSIntNesting;				/*任务嵌套数*/

/*在就绪表中登记任务*/
void OSSetPrioRdy(u8 prio)
{
	OSRdyTbl |= 1 << prio;
}

/*在就绪表中删除任务*/
void OSDelPrioRdy(u8 prio)
{
	OSRdyTbl &= ~(1< 0)
	{
		OS_ENTER_CRITICAL();				//进入临界区
		OSDelPrioRdy(OSPrioCur);			//将任务挂起
		TCB[OSPrioCur].OSTCBDly = ticks;	//设置TCB中任务延时节拍数
		OS_EXIT_CRITICAL();					//退出临界区
		OSSched();
	}
}

/*定时器中断对任务延时处理函数*/
void TicksInterrupt(void)
{
	static u8 i;

	OSTime++;
	for(i=0;i prio)		/*当前任务优先级小于恢复的任务优先级*/
	{
		OSSched();
	}
}

u32 IDELTASK_STK[32];

void OSStart(void)
{
	if(OSRunning == 0)
	{
		OSRunning = 1;
		
		OSTaskCreate(IdleTask, (void *)0, (u32 *)&IDELTASK_STK[31], IdelTask_Prio);	//创建空闲任务

		OSGetHighRdy();					/*获得最高级的就绪任务*/
		OSPrioCur = OSPrioHighRdy;		/*获得最高优先级就绪任务ID*/
		p_OSTCBCur = &TCB[OSPrioCur];
		p_OSTCBHighRdy = &TCB[OSPrioHighRdy];
		OSStartHighRdy();
	}
}

void OSIntExit(void)
{
	OS_ENTER_CRITICAL();
	
	if(OSIntNesting > 0)
		OSIntNesting--;
	if(OSIntNesting == 0)
	{
		OSGetHighRdy();					/*找出任务优先级最高的就绪任务*/
		if(OSPrioHighRdy!=OSPrioCur)	/*当前任务并非优先级最高的就绪任务*/
		{
			p_OSTCBCur = &TCB[OSPrioCur];
			p_OSTCBHighRdy = &TCB[OSPrioHighRdy];
			OSPrioCur = OSPrioHighRdy;
			OSIntCtxSw();				/*中断级任务调度*/
		}
	}

	OS_EXIT_CRITICAL();
}

/*系统空闲任务*/
void IdleTask(void *pdata)
{
	u32 IdleCount = 0;
	while(1)
	{
		IdleCount++;
	}
}

void OSTaskSwHook(void)
{
}


文件RTOS.h
;/*********************** (C) COPYRIGHT 2013 Libraworks *************************
;* File Name	: RTOS.h
;* Author		: 卢晓铭 
;* Version		: V1.0
;* Date			: 01/26/2013
;* Description	: LXM-RTOS asm port
;*******************************************************************************/
#ifndef __RTOS_H
#define __RTOS_H
#include "stm32f10x.h"//加入头文件

typedef struct TaskCtrBlockHead		/*任务控制块数据结构*/
{
	u32 OSTCBStkPtr;				/*保存任务栈顶*/
	u32 OSTCBDly;					/*任务延时时钟*/
}TaskCtrBlock;

#define OS_TASKS 32				/*总任务数*/
#define IdelTask_Prio 31		/*空闲任务优先级*/

extern TaskCtrBlock TCB[OS_TASKS - 1];	/*任务控制块定义*/
extern TaskCtrBlock *p_OSTCBCur;		/*指向当前任务控制块的指针*/
extern TaskCtrBlock *p_OSTCBHighRdy;	/*指向最高优先级就绪任务控制块的指针*/
extern u8 OSPrioCur;					/*当前执行任务*/
extern u8 OSPrioHighRdy;				/*最高优先级*/
extern u8 OSRunning;					/*多任务运行标志0:为运行,1:运行*/
extern u32 OSInterruptSum;				/*进入中断次数*/
extern u32 OSTime;						/*系统时间(进入时钟中断次数)*/
extern u32 OSRdyTbl;					/*任务就绪表,0表示挂起,1表示就绪*/
extern u32 OSIntNesting;				/*任务嵌套数*/


void OSTimeDly(u32 ticks);		/*设置任务延时时间*/
void TicksInterrupt(void);		/*定时器中断对任务延时处理函数*/
void IdleTask(void *pdata);		/*系统空闲任务*/
void OSSched(void);				/*任务切换*/
void OSStart(void);				/*多任务系统开始*/
void OSIntExit(void);			/*中断退出函数*/

void OSTaskCreate(void  (*Task)(void  *parg), void *parg, u32 *p_Stack, u8 TaskID);	/*创建任务函数*/
void OSTaskSuspend(u8 prio);	/*挂起指定任务*/
void OSTaskResume(u8 prio);		/*回复指定的挂起任务*/

void OSTaskSwHook(void);		/*空函数*/
/*in asm function*/
void OS_EXIT_CRITICAL(void);	/*退出临界区*/
void OS_ENTER_CRITICAL(void);	/*进入临界区*/
void OSStartHighRdy(void);		/*调度第一个任务*/
void OSCtxSw(void);				/*函数级任务切换*/
void OSIntCtxSw(void);			/*中断级任务切换*/

#endif

文件RTOS_ASM.s

;/*********************** (C) COPYRIGHT 2013 Libraworks *************************
;* File Name	: RTOS_ASM.s
;* Author		: 卢晓铭 
;* Version		: V1.0
;* Date			: 01/26/2013
;* Description	: LXM-RTOS asm port
;*******************************************************************************/
	IMPORT OSInterruptSum
	IMPORT OSRunning
	IMPORT p_OSTCBCur
	IMPORT p_OSTCBHighRdy
	IMPORT OSTaskSwHook
	IMPORT OSPrioCur
	IMPORT OSPrioHighRdy

	EXPORT OS_ENTER_CRITICAL
	EXPORT OS_EXIT_CRITICAL
	EXPORT OSStartHighRdy
	EXPORT OSCtxSw
	EXPORT OSIntCtxSw
	EXPORT PendSV_Handler

NVIC_INT_CTRL   	EQU     0xE000ED04  ; 中断控制寄存器
NVIC_SYSPRI2    	EQU     0xE000ED20  ; 系统优先级寄存器(2)
NVIC_PENDSV_PRI 	EQU     0xFFFF0000  ; 软件中断和系统节拍中断
                                        ; (都为最低,0xff).
NVIC_PENDSVSET  	EQU     0x10000000  ; 触发软件中断的值.

		PRESERVE8 
        SECTION .text:CODE:NOROOT(2)
        THUMB 
;/***************************************************************************************
;* 函数名称: OS_ENTER_CRITICAL
;*
;* 功能描述: 进入临界区 
;*            
;* 参    数: None
;*
;* 返 回 值: None
;*****************************************************************************************/ 

OS_ENTER_CRITICAL
 
		CPSID   I                       ; Disable all the interrupts
                                                                        
		PUSH 	{R1,R2}      

		LDR 	R1, =OSInterruptSum	    ; OSInterrputSum++
        LDRB 	R2, [R1]
        ADD   	R2, R2, #1
        STRB 	R2, [R1]
		POP     {R1,R2}
  		BX LR

;/***************************************************************************************
;* 函数名称: OS_EXIT_CRITICAL
;*
;* 功能描述: 退出临界区 
;*            
;* 参    数: None
;*
;* 返 回 值: None
;*****************************************************************************************/

OS_EXIT_CRITICAL
		PUSH    {R1, R2}
		LDR     R1, =OSInterruptSum     ; OSInterrputSum--
        LDRB    R2, [R1]
        SUB     R2, R2, #1
        STRB    R2, [R1]
		MOV     R1,  #0	      
		CMP     R2,  #0			        ; if OSInterrputSum=0,enable 
                                        ; interrupts如果OSInterrputSum=0,
		;MSREQ  PRIMASK, R1   
        CPSIE   I
	    POP   	{R1, R2}
		BX LR
;/**************************************************************************************
;* 函数名称: OSStartHighRdy
;*
;* 功能描述: 使用调度器运行第一个任务
;* 
;* 参    数: None
;*
;* 返 回 值: None
;**************************************************************************************/  

OSStartHighRdy
        LDR     R4, =NVIC_SYSPRI2      ; set the PendSV exception priority
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]

        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call
        MSR     PSP, R4

        LDR     R4, =OSRunning         ; OSRunning = TRUE
        MOV     R5, #1
        STRB    R5, [R4]

                                       ;切换到最高优先级的任务
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level
OSStartHang
        B       OSStartHang            ;should never get here

;/**************************************************************************************
;* 函数名称: OSCtxSw
;*
;* 功能描述: 函数级任务切换         
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/
  
OSCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL  	;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR
;/**************************************************************************************
;* 函数名称: OSIntCtxSw
;*
;* 功能描述: 中断级任务切换
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

OSIntCtxSw
		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

;/**************************************************************************************
;* 函数名称: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

PendSV_Handler
    CPSID   I                                                   ; Prevent interruption during context switch
    MRS     R0, PSP                                             ; PSP is process stack pointer 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注
    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, =p_OSTCBCur                                     ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

                                                                ; At this point, entire context of process has been saved
PendSV_Handler_Nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =p_OSTCBCur                                     ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =p_OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context


;************************************************************

END


你可能感兴趣的:(STM32,RTOS)