The whole truth about RTOS. Article # 16. Signals

Original author: Colin Walls
  • Transfer


This article will look at signals, which are the simplest mechanisms of 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. Sections of memory: services and data structures
Article # 14. Sections of memory: introduction and basic services
Article # 13. Task Data Structures and Unsupported API Calls
Article # 12. Services for working with tasks
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: advanced features and context preservation
Article # 9. Planner: implementation
Article # 8. Nucleus SE: Internal Design 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 interruptions
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real-Time Mode
Article # 1. RTOS: introduction.

Using 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. Signals can only be read by the owner of the signal. During reading, the signals are reset. Tasks cannot read or reset signals from other tasks.

Nucleus RTOS has a tool that allows tasks to assign functions that run when another task sets one or more signal flags. This is somewhat reminiscent of an interrupt processing routine. This feature is not supported in Nucleus SE; here, tasks should request signal flags explicitly.

Signal Setup


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

Setting this enable parameter serves as the main signal activator. This provides a well-defined data structure having an appropriate size. In addition, this option activates the API settings.

Activate API Calls


Each API function (utility call) in 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 set to FALSE , thus disabling each service call and preventing the code that implements them from being turned on. To configure the 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 the signaling support turned off will result in a compilation error. If your code uses an API call that has not been activated, a layout error will occur because the implementation code was not included in the application. Of course, the inclusion of two API functions is somewhat unnecessary, since there is no point in activating signal support in the absence of these APIs. Activators have been added for compatibility with other Nucleus SE features.

Call Signals


Nucleus RTOS supports four signal-related overhead calls that provide the following functionality:

  • Sending signals to a given task. Nucleus SE is implemented in the NUSE_Signals_Send () function .
  • Reception of signals. Nucleus SE is implemented in the NUSE_Signals_Receive () function .
  • Signal handler registration. Not implemented in Nucleus SE.
  • Turn on / off (control) signals. Not implemented in Nucleus SE.

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

Signaling and receiving services


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

Since signals are bits, they are best visualized as binary numbers. Since standard C has historically not supported the representation of binary constants (only octal and hexadecimal), Nucleus SE has a useful header file nuse_binary.h , which contains #define characters like 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 that does not affect previously set flags.

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

Parameters:

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

Return value:

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

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

Service call prototype:

STATUS_NUSE_Signals_Send (NUSE_TASK task, U8 signals);

Parameters:

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

Return value:

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

Implementing Signaling in Nucleus SE
The following is the full 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 on the signal flags of the specified task. Locking tasks does not affect signals.

Receiving signals


A task can only read its own set of signal flags. During reading, the flag values ​​are reset.

Call to receive signals in Nucleus RTOS
Service call prototype:
UNSIGNED NU_Receive_Signals (VOID);

Parameters: none.

Return value:
Signal flag values.

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

Service call prototype:
U8 NUSE_Signals_Receive (void);

Parameters: none.

Return value:
Values ​​of signal flags.

Implementing Nucleus SE Signal Reception
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. Locking tasks does not affect signals.

Data structures


Since signals are not independent objects, the use of memory depends on the tasks to which they belong. Below is some information for complete understanding. 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 only used if signal support is enabled.

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

The structure of data placed in RAM


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

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

Structure of data placed in ROM


Signals lack data structures in ROM.

Amounts of memory for storing signal data


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

The amount of data in 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 configured tasks ( 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 Nucleus RTOS API signaling calls are not implemented in Nucleus SE:

Signal Handler Registration


This API call sets up the signal processing procedure (function) for the current task. Nucleus SE does not need this because signal handlers are not supported.

Service call prototype:
STATUS NU_Register_Signal_Handler (VOID (* signal_handler) (UNSIGNED));

Parameters:
signal_handler - function that should be called when receiving signals

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

Control (activation / deactivation) of signals


This service activates and / or deactivates 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 a bit disables it.

Service call prototype:
UNSIGNED NU_Control_Signals (UNSIGNED enable_signal_mask);

Parameters:
enable_signal_mask - bit pattern representing the correct signals.

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

Nucleus RTOS Compatible


When developing Nucleus SE, my goal was to keep the code level compatible with Nucleus RTOS. Signals are no exception, and, from the developer's point of view, they are implemented in much the same way as in 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, Nucleus RTOS API calls can be almost directly transferred to Nucleus SE calls.

Signal handlers


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

Signal availability and quantity


In Nucleus RTOS, tasks can have 32 signal flags. In Nucleus SE, I decided to reduce their number to eight, as this will be enough for simpler applications and will save 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 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 over thirty years, devoting most of his time to firmware. He is now a firmware engineer at Mentor Embedded (a division of Mentor Graphics). Colin Walls often speaks at conferences and seminars, the author of numerous technical articles and two books on firmware. Lives in the UK. Colin's professional blog , e-mail: colin_walls@mentor.com.

Also popular now: