Stap 5: Arduino Sketch
De Arduino schets bevat een statusmachine die ofwel luistert voor seriële opdrachten te parseren, of lanceert een data-acquisitie-sessie. Data-acquisitie treedt op in de vorm van rand-geactiveerd bemonstering of tijd gebaseerde polling. Tijd gebaseerd-polling kan worden trigged door een stijgende of dalende rand.
Ik heb de code ingesteld gebruiken de volgende Arduino Uno pinnen (niet te verwarren met de ATmega pinnen):
- Pin 2: Interrupt 0, de trigger
- Pin 8: Kanaal 1
- Pin 9: Kanaal 2
- Pin 10: Kanaal 3
- PIN 11: Channel 4
Opdracht lus
De lus van seriële commando, controleert of de seriële poort en wacht op de tekens te komen. Elke opdracht is een tekenreeks die eindigt met een '%'. Wanneer een '%' wordt geparseerd, betekent dit dat de huidige tekenreeksbuffer zich naar de router wordt verzonden uit. (De JavaScript-client stuurt een grote string met alle commando's.)
Commando's:
- Beginwaarden %: Schakel de context globale variabelen
- CH {1-4} %: inschakelen van de kanalen 1, 2, 3 of 4
- tijd %n%: stelt u de tijd van het monster op "n" microseconden.
- grens %n%: beperken van het aantal monsters van de byte tot 'n'. Ik voegde dit om mezelf een manier de steekproef om tijd te beheren aangezien ik het niet kan onderbreken zonder verlies van gegevens
- % stijgen: monster op de stijgende rand van de trigger
- % vallen: monster op de dalende rand van de trigger (Opmerking bemonstering werkt op beide randen, ze zijn niet mutex)
- eenmaal %: uitvoeren van een one-shot trigger dat polling modus begint
- starten %: compileren van de bovenstaande opties in globale variabelen, stel de functieverwijzingen en callbacks, en start de bemonstering lus
Na ontvangst van de opdracht start % , de schets configureert de variabelen van de mondiale context en wordt overgeschakeld van een opdracht parseren steekproef. Het kan niet worden onderbroken totdat deze is voltooid en gegevens geeft, tenzij jij een harde reset via de knop of macht.
Tijd gebaseerde Sampling
Als de gebruiker geen elke triggering hebt geselecteerd, bepaalt de schets welke tijd routine dan moet gebruiken. Er zijn drie:
- Zo snel mogelijk: dit is geen vertraging voor maximale snelheid
- delay_ms(): dit wordt de functie delay() gebruikt
- delay_us(): deze maakt gebruik van de functie van delayMicroseconds() , die een maximale limiet van 16,383 VS heeft.
De schets de juiste functie toewijst aan een functiepointer in plaats van gebruik van if/else conditionals, dit versnelt uitvoering, en kunt beter abstractie.
One-Shot Trigger
Als de gebruiker one-shot modus hebt geselecteerd, een interrupt service routine (ISR) is ingesteld op INT0 (de trigger), voor beide RISING, vallen, of verandering. De ISR zal vervolgens de interrupt loskoppelen en beroepen op tijd gebaseerde bemonstering zoals hierboven.
Rand-Triggering
Edge-triggered bemonstering slaat de tijd gebaseerd routines en verzamelt één monster per trigger. De schets een ISR hecht aan INT0 vergelijkbaar met one-shot modus, maar in plaats van ontkoppelen van de interrupt en roepen de tijd gebaseerde routine, verzamelt het gewoon één monster per interrupt. Zoals u zien kunt, als de interrupts sneller komen dan de code kan verwerken, gegevens verloren zullen gaan. De limiet is rond 20us vanwege de grote hoeveelheid code in de steekproef routines.
Bemonstering van de gegevens
Alle van de bemonsteringsmethoden voor de aanroep de dezelfde low-level data-acquisitie-functies gebruiken.
Poging tot het beperken van de overhead, schreef ik in vier verschillende bemonstering routines gebaseerd op het aantal kanalen geselecteerd en bel een functiepointer in plaats van if/else. Elke routine heeft eigen optimalisaties. De opdracht start % handler wordt ingesteld op de juiste functiepointer.
De schets slaat de monsters in een byte buffer [1080]. Dit vertaalt zich naar 8640 bit monsters voor één kanaal. Het aantal bytes monsters moet deelbaar door 2, 3 en 4. Aangezien ik toestaan 1, 2, 3 of 4 kanalen om actief te zijn, moet ik de buffer matrix partitioneren, zodat ik niet hoeft speciale grens controles uit te voeren. Maken de grootte deelbaar door 12 is de beste manier om dit te gaan. Ik het in de code niet afdwingen, maar ik doe in de HTML5-interface: aankondiging dat de controle van de monsters wordt verhoogd met 96 (12 * 8) en bij 8640 (1080 * 8 stopt). Als bits worden gelezen, zijn ze verschoven en verpakt in elke byte. Na elke 8 bits, een byte is opgeslagen in de index en de index wordt verhoogd. Wanneer de index gelijk is aan de diepte van de opslag, bemonstering is voltooid en de gegevens worden verzonden. Als één kanaal is geselecteerd, wordt de gegevens naar de buffer [index] geschreven. Als er twee kanalen zijn geselecteerd, bytes worden geschreven [index] buffer en buffer [index + offset_2]; offset_2 = MAX_BYTE_SAMPLES * 1 / 2 en index is de helft van wat er in één-kanaals modus. Drie kanalen schrijven buffer [index], buffer [index + offset_2] en buffer [index + offset_3]; offset_2 = MAX_BYTE_SAMPLES * 1 / 3 en offset_3 = MAX_BYTE_SAMPLES * 2 / 3, index is 1/3e is het max waarde in één-kanaals mode, enz.
Eens de de buffer vullingen of de index groter is dan de limiet %, de steekproef routines stoppen en de data wordt teruggestuurd.
Gegevens verzenden
De gegevens verzenden van repetitieve returns hetaantal monsters verzameld (gedeeld door het aantal kanalen gebruikt), en de kanalen binaire gegevens. Aangezien elke configuratie van kanalen de gebruiker kiezen kan, deze routine decodeert de kanalen gebruikt en hen dienovereenkomstig etiketteert (een beetje een gedoe, eigenlijk, ik weet dat er is een snellere oplossing daar...). Het haakjes ook de transmissie met de begindata en enddata om te helpen de server coördineren de parser.
Opmerking
Mogelijk merkt u nodeloos globale variabelen. Ik deed dit omdat ik 'm trying to get een handvat over hoe om te minimaliseren van de geheugen footprint om ruimte voor meer monster capaciteit te maken. Een gebied dat werken moet: Ik gebruikte een heleboel String-objecten die groeien en krimpen, en ik heb een heleboel debug char * tekenreeksen rondzweven daar. Ik vermeden gebruiken malloc () en verklaarde de monster buffer op de heap. Ik vond dat 1080 bytes is over de meest ik betrouwbaar kan gebruiken zonder het verpletteren van de schets. Mijn to-do list bevat begrip geheugenbeheer beter op de ATmega en avr-dude / gcc en herschrijven deze schets te voorspelbaarder i.v.m. geheugengebruik.