Dies ist ein Arduino Testprogramm für die Menuführung mit State Machine und Multifunktionstasten, typischerweise auf einem CD Display und 3 Tasten. Die Code entprellt die Tasteneingaben und kann zwischen kurzen und langen Tastendrücken unterschieden. Ein langer Druck löst eine Wiederholung aus (z.B. für das Hochzählen eines Wertes). Vorausgesetzt wird die die Library für die Tastenentprellung Keys, siehe die beiden Artikel in diesem Blog zum Thema Tastenentprellung.
Für die prof. Erstellung von State Machines sind andere Werkzeuge nötig (siehe Links am Schluss).
FSM-1.c
#include "FSM-1.h" #include "Keys.h" // LCD module connections sbit LCD_RS at PORTD2_bit; sbit LCD_EN at PORTD3_bit; sbit LCD_D4 at PORTD4_bit; sbit LCD_D5 at PORTD5_bit; sbit LCD_D6 at PORTD6_bit; sbit LCD_D7 at PORTD7_bit; sbit LCD_RS_Direction at DDD2_bit; sbit LCD_EN_Direction at DDD3_bit; sbit LCD_D4_Direction at DDD4_bit; sbit LCD_D5_Direction at DDD5_bit; sbit LCD_D6_Direction at DDD6_bit; sbit LCD_D7_Direction at DDD7_bit; // --------------------------- LCD end ------------------------------
// alle States typedef enum {stSTART, stSETUP, stMAIN, stSHORT, stOFF, stLOWIN, stMENUINFO} StateType; // Zeigertabelle für die Updatefunktionen void (*StateAction[])() = {fSTART, fSETUP, fMAIN, fSHORT, fOFF, fLOWIN, fMENUINFO}; // Zeigertabelle für die On Entry Funktionen void (*StateEntry[])() = {fSTART_ENTRY, fSETUP_ENTRY, fMAIN_ENTRY, fGEN_ENTRY, fGEN_ENTRY, fGEN_ENTRY, fMENUINFO_ENTRY}; // Zeigertabelle für die On Exit Funktionen void (*StateExit[])() = {fGEN_EXIT, fGEN_EXIT, fGEN_EXIT, fGEN_EXIT, fGEN_EXIT, fGEN_EXIT, fMENUINFO_EXIT}; // Tabelle der Statusnamem zum Drucken/Debuggen char *StateNames[] = {"START", "SETUP", "MAIN", "SHORT", "OFF", "LOWIN", "MENUINFO"}; StateType lastState, currState, nextState; char txt1[] = "LCD State Machine"; char txt2[] = "Andreas Bieri"; char level = 'a';
void setup() // keine State Funktion { // Keys KEY_DDR &= ~ALL_KEYS; // konfigure key port for input KEY_PORT |= ALL_KEYS; // and turn on pull up resistors TCCR0 = (1<<CS02)|(1<<CS00); // divide by 1024 TIMSK = 1<<TOIE0; // enable timer interrupt SEI; // LCD Lcd_Init(); // Initialize LCD }
void fNOOP() { }
void fGEN_ENTRY() { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1,1, "ENTRY"); Lcd_Out(1,7,StateNames[currState]); Delay_ms(1000); // One second pause reset_all_keys(); // ignore all pressed keys } void fGEN_EXIT() { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1,1, "EXIT"); Lcd_Out(1,7,StateNames[currState]); Delay_ms(1000); // One second pause }
void fSTART_ENTRY() { Lcd_Cmd(_LCD_CLEAR); // Clear display Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off Lcd_Out(1,1,txt1); // Write text in first row Lcd_Out(2,1,txt2); // Write text in second row Delay_ms(2000); } void fSTART() { nextState = stMAIN; Delay_ms(1000); // One second pause }
void fMAIN_ENTRY() { Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_BLINK_CURSOR_ON); // Cursor on Lcd_Out(1,1," Hauptloop"); Lcd_Out(2,1," Menu: PB1 lang"); Delay_ms(1000); // One second pause reset_all_keys(); // ignore all pressed keys } void fMAIN() { // Main Key handler if( get_key_long (1<<KEY1 )) // langer Tastendruck für Menu nextState = stMENUINFO; }
void fSETUP_ENTRY() { Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_BLINK_CURSOR_ON); Lcd_Out(1,1,"Setup. +PB2 -PB0"); Lcd_Out(2,1,"Info:PB1 Char: "); Delay_ms(1000); // One second pause reset_all_keys(); // ignore all pressed keys } void fSETUP() { int update = 0; if( get_key_short( 1<<KEY1 )) // kurzer Tastendruck nextState = stMENUINFO; if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )) // kurzer oder repeat { update = 1; level++; } if( get_key_press( 1<<KEY0 ) || get_key_rpt( 1<<KEY0 )) // kurzer oder repeat { update = 1; level--; } if (update > 0) { Lcd_Cmd(_LCD_MOVE_CURSOR_LEFT); Lcd_Chr_CP(level); } }
void fSHORT() { Delay_ms(1000); // One second pause nextState = stOFF; }
void fOFF() { Delay_ms(1000); // One second pause nextState = stLOWIN; }
void fLOWIN() { Delay_ms(1000); // One second pause nextState = stMENUINFO; }
void fMENUINFO_ENTRY() { Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_BLINK_CURSOR_ON); Lcd_Out(1,1,"Info. Setup: PB1S"); Lcd_Out(2,1,"Main: PB0S"); Delay_ms(1000); // One second pause reset_all_keys(); // ignore all pressed keys } void fMENUINFO() { if( get_key_short( 1<<KEY0 )) // kurzer Tastendruck nextState = stMAIN; if( get_key_short( 1<<KEY1 )) // kurzer Tastendruck nextState = stSETUP; } void fMENUINFO_EXIT() { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1,1,StateNames[currState]); Lcd_Out(2,1, "EXIT"); Delay_ms(1000); // One second pause }
// -------------------------------------------------------------------------- void timerinterrupt( void ) org IVT_ADDR_TIMER0_OVF // every 10ms { static uint8_t ct0, ct1, rpt, secac; volatile uint8_t i; TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms i = key_state ^ KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect // missing: reset key repeat when key is released if( (key_state & LONG_MASK) == 0 ) // check secondary function secac = LONG_START; // start delay if( --secac == 0 ){ key_long |= key_state & LONG_MASK; } if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } // -------------------------------------------------------------------------
void main() { setup(); lastState = currState = nextState = stSTART; while(1) { if (lastState != currState) // nach einem Statuswechsel wird die Entryfunktion zu erst zu Beginn // des nächsten Loops ausgeführt { StateEntry[currState](); lastState = currState; } StateAction[currState](); // Hauptroutine mit der hauptsächlichen Arbeit if (currState != nextState) // Status wird geändert { StateExit[currState](); // Exitfunktion des aktuellen State sofort ausführen lastState = currState; currState = nextState; //continue; // Loop neu beginnen } }; }
FSM-1.h
// FSM nach Vorlage embedded.com // Konvention: stXXXX für States fXXXX für die Statefunktion #ifndef FSM #define FSM // ---------------------- State functions ------------------------------ void fSTART(); void fSETUP(); void fSETUP_ENTRY(); void fMAIN(); void fMAIN_ENTRY(); void fSHORT(); void fOFF(); void fLOWIN(); void fMENUINFO(); void fNOOP(); void fSTART_ENTRY(); void fMENUINFO_ENTRY(); void fMENUINFO_EXIT(); void fGEN_ENTRY(); void fGEN_EXIT(); #endif
Links
- State Machine Tools (QP) Es gibt eine Libary inkl. Application Note für Arduino
- QFSM Graphical State Machine Editor
- Yakindu Statechart Tools. LED Tutorial für Arduino. Beispiel Ampel für Arduino.
Beispiel für eine Cross-Compilierung vom PC auf einen Raspberry Pi:
- Motivation für FSM – beachte das Video (auch wenn es offenbar keinen Zusammnhang um Thema hat)
Update 20171209