一步步写STM32 OS【四】OS基本框架

发布者:心有所属最新更新时间:2021-01-12 来源: eefocus关键字:STM32 手机看文章 扫描二维码
随时随地手机看文章

一、上篇回顾


上一篇文章中,我们完成了两个任务使用PendSV实现了互相切换的功能,下面我们接着其思路往下做。这次我们完成OS基本框架,即实现一个非抢占式(已经调度的进程执行完成,然后根据优先级调度等待的进程)的任务调度系统,至于抢占式的,就留给大家思考了。上次代码中Task_Switch实现了两个任务的切换,代码如下:


void Task_Switch()

{

  if(g_OS_Tcb_CurP == &TCB_1)

    g_OS_Tcb_HighRdyP=&TCB_2;

  else

    g_OS_Tcb_HighRdyP=&TCB_1;

  OSCtxSw();

}


我们把要切换任务指针付给跟_OS_Tcb_HighRdyP,然后调用OSCtxSw触发PendSV异常,就实现了任务的切换。如果是多个任务,我们只需找出就绪任务中优先级最大的切换之即可。


二、添加任务调度功能


为了实现这一目标我们至少需要知道任务的状态和时间等数据。我们定义了一个任务状态枚举类型OS_TASK_STA,方便添加修改状态。在OS_TCB结构体中添加了两个成员TimeDly和State,TimeDly是为了实现OS_TimeDly,至于State与优先级一起是作为任务切换的依据。


typedef enum OS_TASK_STA

{

  TASK_READY,

  TASK_DELAY,

} OS_TASK_STA;


typedef struct OS_TCB

{

  OS_STK *StkAddr;

  OS_U32 TimeDly;

  OS_TASK_STA State;

}OS_TCB,*OS_TCBP;


说到任务切换,我们必须面对临界区的问题,在一些临界的代码两端不加临界区进去和退出代码,会出现许多意想不到的问题。以下地方需要特别注意,对关键的全局变量的写操作、对任务控制块的操作等。进入临界区和退出临界区需要关闭和开启中断,我们采用uCOS中的一部分代码:


PUBLIC OS_CPU_SR_Save

  PUBLIC OS_CPU_SR_Restore

  

OS_CPU_SR_Save

    MRS     R0, PRIMASK

    CPSID   I

    BX      LR


OS_CPU_SR_Restore

    MSR     PRIMASK, R0

    BX      LR


#define  OS_USE_CRITICAL    OS_U32 cpu_sr;

#define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}

#define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}

#define  OS_PendSV_Trigger() OSCtxSw()

一个OS至少要有任务表,我们可以用数组,当然也可以用链表。为了简单,我们使用数组,使用数组下表作为优先级。当然,必要的地方一定要做数组越界检查。


#define OS_TASK_MAX_NUM 32

OS_TCBP OS_TCB_TABLE[OS_TASK_MAX_NUM];

为了使OS更完整,我们定义几个全局变量,OS_TimeTick记录系统时间,g_Prio_Cur记录当前运行的任务优先级,g_Prio_HighRdy记录任务调度后就绪任务中的最高优先级。


OS_U32 OS_TimeTick;

OS_U8 g_Prio_Cur; 

OS_U8 g_Prio_HighRdy;

 


下面三个函数与PendSV一起实现了任务的调度功能。


OS_Task_Switch函数功能:找出已就绪最高优先级的任务,并将其TCB指针赋值给g_OS_Tcb_HighRdyP,将其优先级赋值g_Prio_HighRdy。注意其中使用了临界区。


void OS_Task_Switch(void)

