µCOS-III从入门到精通 第九章(时间管理)

参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili

一、延时函数

1、常用函数概览

(1)时间管理是一种建立在时钟节拍上,对任务运行时间管理的一种系统内核机制。

(2)时间管理相关API函数有如下几个(并非全部):

函数

描述

OSTimeDly

以系统时钟节拍为单位进行任务延时(也就是阻塞)

OSTimeDlyHMSM

以时、分、秒、毫秒为单位进行任务延时

OSTimeDlyResume

恢复被延时的任务(不能恢复自身)

2、常用时间管理相关API函数介绍

(1)OSTimeDly函数:

①函数定义:

void OSTimeDly
(
    OS_TICK 		dly,	
    OS_OPT 		opt,
	OS_ERR *		p_err 
) 

形参

描述

dly 

任务延时的系统时钟节拍数

opt 

延时选项

p_err 

指向接收错误代码变量的指针

②延时选项:

opt 

描述

OS_OPT_TIME_DLY 

任务延时的结束时刻为OSTickCtr + dly

OS_OPT_TIME_TIMEOUT 

任务延时的结束时刻为 OSTickCtr + dly

OS_OPT_TIME_MATCH 

任务延时的结束时刻为 dly(绝对时间)

OS_OPT_TIME_PERIODIC

任务延时的结束时刻为OSTCBCurPtr -> TickCtrPrev+dly

[1]OSTickCtr是系统时钟节拍计数器,它是一个32bit的数,会溢出,然后回到0重新开始计数。

[2]OSTCBCurPtr -> TickCtrPrev会记录每个任务最近一次开始运行(进入就绪态)的绝对时间(也就是上次进入就绪态时OSTickCtr的值)。

(2)OSTimeDlyHMSM函数:

①函数定义:

void OSTimeDlyHMSM
(
    CPU_INT16U 	hours,
	CPU_INT16U 	minutes,
	CPU_INT16U 	seconds,
	CPU_INT32U 	milli,
	OS_OPT 		opt,
	OS_ERR *		p_err
) 

形参

描述

hours 

任务延时的小时数

minutes 

任务延时的分钟数

seconds 

任务延时的秒数

milli 

任务延时的毫秒数

opt 

延时选项

p_err 

指向接收错误代码变量的指针

②延时选项(可使用“|”选择多个选项):

opt 

描述

OS_OPT_TIME_HMSM_STRICT

延时时间参数严格按照实际的时间格式
任务延时的小时数(0~99)
任务延时的分钟数(0~59)
任务延时的秒数(0~59)
任务延时的毫秒数(0~999)

OS_OPT_TIME_HMSM_NON_STRICT

延时时间参数无需严格按照实际的时间格式
任务延时的小时数(0~999)
任务延时的分钟数(0~9999)
任务延时的秒数(0~65535)
任务延时的毫秒数(0~4294967295)

OS_OPT_TIME_DLY 

任务延时的结束时刻为 OSTickCtr + dly

OS_OPT_TIME_TIMEOUT 

任务延时的结束时刻为 OSTickCtr + dly

OS_OPT_TIME_PERIODIC

任务延时的结束时刻为OSTCBCurPtr -> TickCtrPrev + dly

OS_OPT_TIME_MATCH 

任务延时的结束时刻为dly

③使用该函数须将宏OS_CFG_TIME_DLY_HMSM_EN置1。

(3)OSTimeDlyResume函数:

①函数定义:

void OSTimeDlyResume
(
	OS_TCB *		p_tcb,
	OS_ERR *		p_err
) 

形参

描述

p_tcb 

指向任务控制块的指针

p_err 

指向接收错误代码变量的指针

②使用该函数须将宏OS_CFG_TIME_DLY_RESUME_EN置1。

二、延时函数演示实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计3个任务——start_task、task1、task2:

[1]start_task:用于创建其它两个任务,然后删除自身。

[2]task1:用于展示延时函数OSTimeDly的相对延时功能。

[3]task2:用于展示延时函数OSTimeDly的绝对延时功能。

②预期实验现象:两个LED灯不同步闪烁。

2、实验步骤

(1)将“任务创建和删除实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)Delay.c文件中的延时函数需修改为如下所示的实现(纯占用CPU资源,这种延时函数其实是不好的,但就该实验而言,仅仅是为了观察实验现象,不需要考虑太多)。

void Delay_ms(u16 time)
{    
   u16 i=0;  
   while(time--)
   {
      i=12000;     //可微调,使延时更加精准
      while(i--) ;    
   }
}

(3)移除task3任务的相关内容,并更改task1和task2的实现,

#include "stm32f10x.h"                  // Device header
#include "os.h"
#include "cpu.h"
#include "LED.h"
#include "Delay.h"
#include <stdio.h>

