μC/OS-III 常用函数汇总
初始化
void OSInit (OS_ERR *p_err)
os_core.c
OSInit() 用于初始化 μC/OS-III 系统,必须在调用任何其他 μC/OS-III 函数之前进行。包括 OSStart() 函数,该函数将启动多任务处理。一旦检测到错误,OSInit() 就会立即返回。
注意/警告
■在调用 OSStart() 之前必须先调用 OSInit() 。
■OSInit() 函数一旦在其调用的任何子函数中检测到错误,就会立即返回。例如,如果 OSInit() 在初始化任务管理器时遇到问题,将会返回相应的错误代码,并且 OSInit() 将不再继续执行。因此,在开始多任务处理之前,用户务必检查错误代码。
void main (void)
{
OS_ERR err;
OSInit(&err);
/* Check “err” */
:
:
OSStart(&err);
/* Check “err” */
}
CPU_Init()
cpu_core.c
函数用于初始化微控制器/中央处理器服务。微控制器/中央处理器提供了多种服务,包括测量中断延迟、获取时间戳,以及在所使用的处理器不支持该指令的情况下模拟零位计数指令等。
CPU_Init() 必须在调用OSInit()前调用
OS_CFG_SCHED_ROUND_ROBIN_EN
此常量(当设置为 1 时)会启用(当设置为 0 时)或禁用(μC/OS-III 的轮询功能的)代码生成。
void OSSchedRoundRobinCfg (CPU_BOOLEAN en,
OS_TICK dflt_time_quanta,
OS_ERR *p_err)
os_core.c
函数用于启用或禁用轮询调度机制。
参数说明:
en 当将其设置为“DEF_ENABLED”时,会启用轮询调度;而当将其设置为“DEF_DISABLED”时,则会禁用该功能。
“dflt_time_quanta” 是分配给任务的默认时间量子值。当创建任务时,如果您将时间量子值设为 0,则会使用此值。换句话说,如果用户未为任务的时间量子值指定非零值,则将使用此值。如果传递 0 作为此参数,则 μC/OS-III 将假定时间量子值为每周期率的 1/10。例如,如果每周期率是 1000 赫兹,则如果将 dflt_time_quanta 传递为 0,则 μC/OS-III 将将时间量子值设置为 10 毫秒。
“p_err” 是指向一个变量的指针,用于保存错误代码:OS_ERR_NONE(无错误):表示调用成功。
void OS_CPU_SysTickInit (CPU_INT32U cnts)
os_cpu_c.c
您的应用程序代码会调用 OS_CPU_SysTickInit() 函数来初始化系统定时器中断。
OS_CPU_SysTickInit() 函数必须知晓要重新加载到系统定时器中的计数值。需要重新加载的计数值取决于 CPU 的时钟频率以及所配置的定时器频率(即 os_cfg_app.h 文件中的 OS_CFG_TICK_RATE_HZ)。
重新加载值通常由第一个运行的应用程序任务按以下方式计算得出:
BSP_CPU_ClkFreq() 是一个位于 bsp.c 文件中的函数,它会返回 CPU 的时钟频率。接下来,我们会根据时钟频率计算重载计数。
系统定时器中断被设置为最低优先级,因为定时器主要用于粗略的时间延迟和超时检测,而我们希望应用程序中断能够优先得到处理。
static void AppTaskStart (void *p_arg)
{
CPU_INT32U cpu_clk_freq; CPU_INT32U cnts;
OS_ERR err;
(void)p_arg;
BSP_Init(); CPU_Init();
cpu_clk_freq = BSP_CPU_ClkFreq();
cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;
OS_CPU_SysTickInit(cnts);
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); BSP_LED_Off(0);
while (DEF_TRUE) { BSP_LED_Toggle(1); OSTimeDlyHMSM(0, 0, 0, 250,
OS_OPT_TIME_HMSM_STRICT,
&err);
}
}
OSCfg_TickRate_Hz
OS_RATE_HZ const OSCfg_TickRate_Hz = (OS_RATE_HZ )OS_CFG_TICK_RATE_HZ;
#define OS_CFG_TICK_RATE_HZ 200u
任务管理
void OSTaskCreate (OS_TCB *p_tcb,
CPU_CHAR *p_name,
OS_TASK_PTR p_task,
void *p_arg,
OS_PRIO prio,
CPU_STK *p_stk_base,
CPU_STK_SIZE stk_limit,
CPU_STK_SIZE stk_size,
OS_MSG_QTY q_size,
OS_TICK time_quanta,
void *p_ext,
OS_OPT opt,
OS_ERR *p_err)
os_task.c
要使 μC/OS-III 能够识别任务,必须创建任务。通过调用 OSTaskCreate() 并提供参数来指定 μC/OS-III 如何管理任务,即可创建任务。任务总是以准备运行的状态被创建。任务可以在多任务开始之前(即在调用 osstart() 之前)创建,也可以由正在运行的任务创建。中断服务例程(ISR)不能创建任务。任务必须编写为无限循环,或者在完成时自行删除。如果任务代码错误地返回,μC/OS-III 将通过调用 OSTaskDel((OS_TCB *)0, serr) 终止该任务。在 Micrium,我们喜欢使用“while (DEF_ON)”来实现无限循环,因为按照惯例,当我们不知道循环将执行多少次时,会使用 while 循环。无限循环就是这种情况。当我们知道循环将执行多少次时,我们更喜欢使用 for 循环。
p_tcb 是指向要使用的任务的 OS_TCB 的指针。假定任务的 TCB 存储将由用户代码分配。您可以按如下方式声明一个“全局”变量,并将指向此变量的指针传递给 OSTaskCreate():
OS_TCB MyTaskTCB;
p_name 是一个指向 ASCII 字符串(以 NUL 字符结尾)的指针,用于为任务指定名称。
该名称可通过调试器或 μC/Probe 显示出来。
p_task 是指向任务的指针(即定义该任务的函数的名称)。
p_arg 是一个指向可选数据区域的指针,该区域用于在创建任务时向任务传递参数。当 μC/OS-III 首次运行该任务时,该任务会认为自己被调用,并传递了参数 p_arg。例如,您可以创建一个 处理异步串行端口的通用任务。p_arg 可用于传递有关它将管理的串行端口的任务信息:端口地址、波特率、位数、奇偶校验等。p_arg 是下面所示任务所接收的参数。
prio 表示任务优先级。数字越小,任务的优先级(即重要性)就越高。如果 OS_CFG_ISR_POST_DEFERRED_EN 被设置为 1,则用户无法使用优先级 0。
任务优先级的数值也必须小于 OS_CFG_PRIO_MAX(即优先级 0、1、OS_CFG_PRIO_MAX - 2 和 OS_CFG_PRIO_MAX - 1 已被预留)。换句话说,任务的优先级应在 2 到 OS_CFG_PRIO_MAX - 3 之间(包括这两个数值)OS_CFG_PRIO_MAX 在os_cfg.h中定义。
p_stk_base 是指向任务栈基地址的指针。任务的栈用于存储局部变量、函数参数、返回地址以及在中断过程中可能使用的 CPU 寄存器。
任务栈必须按照以下方式声明:
CPU_STK MyTaskStk[???];
然后,用户会将 p_stk_base 参数设为该数组首个元素的地址,即 &MyTaskStk[0]。“???” 表示栈的大小。
此栈的大小取决于任务的需求以及预期的中断嵌套情况(除非处理器有专门用于中断的独立栈)。确定栈的大小需要了解为任务自身、所有嵌套函数以及中断所需存储的局部变量所需的字节数,并考虑到嵌套情况的要求。
请注意,您可以从堆中为任务分配栈空间,但在这种情况下,我们不建议删除该任务并释放栈空间,因为这样做可能会导致堆出现碎片化现象,而在嵌入式系统中这是不被允许的。
“stk_limit”用于在任务的栈内定位一个水位限制值,该值可用于监控并确保栈不会溢出。
如果处理器本身没有硬件栈溢出检测功能,或者端口开发者在软件中未实现此功能,那么此值可用于其他用途。例如,某些处理器具有两个栈,一个硬件栈和一个软件栈。硬件栈通常用于跟踪函数调用的嵌套情况,而软件栈则用于传递函数参数。stk_limit 可用于设置硬件栈的大小,如下所示。
“stk_size”指定了任务栈的大小(以元素数量表示)。如果“CPU_STK”被设置为“CPU_INT08U”(参见“os_type.h”),那么“stk_size”就对应于栈中可用的字节数。如果“CPU_STK”被设置为“CPU_INT16U”,那么“stk_size”则包含栈中可用的 16 位项的数量。最后,如果“CPU_STK”被设置为“CPU_INT32U”,那么“stk_size”则包含栈中可用的 32 位项的数量。
q_size A μC/OS-III 任务 包含 一个 可选的内部消息队列(如果 OS_CFG_TASK_Q_EN 大于 0)。此参数指定了该任务通过此消息队列能够接收的消息的最大数量。用户可以将此参数设置为 0 来指定该任务无法接收消息。
time_quanta (时间量子)当轮询功能启用时,time_quanta所占用的时间量(以时钟节拍计)。如果您指定 0,则将使用默认的时间量子,其值为每秒时钟频率除以 10,即OS_CFG_TICK_RATE_HZ / 10。
p_ext 是指向用户自定义内存位置(通常是一个数据结构)的指针,该内存位置用作 TCB(任务控制块)的扩展部分。例如,用户内存可以保存上下文切换期间浮点寄存器中的内容。
“opt”包含特定任务的选项。每个选项由一个位组成。当该位被设置时,该选项就会被选中。μC/OS-III 的当前版本支持以下选项:
OS_OPT_TASK_NONE 表示没有选项。
OS_OPT_TASK_STK_CHK 表示是否允许为该任务进行栈检查。
OS_OPT_TASK_STK_CLR 表示是否需要清除栈。
OS_OPT_TASK_SAVE_FP 表示是否保存浮点寄存器。此选项仅在处理器具有浮点硬件且处理器特定代码会保存浮点寄存器的情况下有效。
p_err 是一个指向变量的指针,该变量将接收错误代码:
OS_ERR_NONE:若函数执行成功。
OS_ERR_PRIO_INVALID:若在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1:若优先级高于允许的最大值(即 > OS_PRIO_MAX - 1)。此外,如果用户将 OS_CFG_ISR_POST_DEFERRED_EN 设置为 1 并尝试使用优先级 0,也会出现此错误。
OS_ERR_STK_INVALID:如果在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1,并且指定了一个空指针作为 p_stk_base 的值。
OS_ERR_STK_SIZE_INVALID:如果在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1,并且指定了的栈大小小于由 OS_CFG_STK_SIZE_MIN(请参阅 os_cfg.h)所指定的值。
OS_ERR_TASK_CREATE_ISR:如果在 os_cfg.h 文件中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1,并且尝试从中断服务程序中创建任务。
OS_ERR_TASK_INVALID:如果在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1,并且指定了一个空指针作为 p_task 的值。
OS_ERR_TCB_INVALID:如果在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1,并且指定了一个空指针作为 p_tcb 的值。
OS_ERR_ILLEGAL_CREATE_RUN_TIME:如果定义了 OS_SAFETY_CRITICAL_IEC61508:在调用 OSSafetyCriticalStart() 之后再次调用此函数,此时您不再被允许创建其他内核对象。
注意/警告
■栈必须使用 CPU_STK 类型进行声明。
■一项任务必须始终调用 μC/OS-III 提供的某种服务来等待时间到期、暂停任务或等待某个对象(等待消息队列、事件标志、互斥锁、信号量、信号或直接发送给任务的消息)。这样就能让其他任务获得对 CPU 的控制权。
您不应使用任务优先级 0、1、OS_CFG_PRIO_MAX - 2 和 OS_CFG_PRIO_MAX - 1,因为这些优先级是专为 μC/OS-III 而预留的。

