关于RTOS的全部真相。 第十九条 信号灯:简介和基本服务



以前的一篇文章(第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 /* Number of semaphores in the system - 0-16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */ 

如果应用程序中没有信号灯,则激活的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_SUSPENDNU_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_SUSPENDNUSE_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; /* check whether a task is blocked */ /* on this semaphore */ 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。

Source: https://habr.com/ru/post/zh-CN429156/


All Articles