Skocz do zawartości

Uniwersalna biblioteka obsługi menu wielopoziomowego - jak to ugryźć?


_LM_

Pomocna odpowiedź

Każda osoba która buduje mniej lub bardziej zaawansowane systemy mikroprocesorowe, prędzej czy później, musi zaimplementować jakiś interfejs użytkownika, czyli menu. Na początku przeważnie odbywa się to na instrukcjach wyboru switch case, ok jest to dobre rozwiązanie dla kilku opcji wyboru. Problem zaczyna się kiedy zachodzi potrzeba zagnieżdżania warstw menu, lub gdy po czasie trzeba dodać kolejną pozycję. 

Nie chcę pisać tutaj rozprawki o tym czy takie podejście jest słuszne czy nie, tak samo, przynajmniej w tej chwili odpuszczam sobie gotowe biblioteki. To co chcę osiągnąć to obsługa warstwy abstrakcyjnej takiego menu. Tu dla osób mniej zorientowanych krótkie objaśnienie co to jest ta warstwa abstrakcyjna:

Cytat

Warstwa abstrakcji sprzętowej (ang. hardware abstraction layer – HAL) – sterownik urządzenia dla płyty głównej. Stanowi ogniwo pośredniczące między sprzętem a jądrem systemu operacyjnego. Odseparowuje konkretną architekturę systemu komputerowego od oprogramowania użytkowego. Dla programisty jest sposobem komunikacji ze sprzętem przez udostępniane funkcje biblioteczne i sterowniki.

wiki

A więc powoli próbuję napisać bibliotekę którą z niewielkimi zmianami będę mógł użyć zarówno dla mikrokontrolerów ośmiobitowych AVR jak i STM32 oraz ESP.

No i z jakiegoś nieznanego mi powodu, nie mogę ruszyć z projektem... hm może to po tym dziadowskim wirusie? 

 Największą trudność sprawia mi określenie założeń, to znaczy:

Jak podzielić bibliotekę funkcjonalnie? Czy zrobić jeden gruby plik .c i .h, czy lepiej osobno. Zestaw plików "silnika" biblioteki a osobno pliki użytkownika? 

W jaki sposób podejść do kwestii wejścia/wyjścia ... tu głupio to zabrzmi ale nawet nie wiem czego chcę 🤔 bo jako wejście(czyli to co rozumiem jako możliwość poruszania się po menu) może służyć zarówno enkoder, przyciski, jak i polecenia z wielu różnorodnych interfejsów. Oczywiście zdaję sobie sprawę z tego że będzie trzeba ostro działać na wskaźnikach na funkcje, callbackach i wielu innych fajnych rzeczach.

Wyjście - tu znów zagwozdka, większość dostępnych libsów ma jako dane wyjściowe wskaźniki na char, a co jeśli będę chciał wyświetlić jakiś obrazek?

A najgorsze, że w ogóle nie mam pomysłu w jaki sposób przesłać do biblioteki dane które mają być edytowane po wyborze opcji. 

Co mam zrobione na teraz, no, właściwie to nic konkretnego. Dlatego też tak bardzo ociągałem się z założeniem tego tematu. No bo jak chcieć uzyskać pomoc kiedy nawet niema punktu zaczepienia, są pewne próby stworzenia typu struktury która będzie opisywała jedną z pozycji menu:

//ptr void func
typedef void(*fptr_t)(void);

typedef enum{
	idle = 0,
	visible
}MENU_STATUS_t;


// menu struct
typedef struct menu{
	const char *text; // pointer to text menu
	fptr_t f;
	struct menu * next; // next menu
	struct menu * prev;
	struct menu * parent;
}menu_t;

Później zastanawiałem się jak skorzystać z tej strukturki tworząc warstwy menu tak jak tutaj:

menu_t menuGlowne[3] = {
		{.text = "Czas",.prev = NULL,.parent = NULL,.f = NULL},
		{.text = "Moc",.prev = NULL,.parent = NULL,.f = NULL},
		{.text = "Jasnosc",.prev = NULL,.parent = NULL,.f = NULL}
};