OS_TCB MyTaskTCB; /*(1)Storage for task's TCB*/
CPU_STK MyTaskStk[200];
void MyTask(void *p_arg) /*(3)The address of the task is its name */
{
while(DEF_ON){
/*Wait for an event*/
/*My task body*/
}
}
void Somecode(void)
{
OS_ERR err;
OSTaskCreate(&MyTaskTCB, /*(1)Address of TCB assigned to the task*/
"Hy Task", /*(2)Name you want to give the task*/
MyTask, /*(3)Address of the task itself*/
(void*)0, /*(4)“P_arg”is not used*/
12, /*(5)Priority you want to assign to the task */
&MyTaskstk[0], /*(6)Base address of task's stack*/
10, /*(7)Watermark limit for stack growth*/
200, /*(8)Stack size in number of CPU_STK elements*/
5, /*(9)Size of task message queue*/
10, /*(10)Time quanta(in number of ticks)*/
(void*)0, /*(11)Extension pointer is not used*/
oS_OPT_TASK_STK_CHK +OS_OPT_TASK_STK_CIR, /*(12)Options*/
&err); /*(13)Error code*/
/*Check “ err” (14)*/
}
void OSStart (OS_ERR *p_err)
os_core.c
OSStart() 函数在 μC/OS-III 环境下启动多任务处理。此函数通常在调用 OSInit() 并创建至少一个应用程序任务之后,从启动代码中被调用。OSStart() 函数不会返回给调用者。一旦 μC/OS-III 运行起来,再次调用 OSStart() 将不会产生任何效果。具体来说,μC/OS-III 会选取在调用 osstart() 之前创建的优先级最高的任务。
void OSTaskDel (OS_TCB *p_tcb,OS_ERR *p_err)
os_task.c
当某个任务不再需要时,可以将其删除。删除任务并不意味着代码会被移除,而是意味着该任务的代码不再由 μC/OS-III 进行管理。当创建仅需运行一次的任务时,可以使用 OSTaskDel() 函数。在这种情况下,该任务不能返回,而应调用 OSTaskDel((OS_TCB *)0, &err) ,该函数会指示 μC/OS-III 删除当前正在运行的任务。
一个任务还可以通过向 OSTaskDel() 函数传递要删除的任务的 OS_TCB 地址来删除另一个任务。
一旦某个任务被删除,其 OS_TCB(操作系统任务控制块)和栈就可以被重新利用来创建另一个任务。这的前提是新任务的栈需求能够通过被删除任务的栈大小来满足。
尽管 μC/OS-III 允许用户在运行时删除任务,但建议尽量避免此类操作。为什么?因为一个任务可以“占用”与其它任务共用的资源。如果在未先释放所占用资源的情况下就删除拥有这些资源的任务,可能会导致异常行为甚至出现死锁的情况。
参数说明
p_tcb 是指向要删除的任务的 TCB 的指针,或者您也可以传递一个空指针来指定调用任务自行删除。如果要删除调用任务,将调用调度程序,以便执行下一个优先级最高的任务。
p_err 是一个指向变量的指针,该变量将接收错误代码:
关键代码段
CPU_SR_ALLOC()
函数 CPU_SR_ALLOC()是为 CPU_CRITICAL_ENTER()和 CPU_CRITICAL_EXIT()申请一个变量:
该宏用于在需要通过禁用中断来保护关键区域的函数中分配一个局部变量。μC/OS-III 如此使用 CPU_SR_ALLOC() :
void OSFunction (void)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
/* Code protected by critical section */ CPU_CRITICAL_EXIT();
:
}
OS_CRITICAL_ENTER()
os.h
仅仅会将 OSSchedLockNestingCtr 的值加一,以此来锁定调度器。这个变量是调度器用来判断自身是否处于锁定状态的依据。当该值不为零时,调度器即处于锁定状态
OS_CRITICAL_EXIT()
os.h
函数会减小 OSSchedLockNestingCtr 的值,当该值降为零时,便会调用调度程序。
定时器管理
void OSTmrCreate (OS_TMR *p_tmr,
CPU_CHAR *p_name,
OS_TICK dly,
OS_TICK period,
OS_OPT opt,
OS_TMR_CALLBACK_PTR p_callback,
void *p_callback_arg,
OS_ERR *p_err)
os_tmr.c
OSTmrCreate() 函数允许用户创建一个软件定时器。该定时器可以设置为持续运行(选项设置为 OS_TMR_OPT_PERIODIC),或者仅运行一次(选项设置为 OS_TMR_OPT_ONE_SHOT)。当定时器计数器降至 0(从指定的周期值开始计数)时,可以执行一个可选的“回调”函数。该回调函数可用于通知任务定时器已过期,或者执行其他任何操作。不过,建议将回调函数保持得尽可能简短。
该计时器是以“停止”模式创建的,因此用户必须调用 OSTmrStart() 函数才能真正启动计时器。如果将计时器配置为“单次执行”模式,且计时器已到期,您需要调用 OSTmrStart() 函数来重新触发计时器,如果不再需要重新触发,则可以调用 OSTmrDel() 函数来删除计时器,或者不再使用该计时器。注意:如果使用“单次执行”模式,您可以使用回调函数来删除计时器。
参数
p_tmr 是指向所需定时器的定时器控制块的指针。假定定时器的存储空间将在应用程序中进行分配。换句话说,您应该像下面这样声明一个“全局”变量,并将此变量的指针传递给 OSTmrCreate() 函数:
OS_TMR MyTmr;
“p_name”是一个指向 ASCII 字符串(以空字符结尾)的指针,用于为定时器指定名称。该名称可通过调试器或 μC/Probe 显示出来。
dly 表示定时器所使用的初始延迟(以定时器时钟周期单位表示)(见上方图表)。如果定时器配置为单次触发模式,此值即为超时时间。如果定时器配置为周期性模式,则此值为在定时器进入周期性模式前需要等待的超时时间。此时间的单位取决于用户调用 OSTmrSignal() 的频率(见 OSTimeTick())。如果 OSTmrSignal() 每隔 1/10 秒调用一次(即 OS_CFG_TMR_TASK_RATE_HZ 设置为 10),dly 就表示在延迟结束前需要等待的 1/10 秒数。
“period”指定了定时器在配置为“周期性”模式时重复运行的周期。在使用“单次”模式时,您应将“周期”设置为 0。时间单位取决于 OSTmrSignal() 被调用的频率。如果 OSTmrSignal() 每隔 1/10 秒被调用(即 OS_CFG_TMR_TASK_RATE_HZ 设置为 10),则“period”指定了定时器在重复之前经过的 1/10 秒的数量。
“opt”用于指定计时器是“单次”模式还是“周期性”模式:
OS_OPT_TMR_ONE_SHOT 表示“单次”模式,而 OS_OPT_TMR_PERIODIC 则表示“周期性”模式。
p_callback 是一个指向函数的指针,该函数将在计时器超时时执行(单次模式),或者在指定的时间间隔到期时执行(周期性模式)。若指针为 NULL,则表示在计时器超时时无需执行任何操作。回调函数的声明方式如下:
void MyCallback(OS_TMR *p_tmr, void *p_arg)
当被调用时,回调函数将接收到指向计时器的指针以及一个参数(p_callback_arg),该参数可用于指示回调函数应执行的操作。请注意,用户可以在回调函数中调用所有与计时器相关的函数(即 OSTmrCreate()、OSTmrDel()、OSTmrStateGet()、OSTmrRemainGet()、OSTmrStart() 和 OSTmrStop())。
在回调函数中切勿进行阻塞操作。
p_callback_arg 是在计时器超时(单次模式)时或在周期结束时(周期性模式)传递给回调函数的一个参数。该指针被声明为“void *”类型,以便它可以指向任何数据。
p_err 是一个指向变量的指针,该变量中存储了此函数返回的错误代码。


