A number of skeleton templates for embedded systems

See also model2.html which a variation in the examples in the bottom

On this page is a number of generic task constructions usefull in embedded systems

The is hold on a generic level but is easy convertable to krnl, FreeRtos, … There are some examples in bewteen written for krnl. You can easily get them up and running.

1. Fixed samplingsfrequency (task synchronized by timed semaphore)

In many systems it is critical to have a rock stable samplingsfrequency for obvious purposes (control, signal processing, …)

  • endless loop where you are wairting by a semaphore which is signalled by the kernel with strict regular intervals

  • if you come to late the signal will be accumulated. Please note limit imposed by max limit set in k_crt_sem

  • Task shall normally be among the higheste prioritized on a preemptive kernel so

  • Task will get the CPU when it is started

  • If you dont have highest task priority you might be delay in execution bq the CPU is taken by some one else.

  • Task can easy check if its behind (there is already a signal waiting at the semaphore when the task enter the wait call in the top of the loop)

  • if behind k_wait returns 1. If we have been waited it returns 0. If timeout (no signal) it returns < 0.

(fixedsampling.ino: click for open/close)

(fixedsampling.ino as raw)

2. Tasks sharing data protected by a critical region (semaphore)

We have two independent tasks:

  • task1 is saving data in a shared variable struct data

  • task2 is retrieving data from the same struct

  • we do protect the data so only one task at a time can access the data

  • priority is in this example no issue

  • A low prioritized task lowTask and high prioritized task controlTask

  • We are using a critical region protected the shared data

  • We are using a standard semaphore to ensure atomic access to the shared data

  • controlTask is running on a fixed samplingsfrequency

  • lowTask is running now and then and might be looping faster than controlTask.

  • We upcount a shared variable every time we deliver data so controlTask can see if any updates has been lost

  • Important rule is minimize your time in critical as much as possible

  • Pittfall

    • controlTask might wait on the critical region because lowTask is inside the region

  • lowTask can ofcourse also pick up data instead of delivering data - see next example

(mutex.ino: click for open/close)

(mutex.ino as raw)

3. Critical region now by ceiling protocol

The example is based on the previous example.

  • We do use a mutex (mutual exclusion) primitive instead of a semaphore.

  • It is basicly the same example but just show how you use a mutex call.

  • It is a semaphore in behind but when you enter the region your priority is set to the value given in k_mut_ceil_set

  • And you revert to your previous priority when leaving the region

  • If your incoming priority is higher than the ceiling priority for the region just dont get the region

  • So check for access like in the example

But

  • A set of mutex call can normally detect if they are paired (an enter should be together with a leave

  • Inheritance or ceiling protocols might be part of the mutex. The two protocols try to maximize real time behavior og reduce or remove deadlock

  • The krnl example uses the mutex calls which also implement immediate priority ceiling protocols when a ceiling priority is added prior to k_start wirh k_mut_ceil_set.

  • So run it without setting ceiling and the code runs just like using pairs of wait and signal

  • Set ceiling prority by k_mut_ceil_set and it will be a immediate ceiling protocol.

  • NB NB NB: if running ceiling protocol a task with priority HIGHER than the ceiling priority will not get the critical region and return value CEILINGFAIL (pt -3)

  • So as usual - dont do as me - please check your return values

(mutex2.ino: click for open/close)

(mutex2.ino as raw)

4. Critical region .. wont wait invariant

The example is based on example 2

The high prority control Task use a wait-check call to see if the critical region is vacant and if so then take it.

Drawback is

  • controlTask might never enter the region

  • could have been solved by ceiling as shown just above …

  • So high priority control task will not be delayed but

  • might never (very bad luck ) enter the critical region

(mutex3.ino: click for open/close)

(mutex3.ino as raw)

5. wait-signal construction from interrupt to a task

Systems often interact with the enviroment.

You can

  • check the environment for state of the world and act upon than

  • you wait and by started or triggerede when something happens (interrupt)

It might be more easy just to let your task sleep and then be started by an interrupt.

Normally and interrupt is pulling a pin on the PCU high or low (depending on configuration)

The code is for an Arudino UNO and a MEGA. Take a look in the initISR function and the head of the interrupt servcie routine

(isem.ino: click for open/close)

(isem.ino as raw)

6. Message passing or buffering

We use a message buffering system - sometimes called a ringbuffer system

Some normal characteristics for a message system

  • has internal buffering of the messages

  • No of elements in the buffer and their size is set by you

  • you have to come with the memory for the buffer (just a char array)

  • if buffer is full you cant deliver any messages - so check your return code (negative if no succes)

  • not like a critical region - you just deliver data

The code is a little complicated :-)

  • This version for an UNO due to integration of interrupt

  • ISR installed on pin 2. Its normally HIGH due to pull up.

  • Activate interrupt by pulling pin2 to to ground

  • ISR (interrupt service routine) will send an integer to message buf named pMsg (ki_send)

  • task t2 is waiting on data coming to this buffer in k_receive

  • task t2 will just forward the received integer to another messagebuf named pMsg2

  • task t1 is waiting on pMsg2 message buffer and will blink with LED upon receiving.

SO here we have a demonstration of simple message passing and integration of interrupt.

The interrupt routine could be for handling a serial prot, a network interface etc. The idea is fast efficient service of the hardware by the interrupt and then a little later the rest of the work carried out by a task. In this way we can ensure short interrupt time which is nice because when we are in the interrupt routine we normally has disabled the interrupt and therefore blocking for all other interrupts that might occur.

(msg1.ino: click for open/close)

(msg1.ino as raw)

(msg2.ino: click for open/close)

(msg2.ino as raw)