menu_t podmenuCzas[2]= {
		{.text = "Godz",.prev = NULL,.next = NULL,.parent = &menuGlowne[0],.f = NULL},
		{.text = "Data",.prev = NULL,.next = NULL,.parent = &menuGlowne[0],.f = NULL}
};

Oczywiście jest to do kitu choćby ze względu na umieszczenie napisów bezpośrednio w strukturze, a już nie mówiąc że .text powinien być wskaźnikiem na "obiekt" czyli np: obrazek a nie bezpośrednio na tekst.

Pomijam w tej chwili że te struktury powinny być umieszczone w pamięci flasch mikrokontrolera, powiedzmy że kiedy już będę miał coś działającego to wtedy zajmę się optymalizacją.

No i takie perypetie mam z tym, najpewniej z (mam nadzieję) rozwojem tego wątku będzie więcej pytań, o których zadaniu zapomniałem lub dopiero się pojawią.

Ah i sprawy techniczne: Ja piszę tylko w C oraz wstępne wersje są testowane na mikrokontrolerze AVR jednak nie stanowi dla mnie problemu podłączyć płytę z STM32 

Edytowano przez _LM_
  • Lubię! 2
Link do komentarza
Share on other sites

2 godziny temu, _LM_ napisał:

Jak podzielić bibliotekę funkcjonalnie? Czy zrobić jeden gruby plik .c i .h, czy lepiej osobno. Zestaw plików "silnika" biblioteki a osobno pliki użytkownika? 

Jedna struktura jeden zestaw plików.

Np. menu.h / menu.cpp, menu_header.h / menu_header.cpp, menu_action.h / menu_action.cpp itp.
Przykładowo w jednym z moich starych projektów każda funkcja wykonywana po UART ma osobny plik, ale to kwestia logiczna (każda funkcja jest traktowana jak osobny typ obiektu). W Twoim przypadku raczej typami będą elementy samego API - menu, może napis/obrazek itp. Ogólnie dzielenie plików to kwestia bardzo abstrakcyjna i w dużej mierze zależy od uznania czegoś za osobny obiekt lub nie - w tym samym przypadku (powyższego projektu) funkcje po stronie komputera są już w jednej klasie, która służy do obsługi tego urządzenia... Punkt widzenia zależy więc od punktu siedzenia.

2 godziny temu, _LM_ napisał:

A najgorsze, że w ogóle nie mam pomysłu w jaki sposób przesłać do biblioteki dane które mają być edytowane po wyborze opcji. 

Wskaźnik na funkcję? (nie pamiętam czy to coś było w C, bo piszę w CPP od dłuższego czasu).

/* Function types definitions */
typedef WORD func(WORD*);

U mnie funkcjonuje coś "takiego" 😛 

Potem mogę tworzyć sobie tworzyć zmienne, do których przypisane są funkcje 😉 

Przykładowo:

WORD hello(WORD* args)
{
	 printf("Hello world!"); // RP2040
}

func* my_function = (func*) &hello;

 

  • Lubię! 1
Link do komentarza
Share on other sites

19 minut temu, H1M4W4R1 napisał:

Wskaźnik na funkcję? (nie pamiętam czy to coś było w C, bo piszę w CPP od dłuższego czasu).

Są, są ... umożliwiają pisanie callbacków i są podstawią działania sterowników w Linuxie. 

Link do komentarza
Share on other sites

No to akurat z palca piszę 

void(*f)(void) //wskaźnik prosty
void(*f)(char * s) //wsk na f która przyjmuje wskaźnik na char
void(*f)(void(*f) (void)) // wsk na fun która przyjmuje wsk na funkcje 
  Itd itp :P Ci z C++ mają łatwiej ;) 

