【uC/OS-III篇】uC/OS-III 创建第一个任务(For STM32)

uC/OS-III 创建第一个任务(For STM32)

日期:2024-3-30 23:55,结尾总结了今天学习的一些小收获

本博客对应的项目源码工程
源码项目工程


1. 首先定义错误码变量

// 用于使用uC/OS函数时返回错误码
OS_ERR err;  

2. 定义任务控制块

// 定义任务控制块,用于描述一个任务
OS_TCB p_tcb;

3. 初始化OS,如果有错误则通过 err 判断

// 初始化 uC/OS-III 中的内部变量和数据结构
OSInit(&err);
  • OSInit() 会初始化 uC/OS-III 的内部变量和数据结构,并创建2~5个系统任务。例如至少会创建空闲任务(OS_IdleTask)和时钟节拍任务

  • 空闲任务主要在其他任务不运行的时候运行,空闲任务优先级最低,默认为63(uC/OS的优先级规则是数字越小优先级越高,默认0~63)

  • 还可能创建统计任务(OS_StackTask())、定时任务(OS_TmrTask())、中断处理队列管理任务(OS_IntQTask())

  • OSInit() 函数注释

/*
	注释者:晨少
	日期:2024年3月29日
	地点:宿舍
*/

void  OSInit (OS_ERR  *p_err)
{
#if (OS_CFG_ISR_STK_SIZE > 0u)
    CPU_STK      *p_stk;   // CPU_STK:     unsigned int 的宏定义
    CPU_STK_SIZE  size;    // CPU_STK_SIZE:unsigned int 的宏定义
#endif



#ifdef OS_SAFETY_CRITICAL   // 系统是否需要符合安全关键(Safety Critical)的需求,在航天、医疗、核电等领域需要用到
    						// 开启安全关键后,会自动检查栈溢出、检查系统各个地方是否有出现错误的可能
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

		// 在系统初始化时可插入一些自定义操作
    OSInitHook();                                               /* Call port specific initialization code */
		// 清除中断嵌套计数器
    OSIntNestingCtr       =           0u;                       /* Clear the interrupt nesting counter */
		// 初始化系统运行状态,当前未启动多任务
    OSRunning             =  OS_STATE_OS_STOPPED;               /* Indicate that multitasking has not started           */
		// 清除锁嵌套计数
    OSSchedLockNestingCtr =           0u;                       /* Clear the scheduling lock counter*/
		// 初始化两个指针,一个是指向当前正在运行的TCB的指针,一个是指向当前最高优先级的TCB的指针
    OSTCBCurPtr           = (OS_TCB *)0;                        /* Initialize OS_TCB pointers to a known state          */
    OSTCBHighRdyPtr       = (OS_TCB *)0;
		// 初始化当前运行任务的优先级 & 最高优先级任务的优先级
    OSPrioCur             =           0u;                    /* Initialize priority variables to a known state       */
    OSPrioHighRdy         =           0u;

#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
    OSSchedLockTimeBegin  =           0u;
    OSSchedLockTimeMax    =           0u;
    OSSchedLockTimeMaxCur =           0u;
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    OSSafetyCriticalStartFlag = OS_FALSE;
#endif

#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
		// 禁用/启动循环调度
    OSSchedRoundRobinEn             = OS_FALSE;
		// OSCfg_TickRate_Hz默认值1000u,即1s有1000个节拍,一个节拍是1ms
		// 那么时间片轮转时长就是 1000u/10u = 100u ,即100ms
    OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;  
#endif

#if (OS_CFG_ISR_STK_SIZE > 0u)
		// 清除异常堆栈,进行堆栈检查
		// 将p_stk设为ISR栈的基地址
    p_stk = OSCfg_ISRStkBasePtr;                             /* Clear exception stack for stack checking. */
    // 如果p_stk为空,说明没有分配ISR栈空间;
		// 反之如果不为空,说明分配了ISR栈空间,则需要对ISR栈空间清零初始化
		if (p_stk != (CPU_STK *)0) {
        size  = OSCfg_ISRStkSize;
        while (size > 0u) {
            size--;
           *p_stk = 0u;
            p_stk++;
        }
    }
		// 检查任务堆栈溢出功能
		// 根据栈的生长方向,将栈底的8个字节初始化为特定值0x5432DCBAABCD2345UL
		// 该区域叫做红区,通过判断红区数据是否被修改,来判断栈是否溢出
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)                           /* Initialize Redzoned ISR stack */
    OS_TaskStkRedzoneInit(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
#endif
#endif

    ...
    ...
        
		// 初始化优先级位表,将空闲任务插入到优先级列表中
    OS_PrioInit();                                              /* Initialize the priority bitmap table */
		// 初始化就绪优先级链表,链表结构体见 OS_RDY_LIST
    OS_RdyListInit();                                           /* Initialize the Ready List  */

    ...
    ...

		// 系统配置初始化,例如空闲任务、中断ISR、消息队列等
    OSCfg_Init();
		// 系统初始化完成
    OSInitialized = OS_TRUE;                                    /* Kernel is initialized  */
}

