📜 ⬆️ ⬇️

The whole truth about the RTOS. Article # 16. Signals



This article will look at signals that are the simplest mechanisms for interaction between tasks in Nucleus SE. They provide a low-cost way to transfer simple messages between tasks.


Previous articles in the series:
Article # 15. Memory sections: services and data structures
Article # 14. Memory sections: introduction and basic services
Article # 13. Task data structures and unsupported API calls
Article # 12. Task Services
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: additional features and context preservation
Article # 9. Scheduler: implementation
Article # 8. Nucleus SE: Inside and Deployment
Article # 7. Nucleus SE: introduction
Article # 6. Other RTOS services
Article # 5. Interaction between tasks and synchronization
Article # 4. Tasks, context switching and interrupts
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.

Use of signals


Signals differ from all other types of kernel objects in that they are not autonomous: signals are associated with tasks and cannot exist without them. If the application is configured to use signals, then each task has a set of eight signal flags.
')
Any task can set signals for another task. Only the task owner of the signal can read signals. In the process of reading the signals are reset. Tasks cannot read or discard signals from other tasks.

In Nucleus RTOS, there is a tool that allows tasks to assign functions that run when another task sets one or more signal flags. This is somewhat similar to the interrupt handling procedure. This feature is not supported in the Nucleus SE, here tasks should request signal flags explicitly.

Signal setup


As with most Nucleus SE objects, the signal setting is determined by the #define directives in nuse_config.h . The main parameter is NUSE_SIGNAL_SUPPORT , which activates support for functionality (for all tasks in the application). The question of specifying the number of signals is not worth it: 8 flags are allocated for each task.

The setting of this enable parameter serves as the main signal activator. This provides a well-defined data structure with an appropriate size. In addition, this parameter activates the API settings.

Activate API calls


Each API function (service call) in the Nucleus SE is activated by the #define directive in nuse_config.h . For signals, these include:

NUSE_SIGNALS_SEND NUSE_SIGNALS_RECEIVE 

By default, they are assigned the value FALSE , thus disabling each service call and preventing the inclusion of the code implementing them. To set up signals in the application, you need to select the necessary API calls and set the corresponding directives to TRUE .

The following is an excerpt from the default nuse_config.h file:

 #define NUSE_SIGNAL_SUPPORT FALSE /* Enables support for signals */ #define NUSE_SIGNALS_SEND FALSE /* Service call enabler */ #define NUSE_SIGNALS_RECEIVE FALSE /* Service call enabler */ 

An activated API function with signal support disabled will result in a compilation error. If your code uses an API call that has not been activated, a build error will occur because the implementation code was not included in the application. Of course, the inclusion of two API functions is in some way unnecessary, since there is no point in activating signal support in the absence of these APIs. Activators have been added for compatibility with other features of Nucleus SE.

Alarm Calls


Nucleus RTOS supports four signal-related service calls, which provide the following functionality:


The implementation of each of these calls is discussed in detail below.

Signal sending and receiving services


The fundamental operations that can be performed on a set of task signals — sending data (can be done by any task) and reading data (and, therefore, clearing data, can only be done by the owner-task). Nucleus RTOS and Nucleus SE provide two basic API calls for these operations, which will be described below.

Since the signals are bits, they are best visualized as binary numbers. Since standard C does not historically support the representation of binary constants (octal and hexadecimal only), Nucleus SE has a useful header file nuse_binary.h , which contains #define characters of the form b01010101 for all 256 8-bit values. The following is an excerpt from the nuse_binary.h file:

 #define b00000000 ((U8) 0x00) #define b00000001 ((U8) 0x01) #define b00000010 ((U8) 0x02) #define b00000011 ((U8) 0x03) #define b00000100 ((U8) 0x04) #define b00000101 ((U8) 0x05) 

Sending Signals


Any task can send signals to any other task in the application. Sending signals involves setting one or more signal flags. This is an OR operation (OR), which does not affect the previously set flags.

Call to send signals to the Nucleus RTOS
Service Call Prototype:
STATUS NU_Send_Signals (NU_TASK * task, UNSIGNED signals);

Options:

task - a pointer to the task control block to which the set signal flags belong;
signals - the value of the set signal flags.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_TASK - incorrect pointer to the task;

Call to send signals to the Nucleus SE
This API call supports the core Nucleus RTOS API.

Service Call Prototype:

STATUS_NUSE_Signals_Send (NUSE_TASK task, U8 signals);

Options:

task - an index (ID) of the task to which the signal flags are being set;
signals - the value of the set signal flags.

Return value:

NUSE_SUCCESS - the service call was successfully completed;
NUSE_INVALID_TASK - incorrect task index.