A ogólnie to typedef jest najlepszy dla takich deklaracji 

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Zgadzam się z tezami @ethanak chociaż osobiście samą strukturę XML'a bym trochę bardziej rozbił na pod-parametry dające trochę więcej możliwości kombinowania... A najlepiej byłoby zapisać to w JSON'ie, no ale nie można mieć wszystkiego...

<menuitem>
	<display>
		<type>text</type>
		<value>Preheat PLA</value>
	</display>
	<action>
		<type>select</type>
		<function>pid_set_temperature</function>
		<data>
			<value>190</value>
			<type>int</type>
		</data>
	</action>
</menuitem>

 

  • Lubię! 1
Link do komentarza
Share on other sites

@H1M4W4R1JSON ma jedną wielką wadę: nie można w nim dodawać komentarzy. O yamlu się nie wypowiem bo nie korzystam.

Co do XML-a - o tym co w nim może być możemy podyskutować o ile kolega bibliotekotwórca przyjmie taki model. Przykładowo - może być dopuszczalny skrócony zapis <name> jak i pełny <displayItem>.

BTW bardziej mi się podoba przekazywanie parametru przez userData - no, ale to lata przyzwyczajenia:)

Link do komentarza
Share on other sites

12 minut temu, H1M4W4R1 napisał:

Mimo to wiele parserów je obsługuje

Nie obsługuje moduł json w Pythonie i wystarczy.

Przy okazji - z tego i tak trzeba zrobić coś w rodzaju DOM żeby sensownie ułożyć kod, a więc czy to XML czy JSON to szczegół na dziś pomijalny.

Edytowano przez ethanak
Link do komentarza
Share on other sites

@H1M4W4R1 i @ethanak dzięki za podpowiedzi, mam takie pytanie co do tego XMLa i JSON tj z implementacją tego, o ile dobrze to zrozumiałem ma to służyć do budowy struktury menu, to w takim razie czy ten kod(xml, json) ma być zaszyty w mikrokontrolerze? czy to nie zajmie zbyt wiele pamięci? 

Co do samego generatora, to myślę że napisanie prostej aplikacji która to zrobi za mnie nie będzie wielkim wyzwaniem. Póki co podejmę próby napisania aplikacji z obsługą menu, a że mam na płytce RTC LCD i przyciski to jest na czym działać.

Link do komentarza
Share on other sites

1 minutę temu, _LM_ napisał:

ten kod(xml, json) ma być zaszyty w mikrokontrolerze? czy to nie zajmie zbyt wiele pamięci? 

Ten kod ma służyć jako coś pośredniego między tym co Ty rozumiesz i tym co rozumie mikrokontroler - w skrócie generuje ci kod w C, który zawiera już gotowe menu. Ale to już rzecz do zrobienia po skończeniu działającego menu stukanego "z palca".

  • Lubię! 1
Link do komentarza
Share on other sites

2 minuty temu, _LM_ napisał:

czy ten kod(xml, json) ma być zaszyty w mikrokontrolerze

Dla jakichś większych maszynek teoretycznie mógłby, ale po co. Zresztą - kolega @H1M4W4R1 już to przed chwilą wyjaśnił.

To powinien być jakiś program, najlepiej w pythonie i bez konieczności instalowania dodatkowych modułów (coś jak esptool.py). Z ciekawości spróbukę wieczorkiem coś naszkicować, zobaczę co wyjdzie.

Link do komentarza
Share on other sites

Cześć,

przyglądam się temu wątkowi i trochę nie rozumiem po co tu jakaś warstwa pośrednia i generator kodu z XML(czy JSON) jeśli dużo łatwiej można to zrobić na maszynie stanów skończonych w kodzie języka C.

Tutaj przykład menu w aplikacji Arduino z uzyciem biblioteki "Fsm" dla Arduino:

#include "TimerThree.h"
#include <Wire.h>
#include <avr/interrupt.h>
#include "DFRobot_LCD.h"
#include "Fsm.h"
#include <SD.h>
#include <SPI.h>

