Finite State maskin på en Arduino (6 / 7 steg)
Steg 6: Ta ditt system till den verkliga världen
Okej, vi klickade runt, används en grafisk editor (normalt förknippas med lägsta-nivå-språk), låt oss få det sak till liv. Först behöver vi en kodgenerator som förvandlar våra tillstånds i C-kod. Det är förvånansvärt enkelt, om det kan se ut svart magi först.
Högerklicka på din modell-mapp och välj Nytt -> kod Generator modell. Klicka dig igenom guiden, och fäst kodgeneratorn tillstånds du gjort innan. Uppmärksamhet: I samma fönstret, det finns en väljare i toppen som övervakas lätt. Använda den för att markera C kod generatorn i stället för Java en och klicka på Slutför när du har markerat kryssrutan bredvid din tillstånds. Normalt, bör generatorn nu arbeta direkt och automatiskt hela tiden. Kontrollera om två mappar skapades, src och src-gen. Om detta inte är fallet, gå till projekt i huvudmenyn och kontrollera om bygga automatiskt aktiveras. Göra det om det inte är, och högerklicka på ditt projekt och välj bygga projekt. En förloppsindikator visas samt de båda nämnda mapparna. När du gjort några ändringar, kan du också högerklicka på filen generator och välj Generera kod artefakter.
Innehållet i den mapp src-gen är ganska intressant. Filen LightCtrl.c innehåller genomförandet av tillstånds. När du inspektera det, hittar du en funktion LightCtrlIface_raise_button (LightCtrl * handtag). Du kan ringa denna funktion för att höja den knapp-event vi förklarat tidigare – exempelvis när du kontrollera din hårdvara knappen pin och se den har en hög nivå. Då finns filen LightCtrlRequired.h, som du behöver ta en titt. Det deklarerar funktioner behöver du implementera. För detta tillstånds, det finns bara två funktioner: lightCtrl_setTimer och lightCtrl_unsetTimer. Vi behöver dessa funktioner eftersom våra tillståndsdiagram använder konstruktion efter 5s. Detta är en ganska praktisk funktion, men vår tillstånds kodgenerator levererar inte en timing tjänst, eftersom det är mycket plattform beroende – din dator klarar timers olikt än små Arduino och timer hantering på Mac & Linux fungerar annorlunda än på Windows.
Lyckligtvis, jag ger dig en timing tjänst, så du inte behöver genomföra din egen. I projektet, skapa en ny mapp, låt oss kalla det scutils för statechart utilutvecklingsgrupper. Du kan kalla det vad du vill eller väljer att inte skapa mappen, det är bara en fråga om organisation. Vi kommer att skapa två filer där, sc_timer_service.c och sc_timer_service.h. Kopia den
koden från GitHub inuti finns (du kan också ladda ner den kompletta projektet från GitHub här):
Nu kan vi börja arbeta på Arduino koden i *.ino-filen i guiden genereras.
Dessutom till Arduino.h, även avr/sleep.h, och naturligtvis våra tillståndsdator och tidstjänsten: LightCtrl.h, LightCtrlRequired.h och sc_timer_service.h. Nu, den regelbundna Arduino grejer behövs: vi definiera stiften för knappen och lampan, och ställa dessa in i setup-funktionen (det är vad den gjordes för). Då måste vi definiera funktioner tillstånds förväntar oss att definiera - lightCtrl_setTimer och lightCtrl_unsetTimer, som vi förklarade tidigare. Här, vi använder bara tidstjänsten, och vi har gjort. Nu, vi bör tänka på hur vi faktiskt vill aktivera LED när vi når staten Ljus på. I grund och botten har vi tre alternativ:
- Vi kan kontrollera om en tillståndsdator är i tillståndet ljus på, och aktivera / avaktivera den LED-baserade på denna information
- Vi kan gå till våra tillstånds och ange en variabel när vi når de stater, som vi kan poll
- Vi kunde lägga till en åtgärd som hanterar ljuset som kallas av tillståndsdiagram på en övergång.
Den första lösningen är riktigt dålig. Skulle vi logik om tillstånds utanför den. Om vi skulle döpa våra stater, skulle det upphöra att fungera korrekt; men dessa namn är avsedda att vara prosaiska och inte logiken med. Med hjälp av variabler är okej, särskilt när du arbetar med skrivbordsprogram. Vi kunde synk med dem varje x millisekunder eller så. Vi vill här använda en operation. Den tillstånds gränssnittsdeklaration lägger du till följande:
funktion setLight(LightOn: boolean): void
Detta förklarar en funktion som accepterar ett booleskt värde som argument och returnerar ingenting (void). Detta bör inte vara nya för dig, endast syntaxen här är olika. Kom ihåg-tillståndsdiagram är inte bundna till ett visst språk, så att syntaxen är generisk. Den här funktionen visas i LightCtrlRequired.h automatiskt. Om det inte spara din tillstånds, högerklicka på ditt projekt och bygga den.
Funktionen deklareras här ser ut så här:
extern void lightCtrlIface_setLight (const LightCtrl * handtag, const sc_boolean lightOn);
Indataparametern handtaget är av typ LightCtrl, är det remitterande till tillstånds. Om du inte är att erfarna i C: stjärnan betecknar en s.k. pekare, så att variabeln innehåller adressen till variabeln tillstånds. Detta hjälper oss att vi kan fungera på det ursprungliga objektet och inte behöver skapa en kopia av den. Så, låt oss genomföra denna funktion:
void lightCtrlIface_setLight (const LightCtrl * handtag, const sc_boolean lightOn) {
IF(lightOn)
digitalWrite (LED_PIN, hög);
annat
digitalWrite (LED_PIN, låg);
}
Som ni kan se, skriva denna funktion är blodiga enkelt – vi behöver inte ens använda handtaget för att tillstånds, vi bara en hög på LED stift om operationens argument är sant och låg annars.
Vi ändrar tillstånds sig så att det ser ut i den första bilden.
Kom ihåg steg 1? Vänster till snedstrecket är nödvändiga indata för övergången är rätt utdata för tillståndsdatorn om denna övergång tas. Resultatet här är att kalla den angivna åtgärden med dessa argument.
#include "Arduino.h"
#include "avr/sleep.h"
#include "src-gen/LightCtrl.h"
#include "src-gen/LightCtrlRequired.h"
#include "scutil/sc_timer_service.h"
#define BUTTON_PIN 3
#define LED_PIN 6
#define MAX_TIMERS 20 //number av timers våra tidstjänsten kan använda
#define CYCLE_PERIOD 10 //number millisekunder som passerar mellan varje tillstånds cykel
statiska osignerade långa cycle_count = 0L; Antal skickade cykler
statiska osignerade långa last_cycle_time = 0L; timestamp av senast
statisk LightCtrl lightctrl;
statisk sc_timer_service_t timer_service;
statiska sc_timer_t timers [MAX_TIMERS];
//! motringning genomförandet för inställningen-upp tid händelser
void lightCtrl_setTimer (LightCtrl * handtag, const sc_eventid evid, const sc_integer time_ms, const sc_boolean periodiska) {
sc_timer_start (& timer_service, (void *) handtag, evid, time_ms, periodiska);
}
//! motringning genomförandet för att avbryta tid händelser.
void lightCtrl_unsetTimer (LightCtrl * handtag, const sc_eventid evid) {
sc_timer_cancel (& timer_service, evid);
}
void lightCtrlIface_setLight (const LightCtrl * handtag, const sc_boolean lightOn) {
IF(lightOn)
digitalWrite (LED_PIN, hög);
annat
digitalWrite (LED_PIN, låg);
}
Setup-funktionen anropas en gång vid start av skiss
void setup()
{
pinMode (BUTTON_PIN, indata);
pinMode (LED_PIN, OUTPUT);
() sc_timer_service_init
& timer_service,
timers,
MAX_TIMERS,
(sc_raise_time_event_fp) & lightCtrl_raiseTimeEvent
);
lightCtrl_init(&lightctrl); initiera tillstånds
lightCtrl_enter(&lightctrl); Ange tillstånds
}
Loop funktionen anropas i en oändlig loop
void loop()
{
osignerade långa current_millies = millis();
IF(digitalRead(BUTTON_PIN))
lightCtrlIface_raise_button(&lightctrl);
om (cycle_count == 0L || (current_millies > = last_cycle_time + CYCLE_PERIOD)) {
sc_timer_service_proceed (& timer_service, current_millies - last_cycle_time);
lightCtrl_runCycle(&lightctrl);
last_cycle_time = current_millies;
cycle_count ++;
}
}
Också, kolla in koden i denna gist med radnummer.
- Linjerna 1-6 innehåller ingår som diskuterats tidigare.
- Linje 8 och 9 definiera hårdvara stift vill vi kommer att använda för våra arduino.
- Linje 11 och 12 definiera hur många timers våra tillstånds kan använda och hur många millisekunder bör passera mellan varje computing cykel av tillstånds.
- Linjen 15 och 16 deklarera variabler som vi kan använda för att räkna cykler och hantera tiden av den senaste.
- Linje 17, 19 och 21 förklarar viktiga variabler för användning av tillstånds: tillstånds själv, tidstjänsten och en mängd timers.
- Linje 24 och 33 definiera funktioner som tillstånds behöver för timern och 33 är funktionen att ange den LED som vi diskuterade tidigare.
- I linje 41 är utan laga kraft setup() en standard funktion av Arduino. Det kallas en gång vid start. Vi använder det för att initiera grejer – våra LED och knappen stift få deras riktning konfigurerad (INPUT är standard, vi gör det för tydlighetens skull), tidstjänsten initieras, tillstånds är initierad och in. In medel för att starta tillståndsdatorn, så den första staten aktiveras – detta är staten posten staten pekar på. Så, vid start, ljuset är avstängd.
- I linje 59, loop funktion följer, som kallas hela tiden av Arduino.
- I linje 61 fånga vi den aktuella tiden med funktionen millis(), som definieras av Arduino biblioteket.
- I linje 63, vi kolla om våra knapp trycks och höja den knapp-händelsen om det är.
- I linje 66 kontrollerar vi om mer än CYCLE_PERIOD millisekunder gått sedan vi senast cyklade våra tillstånds.
- Detta tar någon last från våra arduino och innebär att vi på ett tillförlitligt sätt kan använda upp till 10 millisekunder för våra egna funktioner.
- I linje 68, vi berätta tidstjänsten hur mycket tid har gått sedan den senaste åkallan, berätta tillstånds kör en cykel i linje 70, spara aktuell tid i linje 72, och öka den cykliska inventeringen i linje 73.
Med hjälp av arduino plugin, kan du nu ansluta arduino med LED och knappen ansluten till datorn och använda knappen i det övre verktygsfältet ladda upp programmet till din arduino.
Kretsen visas i bilder två och tre.
Lysdioden är ansluten till en digital pin (6) med ett motstånd på rondellen 200 ohm. Katoden är kopplad till GND.
Tryckknappar har fyra stift, kontrollera vilken av dessa alltid är anslutna och som är anslutna när du trycker på knappen. Sedan, du bifoga digitala PIN-koden (3 används här) på ena sidan, och en pulldown resistor till GND. Detta hindrar stiftet från "flytande", en odefinierad stat och håller den på 0 volt. När knappen trycks och andra sidan är kopplad till VCC, att sidan är "starkare" eftersom den har inget motstånd och spänningen går upp till 5 volt – i grunden en spänningsavdelare där en resistor är 0 ohm. Använd en ganska hög motstånd här, eftersom det begränsar den nuvarande går igenom knappen. 1 kR är minst.
Som ni ser, är detta program logik fullständigt oberoende av den faktiska storleken på våra tillstånds. Det spelar ingen roll om våra tillstånds har 2 eller 20 stater – naturligtvis, om vi vill göra något, vi måste implementera en funktion här och där. Men den viktigaste koden i void loop() alltid förblir ganska liten och möjliggör en modulär programarkitektur. Vi bara ta hand om gränssnitt från tillstånds till våra Arduino maskinvara i vår kod, den autogenererade tillstånds hanterar sin inre logik. Kom ihåg hur vi diskuterat för att nollställa timern när knappen trycks in igen? Du kan nu lägga till en övergång från ljus på staten till sig själv med "knappen" som bevakar evenemanget, och du skulle inte behöva ändra eller lägga till en enda rad i koden. Försök den ute!