Stap 6: Uw systeem brengen in de echte wereld
Oke, we rond geklikt, gebruikt een grafische editor (gewoonlijk geassocieerd met laag-niveau-talen), let's get dat ding tot leven. Eerste plaats moeten wij een codegenerator die onze toestandsdiagram in C-Code transformeert. Dat is verrassend eenvoudig, hoewel het zwarte magie kan lijken op het eerste.
Klik met de rechtermuisknop uw model-map en selecteer Nieuw -> Code Generator Model. Klik op jezelf in de wizard, en bevestig de codegenerator aan de toestandsdiagram die u hebt gemaakt voordat. Let op: In dat zelfde venster, er is een selector in de top, dat is gemakkelijk. Gebruik het om te selecteren van de C-Code generator in plaats van de Java een, en klik op Voltooien nadat u het selectievakje naast uw toestandsdiagram hebt gecontroleerd. Normaal gesproken, de generator zou nu moeten werken direct en automatisch de allertijden. Controleer of de twee mappen zijn gemaakt, src en src-gen. Als dat niet het geval, ga naar het Project in het hoofdmenu en check als bouwen automatisch wordt geactiveerd. Doen als het niet, en klik met de rechtermuisknop op uw project en selecteer Build Project. Een voortgangsbalk moet worden weergegeven naast de beide genoemde mappen. Wanneer u wijzigingen hebt aangebracht, kunt u ook met de rechtermuisknop op het bestand generator en selecteer Genereren Code artefacten.
De inhoud van de map src-gen is heel interessant. Het bestand LightCtrl.c bevat de uitvoeringvan de toestandsdiagram. Wanneer u het inspecteren, vindt u een functie LightCtrlIface_raise_button (LightCtrl * handle). U kunt deze functie aanroepen om te verhogen van de knop-event die wij verklaard eerder – bijvoorbeeld wanneer u van uw hardware knop pin controleren en ziet er op hoog niveau. Dan is er het bestand LightCtrlRequired.h, waartegen u nodig hebt om een kijkje te nemen. Het verklaart de functies die u moet implementeren. Voor deze toestandsdiagram, is er slechts twee functies: lightCtrl_setTimer en lightCtrl_unsetTimer. We moeten deze functies omdat onze toestandsdiagram de construct na 5s gebruikt. Dit is een erg handige functie, maar onze toestandsdiagram codegenerator levert niet een tijdbepalingsdienst, want dat is sterk platform afhankelijk-uw computer aankan timers anders dan de kleine Arduino, en timer handling op Mac & Linux anders dan op Windows werkt.
Gelukkig, ik zal u een tijdbepalingsdienst, zodat u niet hoeft uit te voeren uw eigen. In uw project, een nieuwe map maken, laten we noemen het scutils voor statechart utilities. U kunt het noemen wat je wilt of niet wilt maken van die map, het is gewoon een kwestie van organisatie. We zullen twee bestanden maken daar, sc_timer_service.c en sc_timer_service.h. Kopie de
code van GitHub binnen is er (u kunt ook het volledige project downloaden van GitHub hier):
Nu, kunnen we beginnen te werken aan de code van de Arduino in het *.ino-bestand dat de wizard is gegenereerd.
Bovendien aan de Arduino.h, ook avr/sleep.h, en natuurlijk onze statusmachine en de timerservice: LightCtrl.h, LightCtrlRequired.h en sc_timer_service.h. Nu, is de gewone Arduino spullen nodig: we definiëren de pinnen voor de knop en de LED, en deze instellen binnenkant van de setup-functie (dat is wat het voor werd gemaakt). Dan, we moeten definiëren van de functies die de toestandsdiagram verwacht we definiëren dat - lightCtrl_setTimer en lightCtrl_unsetTimer, zoals al eerder heb uitgelegd. Hier, we gewoon gebruik maken van de timerservice, en we zijn klaar. Nu moeten we reserveonderdelen een gedachte over hoe we eigenlijk de LED te activeren willen wanneer bereiken we de staat Licht op. Kortom, we hebben drie opties:
- We kunnen controleren of de statemachine is in de staat licht op, en activeren / deactiveren van de LED op basis van die informatie
- Wij kunnen naar onze toestandsdiagram gaan en een variabele wanneer bereiken we de Staten, die wij kunnen poll
- We kunnen een bewerking die worden beheerd met het licht dat wordt aangeroepen door de toestandsdiagram op een overgang toevoegen.
De eerste oplossing is echt slecht. Wij hadden logica betreffende de toestandsdiagram daarbuiten. Als wij zou de naam van onze staten, zou het ophouden te werken correct; maar die namen zijn bedoeld om te worden prozaïsche en niet logica gerelateerde. Met behulp van variabelen is oke, vooral bij het werken met desktop applicaties. We kunnen synchroniseren met hen elke x milliseconden of zo. Wij willen hier een bewerking gebruiken. Voeg het volgende toe van de toestandsdiagram interface verklaring:
operation setLight(LightOn: boolean): void
Dit verklaart een functie die accepteert een boolean-waarde als argument en retourneert de waarde nothing (void). Dit moet niet nieuw voor u, alleen de syntaxis hier anders is. Vergeet niet-statecharts zijn niet gebonden aan een bepaalde taal, zodat de syntaxis is generiek. Deze functie wordt automatisch weergegeven in LightCtrlRequired.h . Als dit niet het geval is, uw toestandsdiagram opslaan, klik met de rechtermuisknop op uw project en bouwen.
De functie verklaard hier ziet er zo uit:
extern void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn);
Het handvat van de invoerparameter is van het type LightCtrl, is het de verwijzer op de toestandsdiagram. Bent u niet die ervaren in C: de ster duidt een zogenaamde pointer, zodat de variabele het adres van de variabele toestandsdiagram bevat. Dit helpt ons omdat we van het oorspronkelijke object bedienen kunt en hoeft te maken van een kopie van het. Dus, laten we de uitvoering van deze functie:
void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn) { if(lightOn) digitalWrite(LED_PIN, HIGH); else digitalWrite(LED_PIN, LOW); }
Zoals u zien kunt, schrijven deze functie is bloedige eenvoudig-wij niet zelfs gebruik maken van de ingang naar de toestandsdiagram, alleen we een hoog op de LED-pin als van de operatie argument waar, en weinig anders is.
We wijzigen de toestandsdiagram zelf zodat het lijkt in de eerste foto.
Onthouden stap 1? Links naar de slash is de benodigde input voor de overgang, rechts is de output van de machine staat als deze overgang is genomen. De output hier is om te bellen van de opgegeven bewerking met deze argumenten.
#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 of timers our timer service can use #define CYCLE_PERIOD 10 //number of milliseconds that pass between each statechart cyclestatic unsigned long cycle_count = 0L; //number of passed cycles static unsigned long last_cycle_time = 0L; //timestamp of last cycle static LightCtrl lightctrl; static sc_timer_service_t timer_service; static sc_timer_t timers[MAX_TIMERS];//! callback implementation for the setting up time events void lightCtrl_setTimer(LightCtrl* handle, const sc_eventid evid, const sc_integer time_ms, const sc_boolean periodic){ sc_timer_start(&timer_service, (void*) handle, evid, time_ms, periodic); }//! callback implementation for canceling time events. void lightCtrl_unsetTimer(LightCtrl* handle, const sc_eventid evid) { sc_timer_cancel(&timer_service, evid); }void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn) { if(lightOn) digitalWrite(LED_PIN, HIGH); else digitalWrite(LED_PIN, LOW); }//The setup function is called once at startup of the sketch void setup() { pinMode(BUTTON_PIN, INPUT); pinMode(LED_PIN, OUTPUT); sc_timer_service_init( &timer_service, timers, MAX_TIMERS, (sc_raise_time_event_fp) &lightCtrl_raiseTimeEvent ); lightCtrl_init(&lightctrl); //initialize statechart lightCtrl_enter(&lightctrl); //enter the statechart }// The loop function is called in an endless loop void loop() { unsigned long current_millies = millis(); if(digitalRead(BUTTON_PIN)) lightCtrlIface_raise_button(&lightctrl); if ( 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++; } }
Ook, Controleer de code in deze kern met regelnummers.
- Regels 1-6 bevatten bevat zoals eerder is besproken.
- Lijnen 8 en 9 define hardware pinnen die wij zult willen gebruiken voor onze arduino.
- Lijnen 11 en 12 definiëren hoeveel timers onze toestandsdiagram kunt gebruiken, en hoeveel milliseconden tussen elke computing cyclus van de toestandsdiagram moeten doorgeven.
- Lijnen 15 en 16 Declareer variabelen die we gebruiken kunnen om te tellen de cycli en voor het beheer van de tijd van de laatste cyclus.
- Lijnen 17, 19 en 21 belangrijke variabelen voor het gebruik van de toestandsdiagram declareren: de toestandsdiagram zelf, de timerservice en een array van timers.
- Regels 24 en 33 definiëren functies die de toestandsdiagram nodig voor de timer heeft en lijn 33 is de functie voor het instellen van de LED die we eerder hebben besproken.
- In lijn 41 is void setup een standaard functie van de Arduino. Het heet een keer bij het opstarten. We gebruiken het om te initialiseren spullen-onze LED en knop pinnen krijgen hun richting geconfigureerd (INPUT is standaard, wij doen dat voor de duidelijkheid), de timerservice wordt geïnitialiseerd, de toestandsdiagram is geïnitialiseerd en ingevoerd. Het invoeren van middelen om te starten van de machine staat, zodat de eerste staat is geactiveerd-dit is de staat de ingangspunten van de staat op. Dus, bij het opstarten, het licht is weg.
- In lijn 59 volgt de loop-functie, die heet de hele tijd door de Arduino.
- In lijn 61 vangen we de huidige tijd met de millis() functie, die is gedefinieerd in de bibliotheek van de Arduino.
- In lijn 63, wij controleren als onze knop is ingedrukt, en verhogen van de knop-event als het is.
- In lijn 66 controleren wij als meer dan CYCLE_PERIOD milliseconden verstreken sinds we laatst onze toestandsdiagram gefietst.
- Dit neemt enkele laden uit onze arduino en betekent dat we kunnen maximaal 10 milliseconden betrouwbaar gebruiken voor onze eigen functies.
- In lijn 68, wij vertellen de timerservice hoeveel tijd is verstreken sinds de laatste aanroep, vertellen de toestandsdiagram te lopen één cyclus in lijn 70, opslaan van de huidige tijd in lijn 72, en verhogen de telling van de cyclus in lijn 73.
Met behulp van de arduino plugin, kunt u nu de arduino aansluit met de LED en de knop aangesloten op uw computer en gebruik de knop in de bovenste werkbalk het programma uploaden naar je arduino.
Het circuit is afgebeeld in foto's twee en drie.
De LED is aangesloten op een digital pin (6) met een weerstand van rotonde 200 Ohm. De kathode is gekoppeld aan GND.
Drukknoppen hebben vier pinnen, kijk welke van deze altijd zijn verbonden en die zijn verbonden wanneer u op de knop. Vervolgens koppelt u de digitale pin (3 wordt hier gebruikt) aan de ene kant, en een pulldown weerstand aan GND. Hiermee stopt u de pin uit "drijvende", een ongedefinieerde staat en houdt het op 0 volt. Wanneer de knop wordt ingedrukt en de andere kant is bevestigd aan de VCC, dat kant "stronger" is, omdat er geen weerstand en de spanning gaat tot 5 volt – in principe een voltage divider waar één weerstand 0 Ohm is. Gebruik een vrij hoge weerstand hier, omdat het beperkt de huidige gaan via de knop. 1 kR is minimaal.
Zoals u zien kunt, is de logica van dit programma volledig onafhankelijk van de werkelijke grootte van onze toestandsdiagram. Het maakt niet uit als onze toestandsdiagram 2 of 20 staten heeft – natuurlijk, als we iets doen willen, moeten we hier en daar een functie te implementeren. Maar de belangrijkste code binnenkant void loop altijd blijft vrij klein en voorziet in een modulair programma-architectuur. We hoeven maar te zorgen dat de interfaces van de toestandsdiagram van onze Arduino hardware binnenkant van onze code, de automatisch gegenereerde toestandsdiagram omgaat met haar interne logica. Herinner me hoe we besproken om te herstellen van de timer, wanneer de knop wordt ingedrukt weer? U kan nu een overgang van de Light On staat aan zichzelf toevoegt met "button" als bewaker van de gebeurtenis, en u zou niet moeten wijzigen of toevoegen van één regel in uw code. Probeer het uit!