const int OsXPulsePin   = 8;    //Os x (pozioma) STEP
const int OsXDirPin     = 9;    //Os x (pozioma) DIR
const int OsZPulsePin   = 6;    //Os Z (pionowa) STEP
const int OsZDirPin     = 7;    //Os Z (pionowa) DIR

const int ledPin = 13;         // the number of the LED pin

//Buttons
const int buttonEnter  = 3;      // INT5d
const int buttonDown   = 53;     // PCINT0
const int buttonUp     = 0;      // PCINT8
const int buttonLeft   = 19;     // INT2
const int buttonAlarm  = 2;      // INT4
const int buttonRight  = 18;     // INT3

enum buttons{bAlarm, bEnter, bDown, bUp, bLeft, bRight};

//Stepper motor directions
const int dirRight     = 1;     // kierunek ruchu silnika - prawo
const int dirLeft      = 0;     // kierunek ruchu silnika - lewo
const int pinCS        = 49;    // Pin SS dla karty uSD
const String nazwaPlikuPomiar = "pomiary.txt";

DFRobot_LCD lcd(16,2);  //16 characters and 2 lines of show
File myFile;            //instancja pliku na karcie uSD

volatile bool BAlarm; 
volatile bool BEnter;
volatile bool BLeft;
volatile bool BRight;
volatile bool BUp;
volatile bool BDown;
volatile bool EmergencyStop;

unsigned long impulsy1X, impulsy2X,impulsy1Z, impulsy2Z;
bool generacjaSTEPX,generacjaSTEPZ;
volatile int DirectionXTrigger;
volatile int lastButtonTrigger;
volatile int lastButtonTCpy;
volatile unsigned long startTime;
volatile unsigned long actTime;
volatile int wynikTestKartySD;

//------------- Stuff related to FSM -----------------------------------

//Events FSM
#define BUTTON_ALARM_EVENT  0
#define BUTTON_ENTER_EVENT  1
#define BUTTON_LEFT_EVENT   2
#define BUTTON_RIGHT_EVENT  3
#define BUTTON_UP_EVENT     4
#define BUTTON_DOWN_EVENT   5
#define TEST_KARTYSD_ZDANY_EVENT  6
#define TEST_KARTYSD_NIEZDANY_EVENT  7

/* Definitions of states of FSM -------------------
 * state 1:  Start programu
 */
 
State state_start(&startIn, &checkButtons, NULL);
State state_menu_glowne(&menuGlowneIn, &checkButtons, NULL);
State state_ustawianie_parametrow(&menuUstParamIn, &checkButtons, NULL);
State state_ust_poz_neutralnej(&menuUstPozNeutralnejIn, &checkButtons, NULL);
State state_konfiguracje_eeprom(&menuKonfiguracjeEEpromIn, &checkButtons, NULL);
State state_start_pomiarow(&menustartPomiarowIn, &checkButtons, NULL);
State state_test_kartySD(&menuTestKartySDIn, &checkTestKartySD, NULL);
State state_test_kartyOK(&menuTestKartyOKIn, &checkTestKartySD, NULL);
State state_test_kartyBAD(&menuTestKartyBADIn, &checkTestKartySD, NULL);

State state_szczegoly(&menuSzczegolyIn, &checkButtons, NULL);
State state_alarm(&alarmIn, &checkButtons, NULL);

Fsm fsm(&state_start);

// ---- Transition functions ---------------------

void startIn()
{
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Goniometr v 0.1");
  lcd.setCursor(4, 1);
  lcd.print("Famor S.A.");
}

void alarmIn()
{
  lcd.clear();
  lcd.setCursor(0, 4);
  lcd.print("Emergency Alarm");
}

void menuGlowneIn()
{
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Menu glowne");
  lcd.setCursor(1, 1);
  lcd.print("nacisnij Enter");
}

void menuUstParamIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Ustawianie par.");
  lcd.setCursor(1, 1);
  lcd.print("<-  Enter  ->");
}

void menuUstPozNeutralnejIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Ust. poz.neutral");
  lcd.setCursor(1, 1);
  lcd.print("<-  Enter  ->");
}

void menuKonfiguracjeEEpromIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print(" Konfiguracje");
  lcd.setCursor(1, 1);
  lcd.print("<-  Enter  ->");
}

void menustartPomiarowIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Start pomiarow.");
  lcd.setCursor(1, 1);
  lcd.print("nacisnij Enter");
}

void menuTestKartySDIn()
{
  lcd.clear();
  lcd.setCursor(1, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Test karty SD");
  lcd.setCursor(4, 1);
  lcd.print("poczekaj");

  //-----------------------------
   // SD Card Initialization
  if (SD.begin())
  {
    wynikTestKartySD = 1;
  } else
  {
    wynikTestKartySD = 0;
  } 
}

void menuSzczegolyIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Ust. szczegolow");
  lcd.setCursor(1, 1);
  lcd.print("  Enter  ");
}

void menuTestKartyOKIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Test karty SD");
  lcd.setCursor(4, 1);
  lcd.print("zdany");
}

void menuTestKartyBADIn()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("XXXXXXXXXXXXXXXX");
  lcd.print("Test karty SD");
  lcd.setCursor(3, 1);
  lcd.print("niezdany");
}


//Funkcja wywoluje rozne zdarzenia FSM w zal. od ostatnio nacisnietego klawisza
void checkButtons()
{
    lastButtonTCpy=lastButtonTrigger;
    odlicz10ms();
    if (lastButtonTrigger==lastButtonTCpy) {
      switch (lastButtonTrigger) 
      {
          case bAlarm:
            fsm.trigger(BUTTON_ALARM_EVENT);
            break;
          case bEnter:
            fsm.trigger(BUTTON_ENTER_EVENT);
            break;
          case bLeft:
            fsm.trigger(BUTTON_LEFT_EVENT);
            break;
          case bRight:
            fsm.trigger(BUTTON_RIGHT_EVENT);
            break;
          case bUp:
            fsm.trigger(BUTTON_UP_EVENT);
            break;
          case bDown:
            fsm.trigger(BUTTON_DOWN_EVENT);
            break;
          default:
            ;//pusta instrukcja
      }
    }
    lastButtonTrigger = 99; //wyczysc trigger
}

void checkTestKartySD()
{
  if (wynikTestKartySD) fsm.trigger(TEST_KARTYSD_ZDANY_EVENT);
  else fsm.trigger(TEST_KARTYSD_NIEZDANY_EVENT);
}

void odlicz10ms()
{
   startTime = actTime= 0;
   //odlicz 10 ms
   startTime = micros();
   while(1) {
     actTime = micros();
     if ((actTime-startTime) > 10000UL) { //1000UL == 1ms
       break;
     }      
   }//end while
}

