zl程序教程

您现在的位置是:首页 >  其他

当前栏目

STM32 uC/OS-III移植

2023-04-18 14:43:08 时间

1. uC/OS简介

我们常说的单片机编程通常都是指裸机编程,即不加入任何RTOS(Real Time OperationSystem,实时操作系统)的编程。μC/OS是目前广泛使用的RTOS之一,引入RTOS可实现实现多任务管理 。

2. 多任务

uC/OS-III 支持以多线程的方式,同时运行多个任务,各个任务之间互不影响。以下两个任务同时进入无限循环,但是两个任务可以同时执行,然而需要在循环中主动释放CPU:

void Task_A(void *p_arg){
    while(1){
        OSTimeDly(10); //任务里必须要有类似的主动释放CPU的函数
    }
}
void Task_B(void *p_arg){
    while(1){
        OSTimeDly(10);
    }
}

2.1 时钟节拍

操作系统通过 "时钟节拍" 来实现监控任务,并且主动调度和切换任务执行。

OSTimeDly() 函数通过延迟当前任务指定的系统滴答时钟数来释放CPU。

void OSTimeDly(OS_TICK  dly,//时钟滴答数
              OS_OPT  opt,//绝对时间还是相对时间,OS_OPT_TIME_DLY指定相对于当前的时间,OS_OPT_TIME_PERIODIC指定时钟滴答必须在任务恢复前达到的周期数
              RTOS_ERR *p_err);//接收错误代码的指针

2.2 创建起始任务

创建启动外设的函数 bsp_Init(),在里面初始化GPIO和LED

void BSP_Init(void)
{
    BSP_Tick_Init();
    // MX_GPIO_Init()是由STM32Cubemx创建的函数
    MX_GPIO_Init();
}
​
void BSP_Tick_Init(void)
{
    CPU_INT32U cpu_clk_freq;
    CPU_INT32U cnts;
    cpu_clk_freq = BSP_CPU_ClkFreq();
    
    #if(OS_VERSION>=3000u)
        cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
    #else
        cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
    #endif
    OS_CPU_SysTickInit(cnts);
}

创建新任务的函数OSTaskCreate(),执行这个函数将新建一个任务AppTaskStart():

OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                        (1)
            (CPU_CHAR   *)"App Task Start",                 (2)
            (OS_TASK_PTR ) AppTaskStart,                    (3)
            (void       *) 0,                                       (4)
            (OS_PRIO     ) APP_TASK_START_PRIO,             (5)
            (CPU_STK    *)&AppTaskStartStk[0],                      (6)
            (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,    (7)
            (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,         (8)
            (OS_MSG_QTY  ) 5u,                              (9)
            (OS_TICK     ) 0u,                              (10)
            (void       *) 0,                                       (11)
            (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (12)
            (OS_ERR     *)&err);                                    (13)

2.3 创建多任务

在起始任务AppTaskStart()中再次建立多个任务,然后删除起始任务:

/**
  * 函数功能: 启动任务函数体。
  * 输入参数: p_arg 是在创建该任务时传递的形参
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppTaskStart (void *p_arg)
{
  OS_ERR      err;
​
    MX_USART1_UART_Init();
    
    
  (void)p_arg;
​
  BSP_Init();                                                 /* Initialize BSP functions                             */
  CPU_Init();
    
    
  Mem_Init();                                                 /* Initialize Memory Management Module                  */
​
#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif
​
  CPU_IntDisMeasMaxCurReset();
​
  AppTaskCreate();                                            /* Create Application Tasks                             */
​
  AppObjCreate();                                             /* Create Application Objects                           */
    
    
    OSTaskCreate((OS_TCB     *)&AppTaskATCB,              //(1)任务控制块,由用户自己定义。
            (CPU_CHAR   *)"TaskA Start",                    //(2)任务名称,字符串形式,这里任务名称最好要与任务函数入口名字一致,方便进行调试。
            (OS_TASK_PTR ) Task_A,                          //(3)任务入口函数,即任务函数的名称,需要我们自己定义并且实现。
            (void       *) 0,                               //(4)任务入口函数形参,不用的时候配置为0或者NULL即可,p_arg是指向可选数据区域的指针, 用于将参数传递给任务,因为任务一旦执行,那必须是在一个死循环中,所以传参只在首次执行时有效。
            (OS_PRIO     ) 2,                               //(5)任务的优先级,由用户自己定义。
            (CPU_STK    *)&AppTaskAStk[0],              //(6)指向栈基址的指针(即栈的起始地址)
            (CPU_STK_SIZE) APP_TASK_A_STK_SIZE / 10,    //(7)设置栈深度的限制位置。这个值表示任务的栈满溢之前剩余的栈容量。 例如,指定stk_size值的10%表示将达到栈限制,当栈达到90%满就表示任务的栈已满。
            (CPU_STK_SIZE) APP_TASK_A_STK_SIZE,         //(8)任务栈大小,单位由用户决定,如果CPU_STK 被设置为CPU_INT08U, 则单位为字节,而如果CPU_STK 被设置为CPU_INT16U,则单位为半字,同理,如果CPU_STK被设置为CPU_INT32U, 单位为字。在32位的处理器下(STM32),一个字等于4个字节,那么任务大小就为APP_TASK_START_STK_SIZE * 4字节。
            (OS_MSG_QTY  ) 5u,                              //(9)设置可以发送到任务的最大消息数,按需设置即可。
            (OS_TICK     ) 0u,                              //(10)在任务之间循环时的时间片的时间量(以滴答为单位)。指定0则使用默认值。
            (void       *) 0,                                         // (11)是指向用户提供的内存位置的指针,用作TCB扩展。例如, 该用户存储器可以保存浮点寄存器的内容在上下文切换期间,每个任务执行的时间,次数、任务已经切换等。
            (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),// (12)用户可选的任务特定选项,具体见 代码清单:创建任务-7。
            (OS_ERR     *)&err);                                      // (13)
    
    
    // 删除起始任务
    OSTaskDel ( & AppTaskStartTCB, & err );
}

3. 实验

同时启用三个任务,其中一个任务以1s为周期反转LED灯,另一个任务以3s为周期反转LED灯,第三任务朝串口发送数据:

源码下载:源码

4. 参考

[1] STM32F103C8T6移植uC/OS-III基于HAL库超完整详细过程