0% found this document useful (0 votes)
34 views

Adding Structure

The document discusses event-driven and time-triggered systems for embedded applications. It describes the basic structure of event-driven systems using interrupts and polling, and ways to address issues like switch bouncing. Time-triggered systems using a hardware timer are also introduced.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

Adding Structure

The document discusses event-driven and time-triggered systems for embedded applications. It describes the basic structure of event-driven systems using interrupts and polling, and ways to address issues like switch bouncing. Time-triggered systems using a hardware timer are also introduced.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 65

Adding structure to C

for

Embedded Systems
Antonio Martí Campoy

amarti@disca.upv.es
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

amarti@disca.upv.es

You might also like