参考教程:【正点原子】手把手教你学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 | 延时时间参数严格按照实际的时间格式 |
| OS_OPT_TIME_HMSM_NON_STRICT | 延时时间参数无需严格按照实际的时间格式 |
| 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在这次闪烁轮回中的闪烁频率会产生细微的变化。

856

被折叠的 条评论
为什么被折叠?