Implementing sending signals to the Nucleus SE
Below is the complete code for the NUSE_Signals_Send () function:

 STATUS NUSE_Signals_Send(NUSE_TASK task, U8 signals) { #if NUSE_API_PARAMETER_CHECKING if (task >= NUSE_TASK_NUMBER) { return NUSE_INVALID_TASK; } #endif NUSE_CS_Enter(); NUSE_Task_Signal_Flags[task] |= signals; NUSE_CS_Exit(); return NUSE_SUCCESS; } 

The code is very simple. After any verification of the parameters, the signal values ​​pass through the OR operation to the signal flags of the specified task. Task blocking does not affect the signals.

Reception of signals


A task can only read its own set of signal flags. In the process of reading the values ​​of the flags are reset.

Call to receive signals in the Nucleus RTOS
Service Call Prototype:
UNSIGNED NU_Receive_Signals (VOID);

Parameters: none.

Return value:
Values ​​of signal flags.

Call to receive signals in the Nucleus SE
This API call supports Nucleus RTOS API key functionality.

Service Call Prototype:
U8 NUSE_Signals_Receive (void);

Parameters: none.

Return value:
Values ​​of signal flags.

Implementing Nucleus SE Signal Acquisition
The following is the full code for the NUSE_Signals_Receive () function:

 U8 NUSE_Signals_Receive(void) { U8 signals; NUSE_CS_Enter(); Signals = NUSE_Task_Signal_Flags[NUSE_Task_Active]; NUSE_Task_Signal_Flags[NUSE_Task_Active] = 0; NUSE_CS_Exit(); return signals; } 

The code is very simple. The value of the flags is copied, the initial value is reset, and the copy is returned by the API function. Task blocking does not affect signals.

Data structures


Since signals are not separate objects, memory usage depends on the tasks to which they belong. Below is some information for completeness. Signals use one data structure (in RAM), which, like other Nucleus SE objects, is a table whose dimensions correspond to the number of tasks in the application. This data structure is used only if signal support is enabled.

I highly recommend that the application code does not directly access this data structure, but use the existing API functions. This avoids incompatibility with future versions of the Nucleus SE, unwanted side effects, and also simplifies porting the application to the Nucleus RTOS. The data structure is discussed in detail below to simplify understanding of the principles of work calls and debugging.

The structure of the data placed in the RAM


Data structure:
NUSE_Task_Signal_Flags [] is an array of type U8 with one entry for each configured task, signal flags are stored in this array.

This data structure is initialized with zeros by the NUSE_Init_Task () function when the Nucleus SE is loaded.

The structure of the data placed in the ROM


Signals do not have data structures in ROM.

The amount of memory for storing data signals


As with all Nucleus SE core objects, the amount of memory required for signals is predictable.

The amount of data in the ROM for all signals in the application is 0.

The amount of memory for storing data in RAM (in bytes) for all signals in the application is equal to the number of tasks configured ( NUSE_TASK_NUMBER ). But in fact, this data belongs to the tasks and is described in the previous article about the tasks.

Unrealized API calls


Two signal calls to the API of signals from Nucleus RTOS are not implemented in Nucleus SE:

Signal Handler Registration


This API call sets the signal processing procedure (function) for the current task. This is not necessary in Nucleus SE, since signal handlers are not supported.

Service Call Prototype:
STATUS NU_Register_Signal_Handler (VOID (* signal_handler) (UNSIGNED));

Options:
signal_handler - a function that should be called when receiving signals

Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_POINTER - null pointer to a signal handler ( NULL )

Control (enable / disable) signals


This service activates and / or deactivates the signals for the current task. For each task 32 signals are available. Each signal is represented by a bit in signal_enable_mask . Adding a bit to signal_enable_mask enables the corresponding signal, and deleting the bit disables it.

Service Call Prototype:
UNSIGNED NU_Control_Signals (UNSIGNED enable_signal_mask);

Options:
enable_signal_mask - a bit pattern representing the correct signals.

Return value:
Mask of activating / deactivating the previous signal.

Nucleus RTOS Compatibility


When developing the Nucleus SE, my goal was to maintain the maximum level of compatibility of code with the Nucleus RTOS. Signals are no exception, and, from a developer’s point of view, they are implemented in much the same way as in the Nucleus RTOS. There are some incompatibilities that I considered valid, given that the final code is much easier to understand and can use memory more efficiently. Otherwise, the Nucleus RTOS API calls can be almost directly transferred to the Nucleus SE calls.

Signal handlers


In Nucleus SE, signal handlers are not implemented to simplify the overall structure.

Signal availability and quantity


Nucleus RTOS tasks can have 32 signal flags each. In Nucleus SE, I decided to reduce their number to eight, since this will be enough for simpler applications and saves RAM resources. If necessary, the signals can be completely turned off.

Unrealized API calls


Nucleus RTOS supports four signaling service calls. Of these, two were not implemented in the Nucleus SE. Their description can be found above in the section "Unrealized API calls".

About the author: Colin Walls has been working in the electronics industry for more than thirty years, spending a significant amount of time on embedded software. He is now an embedded software engineer in Mentor Embedded (a division of Mentor Graphics). Colin Walls often speaks at conferences and seminars, author of numerous technical articles and two books on embedded software. Lives in the UK. Colin's professional blog , e-mail: colin_walls@mentor.com.

Source: https://habr.com/ru/post/427439/


All Articles