{

  OS_S32 i;

  OS_TCBP tcb_p;

  OS_USE_CRITICAL

  for(i=0;i  {

    tcb_p=OS_TCB_TABLE[i];

    if(tcb_p == NULL) continue;

    if(tcb_p->State==TASK_READY) break;

  }

  OS_ENTER_CRITICAL();

  g_OS_Tcb_HighRdyP=tcb_p;

  g_Prio_HighRdy=i;

  OS_EXIT_CRITICAL();

}


OS_TimeDly至当前任务为延时状态,并将延时时间赋值给当前TCB的TimeDly成员,并调用OS_Task_Switch函数,然后触发PendSV进行上下文切换。OS_Task_Switch找到就绪状态中优先级最高的,并将其赋值相关全局变量,作为上下文切换的依据。


void OS_TimeDly(OS_U32 ticks)

{

    OS_USE_CRITICAL

    

    OS_ENTER_CRITICAL();

    g_OS_Tcb_CurP->State=TASK_DELAY;

    g_OS_Tcb_CurP->TimeDly=ticks;

    OS_EXIT_CRITICAL();

    OS_Task_Switch();

    OS_PendSV_Trigger();

}


SysTick_Handler实现系统计时,并遍历任务表,任务若是延时状态,就令其延时值减一,若减完后为零,就将其置为就绪状态。


void SysTick_Handler(void)

{

  OS_TCBP tcb_p;

  OS_S32 i;

  OS_USE_CRITICAL

    

  OS_ENTER_CRITICAL();

  ++OS_TimeTick;

    for(i=0;i    {

      tcb_p=OS_TCB_TABLE[i];

      if(tcb_p == NULL) continue;

      if(tcb_p->State==TASK_DELAY) 

      {

        --tcb_p->TimeDly;

        if(tcb_p->TimeDly == 0) 

          tcb_p->State=TASK_READY;

      }

    }

  OS_EXIT_CRITICAL();

}


当所有任务都没就绪怎么办?这时就需要空闲任务了,我们把它设为优先级最低的任务。WFE指令为休眠指令,当来中断时,退出休眠,然后看看有没有已就绪的任务,有则调度之,否则继续休眠,这样可以减小功耗哦。



void OS_Task_Idle(void)

{

  while(1)

  {

    asm("WFE"); 

    OS_Task_Switch();

    OS_PendSV_Trigger();

  }

}


当一个任务只运行一次时(例如下面main.c的task1),结束时就会调用OS_Task_End函数,此函数会调用OS_Task_Delete函数从任务表中删除当前的任务,然后调度任务。



void OS_Task_Delete(OS_U8 prio)

{

  if(prio >= OS_TASK_MAX_NUM) return;

  OS_TCB_TABLE[prio]=0;

}


void OS_Task_End(void)

{

  printf("Task of Prio %d Endn",g_Prio_Cur);

  OS_Task_Delete(g_Prio_Cur);

  OS_Task_Switch();

  OS_PendSV_Trigger();


三、OS实战


下面是完整的main.c代码:


复制代码

#include "stdio.h"

#include "stm32f4xx.h"


#define OS_EXCEPT_STK_SIZE 1024

#define TASK_1_STK_SIZE 128

#define TASK_2_STK_SIZE 128

#define TASK_3_STK_SIZE 128


#define TASK_IDLE_STK_SIZE 1024

#define OS_TASK_MAX_NUM 32

#define OS_TICKS_PER_SECOND 1000


#define  OS_USE_CRITICAL    OS_U32 cpu_sr;

#define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}

#define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}

#define  OS_PendSV_Trigger() OSCtxSw()


typedef signed char OS_S8;

typedef signed short OS_S16;

typedef signed int OS_S32;

typedef unsigned char OS_U8;

typedef unsigned short OS_U16;

typedef unsigned int OS_U32;

typedef unsigned int OS_STK;


typedef void (*OS_TASK)(void);


typedef enum OS_TASK_STA

{

  TASK_READY,

  TASK_DELAY,

} OS_TASK_STA;


typedef struct OS_TCB

{

  OS_STK *StkAddr;

  OS_U32 TimeDly;

  OS_U8 State;

}OS_TCB,*OS_TCBP;


OS_TCBP OS_TCB_TABLE[OS_TASK_MAX_NUM];

OS_TCBP g_OS_Tcb_CurP; 

OS_TCBP g_OS_Tcb_HighRdyP;

OS_U32 OS_TimeTick;

OS_U8 g_Prio_Cur; 

OS_U8 g_Prio_HighRdy;


static OS_STK OS_CPU_ExceptStk[OS_EXCEPT_STK_SIZE];

OS_STK *g_OS_CPU_ExceptStkBase;


static OS_TCB TCB_1;

static OS_TCB TCB_2;

static OS_TCB TCB_3;

static OS_TCB TCB_IDLE;

static OS_STK TASK_1_STK[TASK_1_STK_SIZE];

static OS_STK TASK_2_STK[TASK_2_STK_SIZE];

static OS_STK TASK_3_STK[TASK_3_STK_SIZE];

static OS_STK TASK_IDLE_STK[TASK_IDLE_STK_SIZE];


extern OS_U32 SystemCoreClock;


extern void OSStart_Asm(void);

extern void OSCtxSw(void);

extern OS_U32 OS_CPU_SR_Save(void);

extern void OS_CPU_SR_Restore(OS_U32);


void task_1(void);

void task_2(void);

void task_3(void);


void OS_Task_Idle(void);

void OS_TimeDly(OS_U32);

void OS_Task_Switch(void);

void OS_Task_Create(OS_TCB *,OS_TASK,OS_STK *,OS_U8);

void OS_Task_Delete(OS_U8);

void OS_Task_End(void);

void OS_Init(void);

void OS_Start(void);


void task_1(void)

{

    printf("[%d]Task 1 Runing!!!n",OS_TimeTick);


    OS_Task_Create(&TCB_2,task_2,&TASK_2_STK[TASK_2_STK_SIZE-1],5);

    OS_Task_Create(&TCB_3,task_3,&TASK_3_STK[TASK_3_STK_SIZE-1],7);

}


void task_2(void)

{

  while(1)

  {

    printf("[%d]Task 2 Runing!!!n",OS_TimeTick);

    OS_TimeDly(1000);

  }

}


void task_3(void)

{

  while(1)

  {

    printf("[%d]Task 3 Runing!!!n",OS_TimeTick);

    OS_TimeDly(1500);

  }

}


void OS_Task_Idle(void)

{

  while(1)

  {

    asm("WFE"); 

    OS_Task_Switch();

    OS_PendSV_Trigger();

  }

}


void OS_TimeDly(OS_U32 ticks)

{

    OS_USE_CRITICAL

    

    OS_ENTER_CRITICAL();

    g_OS_Tcb_CurP->State=TASK_DELAY;

    g_OS_Tcb_CurP->TimeDly=ticks;

    OS_EXIT_CRITICAL();

    OS_Task_Switch();

    OS_PendSV_Trigger();

}


void OS_Task_Switch(void)

{

  OS_S32 i;

  OS_TCBP tcb_p;

  OS_USE_CRITICAL

  for(i=0;i  {

    tcb_p=OS_TCB_TABLE[i];

    if(tcb_p == NULL) continue;

    if(tcb_p->State==TASK_READY) break;

  }

  OS_ENTER_CRITICAL();

  g_OS_Tcb_HighRdyP=tcb_p;

  g_Prio_HighRdy=i;

  OS_EXIT_CRITICAL();

}


void OS_Task_Delete(OS_U8 prio)

{

  if(prio >= OS_TASK_MAX_NUM) return;

  OS_TCB_TABLE[prio]=0;

}


void OS_Task_End(void)

{

  printf("Task of Prio %d Endn",g_Prio_Cur);

  OS_Task_Delete(g_Prio_Cur);

  OS_Task_Switch();

  OS_PendSV_Trigger();

}


void OS_Task_Create(OS_TCB *tcb,OS_TASK task,OS_STK *stk,OS_U8 prio)

{

    OS_USE_CRITICAL

    OS_STK  *p_stk; 

    if(prio >= OS_TASK_MAX_NUM) return;

  

    OS_ENTER_CRITICAL();


    p_stk      = stk;

    p_stk      = (OS_STK *)((OS_STK)(p_stk) & 0xFFFFFFF8u);

    

    *(--p_stk) = (OS_STK)0x01000000uL;                          //xPSR

    *(--p_stk) = (OS_STK)task;                                  // Entry Point

    *(--p_stk) = (OS_STK)OS_Task_End;                  // R14 (LR)

    *(--p_stk) = (OS_STK)0x12121212uL;                          // R12

    *(--p_stk) = (OS_STK)0x03030303uL;                          // R3

    *(--p_stk) = (OS_STK)0x02020202uL;                          // R2

    *(--p_stk) = (OS_STK)0x01010101uL;                          // R1

    *(--p_stk) = (OS_STK)0x00000000u;                           // R0

    

    *(--p_stk) = (OS_STK)0x11111111uL;                          // R11

    *(--p_stk) = (OS_STK)0x10101010uL;                          // R10

    *(--p_stk) = (OS_STK)0x09090909uL;                          // R9

    *(--p_stk) = (OS_STK)0x08080808uL;                          // R8

    *(--p_stk) = (OS_STK)0x07070707uL;                          // R7

    *(--p_stk) = (OS_STK)0x06060606uL;                          // R6

    *(--p_stk) = (OS_STK)0x05050505uL;                          // R5

[1] [2]
关键字:STM32 引用地址:一步步写STM32 OS【四】OS基本框架

上一篇:ST推出STM32无线微控制器模块,提升物联网产品开发效率
下一篇:一步步写STM32 OS【三】PendSV与堆栈操作

推荐阅读最新更新时间:2024-11-01 21:00

关于STM32的硬件IIC使用问题解决方案
最近公司上STM32,对新的东西不太熟悉。直接上手,平台配置啥的都还算顺利,毕竟八位机平台的东西在。到硬件IIC的时候就出大问题了,刚刚上板子的PCF8563(RTC),我也懒,直接就用ST官方给的库。刚刚开始几次可以读写PCF8563,后来直接就杯具。查了两天,发现连STAR信号都发不出来,直接卡在IIC时钟使能那个地方。总线永远是忙!实在没办法。。。 求助 几个朋友,其中一种说法是:STM32的抗干扰问题,导致IIC总线有动作,让IIC器件认为忙。要在VDD和VSS,接近管脚的地方加104的电容。有可能是这种原因,不过我手头板子限制,我没办法加电容,我没有试,要不谁试一下。 然后到网上一搜,乖乖,原来不只是我碰到这个问题,N多
[单片机]
STM32的RFID手持终端硬件设计
引言 射频识别(Radlo Frequency Identification,RFID)技术是近年来兴起的一种自动识别技术。该技术可利用射频信号识别对象,利用电磁耦合原理交换信息。与传统的识别技术相比,RFID技术具有读卡器和射频标签无接触的特点。由于RFID技术所具有的优点,它目前被广泛应用于身份识别、电子商务、工业自动化以及交通运输等领域。但是,现有的RFID读卡器在大规模的移动应用中仍存在着一些不足,例如处理速度较慢、人机交互不够友好、成本较高、不能方便与上位机进行通信等。本文设计的RFID手持终端采用STM32F103VET6嵌入式处理器作为主控芯片,CLRC632作为射频读卡芯片,可以读写工作在13.56 MHz的多种
[单片机]
<font color='red'>STM32</font>的RFID手持终端硬件设计
STM32系列第3篇--GPIO初始化
使能和初始化IO口: GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOD,
[单片机]
关于STM32的com事件概念
COM事件是专门为电机控制用的,它只出现在高级定时器TIM1和TIM8中,普通定时器TIM2~TIM4没有COM事件。 COM事件是用于同时控制所有通道的输出转换,在电机控制中同时转换所有通道的输出是十分必要的 比如无刷电机换向时,一般是三相要同时换向的,但是你在软里设置换向时肯定是一次只能设置一相,这就达不到三相同时换向了不。其实简单的办法就是启用 STM32的COM事件,你先逐个设置好每相的换向(注意:此时虽然设置了,但实际上并不会进行换向),然后再调用COM事件,此时,三相将同时换向,就这么来的。阿呆版主,这够清楚了吧 COM事件发生在STM32的高级控制定时器(TIM1、TIM8)的 六步PWM的产生 ,而6-step P
[单片机]
STM32——如何配置通用定时器中断
STM32的定时器 STM32F103ZET6一共有8个定时器,其中分别为: 高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。 除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。 默认调用SystemInit函数情况下: SYSCLK=72M AHB时钟=72M APB1时钟=36M 所以APB1的分频系数=AHB/APB1时钟=2 所以,通用定时器时钟CK_INT=2*36M=72M 定时器中断的一般步骤 实例要求:通过TIM3的中断来控制led1的亮灭 硬件:LED——GPIOB,GPIO_Pin_5 1、使能
[单片机]
<font color='red'>STM32</font>——如何配置通用定时器中断
STM32学习笔记(PWM输出)
本人采用Cube 进行的STM32库函数编程,PWM波的输出配置以及代码如下所示:(经过实际实验证明非常好用) 以下为定时器PWM输出函数,注意:使用时一定要把原来输出函数注释掉。 注释代码如下: // sConfigOC.OCMode = TIM_OCMODE_PWM1; // sConfigOC.Pulse = 0; // sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL
[单片机]
stm32读取DS2411
DS2411的读写时序和DS18B20一致,毕竟同一个公司出的。 下面是代码 源文件 ds2411.c #include ds2411.h #include delay.h #include usart.h // // //复位DS2411 void DS2411_Rst(void) { DS2411_IO_OUT(); //SET PA11 OUTPUT DS2411_DQ_OUT=0; //拉低DQ delay_us(750); //拉低750us DS2411_DQ_OUT=1; //DQ=1 delay_us(15); //15US } //等待D
[单片机]
STM32 DMA彻底研究
typedef struct { u32 DMA_PeripheralBaseAddr; u32 DMA_MemoryBaseAddr; u32 DMA_DIR; u32 DMA_BufferSize; u32 DMA_PeripheralInc; u32 DMA_MemoryInc; u32 DMA_PeripheralDataSize; u32 DMA_MemoryDataSize; u32 DMA_Mode; u32 DMA_Priority; u32 DMA_M2M; } DMA_InitTypeDef; DMA_InitTypeDef 定义于文件“stm32f10x_dma.h” DMA_PeripheralBaseA
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved