前言: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);
        }
    }
}

Logo

鸿蒙生态一站式服务平台。

更多推荐