Signals

A signal is a message that a task can use to wakeup one or more tasks waiting on an
event or events. When waiting on an event, a task is suspended and does not consume
CPU time. Nano-RK supports 32 unique signals. It is possible to wait on multiple
signals such that any one of them can wake a task from sleep. All signals must be
created using nrk_sig_create() before they can be used. Any task that wishes to wakeup on a signal
must register the signal using nrk_sig_register(). There are a few special case signals that are
generated by the kernel used to support event timeouts and notification of special actions. See the
Special Kernel Signals subsection for more information. For more information about signals, please refer
to the basic_signals project that comes with Nano-RK:
basic_signals

Each signal and semaphore requires a system resource. These are statically defined and must be declared in the nrk_cfg.h file as shown below:

1#define NRK_MAX_RESOURCE_CNT       5

Signal and Semaphore Types

1// This is a macro used to convert a signal into a bitmask for nrk_event_wait()
2// See nrk_event_wait() for an example.
3#define SIG(x)  ((uint32_t)1)<<x 
4
5// These are typedefs used to represent signals and semaphores
6typedef int8_t nrk_sig_t;
7typedef uint32_t nrk_sig_mask_t;
8typedef int8_t nrk_sem_t;

nrk_signal_create

void nrk_signal_create()
Parameters: none
Return Values: nrk_sig_t new signal id

This function creates a signal. Upon failure, this function returns NRK_ERROR. Upon success a positive value
representing the signal is returned.

  nrk_sig_t signal_one;
  nrk_sig_t signal_two;
  ...

  signal_one=nrk_signal_create();
  signal_two=nrk_signal_create();

nrk_signal_delete

int8_t nrk_signal_delete(nrk_sig_t sig_id)
Parameters: nrk_sig_t signal id of signal to remove
Return Values: int8_t NRK_OK upon success and NRK_ERROR upon failure

This function deletes a signal sig_id so that it can be reused by different tasks. This function returns NRK_OK upon success and NRK_ERROR on failure.

nrk_event_signal

int8_t nrk_event_signal(nrk_sig_t sig_id)
Parameters: nrk_sig_t signal id of signal to be sent
Return Values: int8_t NRK_OK upon success and NRK_ERROR upon failure
This function is used to signal tasks that are waiting on events using nrk_event_wait().
sig_id is the signal that is sent. This function returns NRK_OK upon success and NRK_ERROR upon failure.
If a lower priority task signals a higher priority task, the high priority task will begin to execute at
the next context swap. Normally this happens when the low priority task suspends, but it is also possible
that a medium priority task could preempt the low priority task causing a context swap that would then
schedule the waiting high priority task. For this reason, processing that needs to be complete before the signaled task
executes should be done before the signals are sent.
  • Errno
    • 1 Signal was not created
    • 2 No task was waiting on signal
1   v=nrk_event_signal( signal_one );
2   if(v==NRK_ERROR) nrk_kprintf( PSTR( "nrk_event_signal failed\r\n" ));

nrk_event_wait

nrk_sig_mask_t nrk_event_signal(nrk_sig_mask_t event_mask)
Parameters: nrk_sig_mask_t event mask of signal to wait on, use SIG macro with signal value
Return Values: nrk_sig_mask_t signal mask that triggered wakeup

This function will wait for a set events. nano-RK supports up to 32 signals. Each signal
represents a bit in a 32 bit number so it is possible to logically
OR multiple signals together if you wish to wait on a combination of events. The 32
bit number returned by nrk_event_wait() corresponds to the signal or signals that were returned.
When waiting on multiple signals, the return value can be used to determine which signal
triggered the wakeup. All signals need to be registered in order for a task to receive them.
This function returns 0 upon failure if a signal is specified that does not exist, or is not
registered.

 1  nrk_sig_mask_t my_sigs;
 2  int8_t v;
 3
 4  // Don't forget to register signal for reception
 5  v=nrk_signal_register(signal_one);
 6  if(v==NRK_ERROR) nrk_kprintf( PSTR( "nrk_signal_register failed\r\n" ));
 7  v=nrk_signal_register(signal_two);
 8  if(v==NRK_ERROR) nrk_kprintf( PSTR( "nrk_signal_register failed\r\n" ));
 9
10  ...
11  // Waiting on signal_one OR signal_two
12  my_sigs=nrk_event_wait( SIG(signal_one) | SIG(signal_two) );
13
14  if(my_sigs==0) nrk_kprintf( PSTR( "nrk_event_wait failed\r\n" ));
15  if(my_sigs & SIG(signal_one))
16     nrk_kprintf( PSTR( "Task got signal 1\r\n") );
17  if(my_sigs & SIG(signal_two))
18     nrk_kprintf( PSTR( "Task got timeout signal2\r\n") );

int8_t nrk_signal_register(nrk_sig_t sig_id);

This function registers a signal sig_id so that a task is able to receive it. This function returns NRK_OK upon success and NRK_ERROR upon failure if the signal does not exist. A signal only needs to be registered for reception and not transmission.

1  v=nrk_signal_register(signal_two);
2  if(v==NRK_ERROR) nrk_kprintf( PSTR( "Error calling nrk_signal_register\r\n" ));

int8_t nrk_signal_unregister(nrk_sig_t sig_id);

This function unregisters a signal sig_id so that the task is no longer able to be unsuspended by that event. This function returns NRK_OK upon success and NRK_ERROR uopn failure.

