Arduino

Democode: Menüführung mit State Machine

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


Update 20171209

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.