Adding structure to C
for
Embedded Systems
Antonio Martí Campoy
[email protected]
Goals
• To get knowledge about event-triggered and
time-triggered
• To get knowledge about embedded
applications structure
• To evaluate and decide the most suitable
structure
A. Martí Campoy Adding structure 2
Plan
• Event driven systems
• Time triggered systems Super Loop
Polling Interrupts
• Real-time schedulers
A. Martí Campoy Adding structure 3
Introduction
• In an Event Driven system, tasks are activated
by events
– Usually by means of interrupts
• In a Time Triggered system, tasks are activated
periodically
– Usually by means of a hardware timer
A. Martí Campoy Adding structure 4
Introduction
• Every problem may be solved in several ways
• However, some ways fit better some problems
• But it is not easy to decide what is the best
way
• Event driven systems give fast response, but
may present unpredictability
• Time triggered gives determinism and
reliability
A. Martí Campoy Adding structure 5
Event driven systems
• In an Event Driven system, tasks are activated
by events
• The program is waiting to peripherals or
hardware modules to:
– Get ready to do a operation
• e.g. a communication device is ready to send a new
data
– External event has happened
• e.g. somebody or something has changed the state of a
digital input
A. Martí Campoy Adding structure 6
Event driven systems
• Since there is no OS, the Super Loop is the
simplest structure
• The application run continuously the Super
Loop
• The body of Super Loop:
– Check state of peripherals: Polling
– Wait hardware signal activation: interrupts
A. Martí Campoy Adding structure 7
Event Driven: Loop and polling
• Super Loop is the simplest structure
void main (void) void peripheral_rdy(void)
{ {
Disable_watchdog(); uint8_t value;
Init_clk(); value = Get_status();
Init_peripherals(); clear_flag();
if (value & Flag)
while (1) return(1);
{ else
If (peripheral_rdy()) return(0);
do_task(); }
}
A. Martí Campoy Adding structure 8
Event Driven: Loop and polling
• Super Loop is the simplest structure
void main (void) void peripheral_rdy(void)
{ {
Disable_watchdog(); uint8_t value;
Init_clk(); value = Get_status();
Init_peripherals(); clear_flag();
if (value & Flag)
while (1) return(1);
{ else
If (peripheral_rdy()) return(0);
do_task(); }
} Advantages:
} 1.- it is simple
2.- It works fine if you are not
very demanding
A. Martí Campoy Adding structure 9
Event Driven: Loop and polling
• Super Loop is the simplest structure
void main (void) void peripheral_rdy(void)
{ {
Disable_watchdog(); uint8_t value;
Init_clk(); value = Get_status();
Init_peripherals(); clear_flag();
if (value & Flag)
while (1) return(1);
{ else
If (peripheral_rdy()) return(0);
do_task(); }
} Disadvantages:
} 1.- Waste cpu time
2.- Problem with some devices
(glitch)
A. Martí Campoy Adding structure 10
Event Driven: Loop and polling
• Mechanical switches are
very common in
embedded systems
• Mechanical switches,
when commute, produces
bounces or glitches
Start
switching
• If you check too fast the input line, one push
may appear as several events
A. Martí Campoy Adding structure 11
Event Driven: Loop and polling
• How to solve the problem of glitches?
– By software, introducing a delay in the Super Loop
– By software, checking again input state
– By hardware, using an RC low-pass filter
A. Martí Campoy Adding structure 12
Event Driven: Loop and polling
• How to solve the problem of glitches?
– By software, introducing a delay in the Super Loop
– By software, checking again input state
– By hardware, using an RC low-pass filter
while (1)
void delay_ms(int time)
{
{
if (Input_port_pin)
some kind of delay:
do_task();
loop, Hw timer…
delay_ms(500);
}
}
A. Martí Campoy Adding structure 13
Event Driven: Loop and polling
• How to solve the problem of glitches?
– By software, introducing a delay in the Super Loop
– By software, checking again input state
– By hardware, using an RC low-pass filter
uint8_t peripheral_rdy()
{ void delay_ms(int time)
if (Input_port_pin) {
{ some kind of delay:
delay_ms(50); loop, Hw timer…
if (input_port_pin) return(1); }
}
return(0);
}
A. Martí Campoy Adding structure 14
Event Driven: Loop and polling
• How to solve the problem of glitches?
– By software, introducing a delay in the Super Loop
– By software, checking again input state
– By hardware, using an RC low-pass filter
Input
signal
Filter
output
A. Martí Campoy Adding structure 15
Event Driven: Loop and Interrupts
• Using interrupts: the smart choice!
• When the peripheral is ready, it activates a
physical signal
• A flag is set
• If the microcontroller is in low-power mode,
interrupt wakes-up it!!!
• The CPU jumps to an Interrupt Service Routine
(ISR)
• Task may be executed inside/outside the ISR
A. Martí Campoy Adding structure 16
Event Driven: Loop and Interrupts
• Run task inside ISR
void main (void) #pragma vector=PERIPHERAL_VECTOR
{ __interrupt void Periphe_ISR (void)
Disable_watchdog(); {
Init_clk(); Task();
Init_peripherals(); Clear_Interrupt_Flag();
//__enable_interrupt(); //__low_power_mode_n();
__low_power_mode_n(); }
while (1)
{
}
} It’s not needed. uC
comes back to
previous mode
n= from 0 to 4 (normal to ULP)
Enables interrupts (GIE bit)!!
A. Martí Campoy Adding structure 17
Event Driven: Loop and Interrupts
• Run task inside ISR
void main (void) #pragma vector=PERIPHERAL_VECTOR
{ __interrupt void Periphe_ISR (void)
Disable_watchdog(); {
Init_clk(); Clear_Interrupt_Flag();
Init_peripherals(); Task();
//__enable_interrupt(); //__low_power_mode_n();
__low_power_mode_n(); }
while (1)
{
}
} - Calling functions adds an overhead
(preparing stack, saving registers…)
- Also you should be aware of task
execution time because you are
locking other interrupts
A. Martí Campoy Adding structure 18
Event Driven: Loop and Interrupts
• Run task inside ISR
void main (void) #pragma vector=PERIPHERAL_VECTOR
{ #pragma inline=forced
Disable_watchdog(); __interrupt void Periphe_ISR (void)
Init_clk(); {
Init_peripherals(); Clear_Interrupt_Flag();
//__enable_interrupt(); Task();
__low_power_mode_n(); //__low_power_mode_n();
while (1) }
{
}
} Options:
- Inline function
- #pragma inline=forced
before declaration
- Moving task out of the ISR
A. Martí Campoy Adding structure 19
Event Driven: Loop and Interrupts
• Executing task out of ISR uint8_t P1; //Global
void main (void) #pragma vector=PERIPHERAL_VECTOR
{ __interrupt void Periphe_ISR (void)
extern uint8_t P1; {
Disable_watchdog(); Clear_Interrupt_Flag();
Init_clk(); P1 = 1;
Init_peripherals(); __low_power_mode_off_on_exit();
//__enable_interrupt(); }
P1 = 0;
while (1) Comments:
{
__low_power_mode_n(); - Task may suffer some delay or
if (P1) jitter
{ - Clearing the flag in main is a
P1=0;
Task(); “critical section”, so it must be an
} “atomic operation”
} Mandatory!!!
} A. Martí Campoy Adding structure 20
Event Driven: Loop and Interrupts
• Executing task out of ISR uint8_t P1; //Global
void main (void) #pragma vector=PERIPHERAL_VECTOR
{ __interrupt void Periphe_ISR (void)
extern uint8_t P1; {
Disable_watchdog(); Clear_Interrupt_Flag();
Init_clk(); P1 = 1;
Init_peripherals(); __low_power_mode_off_on_exit();
//__enable_interrupt(); }
P1 = 0;
while (1) The critical section includes only
{
__low_power_mode_n(); clearing the flag
disable_interrupt();
if (P1) {
P1=0;
resume_interrupt(); Is it mandatory?
Task();}
resume_interrupt(); }
} A. Martí Campoy Adding structure 21
Event Driven: Loop and Interrupts
• Executing task out of ISR
• It is better to use keyword “__monitor”
• It is place before function declaration
• When the function is called and before to run,
save interrupts state (OLD = GIE) and disable
them (GIE = 0)
• Just finished the execution of the function,
restore interrupts state (GIE = OLD)
• Global variable is not needed
A. Martí Campoy Adding structure 22
Event Driven: Loop and Interrupts
• Executing task out of ISR
void main (void) static uint8_t P1=0;
{
Disable_watchdog(); #pragma vector=PERIPHERAL_VECTOR
Init_clk(); __interrupt void Periphe_ISR (void)
Init_peripherals(); {
Clear_Interrupt_Flag();
while (1) P1 = 1;
{ __low_power_mode_off_on_exit();
__low_power_mode_n(); }
if (Check_P1()){Task();}
} __monitor uint8_T Check_P1 (void)
} {
uint8_t copy;
copy = P1;
Interrupts are P1 = 0;
disabled while return (copy);
}
running the function
A. Martí Campoy Adding structure 23
Event Driven: Loop and Interrupts
• Several tasks
void main (void) static uint8_t P1=0, P2=0,..Pn=0;
{
Disable_watchdog(); #pragma vector=px_VECTOR
Init_clk(); __interrupt void px_ISR (void)
Init_peripherals(); {
Clear_Interrupt_Flag();
Px=1;
while (1) __low_power_mode_off_on_exit();
{ }
__low_power_mode_n();
if (Check_P1) {Task_p1();}
if (Check_P2) {Task_p2();} Disadvantages:
}
-Response time to event is
}
unknown
__monitor functions!!!
A. Martí Campoy Adding structure 24
Time Triggered
• In Time Triggered systems, tasks execute
periodically, e.g.:
– Checking if a peripheral is ready
– Sampling an analog signal
– Checking the output of a sensor
– Whatever you have to do, may be done
periodically (It depends on the period)
• Since there is no OS, the Super Loop is the
simplest structure
A. Martí Campoy Adding structure 25
Time Triggered: Simple delay
• Super Loop is the simplest structure
void main (void) void delay_ms(unsigned long time)
{ {
Disable_watchdog(); while (--time!=0);
Init_clk(); }
Init_peripherals();
while (1)
{ Advantages:
do_task();
delay_ms(1000);
1.- It is simple
} 2.- It works fine if you are not
} very demanding
A. Martí Campoy Adding structure 26
Time Triggered: Simple delay
• Super Loop is the simplest structure
void main (void) void delay_ms(unsigned long time)
{ {
Disable_watchdog(); while (--time!=0);
Init_clk(); }
Init_peripherals();
while (1) Disadvantages:
{
do_task();
- Delay time is imprecise. It
delay_ms(1000); depends on compiler work,
}
microcontroller architecture..
} - Waste cpu time.
- No low power
A. Martí Campoy Adding structure 27
Time Triggered: HW timer delay
• Time must be handled with accuracy
• Microcontrollers have hardware timers to
generate precisely periodic events
• Timers run in parallel with CPU
• There are several ways to use the timer to
schedule a task (function)
– Waiting the timer to finish, stopping execution in a
loop (and waste CPU time and power)
– Waiting the timer to finish, without stopping
execution (maybe doing other jobs?)
– Using interrupts
A. Martí Campoy Adding structure 28
Time Triggered: HW timer delay
• Blocking wait using a hardware delay
void main (void) void delay_ms(int time)
{ {
Disable_watchdog(); Set_timer(time);
Init_clk(); Clear_Timer_flag();
Init_peripherals(); Start_timer();
while (!Timer_flag);
while (1) Stop_timer();
{ }
do_task();
delay_ms(1000);
} Disadvantages:
- Still wasting cpu time…
}
- No low power
- Is Task executed every 1000ms?
A. Martí Campoy Adding structure 29
Time Triggered: HW timer delay
• Non blocking
void main (void) void do_task(int time)
{ {
Disable_watchdog(); Stop_timer();
Init_clk(); Set_timer(time);
Init_peripherals(); Clear_Timer_flag();
Set_timer(1000); Start_timer();
Clear_Timer_flag(); Task();
Start_timer(); }
while (1)
{
Disadvantages:
if (Timer_flag)
{ -Significant jitter in task execution
do_task(1000); -Still wasting cpu time if only one
}
task
}
} -Very bad idea to run other tasks
A. Martí Campoy Adding structure 30
Time Triggered: Schedulers
• In fact, we do not want a “delay”
• We want to run a task periodically
• That is, we want an SCHEDULER
• This scheduler may be:
– Hardware, using timers
– Software, with hardware support
• A timer will be the base “tick”
• Remember, software timers don’t work properly
A. Martí Campoy Adding structure 31
Time Triggered: HW scheduler
• MSP430 offers hardware resources to create
an scheduler
– Continuous mode and the use of several compare
registers
– Every time TAR matches a compare register, raises
an interrupt, and the ISR must do:
• Signals the activation of related task
– Task may run inside/outside the ISR
• And adds to compare register the period of task
– It’s not needed to check if new value for compare register is
over 216 . Hardware rolls over in right way.
A. Martí Campoy Adding structure 32
Time Triggered: HW scheduler
A. Martí Campoy Adding structure 33
Time Triggered: HW scheduler
• Example of code (I) (periods in Timer counts)
void main (void) #define PERIOD1 100
{ #define PERIDO2 150
Disable_watchdog(); #define PERIOD3 200
Init_clk();
Init_peripherals(); void Init_scheduler()
__enable_interrupt(); {
Init_scheduler(); Set_timer(time);
while (1) TACCR0 = PERIOD1;
{ TACCR1 = PERIOD2;
} TACCR2 = PERIOD3;
} TACCTL0 = 0; TACCTL0 |= CCIE;
TACCTL1 = 0; TACCTL1 |= CCIE;
TACCTL2 = 0; TACCTL2 |= CCIE;
TACTL |= MC_2; //CONTINUOUS
Nothing to do in main? }
A. Martí Campoy Adding structure 34
Time Triggered: HW scheduler
• Example of code (II)
//CCR0 //CCR1 & CCR2
#pragma vector = TIMERA0_VECTOR #pragma vector = TIMERA1_VECTOR
__interrupt void TimerA0_ISR () __interrupt void TimerA1_ISR ()
{ {
TACCR0 += PERIOD1; copy_of_taiv = TAIV;
Task1(); if (copy_of_taiv == TAIV_TACCR1)
} {
TACCTL1 &= ~CCIFG;
TACCR1 += PERIOD2;
Task2();
}
if (copy_of_taiv == TAIV_TACCR2)
{
TACCTL2 &= ~CCIFG;
Yes, nothing to do in TACCR2 += PERIOD3;
Task3();
main! }
}
A. Martí Campoy Adding structure 35
Time Triggered: HW scheduler
• How to add low power modes?
#define PERIOD1 100
void main (void) #define PERIDO2 150
{ #define PERIOD3 200
Disable_watchdog();
Init_clk(); void Init_scheduler()
Init_peripherals(); {
//__enable_interrupt(); Set_timer(time);
Init_scheduler(); TACCR0 = PERIOD1;
__low_power_mode_n(); TACCR1 = PERIOD2;
while (1) TACCR2 = PERIOD3;
{ TACCTL0 = 0; TACCTL0 |= CCIE;
} TACCTL1 = 0; TACCTL1 |= CCIE;
} TACCTL2 = 0; TACCTL2 |= CCIE;
TACTL |= MC_2; //CONTINUOUS
}
Only add one sentence.
//ISRs for CCR0, CCR1 & CCR2
No other modification ...
is needed. ...
A. Martí Campoy Adding structure 36
Time Triggered: HW scheduler
• How to deal with large periods?
– If periods of tasks are larger than maximum period of timer,
we need to raise several interrupts for each period of task
Periods
Timer period
Task 1
Period of
CCR0
– We can divide the period of task in 𝑛𝑛 subperiods with:
𝑃𝑃𝑃𝑃𝑃𝑃𝑃𝑃𝑃𝑃𝑃𝑃 𝑜𝑜𝑜𝑜 𝑡𝑡𝑡𝑡𝑡𝑡𝑡𝑡
𝑛𝑛 =
𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝 𝑜𝑜𝑜𝑜 𝑡𝑡𝑡𝑡𝑡𝑡𝑡𝑡𝑡𝑡
A. Martí Campoy Adding structure 37
Time Triggered: HW scheduler
• How to deal with larger periods?
Periods
Timer period
Task 1
Period of
CCR0
Given a Timer A clock frequency of 4 MHz and a period for Task 1 of
26 ms:
Maximum period of timer is 216 / 4·106 = 16,384 ms.
Period of Task 1 is larger: 26 ms/1 = 26 ms > 16,384 ms.
Trying 26 ms/2 = 13 ms < 16,384 ms → We need 2 ‘Ticks’ per period.
Period of interrupt = 13 ms → TACCR0 = 4·106 x 13·10-3 = 52000.
In every interrupt → TACCR0 += 52000;
A. Martí Campoy Adding structure 38
Time Triggered: HW scheduler
• How to deal with larger periods?
#define PERIOD1 100 //CCR1 & CCR2
#define PERIDO2 150 #pragma vector = TIMERA1_VECTOR
#define PERIOD3 200 __interrupt void TimerA1_ISR ()
{
SUB_PERIOD1 = PERIOD1/n1; copy_of_taiv = TAIV;
SUB_PERIOD2 = PERIOD1/n2; if (copy_of_taiv == TAIV_TACCR1)
SUB_PERIOD3 = PERIOD1/n3; {
TACCTL1 &= ~CCIFG;
TACCR1 += SUB_PERIOD2;
//CCR0
Task2();
#pragma vector = TIMERA0_VECTOR
}
__interrupt void TimerA0_ISR ()
if (copy_of_taiv == TAIV_TACCR2)
{
{
TACCR0 += SUB_PERIOD1;
TACCTL2 &= ~CCIFG;
Task1();
TACCR2 += SUB_PERIOD3;
}
Task3();
}
void main (void) }
{ ... }
A. Martí Campoy Adding structure 39
Time Triggered: HW scheduler
• How to deal with larger periods?
#define PERIOD1 100 //CCR1 & CCR2
#define PERIDO2 150 #pragma vector = TIMERA1_VECTOR
#define PERIOD3 200 __interrupt void TimerA1_ISR ()
{
SUB_PERIOD1 = PERIOD1/n1; copy_of_taiv = TAIV;
SUB_PERIOD2 = PERIOD1/n2; if (copy_of_taiv == TAIV_TACCR1)
SUB_PERIOD3 = PERIOD1/n3; {
TACCTL1 &= ~CCIFG;
static uint8_t c1, c2, c3 = 0; TACCR1 += SUB_PERIOD2;
c2++;
//CCR0
if (c2 == n2){ c2=0; Task2();}
#pragma vector = TIMERA0_VECTOR
}
__interrupt void TimerA0_ISR ()
if (copy_of_taiv == TAIV_TACCR2)
{
{
TACCR0 += SUB_PERIOD1;
TACCTL2 &= ~CCIFG;
c1++;
TACCR2 += SUB_PERIOD3;
if (c1 == n1){ c1=0; Task1();}
c3++;
}
if (c3 == n3){ c3=0; Task3();}
void main (void) }
{ ... } }
A. Martí Campoy Adding structure 40
Time Triggered: HW scheduler
• How to move execution of tasks to main loop?
#define PERIOD1 100 //CCR1 & CCR2
#define PERIDO2 150 #pragma vector = TIMERA1_VECTOR
#define PERIOD3 200 __interrupt void TimerA1_ISR ()
{
SUB_PERIOD1 = PERIOD1/n1; copy_of_taiv = TAIV;
SUB_PERIOD2 = PERIOD1/n2; if (copy_of_taiv == TAIV_TACCR1)
SUB_PERIOD3 = PERIOD1/n3; {
TACCTL1 &= ~CCIFG;
static uint8_t c1, c2, c3 = 0; TACCR1 += SUB_PERIOD2;
c2++;
//CCR0
if (c2 == n2){ c2=0; Task2();}
#pragma vector = TIMERA0_VECTOR
}
__interrupt void TimerA0_ISR ()
if (copy_of_taiv == TAIV_TACCR2)
{
{
TACCR0 += SUB_PERIOD1;
TACCTL2 &= ~CCIFG;
c1++;
TACCR2 += SUB_PERIOD3;
if (c1 == n1){ c1=0; Task1();}
c3++;
} Move to if (c3 == n3){ c3=0; Task3();}
main }
void main (void)
{ ... } }
A. Martí Campoy Adding structure 41
Time Triggered: HW scheduler
• How to move execution of tasks to main loop?
//CCR0 interrupt
... Mandatory!!!
TACCR0 += SUB_PERIOD1;
c1++; //CCR1 & CCR2 interrupt
__low_power_mode_off_on_exit(); ...
copy_of_taiv = TAIV;
void main (void) if (copy_of_taiv == TAIV_TACCR1)
{ {
Disable_watchdog(); TACCTL1 &= ~CCIFG;
Init_clk(); TACCR1 += SUB_PERIOD2;
Init_peripherals(); c2++;
//__low_power_mode_n(); }
Init_scheduler(); if (copy_of_taiv == TAIV_TACCR2)
while (1) {
{ TACCTL2 &= ~CCIFG;
__low_power_mode_n(); TACCR2 += SUB_PERIOD3;
if (Check_c1()){Task1();} c3++;
if (Check_c2()){Task2();} }
if (Check_c3()){Task3();} __low_power_mode_off_on_exit();
}}
__monitor functions!!! A. Martí Campoy Adding structure 42
Time Triggered: HW scheduler
• How to move execution of tasks to main loop?
__monitor uint8_T Check_c1 (void)
{
uint8_t copy; Why use >= instead ==?
copy = 0;
if (c1 >= n1)
{ Try to correct delayed
c1 = 0; /*c1=c1–n1;*/ activations. But be
copy = 1; careful. What happens if
}
return (copy); c1 > 2*n1?
}
The function has a
unique return!
A. Martí Campoy Adding structure 43
Time Triggered: HW scheduler
• Hardware scheduler
– It is the best option
• It is easy to program
• Timing is accurate
• It easily supports low-power mode
• Hardware always works properly
A. Martí Campoy Adding structure 44
Time Triggered: HW scheduler
• Exercise
– Given:
• Timer clock frequency: 3.2 MHz.
• Timer counter size: 16 bits.
• Task 1 period: 10 ms.
• Task 2 period: 26 ms.
• Task 3 period: 57 ms.
– Calculate:
• Maximum period of timer.
• Sub_periods for T1, T2 and T3.
• Values for TACCR0, TACCR1 and TACCR2.
A. Martí Campoy Adding structure 45
Time Triggered: Software scheduler
• If your µC has not continuous mode or similar,
you can create a software scheduler
• Software schedulers are based on:
– Hardware timers and Interrupts
• We use up mode
– In other architectures it works in “down” mode
– A unique fixed-period signal is created
• It is very useful for systems with a unique task
A. Martí Campoy Adding structure 46
Time Triggered: Software scheduler
• Non blocking (one task)
void main (void) #pragma vector=TIMER_VECTOR
{ __interrupt void Timer_ISR (void)
Disable_watchdog(); {
Init_clk(); Clear_Interrupt_Flag();
Init_peripherals(); Task();
Set_timer(1000); }
//enable_interrupt();
Start_timer(); Advantages:
__low_power_mode_n(); - It’s easy
while (1)
{ -The frequency is exact
//__low_power_mode_n(); -Low Power modes can be used
} -In MSP430, when ISR ends, returns
}
to previous power mode, so it is no
needed in main loop
A. Martí Campoy Adding structure 47
Time Triggered: Software scheduler
• Non blocking (one task)
void main (void) #pragma vector=TIMER_VECTOR
{ __interrupt void Timer_ISR (void)
Disable_watchdog(); {
Init_clk(); Clear_Interrupt_Flag();
Init_peripherals(); Task();
Set_timer(1000); }
//enable_interrupt();
Start_timer();
__low_power_mode_n();
while (1)
Disadvantages:
{}
} -Be careful with overrun
-Overhead when calling task. Inline?
A. Martí Campoy Adding structure 48
Time Triggered: Software scheduler
• Run task from Super Loop (one task)
extern uint16_t Now; //global #pragma vector=TIMER_VECTOR
__interrupt void Timer_ISR (void)
void main (void) {
{ Clear_Interrupt_Flag();
Disable_watchdog(); Now = 1;
Init_clk(); __low_power_mode_off_on_exit();
Init_peripherals(); }
Set_timer(1000);
//enable_interrupt();
Start_timer();
Now = 0;
Disadvantages:
while (1) - Clearing flag “Now” needs to
{ be an atomic operation?
__low_power_mode_n();
if (Check_Now()) {Task();}
-Check_Now() is a monitor
} function!!!
}
A. Martí Campoy Adding structure 49
Time Triggered: Software scheduler
• It is easy to deal with large periods
extern uint16_t Now; //Global #pragma vector=TIMER_VECTOR
#define COUNT 100 __interrupt void Timer_ISR (void)
void main (void) {
{ Clear_Interrupt_Flag();
Disable_watchdog(); Now++;
Init_clk(); __low_power_mode_off_on_exit();
Init_peripherals(); }
Set_timer(1000);
//enable_interrupt(); Advantages:
Start_timer();
Now = 0;
-It is the best way if timer
while (1) frequency is too high vs Task
{ period
__low_power_mode_n();
if (Check_Now()) __monitor if (Now >= count)…
{Task();}
} }
A. Martí Campoy Adding structure 50
Time Triggered: Software scheduler
• If there are several tasks with different periods:
– If there is a “greatest common divisor” (GCD) for all task’s
periods, use a fixed frequency interrupt timer
Periods
T1
T2
Timer
– The main advantage is its easiness of programming
– The main disadvantage is that may be interrupt activations
when there is nothing to do (no task scheduled)
– This disadvantage is more important when using
“low_power modes” A. Martí Campoy Adding structure 51
Time Triggered: Software scheduler
• If there are several tasks with different periods:
– If there is a “greatest common divisor” (GCD) for all task’s
periods, use a fixed frequency interrupt timer
Periods
T1
T2
Timer
Some tasks should wait Hyperperiod
the others to end.
Use of priorities?
A. Martí Campoy Adding structure 52
Time Triggered: Software scheduler
• If there are several tasks with different periods:
– If there is a “greatest common divisor” (GCD) for all task’s
periods, use a fixed frequency interrupt timer
– Exercise: calculate the interrupt frequency for the
following set of tasks:
• Period Task1: 100ms
• Period Task2: 150ms
• Period Task3: 250ms
• Period Task4: 400ms
A. Martí Campoy Adding structure 53
Time Triggered: Software scheduler
• Example of code (periods and GCD in Timer counts)
#define PERIOD1 100 #pragma vector=TIMER_VECTOR
#define PERIDO2 150 __interrupt void Timer_ISR (void)
#define PERIOD3 xxx {
Clear_Interrupt_Flag();
#define GCD zzz
Now_1 += GCD;
uint16_t Now1, Now2, Now3 = 0;//Global? Now_2 += GCD;
Now_3 += GCD;
void main (void) }
{
Set_timer_and_run (GCD);
while (1)
{ - Jitter may be significant
if (Check_Now_1()) {Task1();}
if (Check_Now_2()) {Task2();}
- There are no priorities
if (Check_Now_3()) {Task3();}
} }
__monitor Check_Now_x ()
if (Now_x >= Period_x)…
A. Martí Campoy Adding structure 54
Time Triggered: Software scheduler
• It is possible to add priorities
#define PERIOD1 100 __monitor uint8_t Check_run()
#define PERIDO2 150
#define PERIOD3 xxx {
#define GCD zzz
uint8_t task_number;
uint16_t Now1, Now2, Now3 = 0;
//Global?
if (Now1 >= PERIOD1 )
void main (void) {Now1 = 0; task_number = 1}
{ else if (Now2 >= PERIOD2 )
uint8_t task_to_run = 0;
{Now2 = 0; task_number = 2}
Set_timer_and_run (GCD_ms);
while (1)
...
{ ...
task_to_run = Check_run(); else if (Now3 >= PERIODN )
if (task_to_run == 1) {Task1();} {Now3 = 0; task_number = 3}
if (task_to_run == 2) {Task2();}
if (task_to_run == 3) {Task3();} return (task_number);
} }
}
A. Martí Campoy Adding structure 55
Time Triggered: Software scheduler
• If there are several tasks with different periods:
– If there is no GCD for all periods (or it’s very small), you
need to reprogram the timer to generate an interrupt in
the next task activation
Periods
T1
T2
Timer
– It’s a bit harder to program, but more flexible
– Is very easy to use and benefits from “Low Power Modes”
A. Martí Campoy Adding structure 56
Time Triggered: Software scheduler
• If there are several tasks with different periods:
– If there is no GCD for all periods (or it’s very small), you
need to reprogram the timer to generate an interrupt in
the next task activation
– Exercise: calculate the interrupt frequency for the
following set of tasks:
• Period Task1: 100ms
• Period Task2: 115ms
• Period Task3: 200ms
• Period Task4: 410ms
A. Martí Campoy Adding structure 57
Time Triggered: Software scheduler
• Example of code (part 1)
#define P1 100
#define P2 112 #pragma vector=TIMER_VECTOR
#define P3 xxx __interrupt void Timer_ISR (void)
uint32_t Na1, Na2, Na3 = 0; {
uint8_t To_run1, To_run2, To_run3 = 0; Clear_Interrupt_Flag();
uint32_t Now=0; Run = 1;
void main (void) }
{
HP=Calculate_hyperperiod(P1,P2,…P3)
Run = 1;
while (1) {
if (Check_Run()) //monitor function
{ if (Now==Na1) {Na1+=P1;To_run1=1;}
if (Now==Na2) {Na2+=P2;To_run2=1;}
if (Now==Na3) {Na3+=Pn;To_run3=1;}
set_timer_next();
If (To_run1){To_run1=0;Task1();}
If (To_run2){To_run2=0;Task2();}
If (To_run3){To_runN=0;Task3();}
}} }
A. Martí Campoy Adding structure 58
Time Triggered: Software scheduler
• Example of code (part 2)
void set_timer_next (void)
{
Next = HP; //in fact, max timer value
if ( (Na1–Now) > 0) Next= Na1–Now;
if ( (Next>(Na2–Now) ) && ((Na2–Now)>0) Next= Na2-Now;
if ( (Next>(Na3–Now) ) && ((Na3–Now)>0) Next= Na3-Now;
Program_timer (Next); //Write new timer value
if (Na1 == HP) Na1=0;
if (Na2 == HP) Na2=0;
if (Na3 == HP) Na3=0;
Now+=Next;
if (Now==HP) Now = 0;
}
A. Martí Campoy Adding structure 59
Time Triggered: Software scheduler
• Previous code
– Has small jitter
– Allow to use other interrupts (carefully)
– Set task priorities
– Its needed to reset time counters when reach hyperperiod
– Variables has to be able to count up to hyperperiod value
– If time between two consecutive activations is longer than
one simple timer overflow, ISR has to deal with this
problem
• But:
– If some code is added to the SuperLoop, reprogramming
the timer may be delayed
A. Martí Campoy Adding structure 60
Time Triggered: Software scheduler
• Example of code (part 1) #pragma vector=TIMER_VECTOR
#define P1 100 __interrupt void Timer_ISR(void)
#define P2 112
{
#define P3 xxx
uint32_t Na1, Na2, Na3 = 0; Clear_Interrupt_Flag();
uint8_t To_run1, To_run2, To_run3 = 0; if (Now==Na1)
long Now=0; {Na1+=P1;To_run1=1;}
void main (void) if (Now==Na2)
{ {Na2+=P2;To_run2=1;}
HP=Calculate_hyperperiod(P1,P2,…P3) if (Now==Na3)
Program_timer (0); // {Na3+=P3;To_run3=1;}
while (1) {
if (Check_Run()) //monitor function
{ If (Check_To_run_1()){Task1();} set_timer_next();
If (Check_To_run_2()){Task2();}
If (Check_To_run_3()){Task3();} Run = 1;
}} } }
__monitor Check_To_run_x ()
if (To_run_x == 1)…
A. Martí Campoy Adding structure 61
Time Triggered: Software scheduler
• Example of code (part 2)
void set_timer_next (void)
{
Next = HP; //in fact, max timer value
if ( (Na1–Now) > 0) Next= Na1–Now;
if ( (Next>(Na2–Now) ) && ((Na2–Now)>0) Next= Na2-Now;
if ( (Next>(Na3–Now) ) && ((Na3–Now)>0) Next= Na3-Now;
Program_timer (Next); //Write new timer value
if (Na1 == HP) Na1=0;
if (Na2 == HP) Na2=0;
if (Na3 == HP) Na3=0;
Now+=Next;
if (Now==HP) Now = 0;
}
A. Martí Campoy Adding structure 62
Real-time schedulers
• A Real-Time system is a system where tasks must:
– Do its work properly
– Do its work within a deadline
• Most embedded systems are Real-Time Systems
• There are several kinds of RTS’s
– Hard or Soft real-time systems
– Non Pre-emptive or Pre-emptive scheduled
– Pre-emptive scheduled
• Fixed priorities (Rate monotonic, Deadline monotonic)
• Earliest deadline first
A. Martí Campoy Adding structure 63
Real-time schedulers
• The core of a Real-Time application is the Scheduler
• It decides which task, and when, enters execution
• In most of cases, a RTOS is needed to meet
requirements
• Finally, schedulability analysis may be accomplished
by means of:
– Simulation
– Measurement
– Analysis
A. Martí Campoy Adding structure 64
Adding structure to C
for
Embedded Systems
Antonio Martí Campoy
[email protected]