CPU_BOOLEAN OSTmrStart (OS_TMR *p_tmr,
OS_ERR *p_err)
os_tmr.c
函数允许用户启动(或重新启动)计时器的倒计时过程。该计时器必须在此之前已创建完成。
参数
“p_tmr” 是指向要启动(或重新启动)的计时器的指针。
“p_err” 是一个指向错误代码的指针,其值可以是以下任意一种:
OS_ERR_NONE:若定时器已启动。
OS_ERR_OBJ_TYPE:如果在 os_cfg.h 中的“OS_CFG_OBJ_TYPE_CHK_EN”被设置为 1,则表示“p_tmr”未指向定时器。
OS_ERR_TMR_INVALID:如果在 os_cfg.h 中的“OS_CFG_ARG_CHK_EN”被设置为 1,则表示“p_tmr”为 NULL 指针。
OS_ERR_TMR_INACTIVE:“p_tmr”指向的是一个未激活的定时器。在
换句话说,如果指向的是已被删除或未创建的计时器,就会出现此错误。
OS_ERR_TMR_INVALID_STATE 表示该计时器处于无效状态。
OS_ERR_TMR_ISR 如果在 os_cfg.h 中的 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1:此函数是从中断服务程序中调用的,这是不允许的。
返回值
DEF_TRUE 表示计时器已启动
DEF_FALSE 表示出现错误。
注意事项/警告
■ 不要从中断服务程序中调用此函数。
■计时器必须在此之前已创建完成。
OS_TS_GET()
os_cpu.h
是一个用于获取当前时间戳的宏。预计该时间戳的类型为 CPU_TS,通常会被声明为至少为 32 位的数值。
void OSSched (void)
os_core.c
OSSched() 函数允许任务调用调度程序。在执行了一系列“操作”(其中您将 OS_OPT_POST_NO_SCHED 作为操作选项进行设置)之后,您应使用此函数。
OSSched() 只能由任务级别的代码调用。此外,如果调度器处于锁定状态(即,之前调用了 OSSchedLock() 函数),那么 OSSched() 将不会产生任何效果。
如果存在一个优先级高于当前调用任务的任务且已准备好运行,那么 OSSched() 函数将会进行任务切换,将控制权转交给该任务。
消息传递
void OSQCreate (OS_Q *p_q,
CPU_CHAR *p_name,
OS_MSG_QTY max_qty,
OS_ERR *p_err)
os_q.c
函数用于创建一个消息队列。消息队列使得任务或中断服务程序能够将指针大小的变量(即消息)发送给一个或多个任务。所发送消息的具体含义取决于应用程序的需求。
参数
p_q 是指向消息队列控制块的指针。假定消息队列的存储空间将在应用程序中进行分配。用户需要像下面这样声明一个“全局”变量,并将指向该变量的指针传递给 OSQCreate() 函数:
OS_Q MyMsgQ;
p_name 是指向用于命名消息队列的 ASCII 字符串的指针。该名称可以通过调试器或 μC/Probe 显示出来。
msg_qty 表示消息队列的最大大小(必须为非零值)。如果用户不打算限制队列的大小,只需传递一个非常大的数字即可。当然,如果在 OS_MSGs 池中没有足够的 OS_MSGs,后置调用(即 OSQPost())将直接失败,并且一个错误代码将表明没有更多的 OS_MSGs 可以使用。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:如果调用成功且互斥体已创建。
OS_ERR_CREATE_ISR:如果在 os_cfg.h 中的 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1 时发生。
在 os_cfg.h 中:如果尝试从中断服务程序中创建消息队列。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 设置为 1 时发生。
即 p_q 是一个空指针时。
OS_ERR_Q_SIZE:如果在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 设置为 1 时发生。
即指定的大小为 0 时。
OS_ERR_ILLEGAL_CREATE_RUN_TIME:如果定义了 OS_SAFETY_CRITICAL_IEC61508:您在调用 OSSafetyCriticalStart() 之后调用了此函数,因此不再允许创建其他内核对象。
注意/警告
必须先创建队列,然后才能使用它们。
OS CommQ;
void main (void)
{
OS ERR err;
OSInit (&err) ; /* Initialize pC/OS-III */
osQCreate (&CommQ,
"Comm Queue",
10, /* Create COMM Q */
&err);
/*Check "err" */
osstart (); /*Start Multitasking*/
}
void OSQPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
OS_ERR *p_err)
os_q.c
通过消息队列向任务发送消息。消息是一个指针大小的变量,其使用方式取决于应用程序。如果消息队列已满,则会向调用者返回错误代码。在这种情况下,OSQPost() 立即返回给其调用者,而该消息不会被放入消息队列中。
如果任何任务正在等待将消息发送至消息队列,那么优先级最高的任务将接收到该消息。如果等待接收消息的任务的优先级高于发送消息的任务,那么优先级更高的任务将恢复运行,而发送消息的任务将被暂停;也就是说,会发生上下文切换。消息队列可以采用先进先出(OS_OPT_POST_FIFO)或后进先出(OS_OPT_POST_LIFO)的方式,具体取决于在 opt 参数中指定的值。
如果在消息队列中有任何任务正在等待接收消息,OSQPost() 允许用户将消息发送至队列中等待的优先级最高的任务(将 opt 设置为 OS_OPT_POST_FIFO 或 OS_OPT_POST_LIFO),或者发送至消息队列中所有等待的任务(将 opt 设置为 OS_OPT_POST_ALL)。无论哪种情况,只要 opt 不同时设置为 OS_OPT_POST_NO_SCHED,就会进行调度。
参数
p_q 是指向正在被发布的消息队列的指针。
p_void 表示实际发布的消息。p_void 是一个指针大小的变量。其含义取决于具体的应用程序。
“msg_size”指定了消息的大小(以字节为单位)。
opt 决定了执行的 POST 类型。最后两个选项可以添加到 OS_OPT_POST_FIFO 或 OS_OPT_POST_LIFO 中,以创建不同的组合:
OS_OPT_POST_FIFO:将消息发送至队列尾部(FIFO),或者发送给单个等待的任务。
OS_OPT_POST_LIFO:将消息发送至队列前端(LIFO),或者发送给单个等待的任务。
OS_OPT_POST_ALL:将消息发送给所有正在等待该队列的任务。此选项可添加至 OS_OPT_POST_FIFO 或 OS_OPT_POST_LIFO 中。
OS_OPT_POST_NO_SCHED:此选项表示在发送后不调用调度程序,因此调用者将被恢复执行,即使消息被发送至由优先级高于调用者的任务构成的消息队列中。
如果调用 OSQPost() 的任务(或中断服务程序)将执行其他发送操作,则应使用此选项。在这种情况下,调用者不想在完成之前重新调度,并且多个发送操作需要同时生效。
“p_err”是一个指向变量的指针,该变量将存储此函数返回的错误代码。
OS_ERR_NONE:若队列中无任务在等待处理。此时,返回值也为 0。
OS_ERR_MSG_POOL_EMPTY:若不再有可用的 OS_MSG 结构来存储消息。
OS_ERR_OBJ_PTR_NULL:若在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1 且 p_q 是一个空指针时。
OS_ERR_OBJ_TYPE:若在 os_cfg.h 中将 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1 且 p_q 不指向消息队列时。
OS_ERR_Q_MAX:若队列已满,因而无法再接收更多消息。
注意/警告
■队列必须先创建完成才能使用。
■可能的选项组合有:
OS_OPT_POST_FIFO
OS_OPT_POST_LIFO
OS_OPT_POST_FIFO + OS_OPT_POST_ALL
OS_OPT_POST_LIFO + OS_OPT_POST_ALL
OS_OPT_POST_FIFO + OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_NO_SCHED
OS_OPT_POST_FIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED
■尽管下面的示例展示了从任务中调用 OSQPost() 函数,但实际上也可以从中断服务程序中调用该函数。
OS_Q CommQ;
CPU_INT08U CommRxBuf[100];
void CommTaskRx (void *p arg)
{
OS ERR err;
(void) &p arg;
while (DEF_ON)
{
oSQPost (&CommQ,
&CommRxBuf[0],
sizeof (CommRxBuf),
OS OPT POST OPT FIFO + OS OPT POST ALL + OS OPT POST NO SCHED,
&err);
/* Check "err" */
}
}
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
os_q.c
当任务需要从消息队列接收消息时,会使用 OSQPend() 函数。这些消息通过消息队列由中断服务程序(ISR)发送给任务,或者由使用 OSQPost() 函数的其他任务发送。接收到的消息是指针大小的变量,其使用方式取决于应用程序。如果在调用 OSQPend() 时消息队列中已存在至少一个消息,则会检索该消息并返回给调用者。
如果消息队列中没有消息且对 opt 参数指定了 OS_OPT_PEND_BLOCKING,则 OSQPend() 会暂停当前任务,直到接收到消息或用户指定的超时时间到期。如果向消息队列发送了消息并且有多个任务在等待该消息,μC/OS-III 会恢复正在等待的最高优先级任务。
使用 OSTaskSuspend() 暂停的挂起任务可以接收消息。但是,该任务会保持暂停状态,直到通过调用 OSTaskResume() 重新启动。
如果队列中没有消息且对 opt 参数指定了 OS_OPT_PEND_NON_BLOCKING,则 OSQPend() 会返回给调用者一个适当的错误代码,并返回一个空指针。
参数
p_q 是指向接收消息的队列的指针。
timeout 用于在指定的时钟周期内未从消息队列接收到消息时允许任务继续执行。若 timeout 值为 0,则表示任务愿意永远等待消息。该 timeout 值与时钟周期不同步。计数器从下一个时钟周期开始递减,这可能会立即发生。
opt 决定在队列中没有消息可用时是否阻塞。
此参数必须设置为:OS_OPT_PEND_BLOCKING 或 OS_OPT_PEND_NON_BLOCKING。
请注意,当指定 OS_OPT_PEND_NON_BLOCKING 时,应将 timeout 参数设置为 0,因为使用此选项时 timeout 值无关紧要。
p_msg_size 是指向将接收消息大小(以字节数表示)的变量的指针。
p_ts 是指向将接收消息接收时间戳的变量的指针。传递 NULL 指针是有效的,表示用户不需要该时间戳。当用户希望任务能够知晓消息队列何时被发布,或者任务在消息队列发布后恢复运行所花费的时间时,使用时间戳是非常有用的。在后一种情况下,您需要调用 OS_TS_GET() 函数,并计算当前时间戳值与 *p_ts 的差值。换句话说:
delta = OS_TS_GET() - *p_ts;
p_err 是一个指向用于保存错误代码的变量的指针。
OS_ERR_NONE:当接收到消息时。
OS_ERR_OBJ_PTR_NULL:在 os_cfg.h 中,如果 OS_CFG_ARG_CHK_EN 设置为 1 且 p_q 是一个空指针,则此错误发生。
OS_ERR_OBJ_TYPE:在 os_cfg.h 中,如果 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1 且 p_q 不指向消息队列,则此错误发生。
OS_ERR_OPT_INVALID:在 os_cfg.h 中,如果 OS_CFG_ARG_CHK_EN 设置为 1 且您指定了无效的选项,则此错误发生。
OS_ERR_PEND_ABORT:如果 pend 被因另一个任务调用 OSQPendAbort() 而中止。
OS_ERR_PEND_ISR:在 os_cfg.h 中,如果 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1 且该函数是从中断服务程序(ISR)中调用的,则此错误发生。
OS_ERR_PEND_WOULD_BLOCK:如果此函数在 opt 参数设置为 OS_OPT_PEND_NON_BLOCKING 且队列中没有消息的情况下被调用,则此错误发生。
OS_ERR_PTR_INVALID:在 os_cfg.h 中,如果 OS_CFG_ARG_CHK_EN 设置为 1 且 p_msg_size 是一个空指针,则此错误发生。
OS_ERR_SCHED_LOCKED:在调度器锁定时调用此函数。
OS_ERR_TIMEOUT:如果在指定的超时时间内未接收到消息,则此错误发生。
返回值
消息(即指针)或空指针(如果未接收到消息)。请注意,实际消息有可能为一个空指针,因此您应检查返回的错误代码,而非依赖返回的值。
注意/警告
■队列必须在使用前先创建。
■用户不能在中断服务程序中调用 OSQPend() 函数。
OS_Q CommQ;
void CommTask (void *p_arg)
{
OS_ERR err;
void *p_msg; OS_MSG_SIZE msg_size; CPU_TS ts;
(void)&p_arg; while (DEF_ON) {
p_msg = OSQPend(CommQ,
100,
OS_OPT_PEND_BLOCKING,
&msg_size,
&ts,
&err);
/* Check “err” */
}
}
OSTCBCurPtr->ExtPtr
OSTCBCurPtr 指向当前正在运行且调用了 OSSched() 函数的任务的 OS_TCB(任务控制块)
此字段包含一个指向用户可定义的数据区域的指针,该数据区域用于根据需要扩展 TCB(任务控制块)。此指针作为在 OSTaskCreate() 中传递的参数提供。由于它总是在 .StkPtr 后面,所以从汇编语言中很容易访问它。如果所使用的处理器具有浮点单元(FPU),则可以使用 .ExtPtr 为保存 FPU 的上下文添加存储空间。
时间管理
void OSTimeDly (OS_TICK dly,
OS_OPT opt,
OS_ERR *p_err)
os_time.c
函数允许任务延迟自身一定数量的时钟周期。延迟可以是相对的(相对于当前时间进行延迟)、周期性的(在固定的时间间隔内发生延迟)或者绝对的(直到达到某个特定时间才停止)。
在相对模式下,当时钟周期数大于零时,就会重新调度任务。延迟时间为 0 表示任务不会被延迟,此时 OSTimeDly() 将立即返回给调用者。
在周期模式下,必须指定一个非零的周期,否则函数会立即返回并附带适当的错误代码。周期是以“ticks”为单位指定的。
在绝对模式下,重新调度总是会发生,因为所有的延迟值都是有效的。
实际的延迟时间取决于时钟频率(请参阅 os_cfg_app.h 中的 OS_CFG_TICK_RATE_HZ)。
參數
dly 表示期望的延迟时间,以时钟周期数表示。根据 opt 字段的值,延迟可以是相对的或绝对的。
相对延迟意味着延迟是从“当前时间 + 延迟时间”开始的。
周期性延迟指的是延迟的周期(以计时单位的数量表示)。μC/OS-III 将当前时间加上延迟值保存在 .TcikCtrPrev 中,这样下次调用 OSTimeDly() 时,我们就会使用 .TickDlyPrev 加上延迟值。
绝对延迟则意味着任务会在 OSTickCtr 达到由 dly 指定的值时被唤醒。
opt 用于指示延迟是绝对的还是相对的:
OS_OPT_TIME_DLY 表示指定相对延迟。
OS_OPT_TIME_PERIODIC 表示周期模式。
OS_OPT_TIME_MATCH 表示当 OSTickCtr 达到由 dly 指定的值时,任务将被唤醒。
p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
OS_ERR_NONE:如果调用成功,且任务已从期望的延迟中返回。
OS_ERR_OPT_INVALID:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1(表示存在有效的选项但未指定)。
OS_ERR_TIME_DLY_ISR:如果在 os_cfg.h 中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1(表示从中断服务程序中调用此函数)。
OS_ERR_TIME_ZERO_DLY:如果在将选项设置为 OS_OPT_TIME_DLY 时指定了 0 的延迟值(而该选项被设置为 OS_OPT_TIME_MATCH 时,0 的值是有效的)。
void TaskX (void *p_arg)
{
OS_ERR err;
while (DEF_ON)
{
OSTimeDly(10,
OS_OPT_TIME_PERIODIC,
&err);
/* Check “err” */
}
}
void OSTimeDlyHMSM (CPU_INT16U hours,
CPU_INT16U minutes,
CPU_INT16U seconds,
CPU_INT32U milli,
OS_OPT opt,
OS_ERR *p_err)
os_time.c
函数允许任务根据用户指定的时间间隔(以小时、分钟、秒和毫秒为单位)进行延迟。这种格式比直接指定“时钟节拍”(如 OSTimeDly() 中那样)更为便捷和自然。当至少有一个参数不为零时,会重新安排任务。延迟是从调用此函数时的时刻开始计算的。
μC/OS-III 允许用户在表明此函数不严格要求传递的值时指定几乎任何值(opt==OS_OPT_TIME_HMSM_NON_STRICT)。例如,这可以用于将任务延迟数千毫秒。
参数说明
hours 表示任务延迟的小时数。根据 opt 值的不同,有效范围为 0..99(OS_OPT_TIME_HMSM_STRICT),或 0..999(OS_OPT_TIME_HMSM_NON_STRICT)。请注意,不建议将任务延迟数小时,因为在这种长时间内,任务的反馈将无法获取。
minutes 表示任务延迟的分钟数。有效值范围为 0 到 59(OS_OPT_TIME_HMSM_STRICT),或 0..9,999(OS_OPT_TIME_HMSM_NON_STRICT)。请注意,不建议将任务延迟数十到数百分钟,因为在此期间将无法获取该任务的反馈信息。
“seconds”表示任务延迟的秒数。有效值范围为 0 到 59(OS_OPT_TIME_HMSM_STRICT),或者 0 到 65,535(OS_OPT_TIME_HMSM_NON_STRICT)。
“milli”表示任务延迟的毫秒数。有效值范围为 0 到 999(OS_OPT_TIME_HMSM_STRICT),或者 0 到 4,294,967,295(OS_OPT_TIME_HMSM_NON_STRICT)。
(注意:此参数的分辨率是以时钟频率的倍数来确定的。例如,如果时钟频率设置为 100Hz,则 4 毫秒的延迟不会产生实际延迟,因为延迟会被四舍五入到最近的时钟周期。因此,15 毫秒的实际延迟为 20 毫秒。)
“opt”是所需的模式,可以是:
OS_OPT_TIME_HMSM_STRICT(见上文)
OS_OPT_TIME_HMSM_NON_STRICT(见上文)
“p_err”是一个指向包含此函数返回的错误代码的变量的指针。
OS_ERR_NONE:如果调用成功且任务已从期望的延迟中返回。
OS_ERR_TIME_DLY_ISR:如果在 os_cfg.h 中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1
(即在中断服务程序中调用此函数)。
OS_ERR_TIME_INVALID_HOURS:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1
(未指定小时的有效值)。
OS_ERR_TIME_INVALID_MINUTES:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1
(未指定分钟的有效值)。
OS_ERR_TIME_INVALID_SECONDS:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1
(未指定秒的有效值)。
OS_ERR_TIME_INVALID_MILLISECONDS:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1
(未指定毫秒的有效值)。
OS_ERR_TIME_ZERO_DLY:如果指定的延迟为 0(因为所有时间参数均为 0)。
注意事项/警告:
■ 请注意,OSTimeDlyHMSM(0,0,0,0,OS_OPT_TIME_HMSM_???,&err)(即小时、分钟、秒、毫秒均为 0)不会产生延迟,并且该函数会返回给调用方。
■ 总延迟(以时钟周期计)不得超过 OS_TICK 变量所能容纳的最大可接受值。通常,OS_TICK 是一个 32 位值。
void TaskX (void *p_arg)
{
OS_ERR err;
while (DEF_ON) {
OSTimeDlyHMSM(0,
0,
1,
0, OS_OPT_TIME_HMSM_STRICT,
&err); /* Delay task for 1 second */
/* Check “err” */
:
}
}
OSTimeTick()
每次发生时钟中断时,时钟中断服务程序(ISR)都必须调用此函数。μC/OS-III 利用此函数来更新其他系统调用所使用的时间延迟和超时时间。OSTimeTick( ) 被视为 μC/OS-III 的内部函数。

