29
Handling Asynchronous Events in MCUs The Finite-State Machine Design Pattern Corrado Santoro ARSLAB - Autonomous and Robotic Systems Laboratory Dipartimento di Matematica e Informatica - Universit` a di Catania, Italy [email protected] L.A.P. 1 Course Corrado Santoro The FSM Design Pattern

Handling Asynchronous Events in MCUs

Embed Size (px)

Citation preview

Page 1: Handling Asynchronous Events in MCUs

Handling Asynchronous Events in MCUs

The Finite-State Machine Design Pattern

Corrado Santoro

ARSLAB - Autonomous and Robotic Systems LaboratoryDipartimento di Matematica e Informatica - Universita di Catania, Italy

[email protected]

L.A.P. 1 Course

Corrado Santoro The FSM Design Pattern

Page 2: Handling Asynchronous Events in MCUs

The “toggle” Example

We want to toggle LED in RB0 each time the button in RA3 is

pressed.

Electrical considerations:

the logic of the LED is inverted

LATBbits.LATB0 = 1; implies LED off;

LATBbits.LATB0 = 0; implies LED on;

the logic of the pushbutton is also inverted

Button pressed implies PORTAbits.RA3 == 0;

Button NOT pressed implies PORTAbits.RA3 == 1;

Corrado Santoro The FSM Design Pattern

Page 3: Handling Asynchronous Events in MCUs

The “toggle” example

The listing✞main()

{

TRISAbits.TRISA3 = 1; // RA3 as input

TRISBbits.TRISB0 = 0; // RB0 as output

LATBbits.LATB0 = 1; // turn led off initially

for (;;) { // loop forever

if (PORTAbits.RA3 == 0)

LATBbits.LATB0 = !LATBbits.LATB0;

}

}

✡✝ ✆

This code won’t work: the loop is executed a very high speed,

so the “if” is true many times and the LED toggles continuously!

Corrado Santoro The FSM Design Pattern

Page 4: Handling Asynchronous Events in MCUs

Let’s analyse the signal...

Pressing/releasing a button implies the following timing:

We have to identify the PRESSED event, i.e.a transition from

high-to-low.

Solution 1

Identify that the button is pressed (check that RA3 is “0”)

Do the proper actions (i.e. toggle the LED)

wait for button release before continuing (ensure that

RA3 is “1”)

Corrado Santoro The FSM Design Pattern

Page 5: Handling Asynchronous Events in MCUs

Let’s analyse the signal...

Solution 1

Identify that the button is pressed (check that RA3 is “0”)

Do the proper actions (i.e. toggle the LED)

wait for button release before continuing (ensure that RA3 is “1”)

✞main()

{

TRISAbits.TRISA3 = 1; // RA3 as input

TRISBbits.TRISB0 = 0; // RB0 as output

LATBbits.LATB0 = 1; // turn led off initially

for (;;) { // loop forever

if (PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

while (PORTAbits.RA3 == 0) {} // busy-wait for release

}

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 6: Handling Asynchronous Events in MCUs

The “toggle” Example v2

We want to:

Toggle LED in RB0 each time the button in RA3 is pressed.

Toggle LED in RB1 each time the button in RA2 is pressed.

The solution is quite simple now: let’s duplicate the code!

Corrado Santoro The FSM Design Pattern

Page 7: Handling Asynchronous Events in MCUs

The “toggle” Example v2✞main()

{

TRISAbits.TRISA3 = 1; // RA3 as input

TRISAbits.TRISA2 = 1; // RA2 as input

TRISBbits.TRISB0 = 0; // RB0 as output

TRISBbits.TRISB1 = 0; // RB1 as output

LATBbits.LATB0 = 1; // turn led off initially

LATBbits.LATB1 = 1; // turn led off initially

for (;;) { // loop forever

if (PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

while (PORTAbits.RA3 == 0) {} // busy-wait for release

}

else if (PORTAbits.RA2 == 0) {

LATBbits.LATB1 = !LATBbits.LATB1; // toggle the led

while (PORTAbits.RA2 == 0) {} // busy-wait for release

}

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 8: Handling Asynchronous Events in MCUs

The “toggle” Example v3

We want to:

Toggle LED in RB0 each time the button in RA3 is pressed.

Toggle LED in RB1 each time the button in RA2 is pressed.

...meanwhile, we want to make LED in RB2 flash at a

certain frequency!!

To do this, we should employ a hardware or software timer, but

it won’t work since we have busy-waits!!!

Indeed, during busy-waits, the program cannot do anything

else: I could leave my finger on the button for a very long

time!!!! :-D

Corrado Santoro The FSM Design Pattern

Page 9: Handling Asynchronous Events in MCUs

The non-working “toggle” Example v3

✞main()

{

long count = 0; // 32 bit is better!

// ... set-up ports by TRIS registers

for (;;) { // loop forever

if (PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

while (PORTAbits.RA3 == 0) {} // busy-wait for release

}

else if (PORTAbits.RA2 == 0) {

LATBbits.LATB1 = !LATBbits.LATB1; // toggle the led

while (PORTAbits.RA2 == 0) {} // busy-wait for release

}

++count;

if (count == MAX) {

LATBbits.LATB2 = !LATBbits.LATB2; // toggle the led

count = 0;

}

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 10: Handling Asynchronous Events in MCUs

No Busy-Waits!!

In the previous solution the variable count won’t be

incremented during busy-waits, so the flashing frequency is not

guaranteed!

Solution 2: Let’s encode a STATE

Use a variable to represent the button state you wish to

identify, e.g. CHECK FOR PRESS,

CHECK FOR RELEASE

Check the button’s state according to the state variable

Update the state variable

Corrado Santoro The FSM Design Pattern

Page 11: Handling Asynchronous Events in MCUs

No Busy-Waits✞#define CHECK_FOR_PRESS ...

#define CHECK_FOR_RELEASE ...

main()

{

int state = CHECK_FOR_PRESS;

// ... set-up ports by TRIS registers

for (;;) { // loop forever

...

if (state == CHECK_FOR_PRESS && PORTAbits.RA3 == 0) {

// OK!! We have got button press

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

// now we have to check only released

state = CHECK_FOR_RELEASE;

}

else if (state == CHECK_FOR_RELEASE && PORTAbits.RA3 == 1) {

// OK!! We have got button release

// let us update the state

state = CHECK_FOR_PRESS;

}

...

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 12: Handling Asynchronous Events in MCUs

No Busy-Waits with two buttons

✞#define CHECK_FOR_PRESS ...

#define CHECK_FOR_RELEASE ...

main()

{

int state1 = CHECK_FOR_PRESS, state2 = CHECK_FOR_PRESS;

// ... set-up ports by TRIS registers

for (;;) { // loop forever

...

if (state1 == CHECK_FOR_PRESS && PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

state1 = CHECK_FOR_RELEASE;

}

else if (state1 == CHECK_FOR_RELEASE && PORTAbits.RA3 == 1) {

state1 = CHECK_FOR_PRESS;

}

if (state2 == CHECK_FOR_PRESS && PORTAbits.RA2 == 0) {

LATBbits.LATB1 = !LATBbits.LATB1; // toggle the led

state2 = CHECK_FOR_RELEASE;

}

else if (state2 == CHECK_FOR_RELEASE && PORTAbits.RA2 == 1) {

state2 = CHECK_FOR_PRESS;

}

...

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 13: Handling Asynchronous Events in MCUs

No Busy-Waits with two buttons and flash✞#define CHECK_FOR_PRESS ...

#define CHECK_FOR_RELEASE ...

main()

{

int state1 = CHECK_FOR_PRESS, state2 = CHECK_FOR_PRESS;

long count = 0; // 32 bit is better!

// ... set-up ports by TRIS registers

for (;;) { // loop forever

if (state1 == CHECK_FOR_PRESS && PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0; // toggle the led

state1 = CHECK_FOR_RELEASE;

}

else if (state1 == CHECK_FOR_RELEASE && PORTAbits.RA3 == 1) {

state1 = CHECK_FOR_PRESS;

}

if (state2 == CHECK_FOR_PRESS && PORTAbits.RA2 == 0) {

LATBbits.LATB1 = !LATBbits.LATB1; // toggle the led

state2 = CHECK_FOR_RELEASE;

}

else if (state2 == CHECK_FOR_RELEASE && PORTAbits.RA2 == 1) {

state2 = CHECK_FOR_PRESS;

}

++count;

if (count == MAX) {

LATBbits.LATB2 = !LATBbits.LATB2; // toggle the led

count = 0;

}

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 14: Handling Asynchronous Events in MCUs

The Finite-State Machine Abstraction

Systems which behave by responding to events which cause actionsare usually programmed using the finite-state machine (FSM)

abstraction/design pattern.

A finite-state machine can be formulated as

A set of states S;

A set of events E ;

A set of action A;

A function f (s, e) → (a, s′) which, given the current state s ∈ S

and the event occurred e ∈ E , gives the action a ∈ A to be

performed and the new state s′∈ S.

A FSM is usually represented graphically by means of a directed

graph in which:

Vertices are states

Edges are state transitions

Corrado Santoro The FSM Design Pattern

Page 15: Handling Asynchronous Events in MCUs

The FSM of our “keyboard”

Corrado Santoro The FSM Design Pattern

Page 16: Handling Asynchronous Events in MCUs

The FSM in action

✞#define IDLE ...

#define KEY_A ...

#define KEY_B ...

#define KEY_C ...

main()

{

int state = IDLE;

// ... set-ups

for (;;) { // loop forever

switch (state) {

case IDLE:

// check events related to IDLE state and change state

break;

case KEY_A:

// check events related to KEY_A state and change state

break;

case KEY_B:

// check events related to KEY_B state and change state

break;

case KEY_C:

// check events related to KEY_C state and change state

break;

}

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 17: Handling Asynchronous Events in MCUs

The FSM in action (1)

✞main()

{

int state = IDLE;

// ... set-ups

for (;;) { // loop forever

switch (state) {

case IDLE:

if (PORTAbits.RA3 == 0) {

LATBbits.LATB0 = !LATBbits.LATB0;

state = KEY_A;

}

else if (PORTAbits.RA2 == 0) {

LATBbits.LATB1 = !LATBbits.LATB1;

state = KEY_B;

}

else if (PORTAbits.RC2 == 0) {

// ....

state = KEY_C;

}

break;

...

}

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 18: Handling Asynchronous Events in MCUs

The FSM in action (2)

✞main()

{

int state = IDLE;

// ... set-ups

for (;;) { // loop forever

switch (state) {

...

case KEY_A:

if (PORTAbits.RA3 == 1) {

state = IDLE;

}

break;

case KEY_B:

if (PORTAbits.RA2 == 1) {

state = IDLE;

}

break;

...

}

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 19: Handling Asynchronous Events in MCUs

The FSM in action (3)

✞main()

{

int state = IDLE;

// ... set-ups

for (;;) { // loop forever

switch (state) {

...

case KEY_C:

if (PORTAbits.RC2 == 1) {

state = IDLE;

}

break;

}

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 20: Handling Asynchronous Events in MCUs

Exercise!!

Toggle LED in RB0 each time the button in RA3 is pressed.

Toggle LED in RB1 each time the button in RA2 is pressed.

Cycle LEDs from RB2 to RB5 each time button RC2 is

pressed.

Turn off all leds after about 4 seconds of inactivity.

Corrado Santoro The FSM Design Pattern

Page 21: Handling Asynchronous Events in MCUs

Execise, a top-down approach

✞main()

{

init_ports();

init_soft_timer();

for (;;) {

switch(get_key_event()) {

case EV_KEY_A: toggle_led(0); clear_timer(); break;

case EV_KEY_B: toggle_led(1); clear_timer(); break;

case EV_KEY_C: cycle_leds(); clear_timer(); break;

}

if (timer_elapsed()) {

all_leds_off();

clear_timer();

}

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 22: Handling Asynchronous Events in MCUs

Execise, ports and leds

✞void init_ports(void)

{

TRISAbits.TRISA3 = 1;

TRISAbits.TRISA2 = 1;

TRISCbits.TRISC2 = 1;

TRISB = 0xc0; // B0-->B5 = outputs, B6, B7 = inputs

LATB = 0xff; // all leds off

}

void toggle_led(int led)

{

if (led == 0) LATBbits.LATB0 = !LATBbits.LATB0;

else if (led == 1) LATBbits.LATB1 = !LATBbits.LATB1;

}

void all_leds_off(void)

{

LATB = 0xff;

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 23: Handling Asynchronous Events in MCUs

Execise, ports and leds✞int cycle = 0;

void cycle_leds(void)

{

if (cycle == 0) {

LATBbits.LATB2 = 0;

LATBbits.LATB3 = 1;

LATBbits.LATB4 = 1;

LATBbits.LATB5 = 1;

cycle = 1;

}

else if (cycle == 1) {

LATBbits.LATB2 = 1;

LATBbits.LATB3 = 0;

LATBbits.LATB4 = 1;

LATBbits.LATB5 = 1;

cycle = 2;

}

else if (cycle == 2) {

LATBbits.LATB2 = 1;

LATBbits.LATB3 = 1;

LATBbits.LATB4 = 0;

LATBbits.LATB5 = 1;

cycle = 3;

}

else if (cycle == 3) {

LATBbits.LATB2 = 1;

LATBbits.LATB3 = 1;

LATBbits.LATB4 = 1;

LATBbits.LATB5 = 0;

cycle = 0;

}

}

✡✝ ✆Corrado Santoro The FSM Design Pattern

Page 24: Handling Asynchronous Events in MCUs

Execise, ports and leds

✞int cycle = 0;

void cycle_leds(void)

{

LATB = (LATB & 0xc3) | (˜(0x4 << cycle) & 0x3c);

cycle = (cycle + 1) % 4;

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 25: Handling Asynchronous Events in MCUs

Execise, key events

✞// events

#define EV_NONE ....

#define EV_KEY_A ....

#define EV_KEY_B ....

#define EV_KEY_C ....

// states

#define IDLE ...

#define KEY_A ...

#define KEY_B ...

#define KEY_C ...

int key_state = IDLE;

int get_key_event(void)

{

switch (key_state) {

case KEY_A:

...

break;

....

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 26: Handling Asynchronous Events in MCUs

Execise, key events

✞int get_key_event(void)

{

switch (key_state) {

case IDLE:

if (PORTAbits.RA3 == 0) {

key_state = KEY_A;

return EV_KEY_A;

}

else if (PORTAbits.RA2 == 0) {

key_state = KEY_B;

return EV_KEY_B;

}

else if (PORTAbits.RC2 == 0) {

key_state = KEY_C;

return EV_KEY_C;

}

else

return EV_NONE;

break;

...

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 27: Handling Asynchronous Events in MCUs

Execise, key events

✞int get_key_event(void)

{

switch (key_state) {

....

case KEY_A:

if (PORTAbits.RA3 == 1) {

key_state = IDLE;

}

return EV_NONE;

break;

case KEY_B:

if (PORTAbits.RA2 == 1) {

key_state = IDLE;

}

return EV_NONE;

break;

case KEY_C:

if (PORTAbits.RC2 == 1) {

key_state = IDLE;

}

return EV_NONE;

break;

}

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 28: Handling Asynchronous Events in MCUs

Execise, soft timer

✞long soft_timer = 0;

void init_soft_timer(void)

{

soft_timer = 0;

}

void clear_timer(void)

{

soft_timer = 0;

}

int timer_elapsed(void)

{

++soft_timer;

if (soft_timer > MAX_TIMER)

return 1;

else

return 0;

}

✡✝ ✆

Corrado Santoro The FSM Design Pattern

Page 29: Handling Asynchronous Events in MCUs

Handling Asynchronous Events in MCUs

The Finite-State Machine Design Pattern

Corrado Santoro

ARSLAB - Autonomous and Robotic Systems LaboratoryDipartimento di Matematica e Informatica - Universita di Catania, Italy

[email protected]

L.A.P. 1 Course

Corrado Santoro The FSM Design Pattern