
以前的一篇文章(第5条)中提到了信号灯。 他们的主要任务是控制对资源的访问。
该系列中的先前文章:
第十八条 事件标志组:助手服务和数据结构第十七条 事件标志组:简介和基本服务第十六条 讯号第十五条 内存分区:服务和数据结构第十四条 内存部分:简介和基本服务第十三条 任务数据结构和不受支持的API调用第十二条 任务处理服务第11条 任务:API的配置和介绍第10条 计划程序:高级功能和上下文保留第9条。 调度程序:实施第8条 Nucleus SE:内部设计和部署第7条 Nucleus SE:简介第6条。 其他RTOS服务第5条 任务交互和同步第4条 任务,上下文切换和中断第3条 任务与计划第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。
使用信号量
在Nucleus SE中,信号量是在组装阶段定义的。 一个应用程序最多可以有16个信号灯。 如果未指定信号灯,则应用程序中将不包括服务调用和数据结构的代码。
信号量是
U8类型的计数器,对其访问的控制方式使得多个任务可以使用它。 一个任务可以减少信号量计数器的值(捕获)或增加信号量计数器的值(释放)。 尝试捕获具有空值的信号量可能会导致错误或任务挂起,具体取决于所选的API调用参数和Nucleus SE配置。
设置信号灯
信号量
与大多数Nucleus SE对象一样,信号量的设置由
nuse_config.h中的
#define指令确定。 主要参数是
NUSE_SEMAPHORE_NUMBER ,它确定应用程序中的信号灯数量。 默认情况下,该参数设置为0(在应用程序中未使用信号灯),并且可以采用不超过16的任何值。不正确的值将导致编译错误,这将通过检入
nuse_config_check.h生成(此文件包含在
nuse_config.c中 ,这意味着它可以编译)因此,#
error指令将
触发 。
选择一个非零值可以用作信号量的主要激活器。 定义数据结构时使用此参数,其大小取决于其值(有关更多详细信息,请参见本文的进一步内容)。 此外,非零值会激活API设置。
激活API调用
Nucleus SE中的每个API函数(实用程序调用)
都由nuse_config.h中的
#define指令激活。 对于信号量,这些包括:
NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT
默认情况下,它们设置为
FALSE ,从而禁用每个服务调用并阻止包含实现它们的代码。 要设置信号灯,您需要选择必要的API调用并将相应的指令设置为
TRUE 。
以下是默认
nuse_config.h文件的摘录。
#define NUSE_SEMAPHORE_NUMBER 0 #define NUSE_SEMAPHORE_OBTAIN FALSE #define NUSE_SEMAPHORE_RELEASE FALSE #define NUSE_SEMAPHORE_RESET FALSE #define NUSE_SEMAPHORE_INFORMATION FALSE #define NUSE_SEMAPHORE_COUNT FALSE
如果应用程序中没有信号灯,则激活的API函数将导致编译错误(始终启用的
NUSE_Semaphore_Count()除外)。 如果您的代码使用尚未激活的API调用,则会发生布局错误,因为实现代码未包含在应用程序中。
实用程序信号灯调用
Nucleus RTOS支持八个服务调用,这些调用提供以下功能:
- 信号量捕获。 Nucleus SE在NUSE_Semaphore_Obtain()函数中实现。
- 释放信号量。 在Nucleus SE中,它是在NUSE_Semaphore_Release()函数中实现的。
- 通过释放所有暂停的任务(重新引导)使信号量返回未使用状态。 Nucleus SE在NUSE_Semaphore_Reset()中实现 。
- 提供有关特定信号量的信息。 Nucleus SE在NUSE_Semaphore_Information()中实现 。
- 返回应用程序中已配置信号灯的数量。 Nucleus SE在NUSE_Semaphore_Count()中实现 。
- 向应用程序添加新的信号灯。 未实施Nucleus SE。
- 从应用程序中删除信号量。 未实施Nucleus SE。
- 返回所有信号量的指针。 未实施Nucleus SE。
每个服务调用的实现将在下面详细描述。
实用程序调用以捕获和释放信号量
可以对信号量执行的基本操作是捕获和释放(减少和增加值)。 Nucleus RTOS和Nucleus SE为这些操作提供了两个基本的API调用。
信号量捕获
Nucleus RTOS实用程序调用捕获信号量非常灵活,并且允许您隐式地暂停任务,或者如果当前无法执行操作(例如,尝试捕获值为零的信号量),则以特定的超时暂停任务。 Nucleus SE提供了相同的功能,仅任务暂停是可选的,并且未实现超时。
在Nucleus RTOS中捕获信号量的挑战服务电话原型:
状态NU_Obtain_Semaphore(NU_SEMAPHORE *信号量,UNSIGNED挂起);参数:
信号量 -指向用户提供的信号量控制块的指针;
pause-任务挂起参数,可以采用值
NU_NO_SUSPEND或
NU_SUSPEND以及超时值。
返回值:
NU_SUCCESS-调用成功完成;
NU_UNAVAILABLE-信号
量具有空值;
NU_INVALID_SEMAPHORE-指向信号量的无效指针;
NU_INVALID_SUSPEND-尝试从与任务无关的线程中暂停;
NU_SEMAPHORE_WAS_RESET-在挂起任务时重置了信号灯。
在Nucleus SE中捕获信号量的挑战该API调用支持Nucleus RTOS API的核心功能。
服务电话原型:
状态NUSE_Semaphore_Obtain(NUSE_SEMAPHORE信号量,U8挂起);参数:
信号量 -使用的信号量的索引(ID);
pause-任务挂起参数,可以是
NUSE_NO_SUSPEND或
NUSE_SUSPEND 。
返回值:
NUSE_SUCCESS-呼叫已成功完成;
NUSE_UNAVAILABLE-信号量为空值;
NUSE_INVALID_SEMAPHORE-无效的信号量索引;
NUSE_INVALID_SUSPEND-尝试从与任务无关的线程中暂停或禁用API阻止功能时;
NUSE_SEMAPHORE_WAS_RESET-任务挂起时重置了信号灯;
在Nucleus SE中实现信号量捕获根据是否激活了对阻塞(暂停)任务的支持,使用条件编译选择功能代码
NUSE_Semaphore_Obtain()的版本(检查参数后)。 考虑这两种选择。
如果未激活锁定,则此API调用的逻辑非常简单:
if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; } else /* semaphore unavailable */ { return_value = NUSE_UNAVAILABLE; }
检查信号量计数器的值,如果不等于零,则减小。
如果激活了任务锁定,则逻辑将变得更加复杂:
do { if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* semaphore unavailable */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_UNAVAILABLE; } else { /* block task */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4) | NUSE_SEMAPHORE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND);
一些澄清可能会有所帮助。
代码放置在
do ... while循环中 ,该
循环在
suspend参数为
NUSE_SUSPEND时运行 。
如果信号量具有非零值,则它会减小。
暂 挂变量设置为
NUSE_NO_SUSPEND ,并且API调用终止并返回
NUSE_SUCESS 。
如果信号量为null,并且
suspend变量设置为
NUSE_NO_SUSPEND ,则API调用将返回
NUSE_UNAVAILABLE 。 如果将挂起设置为
NUSE_SUSPEND ,任务将暂停。 调用完成后(例如,任务恢复时),如果返回值为
NUSE_SUCCESS (表示释放信号量后而不是在重置后恢复了任务),则循环从头开始。
信号量释放
对Nucleus RTOS API进行实用程序调用以释放信号量非常简单:信号量计数器的值增加,并返回成功消息。 Nucleus SE提供了相同的功能,但具有额外的溢出检查功能。
挑战在Nucleus RTOS中释放信号量服务电话原型:
状态NU_Release_Semaphore(NU_SEMAPHORE *信号量);参数:
信号量 -指向用户提供的信号量控制块的指针。
返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_SEMAPHORE-无效的信号量指针。
挑战在Nucleus SE中释放信号量该API调用支持Nucleus RTOS API的核心功能。
服务电话原型:
状态NUSE_Semaphore_Release(NUSE_SEMAPHORE信号量);参数:
信号量 -释放的信号量的索引(ID)。
返回值:
NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_SEMAPHORE-无效的信号量索引;
NUSE_UNAVAILABLE-信号量的值为255,无法增加。
在Nucleus SE中实现信号量释放功能代码
NUSE_Semaphore_Release() (检查参数后)是通用的,而不管是否激活了任务锁。 检查信号量计数器的值,如果该值小于255,则增加。
如果激活了对API阻止调用(任务挂起)的支持,则使用条件编译选择其他代码:
NUSE_CS_Enter(); if (NUSE_Semaphore_Counter[semaphore] < 255) { NUSE_Semaphore_Counter[semaphore]++; return_value = NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; NUSE_Semaphore_Blocking_Count[semaphore]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } #endif } else { return_value = NUSE_UNAVAILABLE; } NUSE_CS_Exit(); return return_value;
如果此信号量上有任何任务挂起,则它们中的第一个将恢复。
下面的文章将描述与信号量及其数据结构相关的其他API调用。
关于作者: Colin Walls在电子行业工作了30多年,大部分时间用于固件。 他现在是Mentor Embedded(Mentor Graphics的一个部门)的固件工程师。 Colin Walls经常在会议和研讨会上发表演讲,他撰写了许多技术文章并撰写了两本有关固件的书。 居住在英国。
Colin的专业
博客 ,电子邮件:colin_walls@mentor.com。