F5-8(1) 通常会使用并配置一个硬件定时器,使其以 10 至 1000 赫兹的频率产生中断(请参见 os_cfg_app.h 中的 OS_CFG_TICK_RATE)。这个定时器通常被称为“节拍定时器”。实际使用何种频率取决于诸如处理器速度、所需的时间分辨率以及处理节拍定时器所能允许的开销等因素。
定时中断不一定由定时器产生。
F5-8(2) 假设 CPU 中断已启用,CPU 接受时钟中断,抢占当前任务,并向时钟中断服务例程(ISR)进行向量处理。时钟 ISR 必须调用 OSTimeTick()(见 os_time.c),这完成了 μC/OS-III 所需的大部分工作。然后,时钟 ISR 清除定时器中断(并可能重新加载定时器以准备下一次中断)。然而,某些定时器可能需要在调用 OSTimeTick() 之前处理,而不是像下面所示在之后处理。
F5-8(3) OSTimeTick() 函数会调用 μC/OS-III 提供的一项服务,以向定时任务发出信号并使该任务准备好运行。一旦定时任务成为最重要的任务,它就会立即执行。定时任务可能不会立即运行的原因是:
定时中断可能会中断优先级高于定时任务的任务,并且在完成定时中断服务程序后,μC/OS-III 会恢复被中断的任务。
F5-8(4) 当定时任务执行时,它会遍历所有等待时间到期或等待具有超时时间的内核对象的任务列表。从此时起,这将被称为定时任务列表。定时任务会将定时任务列表中时间或超时已到期的所有任务准备好以便运行。下面将对此过程进行解释。
OS_TmrTask()
OS_TmrTask() 是由 μC/OS-III 创建的任务(假设在 os_cfg.h 中将 OS_CFG_TMR_EN 设置为 1)其优先级可由用户通过 μC/OS-III 的配置文件 os_cfg_app.h 进行设置(请参阅 OS_CFG_TMR_TASK_PRIO)。OS_TmrTask() 通常被设置为中等优先级。
OS_TmrTask() 是一个周期性任务,它使用与生成时钟脉冲相同的中断源。然而,定时器的更新频率通常较低(例如,通常为每秒 10 次左右),因此在软件中会将定时器的脉冲率进行分频处理。如果脉冲率是 1000 赫兹,而期望的定时器频率是 10 赫兹,那么定时器任务将在每 100 次脉冲中断时被触发,如图 12-6 所示。