4. 使用 OSTaskCreate 创建第一个任务,使用 OSStart 开始运行系统

...

/* 定义栈区域 */
#define		TASK_START_STK_SIZE		100    // 栈深度,单位4字节,最小 64 * 4字节
CPU_STK  MyTask1_StartStk[TASK_START_STK_SIZE];

/* 任务函数 */
void MyTask1(void *p_arg);    


int main(void)
{
    ...
    ...

    // 创建第一个uC/OS任务
    OSTaskCreate ((OS_TCB      *)&p_tcb,         	// 任务控制块地址
                  (CPU_CHAR     *)"MyTask1", 			// 任务名
                  (OS_TASK_PTR  )MyTask1,				// 任务函数地址
                  (void        *)0,								// 参数
                  (OS_PRIO      )3,								// 任务优先级
                  (CPU_STK     *)&MyTask1_StartStk[0],   // 任务栈空间基地址
                  (CPU_STK_SIZE )TASK_START_STK_SIZE/10, // 代表栈溢出警告之前栈内应该剩余的空间,即栈极限深度
                  (CPU_STK_SIZE )TASK_START_STK_SIZE,    // 任务栈深度,单位4字节
                  (OS_MSG_QTY   )0,
                  (OS_TICK      )0,
                  (void        *)0,
                  // 任务可选项,创建任务时清空栈空间,运行时检查任务栈的使用情况
                  (OS_OPT       )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  (OS_ERR      *)&err);
    // 启动OS
    OSStart(&err);
}

5. 第一个任务,先使用不精确的演示来做

//微秒级的延时
void delay_us(uint32_t delay_us)
{    
  volatile unsigned int num;
  volatile unsigned int t;
 
  
  for (num = 0; num < delay_us; num++)
  {
    t = 11;
    while (t != 0)
    {
      t--;
    }
  }
}
//毫秒级的延时
void delay_ms(uint16_t delay_ms)
{    
  volatile unsigned int num;
  for (num = 0; num < delay_ms; num++)
  {
    delay_us(1000);
  }
}


/*
	uC/OS 任务
*/
char flag1 = 0;   // 需要定义成全局变量才能添加到仿真中的逻辑分析仪
void MyTask1(void *p_arg)
{
	
	while(1)
	{
		flag1 = 0;
		delay_ms(200);
		flag1 = 1;
		delay_ms(200);
	}
}

6. 使用仿真,使用逻辑分析仪来看变量的变化

需要先改几个地方

(1)设置频率为8MHz

在这里插入图片描述

 (2)勾选仿真

在这里插入图片描述

​ (3)将原本的DCM.DLL改成 DARMSTM.DLL ,Parameter 修改为自己的板子型号,例如 -pSTM32F103C8

在这里插入图片描述

​ (4)将 flag1 变量添加到逻辑分析仪中

在这里插入图片描述
在这里插入图片描述

​ (5)一键运行,观察逻辑分析仪

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以观察到 flag1 变量的变化,由于延时不是很精确,可以看到跳变周期为约为 0.3925s

​ (6)将flag1改为LED闪烁功能

void MyTask1(void *p_arg)
{
	
	while(1)
	{
		LED(ON);
		delay_ms(200);
		LED(OFF);
		delay_ms(200);
	}
}

在这里插入图片描述


PS:

收获1:局部变量和 static 类型的变量不能添加到仿真中的逻辑分析仪

收获2:UCOS-III 的任务栈有最小显示,单位不是字,而是4个字节

// 在 os_cfg.h 中

// 最小任务栈大小(单位4字节)
#define OS_CFG_STK_SIZE_MIN                       64u           /* Minimum allowable task stack size   

收获3:了解到任务栈的深度标记,代表栈溢出警告之前栈内应该剩余的空间,即栈极限深度,在本例中,当剩余栈空间小于任务栈空间的10%时,就达到了栈极限深度。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值