/* START_TASK 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     START_TASK_PRIO         1
#define     START_TASK_STACK_SIZE   256
CPU_STK     start_task_stack[START_TASK_STACK_SIZE];
OS_TCB      start_task_tcb;
void start_task(void);

/* TASK1 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     TASK1_PRIO              4
#define     TASK1_STACK_SIZE        256
CPU_STK     task1_stack[TASK1_STACK_SIZE];
OS_TCB      task1_tcb;
void task1(void);

/* TASK2 任务 配置
 * 包括: 任务优先级 任务栈大小 任务控制块 任务栈 任务函数
 */
#define     TASK2_PRIO              3
#define     TASK2_STACK_SIZE        256
CPU_STK     task2_stack[TASK2_STACK_SIZE];
OS_TCB      task2_tcb;
void task2(void);

void UCOS_Test(void)
{
	OS_ERR err;
	
	OSInit(&err);    //初始化μC/OS-III

	//创建Start Task
    OSTaskCreate (&start_task_tcb,
                  "start_task",
                  (OS_TASK_PTR)start_task,
                  NULL,
                  START_TASK_PRIO,
                  start_task_stack,
                  START_TASK_STACK_SIZE / 10,
                  START_TASK_STACK_SIZE,
                  0,
                  0,
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);
    
    /* 开始任务调度 */
    OSStart(&err);
}

void start_task(void)              
{
    OS_ERR err;     //接收错误代码使用,对错误代码进行分情况处理可增强程序鲁棒性
    CPU_Init();     //初始化CPU库
    CPU_SR_ALLOC();
    
	//滴答定时器重装载值 = 系统主频 / 滴答定时器中断频率(滴答定时器是递减计数)
    CPU_INT32U cnts = SystemCoreClock / OS_CFG_TICK_RATE_HZ;
    OS_CPU_SysTickInit(cnts);   //配置Systick中断及优先级
    
    CPU_CRITICAL_ENTER();           //进入临界区(关中断)
	
    //创建task1
    OSTaskCreate (&task1_tcb,
                  "task1",
                  (OS_TASK_PTR)task1,
                  0,
                  TASK1_PRIO,
                  task1_stack,
                  TASK1_STACK_SIZE / 10,
                  TASK1_STACK_SIZE,
                  0,
                  0,
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);

    //创建task2
    OSTaskCreate (&task2_tcb,
                  "task2",
                  (OS_TASK_PTR)task2,
                  0,
                  TASK2_PRIO,
                  task2_stack,
                  TASK2_STACK_SIZE / 10,
                  TASK2_STACK_SIZE,
                  0,
                  0,
                  0,
                  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                  &err);      
    
    CPU_CRITICAL_EXIT();            //退出临界区(开中断)
    OSTaskDel(NULL, &err);   //删除任务自身
}

void task1(void)
{
	OS_ERR err;
	while(1)     //task1永远不停歇(除非被其它任务删除)
	{
		LED1_Turn();      //LED1状态翻转
		Delay_ms(100);    //死等100ms
		OSTimeDly(500,OS_OPT_TIME_DLY, &err);       //自我阻塞500ms(相对延时)
	}
}

void task2(void)
{
	OS_ERR err;
	while(1)     //task2永远不停歇(除非被其它任务删除)
	{
		LED2_Turn();      //LED1状态翻转
		Delay_ms(100);    //死等100ms
		OSTimeDly(500, OS_OPT_TIME_PERIODIC, &err); //自我阻塞约400ms(绝对延时)
	}
}

(4)程序完善好后点击“编译”,然后将程序下载到开发板上。

3、程序执行流程

(1)main函数全流程:

①初始化LED模块、按键模块。

②调用UCOS_Test函数。

(2)测试函数全流程:

①创建任务start_task。

②开启任务调度器。

(3)多任务调度执行阶段(发生在开启任务调度器以后):

①start_task任务函数首先进入临界区,在临界区中start_task任务不会被其它任务打断,接着start_task任务依次创建任务task1、task2,然后删除自身,接着退出临界区,让出CPU资源。

②task2的优先级较高,优先执行task2,如下所示,在不考虑task1的情况下(或者说task1运行时task2一直处于自我阻塞状态),task2依次执行LED2状态翻转、死等100ms及绝对延时500ms,这些操作加上阻塞时间共500ms。

③task2阻塞时task1可以执行,task1依次执行LED1状态翻转、死等100ms及相对延时500ms,这些操作加上阻塞时间共约600ms。

④随着时间的推移,task1在进行死等的时候很可能task2的阻塞时间正好结束,由于task2的优先级较高,task1会直接被打断,从而引发task1非预期的等待,这就导致LED1在这次闪烁轮回中的闪烁频率会产生细微的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值