Lecture: | 34 |
Objective: | An introduction to Real Time Operating Systems.
|
Extra: |
Salvo |
Code: | vendingMachine.c |
Embedded Software Architectures
The architectures are driven by the need for response time.
- Round-Robin (or superloop).
- Round-Robin with interrupts
- Non-preemptive Real-Time Operating System
- Preemptive Real-Time Operating System
Round-Robin (or superloop)
A main loop checks each of the I/O devices and services each in a
prescribed order.
void main() {
init();
while(1) {
task1();
task2();
...
} }
Example
A digital multimeter which checks the position of a switch, reads a value
from a proble, performs an ADC conversion and then displays the result on
an LCD is such an example.
Advantage
Works well when there are few I/O devices, no lengthy processing,
and no tight response requirements.
Disadvantage
If any device has a response time which is less than the time required
to get around the superloop. If any of the tasks requires length
processing. Modification made to meet requirements results in a
fragile architecture.
Round-Robin with interrupts
A main loop checks each of the I/O devices and services each in a
prescribed order. Interrupts are used to deal with the time
constrained I/O devices.
int8 data_for_device_A;
void main() {
init();
while(1) {
task1();
task2();
...
if (global_flag_A) taskA();
} }
void ISR_deviceA() {
service_A(data_for_device_A);
set(global_flag_A);
}
Example
36 position rotary encoder which selects which function to perform
on a DS1302 real time clock. Assume that it takes 100mS to read the
time from the DS1302. We assume that we want to be able to monitor the
rotary encoder when it is turned slower than 1 rotation per second.
This means that we must examine the rotary encoder at least
36 clicks/sec * 4 detents / click = 144 detents / sec or 7mS / detent.
Thus, we need to put either the DS1302 or the rotart encoder onto an
interrupt so that we can perform both tasks.
Advantage
Simple.
Disadvantage
Open to problems associated with shared data. All the tasks in main
operate with the same priority. For example, a laser printer spends
lots of time calculating where to put the tiny dots of ink. Main would
then get "stuck" working on this task at the exclusion of all the other
tasks. Moving the other tasks into ISRs is a solution, but then a low
priority interrupt might take to long to service. In addition, if there
were a pair of time consuming tasks then one of them would always have
to wait for the other.
Shared Data Problem
As soon as we use interrupts to share information between an ISR and
main we can create problems. The foundation for these problems arises
from the fact that we don't want the ISR to perform all the work.
Generally, we use the ISR to manipulate some I/O device and pass
all the work of actually processing the information from the sensor
to main.
The problems that can arise from sharing data between an ISR and
main are shown in the following code snippet. In this example we
are to monitor the temperature from two sensors and set off an
alarm if these are different (we are to assume that this condition
indicates that there is a problem with the sensors that someone needs
to know about).
int8 iTemp[2];
void interrupt ReadTemp(void) {
iTemp[0] = ReadSensor0;
iTemp[1] = ReadSensor1;
}
void main(void) {
int8 iTemp0, iTemp1;
while(1) {
iTemp0 = iTemp[0];
iTemp1 = iTemp[1];
if (iTemp0 != iTemp1)
Set off the klaxon;
} }
So where is the error? Well it arises out of a sequence of events.
- The temperature is changing.
- We have just finished executing the "iTemp0..." line in main
- The interrupt occurs and updates both values of iTemp[]
The alarm will sound even though there was no real error. Even worse this
error will
not be repeatable. This is a instance of a so-called
Heisenberg. How about trying the following fix?
int8 iTemp[2];
void interrupt ReadTemp(void) {
iTemp[0] = ReadSensor0;
iTemp[1] = ReadSensor1;
}
void main(void) {
int8 iTemp0, iTemp1;
while(1) {
if (iTemp[0] != iTemp[1])
Set off the klaxon;
} }
Well this really doesn;t work either. So how to we solve this problem.
Well go the source. The interrupt is the real culprit, if it
occurs when we are making the comparison then we can get a problem.
The solution will consist of making a "critical section" around the
comparison, not allowing interrupts to occur.
int8 iTemp[2];
void interrupt ReadTemp(void) {
iTemp[0] = ReadSensor0;
iTemp[1] = ReadSensor1;
}
void main(void) {
int8 iTemp0, iTemp1;
while(1) {
disable_interrupts();
iTemp0 = iTemp[0];
iTemp1 = iTemp[1];
enable_interrupts();
if (iTemp0 != iTemp1)
Set off the klaxon;
} }
Interrupt Latency
If we enable and disable interrupts to solve the problems associated
with the shared data problem then we will increase the
interrupts
latency; the time delay between the occurrence of an interrupting
event and its being serviced. In some cases you will need to
calculate the latency, so how can you do this? You need to know
4 things.
- The longest period of time that the interrupt is disabled.
- The length of time required to service interrupts at a higher
priority level.
- The time required to enter an ISR. This is MCU book-keeping
required to save the state of the MCU so that its not perturbed by
the ISR.
- How long if takes the ISR to set itself up and then "service"
the interrupting event.
Lets look at an example
- You have to disable interrupts for 125us for your task code
to use a pair of temperature variables it shares with the
interrupt routine that reads the temperatures from the hardware and
writes them into the variables.
- You have to disable interrupts for 250us for your task code to
get the tie variables from variables it shares with the interrupt routine that
responds to the timer interrupt.
- It takes 10us for the PIC to switch contexts.
- You must complete a response within 625us when you get a special
signal (an interrupt) from another processor in your system; the
inter-processor interrupt routine takes 300us to complete.
This solution assumes that the inter-process communication is given
the highest priority and there are no other interrupts with that
interrupt priority. Always work these types of problems assuming
a worst-case scenario from the current state of the MCU to the resolution
of the interrupt. That is assume that we have just entered the
portion of the foreground code which disables the interrupts. Interrupts
are disabled for 250us, it takes 10us to switch to the inter-process ISR,
and inter-process communications require 300us. Thus it takes 560us to
service this requires, well within the 625us requirement. What if we
assumed that all the interrupts were of the same priority?
Real Time Operating Systems
A RTOS allows a collection of independent tasks to simultaneously
use the MCU. A task are small embedded systems programs with a
superloop structure. Tasks have a priority which determines
their relative importance to one another. Since we have only
one processor, we know that only one of the tasks can actually be
running at a time. Hence, the tasks will have to be in variety
of states.
Non-preemptive Real Time Operating System
Advantage
Simple to write a non-preemptive RTOS. Simple to program applications.
Disadvantage
The longest delay to service a high priority event is the time required
by the longest task. The RTOS cannot preempt any running task. Consequently
a bug in one task may very well bring the entire system down. Using
an RTOS consumes system resources (memory and MCU processing time).
Preemptive Real-Time Operating System
A preemptive RTOS can suspend one task to run another.
Advantage
The response time of the system is stable if the code is changed.
Disadvantage
Using an RTOS consumes system resources (memory and MCU processing time).
They increase the delivery cost of your product.