nrk_sig_mask_t nrk_signal_get_registered_mask();

This function returns the current registered signal mask for a task.

Semaphores

A semaphore is a protected variable and constitutes the classic method for restricting
access to shared resources (e.g. storage,actuators etc) in a multiprogramming environment.
Nano-RK implements semaphores and signals such that
tasks that are suspended on an event or waiting for access to a semaphore will not be
scheduled until the corresponding signal is sent or semaphore becomes available. For more information
on using semaphores please refer to the basic_sem project that comes with the Nano-RK distribution:
basic_sem

Note that the Atmel ISA does not have a test-and-set instruction, so we provide a best effort implementation by disabling interrupts.

nrk_sem_t nrk_sem_create(uint8_t count, uint8_t ceiling_priority);*

This function creates a semaphore resource, with a priority ceiling value used by the task when accessing the resource. This facilitates the Priority Ceiling Protocol Emulation (PCPE) algorithm used in Nano-RK to avoid priority inversion. count specifies the number of entries allowed into the critical section. ceiling_priority sets the ceiling value for the task; Note if
using PCPE this should be the highest priority task accessing the critical section. Below is an example of declaring and creating a semaphore:

1   nrk_sem_t *my_semaphore;
2   ...
3   my_semaphore = nrk_sem_create(1,4);
4   if(my_semaphore==NULL) nrk_kprintf( PSTR("Error creating Semaphore\r\n" ));

This created a semaphore with a count of 1 (also called a mutex or binary semaphore) with a priority ceiling value of 4.

int8_t nrk_sem_delete(nrk_sem_t *rsrc );

This function deletes an existing semaphore which will free the resource for reuse. This function returns NRK_OK upon success and NRK_ERROR on failure. Errno is set depending on the error type:
  • Errno
    • 1 Signal Not Found
    • 2 Signal Index too large

int8_t nrk_sem_pend(nrk_sem_t *rsrc );

Semaphore pend takes the address of the created semaphore and attempts to access the resource. If the resource is available,
pend will decrement the resource counter and allow the program to continue, otherwise pend will suspend until the resource is
posted by another task. This can be used to protect critical sections of code. Below is an example of a task pending on a semaphore:
  • Errno
    • 1 Signal Not Found
    • 2 Signal Index too large
1   nrk_kprintf( PSTR("Task accessing semaphore\r\n"));
2   v = nrk_sem_pend(my_semaphore);
3   if(v==NRK_ERROR) nrk_kprintf( PSTR("Error calling pend\r\n"));
4   nrk_kprintf( PSTR("Task is now holding semaphore\r\n"));

int8_t nrk_sem_post(nrk_sem_t rsrc);*

Semaphore post takes the address of a created semaphore and releases access to the resource.
This should be called after exiting a critical section that was pended. Below is an example of a task posting a semaphore:
  • Errno
    • 1 Signal Not Found
    • 2 Signal Index too large
      1   v = nrk_sem_post(my_semaphore);
      2   if(v==NRK_ERROR) nrk_kprintf( PSTR("Error calling post\r\n"));
      3   nrk_kprintf( PSTR("Task released semaphore\r\n"));
      

int8_t nrk_sem_query(nrk_sem_t *rsrc );

This returns the current count value of the semaphore. This can be used to check if the semaphore will allow access without actually trying to lock it.
  • Errno
    • 1 Signal Not Found
    • 2 Signal Index too large

Special Kernel Signals

nrk_wakeup_signal

Sometimes it might be convenient to have a timeout associated with an nrk_event_wait() call. This can be achieved using the kernel generated nrk_wakeup_signal. This signal is sent on a per-task basis (not globally) when the task’s internal next-wakeup timer expires. Normally, a task’s next wakeup is set for its next period, however this can be adjusted using the nrk_set_next_wakeup() function. nrk_wakeup_signal is created by the kernel during nrk_init(). In order for nrk_event_wait() to receive the nrk_wakeup_signal from a task it must simply be registered and muxed in like any other signal. Note, each task has its own instance of the nrk_wakeup_signal. Unlike other signals, nrk_wakeup_signal is not globally broadcast to all tasks. See below for an example of how to use it as an event timeout.

int8_t nrk_set_next_wakeup(nrk_time_t timeout);

This function sets the task's next wakeup timer. It returns NRK_OK upon success and NRK_ERROR on failure. This function does not
suspend the task, it only changes when the next wakeup will happen if the task suspends on an event. Calling other suspend functions like nrk_wait_until_next_period() will replace whatever wakeup value you might have previously set.

 1   nrk_time_t timeout;
 2
 3   timeout.secs=10;
 4   timeout.nano_secs=0;
 5   ...
 6
 7   nrk_set_next_wakeup(timeout);
 8   my_sigs=nrk_event_wait( SIG(signal_one) | SIG(nrk_wakeup_signal) );
 9
10   // Lets check which signal we got...
11   if(my_sigs==0)                        nrk_kprintf( PSTR( "Error calling nrk_event_wait()\r\n" ));
12   if(my_sigs & SIG(signal_one))         nrk_kprintf( PSTR( "Task got signal_one\r\n") );
13   if(my_sigs & SIG(nrk_wakeup_signal))  nrk_kprintf( PSTR( "Task got timeout signal! \r\n") );
Prev: Stack Next: Device Drivers Top