Programming Real-Time Embedded Systems - EPFL
Programming Real-Time Embedded Systems - EPFL
Embedded Systems
Grégory Mermoud
EPFL, SS 2008-2009
http://disal.epfl.ch/teaching/embedded_systems/
Outline
Embedded systems constraints (memory, computation,
communication bandwidth, energy, e.g.).
Real-time issues in embedded systems programming:
Perception-to-action loop
Interrupts
Scheduling
Operating system and device drivers
Memory issues in embedded systems programming:
Data storage: primary, secondary and tertiary
Memory architectures and dsPIC architecture (e-puck)
Compression: using computation for saving memory
Conclusion
Embedded systems
Programming embedded systems is all about resource
management, i.e. dealing with delays and time constraints
imposed by the environment and limitations of the hardware
(memory, computation, energy, communication bandwidth).
When programming (or simply using) an embedded system, you
must bear in mind its limitations and find appropriate
techniques for dealing with them.
Energy, memory and computation are often very limited and
precious in real-time embedded system applications.
Often, you need to make a trade-off between computation,
memory and communication.
Fortunately, computer and electronic engineers have developed
a lot of useful techniques to perform these trade-offs.
Programming Embedded System
Modern embedded systems have increasing
software complexity.
Some applications may be critical and
improper software design can cause real
catastrophes!
Ariane 5 explosion: a 64 bit floating point
number relating to the horizontal velocity of the
rocket was converted to a 16 bit signed integer.
The number was larger than 32,767, the largest
integer storeable in a 16 bit signed integer, and
thus the conversion failed:
double h_v = horizontal_velocity();
short int h_v2 = (short int) h_v;
sensors actuators
processing time
analog-digital
Perception
Action
conversion time actuator delay
Computation
void InitTMR1(void) {
T1CON = 0;
T1CONbits.TCKPS = 0; // prescaler = 256
TMR1 = 0; // clear timer 1 Interrupt set-up and
PR1 = (MILLISEC/18.00); // 18KHz interrupt (T = 55 us)
initialization
IFS0bits.T1IF = 0; // clear interrupt flag
IEC0bits.T1IE = 1; // set interrupt enable bit
T1CONbits.TON = 1; // start Timer1
}
void main() {
// initialize Timer 1
InitTMR1();
10µs 35µs 10µs 35µs 10µs 35µs 10µs 35µs 10µs 35µs
The main loop gets executed, but the only thing it does is to put the
microcontroller in sleep mode.
In sleep mode, timers and hardware interrupts are still active.
Each 55µs, Timer 1 triggers an interrupt, thus wakening the microcontroller.
The task X() gets executed during approximately 10µs. Then, the
microcontroller can get back to sleep for 35µs.
Even if the duration X() is different, the timing is not affected.
Here, the microcontroller is in sleep mode during 64% of the time: very
interesting from an energy point of view.
Interrupts: some definitions
Interrupt Service Routine (ISR): simply another program function that gets
executed upon trigger of its associated interrupt.
Interrupt vector: a fixed address that contains the memory address of the ISR.
Interrupt flag: one bit in a register that indicates whether the interrupt has
been triggered or not.
Interrupt mask: one bit in a register that controls whether the interrupt can be
triggered or not
Non Maskable Interrupt (NMI): an interrupt that is always active.
Asynchronous event: an event that could happen at any time (not necessarily
synchronized with the clock).
Time-triggered interrupt: an interrupt that is triggered by a timer in a
periodic fashion.
Event-triggered interrupt: an interrupt that is triggered by an (external) event
(e.g., user input, A/D conversion, sensor measurement).
Microphone on the real e-puck
First, you need a buffer to read the data from the microphone:
#define SAMPLELEN 1840 // for buffer allocation
static int values[SAMPLELEN]; // buffer and index for storing
static int valuesw; // index
void InitTMR1(void) {
T1CON = 0;
T1CONbits.TCKPS = 0; // prescaler = 256
TMR1 = 0; // clear timer 1
PR1 = (MILLISEC/18.00); // 18KHz interrupt (T = 55 us)
IFS0bits.T1IF = 0; // clear interrupt flag
IEC0bits.T1IE = 1; // set interrupt enable bit
T1CONbits.TON = 1; // start Timer1
}
Microphone on the real e-puck
Now, we need to define an Interrupt Service Routine (ISR) that will be
executed each 55µs.
This function is very simple:
First, it clears the interrupt flag so that the interrupt can be triggered again.
Then, it checks whether the buffer is full. If it is the case, it just returns
without doing anything.
Then, it calls the function e_read_ad(int channel) for initiating a new
analog/digital conversion.
Finally, the new value is added to the buffer and the index is incremented.
Note that we use an active waiting loop, which is not really optimal from the
energy/scheduling point of view.
A better practice would consist in putting the microcontroller in sleep mode
and using a software interrupt when the buffer is full to waken it and print the
values in another ISR.
Multiple I/O = Multiple tasks
Multiple I/O = Multiple tasks
Read
Sensing accelerometer
Read IR sensors Read camera
Crash Braitenberg
Process image
detection algorithm
Processing
5ms 10ms 300ms
Object recognition and detection
Task A
Task B
Turn LEDs Update wheel
Task C
Actuation on motors
Dependency Maximal delay
Scheduling is the key!
Object recognition and detection
Braitenberg
algorithm Read camera
Update wheel
Process image
motors
Braitenberg
algorithm
Robot memory
time
read and process
100ms
write
33ms
Problem!
write at 0x1
1 image = 600 KB
dsPIC memory = 8KB
Robot memory