void setup()
{
  pinMode(OsXPulsePin, OUTPUT);
  pinMode(OsXDirPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  
  pinMode(buttonEnter, INPUT_PULLUP);
  pinMode(buttonDown, INPUT_PULLUP);
  pinMode(buttonUp, INPUT_PULLUP);
  pinMode(buttonLeft, INPUT_PULLUP);
  pinMode(buttonAlarm, INPUT_PULLUP);
  pinMode(buttonRight, INPUT_PULLUP);

  pinMode(pinCS, OUTPUT);

  // turn LED off:
  digitalWrite(ledPin, LOW);

  DirectionXTrigger = dirRight;
  digitalWrite(OsXDirPin, DirectionXTrigger);

  impulsy1X = 0UL;
  impulsy2X = 0UL;
  generacjaSTEPX = true;
  impulsy1Z = 0UL;
  impulsy2Z = 0UL;
  generacjaSTEPZ = true;
  EmergencyStop = false;
  lastButtonTrigger = 99;
  wynikTestKartySD = 0;

  attachInterrupt(digitalPinToInterrupt(buttonAlarm), alarmInterrupt,  CHANGE); //Duzy czerwony button ALARM 
  attachInterrupt(digitalPinToInterrupt(buttonEnter), BEnterInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(buttonLeft),  BLeftInterrupt,  CHANGE);
  attachInterrupt(digitalPinToInterrupt(buttonRight), BRightInterrupt, CHANGE);

  cli();
  PCICR |= 0b00000011; // Enables Ports B and C Pin Change Interrupts (maskowalne)
  PCMSK0 |= 0b00000001; // PCINT0
  PCMSK1 |= 0b00000001; // PCINT8
  sei();
  
  Timer3.initialize(25); //25 us         // initialize timer3, and set a 25 us second period
  //Timer3.pwm(7, 512);                  // setup pwm on pin 7, 50% duty cycle
  Timer3.attachInterrupt(tim3Callback);  // attaches callback() as a timer overflow interrupt

  // initialize LCD
  lcd.init();
  lcd.clear();
  //Initialize buttons state
  BAlarm=BEnter=BLeft=BRight=BUp=BDown = false;

/*
State state_start(&startIn, &checkButtons, NULL);
State state_menu_glowne(&menuGlowneIn, &checkButtons, NULL);
State state_ustawianie_parametrow(&menuUstParamIn, &checkButtons, NULL);
State state_ust_poz_neutralnej(&menuUstPozNeutralnejIn, &checkButtons, NULL);
State state_konfiguracje_eeprom(&menuKonfiguracjeEEpromIn, &checkButtons, NULL);
State state_start_pomiarow(&menustartPomiarowIn, &checkButtons, NULL);

State state_test_kartySD(&menuTestKartySDIn, &checkTestKartySD, NULL);
State state_test_kartyOK(&menuTestKartyOKIn, &checkTestKartySD, NULL);
State state_test_kartyBAD(&menuTestKartyBADIn, &checkTestKartySD, NULL);

State state_szczegoly(&menuSzczegolyIn, &checkButtons, NULL);
State state_alarm(&alarmIn, &checkButtons, NULL);

#define TEST_KARTYSD_ZDANY_EVENT  6
#define TEST_KARTYSD_NIEZDANY_EVENT  7
*/

  // ---- Setup FM ---------------------
  fsm.add_timed_transition(&state_start, &state_menu_glowne, 3000, NULL);
  fsm.add_transition(&state_menu_glowne, &state_ustawianie_parametrow, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_ust_poz_neutralnej, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_konfiguracje_eeprom, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_ustawianie_parametrow, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom,  &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_start_pomiarow, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_ust_poz_neutralnej, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_konfiguracje_eeprom, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_test_kartySD, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_test_kartySD, &state_test_kartyOK, TEST_KARTYSD_ZDANY_EVENT, NULL);
  fsm.add_transition(&state_test_kartySD, &state_test_kartyBAD, TEST_KARTYSD_NIEZDANY_EVENT, NULL);
  
  // przejscie z alarmu na poczatek programu po 4 s
  fsm.add_timed_transition(&state_alarm, &state_start, 4000, &outAlarm);//powrot z alarmu na pocz. programu
  // dodaj obskluge Alarmu dla wszystkich stanow ----
  fsm.add_transition(&state_start, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_menu_glowne, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_szczegoly, &state_alarm, BUTTON_ALARM_EVENT, NULL);

  
} //end Setup()

void outAlarm() {
   lastButtonTrigger = 99;
}
 
void tim3Callback()
{
   if (!EmergencyStop) {
   noInterrupts();
   //digitalWrite(8, digitalRead(8) ^ 1);
     
  if (generacjaSTEPX){
     digitalWrite(OsXPulsePin, digitalRead(OsXPulsePin) ^ 1);
     impulsy1X++;
     if (impulsy1X > 10*3333UL){ //1 st - 3333UL
       generacjaSTEPX = false;
       impulsy1X = 0UL;
       impulsy2X = 0UL;
     }
  } else {
    impulsy2X++;
    if (impulsy2X > 40000UL){ //1s -40000UL
       generacjaSTEPX = true;
       impulsy1X = 0UL;
       impulsy2X = 0UL;
    }
  }
  interrupts();
 } 
}

//PCINT0 Int Pin:53
ISR(PCINT0_vect)
{
  noInterrupts();
  if (digitalRead(buttonDown) == LOW) {
     BDown=true;
     lastButtonTrigger = bDown;
  } else {
     BDown=false;
  }
  interrupts();
}

//PCINT8 Int Pin:0
ISR(PCINT1_vect)
{
  noInterrupts();
  if (digitalRead(buttonUp) == LOW) {
     BUp=true;
     lastButtonTrigger = bUp;
  } else {
     BUp=false;
  }
  interrupts();
}

void alarmInterrupt() {
  noInterrupts();
  if (digitalRead(buttonAlarm) == LOW) {
    EmergencyStop = true;
    digitalWrite(ledPin, HIGH);
    BAlarm=true;
    lastButtonTrigger = bAlarm;
  } else {
    digitalWrite(ledPin, LOW);
    BAlarm=false;
  }
  interrupts();
}

void BEnterInterrupt() {
  noInterrupts();
  if (digitalRead(buttonEnter) == LOW) {
    BEnter=true;
    lastButtonTrigger = bEnter;
  } else {
    BEnter=false;
  }
  interrupts();
}

void BLeftInterrupt() {
  noInterrupts();
  if (digitalRead(buttonLeft) == LOW) {
     BLeft=true;
     DirectionXTrigger = dirLeft;
     lastButtonTrigger = bLeft;
  } else {
     BLeft=false;
  }  
  interrupts();
}

void BRightInterrupt() {
  noInterrupts();
  if (digitalRead(buttonRight) == LOW) {
     BRight=true;
     DirectionXTrigger = dirRight;
     lastButtonTrigger = bRight;
  } else {
     BRight=false;
  } 
  interrupts();
}

void showButtonsStateLCD(bool Enter, bool Down, bool Up, bool Left, bool Right) {
    lcd.setCursor(0, 0);
    if (Enter) lcd.print("Enter ");
    else lcd.print("      ");
    if (Down) lcd.print("Down ");
    else lcd.print("     ");
    if (Up) lcd.print("Up ");
    else lcd.print("   ");
    lcd.setCursor(0, 1);
    if (Left) lcd.print("Left ");
    else lcd.print("     ");
    if (Right) lcd.print("Right ");
    else lcd.print("      ");
}
 
void loop()
{  
  fsm.run_machine();
  digitalWrite(OsXDirPin, DirectionXTrigger);
  //showButtonsStateLCD(BEnter, BDown, BUp, BLeft, BRight);
  delay(100);

 }

Aplikacja reaguje na zdarzenia zarówno od klawiszy jak i po odliczniu jekiegoś czasu.

Kluczowa jest definicja stanów FSM:

State state_start(&startIn, &checkButtons, NULL);
State state_menu_glowne(&menuGlowneIn, &checkButtons, NULL);
State state_ustawianie_parametrow(&menuUstParamIn, &checkButtons, NULL);
State state_ust_poz_neutralnej(&menuUstPozNeutralnejIn, &checkButtons, NULL);
State state_konfiguracje_eeprom(&menuKonfiguracjeEEpromIn, &checkButtons, NULL);
State state_start_pomiarow(&menustartPomiarowIn, &checkButtons, NULL);
State state_test_kartySD(&menuTestKartySDIn, &checkTestKartySD, NULL);
State state_test_kartyOK(&menuTestKartyOKIn, &checkTestKartySD, NULL);
State state_test_kartyBAD(&menuTestKartyBADIn, &checkTestKartySD, NULL);

State state_szczegoly(&menuSzczegolyIn, &checkButtons, NULL);
State state_alarm(&alarmIn, &checkButtons, NULL);

Fsm fsm(&state_start);

oraz przejść miedzy stanami:

  // ---- Setup FM ---------------------
  fsm.add_timed_transition(&state_start, &state_menu_glowne, 3000, NULL);
  fsm.add_transition(&state_menu_glowne, &state_ustawianie_parametrow, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_ust_poz_neutralnej, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_konfiguracje_eeprom, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_ustawianie_parametrow, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom,  &state_szczegoly, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_start_pomiarow, BUTTON_RIGHT_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_ust_poz_neutralnej, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_konfiguracje_eeprom, BUTTON_LEFT_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_test_kartySD, BUTTON_ENTER_EVENT, NULL);
  fsm.add_transition(&state_test_kartySD, &state_test_kartyOK, TEST_KARTYSD_ZDANY_EVENT, NULL);
  fsm.add_transition(&state_test_kartySD, &state_test_kartyBAD, TEST_KARTYSD_NIEZDANY_EVENT, NULL);
  
  // przejscie z alarmu na poczatek programu po 4 s
  fsm.add_timed_transition(&state_alarm, &state_start, 4000, &outAlarm);//powrot z alarmu na pocz. programu
  // dodaj obskluge Alarmu dla wszystkich stanow ----
  fsm.add_transition(&state_start, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_menu_glowne, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_ustawianie_parametrow, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_ust_poz_neutralnej, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_konfiguracje_eeprom, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_start_pomiarow, &state_alarm, BUTTON_ALARM_EVENT, NULL);
  fsm.add_transition(&state_szczegoly, &state_alarm, BUTTON_ALARM_EVENT, NULL);

I ważna jest generacja zdarzeń od przycisków (dla przejść stanów w FSM:

//Funkcja wywoluje rozne zdarzenia FSM w zal. od ostatnio nacisnietego klawisza
void checkButtons()
{
    lastButtonTCpy=lastButtonTrigger;
    odlicz10ms();
    if (lastButtonTrigger==lastButtonTCpy) {
      switch (lastButtonTrigger) 
      {
          case bAlarm:
            fsm.trigger(BUTTON_ALARM_EVENT);
            break;
          case bEnter:
            fsm.trigger(BUTTON_ENTER_EVENT);
            break;
          case bLeft:
            fsm.trigger(BUTTON_LEFT_EVENT);
            break;
          case bRight:
            fsm.trigger(BUTTON_RIGHT_EVENT);
            break;
          case bUp:
            fsm.trigger(BUTTON_UP_EVENT);
            break;
          case bDown:
            fsm.trigger(BUTTON_DOWN_EVENT);
            break;
          default:
            ;//pusta instrukcja
      }
    }
    lastButtonTrigger = 99; //wyczysc trigger
}

Podany kod jest bardzo wczesną wersją programu (potem zosał on mocno rozbudowany). Kod dla ArduinoMega2560.

Podrawiam

Link do komentarza
Share on other sites

3 minuty temu, FlyingDutch napisał:

jeśli dużo łatwiej można to zrobić na maszynie stanów skończonych w kodzie języka C.

Odpowiem Ci anegdotką:

Dużo łatwiej jest zlecić komuś zrobienie prototypu urządzenia elektronicznego niż samemu sobie projektować schemat i płytkę... Idąc tym tokiem myślenia po co mamy hobby, skoro można komuś zlecić wykonanie interesującej nas rzeczy?

Jeżeli kolega zrobi to na strukturach to zobaczy, czy to go zadowala, jeżeli nie to będzie próbował to poprawić i może pójdzie do FSM, może w innym kierunku. Korzystając z Twojego toku rozumowania byśmy nadal biegali z kamieniami, bo nikt by nie wpadł by przywiązać kamień do patyka, bo przecież łatwiej jest uderzać kamieniem, niż go przywiązać, ale przywiązany kamień jest dużo skuteczniejszy w działaniu niż ten, który nie jest.

Jak coś nie obraź się, ale eksperymentowanie jest źródłem postępu, który ułatwia codzienne funkcjonowanie.

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.