Upload
corrado-santoro
View
461
Download
0
Embed Size (px)
Citation preview
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
L.A.P. 1 Course
Corrado Santoro The FSM Design Pattern
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
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
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
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
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
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
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
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
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
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
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
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
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
The FSM of our “keyboard”
Corrado Santoro The FSM Design Pattern
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
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
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
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
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
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
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
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
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
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
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
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
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
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
L.A.P. 1 Course
Corrado Santoro The FSM Design Pattern