资源管理
互斥量
void OSMutexCreate ( OS_MUTEX *p_mutex,
CPU_CHAR *p_name,
OS_ERR *p_err)
os_mutex.c
OSMutexCreate() 函数用于创建并初始化一个互斥锁。互斥锁用于确保对某一资源的独占访问权。
参数
p_mutex 是指向一个互斥锁控制块的指针,该控制块必须在应用程序中进行分配。用户需要按照以下方式声明一个“全局”变量,并将此变量的指针传递给 OSMutexCreate() 函数:
OS_MUTEX 类型的变量 MyMutex;
p_name 是一个指向 ASCII 字符串的指针,用于为互斥信号量指定名称。该名称可由调试器或 μC/Probe 显示出来。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:如果调用成功且互斥体已创建。
OS_ERR_CREATE_ISR:如果在 os_cfg.h 中的 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1 时(即在中断服务程序中尝试创建互斥体)。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 设置为 1 时(即 p_mutex 是一个空指针时)。
OS_ERR_ILLEGAL_CREATE_RUN_TIME:如果在 IEC61508 中定义了 OS_SAFETY_CRITICAL_IEC61508:您在调用 OSSafetyCriticalStart() 之后调用了此函数,因此不再允许创建其他内核对象。
注意/警告
互斥锁必须在使用前先创建。
OS_MUTEX DispMutex;
void main (void)
{
OS_ERR err;
OSInit(&err); /* Initialize μC/OS-III */
OSMutexCreate(&DispMutex, /* Create Display Mutex */
“Display Mutex”,
&err);
/* Check “err” */
OSStart(&err); /* Start Multitasking*/
}
void OSMutexPend (OS_MUTEX *p_mutex,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
os_mutex.c
OSMutexPend() 用于当任务需要独占访问某个资源时。如果任务调用 OSMutexPend() 并且互斥锁可用,OSMutexPend() 将互斥锁交给调用者并返回给调用者。请注意,实际上并没有任何东西交给调用者,只是如果 p_err 被设置为 OS_ERR_NONE,调用者可以假定它拥有该互斥锁。
然而,如果互斥锁已被另一个任务持有,OSMutexPend() 函数会将调用任务放入该互斥锁的等待列表中。该任务会一直等待,直到持有互斥锁的任务释放互斥锁以及相关资源,或者直到指定的超时时间到期。如果在超时到期之前互斥锁被释放,μC/OS-III 会恢复等待该互斥锁的最高优先级任务。
请注意,如果互斥锁由优先级较低的任务持有,那么 OSMutexPend() 会将持有互斥锁的任务的优先级提升到请求互斥锁的任务的优先级相同。当持有者释放互斥锁时(参见 OSMutexPost()),其优先级将恢复到原来的优先级。
OSMutexPend() 允许嵌套。同一个任务可以多次调用 OSMutexPend()。但是,该任务随后必须调用相同次数的 OSMutexPost() 来释放互斥锁。
参数
p_mutex 是指向互斥锁的指针。
超时值指定了一个超时时间(以时钟周期为单位),用于在指定的超时时间内互斥锁未被置位(即未被发布)时允许任务恢复执行。超时值为 0 表示任务愿意无限期等待互斥锁。超时值与时钟周期不同步。超时计数在下一个时钟周期递减,这可能会立即发生。
opt 决定用户是否希望在互斥锁不可用时阻塞。
此参数必须设置为以下值之一:
OS_OPT_PEND_BLOCKING 或 OS_OPT_PEND_NON_BLOCKING。
请注意,当指定 OS_OPT_PEND_NON_BLOCKING 时,超时参数应设置为 0,因为使用此选项时超时值无关紧要。
p_ts 是一个指向时间戳的指针,该时间戳表示互斥锁被发布、挂起被中止或互斥锁被删除的时间。如果传递一个空指针(即 (CPU_TS *)0),调用方将不会收到时间戳。换句话说,传递空指针是有效的,并表示不需要时间戳。
当任务需要了解互斥锁被发布的具体时间,或者在互斥锁被发布后任务恢复所花费的时间时,时间戳会很有用。在后一种情况下,用户必须调用 OS_TS_GET() 并计算当前时间戳值与 *p_ts 之间的差值。换句话说:
delta = 当前时间戳 - *p_ts
p_err 是一个指向变量的指针,该变量用于保存错误代码:
OS_ERR_NONE:如果调用成功且互斥锁可用。
OS_ERR_MUTEX_NESTING:如果调用任务已拥有该互斥锁,但尚未发布所有嵌套值。
OS_ERR_MUTEX_OWNER:如果调用任务已拥有该互斥锁。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1:如果 p_mutex 是空指针。
OS_ERR_OBJ_TYPE:如果在 os_cfg.h 中将 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1:如果用户未传递指向互斥锁的指针。
OS_ERR_OPT_INVALID:如果在 os_cfg.h 中将 OS_CFG_ARG_CHK_EN 设置为 1:如果未指定有效的选项。
OS_ERR_PEND_ISR:如果在 os_cfg.h 中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1:如果从中断服务例程尝试获取互斥锁。
OS_ERR_SCHED_LOCKED:如果在调度器锁定时调用此函数。
OS_ERR_TIMEOUT:如果在指定的超时时间内互斥锁不可用。
返回值
无
注意事项/警告
■互斥锁必须在使用前创建。
不要挂起拥有互斥锁的任务。同时,互斥锁的所有者也不应等待任何其他 μC/OS-III 对象(即信号量、事件标志或队列),并且不要延迟拥有互斥锁的任务。代码应尽快释放资源。
OS_MUTEX DispMutex;
void DispTask (void *p_arg)
{
OS_ERR err; CPU_TS ts;
(void)&p_arg; while (DEF_ON) {
OSMutexPend(&DispMutex,
0,
OS_OPT_PEND_BLOCKING,
&ts,
&err);
/* Check “err” */
}
}
void OSMutexPost (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err);
os_mutex.c
通过调用 OSMutexPost() 可以使互斥锁处于活动状态(即释放状态)。只有在先调用 OSMutexPend() 获取到互斥锁的情况下,才应调用此函数。如果拥有该互斥锁的任务的优先级在更高优先级的任务尝试获取该互斥锁时被提升,那么此时该任务的原始优先级将被恢复。如果有多个任务正在等待该互斥锁,那么该互斥锁将被分配给正在等待该互斥锁的优先级最高的任务。然后会调用调度程序以确定唤醒的任务是否现在是准备运行的最高优先级任务,如果是,则进行上下文切换以运行已准备的任务。如果没有任何任务在等待该互斥锁,那么该互斥锁的值将简单地设置为可用状态。
参数说明
p_mutex 是指向互斥锁的指针。
“opt”决定了所执行的 POST 的类型。
OS_OPT_POST_NONE 未选择任何特殊选项。
OS_OPT_POST_NO_SCHED 在执行完后不调用调度程序,因此即使已发布互斥锁且有更高优先级的任务正在等待该互斥锁,调用者也会继续执行。若调用者调用 OSMutexPost() 时将执行其他操作,若用户不想在所有操作完成之前重新调度,并且多个操作应同时生效,则应使用此选项。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:如果调用成功且互斥锁可用。
OS_ERR_MUTEX_NESTING:如果互斥锁的所有者已将该互斥锁嵌套,并且尚未完全取消嵌套。
OS_ERR_MUTEX_NOT_OWNER:如果调用者并非互斥锁的所有者,因此不允许释放它。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 设置为 1,则表示 p_mutex 是一个空指针。
OS_ERR_OBJ_TYPE:如果在 os_cfg.h 中的 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1,则表示未传递指向互斥锁的指针。
OS_ERR_POST_ISR:如果在 os_cfg.h 中的 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1,则表示从中断服务程序中尝试发布该互斥锁。
注意/警告
■互斥锁必须在使用前先创建。
■切勿在中断服务程序中调用此功能。
OS_MUTEX DispMutex;
void TaskX (void *p_arg)
{
OS_ERR err;
(void)&p_arg; while (DEF_ON) {
:
OSMutexPost(&DispMutex,
OS_OPT_POST_NONE,
&err);
/* Check “err” */
:
}
}
信号量
void OSSemCreate (OS_SEM *p_sem,
CPU_CHAR *p_name,
OS_SEM_CTR cnt,
OS_ERR *p_err)
os_sem.c
OSSemCreate() 函数用于初始化一个信号量。当一个任务需要独占访问某一资源、需要与中断服务程序或另一个任务同步其活动,或者需要等待某个事件发生时,就会使用信号量。您可以使用信号量向一个或多个任务发出事件发生的信号,并使用互斥锁来保护共享资源。然而,从技术上讲,信号量允许同时使用这两种方式。
参数说明
p_sem 是指向信号量控制块的指针。假定信号量的存储空间将在应用程序中进行分配。换句话说,您需要像下面这样声明一个“全局”变量,并将此变量的指针传递给 OSSemCreate() 函数:
OS_SEM MySem;
“p_name”是一个指向 ASCII 字符串的指针,用于为信号量指定名称。该名称可通过调试器或 μC/Probe 显示出来。
“cnt”用于指定信号量的初始值。
如果该信号量用于资源共享,那么您需要将信号量的初始值设置为该信号量所保护的相同资源的数量。如果只有一个资源,那么值应设置为 1(这被称为二进制信号量)。对于多个资源,将值设置为资源的数量(这被称为计数信号量)。
如果将信号量用作信号传递机制,那么您应当将初始值设为 0 。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:如果调用成功且信号量已创建。
OS_ERR_CREATE_ISR:如果在 os_cfg.h 中的 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1
则表示从中断中调用创建了信号量。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 设置为 1
则表示 p_sem 是一个空指针。
OS_ERR_OBJ_TYPE:如果在 os_cfg.h 中的 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1
则表示 p_sem 已被初始化为不同的对象类型。
OS_ERR_ILLEGAL_CREATE_RUN_TIME:如果在 IEC61508 中的 OS_SAFETY_CRITICAL_IEC61508 被定义:在调用 OSSafetyCriticalStart() 之后调用此操作,因此不再允许创建其他内核对象。
注意/警告
信号量必须在使用前先创建。
OS_SEM SwSem;
void main (void)
{
OS_ERR err;
OSInit(&err); /* Initialize μC/OS-III */
OSSemCreate(&SwSem, /* Create Switch Semaphore */
“Switch Semaphore”,
0,
&err);
/* Check “err” */
:
:
OSStart(&err); /* Start Multitasking*/
}
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
os_sem.c
OSSemPend() 函数用于当一个任务需要独占使用某一资源、需要与中断服务程序或其他任务同步其活动,或者需要等待某个事件发生时使用。
当信号量用于资源共享时,如果一个任务调用 OSSemPend() 函数,并且信号量的值大于 0,OSSemPend() 会减小信号量的值并返回给调用者。然而,如果信号量的值为 0,OSSemPend() 会将正在调用的任务放入信号量的等待队列中。该任务会一直等待,直到信号量的所有者(在这种情况下总是是一个任务)通过调用 OSSemPost() 来释放信号量,或者指定的超时时间到期。如果在超时到期之前信号量已被触发,则 μC/OS-III 会恢复等待该信号量的最高优先级任务。
当信号量被用作信号传递机制时,调用任务会一直等待,直到有任务或中断服务例程通过调用 OSSemPost() 来向信号量发出信号,或者指定的超时时间到期。如果在超时时间到期之前信号量已被触发,则 μC/OS-III 会恢复等待该信号量的最高优先级任务。
通过使用 OSTaskSuspend() 函数暂停的任务可以获取信号量。但该任务会一直处于暂停状态,直到通过调用 OSTaskResume() 函数将其恢复为止。
OSSemPend() 函数还会返回 pend 是否已被中止,或者该信号量是否已被删除。
参数说明
p_sem 是指向信号量的指针。
timeout 如果在指定的时钟周期内未发布信号量,则允许任务继续执行。若超时值设为 0,则表示任务会永远等待该信号。超时值与时钟周期不同步。超时计数从下一个时钟周期开始递减,这可能会立即发生。
“opt”参数用于指定在信号量不可用的情况下,该调用是应阻塞执行还是不进行阻塞。
OS_OPT_PEND_BLOCKING 会阻止调用者,直到信号量可用或者出现超时情况。
OS_OPT_PEND_NON_BLOCKING 如果信号量不可用,OSSemPend() 不会阻塞,而是会向调用者返回适当的错误代码。
p_ts 是一个指向变量的指针,该变量将接收信号量被发布、挂起操作被中止或被删除时的时间戳。传递一个空指针是有效的,并表示无需提供时间戳。
当任务需要了解信号量何时被发布,或者任务在信号量发布后恢复运行所花费的时间时,使用时间戳是非常有用的。在后一种情况下,调用 OS_TS_GET() 函数,并计算当前时间戳的值与 *p_ts 的差值。换句话说:
delta = OS_TS_GET() - *p_ts;
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:若信号量可用。
OS_ERR_OBJ_DEL:若信号量已被删除。
OS_ERR_OBJ_PTR_NULL:若在 os_cfg.h 中的 OS_CFG_ARG_CHK_EN 被设置为 1 且 p_sem 是一个空指针时。
OS_ERR_OBJ_TYPE:若在 os_cfg.h 中的 OS_CFG_OBJ_TYPE_CHK_EN 被设置为 1 且 p_sem 不指向信号量时。
“OS_ERR_OPT_INVALID” 错误:如果“OS_CFG_ARG_CHK_EN”标志被设置为 1(位于“os_cfg.h”文件中)的话。“不”
OS_OPT_PEND_NON_BLOCKING
OS_OPT_PEND_BLOCKING或者
OS_ERR_PEND_ABORT:如果挂起操作被中止
OS_ERR_PEND_ISR:如果在 os_cfg.h 文件中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1
(即此函数是从中断服务程序中调用的)
OS_ERR_PEND_WOULD_BLOCK:如果按照指定方式调用此函数
(使用了 OS_OPT_PEND_NON_BLOCKING 选项,并且该信号量不可用)
OS_ERR_SCHED_LOCKED:如果在调度器已锁定的情况下调用此函数
OS_ERR_TIMEOUT:如果在指定的超时时间内信号量未被触发。
返回值
信号量计数值的新值。
注意/警告
信号量必须在使用前先创建。
OS_SEM SwSem;
void DispTask (void *p_arg)
{
OS_ERR err;
CPU_TS ts;
(void)&p_arg;
while (DEF_ON) {
(void)OSSemPend(&SwSem,
0,
OS_OPT_PEND_BLOCKING,
&ts,
&err);
/* Check “err” */
}
}
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
os_sem.c
通过调用 OSSemPost() 函数可以向信号量发出信号。如果信号量的值为 0 或更大,则会将其值加 1,然后 OSSemPost() 函数会返回给调用者。如果有任务正在等待信号量被触发,则 OSSemPost() 会从等待列表中移除等待该信号量的优先级最高的任务,并使该任务准备好运行。然后会调用调度程序来确定唤醒的任务是否现在是准备好运行的优先级最高的任务。
参数说明
p_sem 是指向信号量的指针。
“opt”决定了所执行操作的类型。
OS_OPT_POST_1:仅向处于信号量等待状态且优先级最高的任务发送通知并完成操作。
OS_OPT_POST_ALL:向所有处于信号量等待状态的任务发送通知。
只有在信号量被用作信号传递机制的情况下,才应使用此选项,而绝不能在信号量用于保护共享资源时使用。告知所有共享资源的任务它们都可以访问该资源是没有意义的。
OS_OPT_POST_NO_SCHED:此选项表示调用者不希望在发送通知后调用调度程序。此选项可与前面的两个选项之一结合使用。
如果调用 OSSemPost() 的任务(或中断服务程序)将执行额外的发送操作,并且用户不想阻止用户这样做,则应使用此选项。
重新安排时间直至全部完成,并且多条公告将同时生效。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:表示没有任务在该信号量上等待。在这种情况下,返回值也为 0。
OS_ERR_OBJ_PTR_NULL:表示在 os_cfg.h 文件中,如果 OS_CFG_ARG_CHK_EN 设置为 1 且 p_sem 是一个空指针,则出现此错误。
OS_ERR_OBJ_TYPE:表示在 os_cfg.h 文件中,如果 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1 且 p_sem 不指向信号量,则出现此错误。
OS_ERR_SEM_OVF:表示如果执行置位操作会导致信号量计数器溢出,则出现此错误。
返回值
信号量计数值的当前值
注意/警告
■信号灯必须在使用前先创建。
■您也可以在中断服务程序中向信号量发送消息,但该信号量必须用作信号传递机制,而不能用于保护共享资源。
OS_SEM SwSem;
void TaskX (void *p_arg)
{
OS_ERR err;
OS_SEM_CTR ctr;
(void)&p_arg; while (DEF_ON) {
ctr = OSSemPost(&SwSem,
OS_OPT_POST_1 + OS_OPT_POST_NO_SCHED,
&err);
/* Check “err” */
}
}
void OSSemSet (OS_SEM *p_sem,
OS_SEM_CTR cnt,
OS_ERR *p_err)
os_sem.c
OSSemSet() 函数用于更改信号量计数的当前值。通常在将信号量用作信号传递机制时会选用此函数。然后,可以使用 OSSemSet() 将计数重置为任意值。如果信号量的计数已经为 0,则只有在没有任务等待该信号量的情况下,计数才会被改变。
参数说明
p_sem 是指向用于作为信号机制使用的信号量的指针。
cnt 是该信号量应设置的期望计数值。
p_err 是一个指向变量的指针,该变量用于存储错误代码:
OS_ERR_NONE:如果计数值发生了变化,或者没有变化但是因为有一个或多个任务正在等待该信号量。
OS_ERR_OBJ_PTR_NULL:如果在 os_cfg.h 文件中将 OS_CFG_ARG_CHK_EN 设置为 1,则表示 p_sem 是一个空指针时的情况。
OS_ERR_OBJ_TYPE:如果在 os_cfg.h 文件中将 OS_CFG_OBJ_TYPE_CHK_EN 设置为 1,则表示 p_sem 不指向信号量时的情况。
OS_ERR_SET_ISR:如果在 os_cfg.h 文件中将 OS_CFG_CALLED_FROM_ISR_CHK_EN 设置为 1,则表示此函数是从中断服务程序中调用的时的情况。
OS_ERR_TASK_WAITING:如果有任务正在等待该信号量时的情况。
注意/警告
若该信号量用于保护共享资源,则请勿使用此功能。
OS_SEM SwSem;
void Task (void *p_arg)
{
OS_ERR err;
(void)&p_arg;
while (DEF_ON) {
OSSemSet(&SwSem, /* Reset the semaphore count */
0,
&err);
/* Check “err” */
}
}
如果可以的话,您是否应该使用信号量而非互斥锁呢?
如果参与争夺共享资源的各个任务都没有需要满足的截止时间要求,那么就可以用信号量来代替互斥锁。
然而,如果需要在规定时间内完成任务,那么在访问共享资源之前就应该使用互斥锁。信号量存在无限制的优先级反转问题,而互斥锁则不存在这一问题。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)