Stap 5: Bitbanging een Pulse Wave op een ATMega328p Microcontroller
Aangezien de Arduino Uno R3 ontwikkeling van bestuur een 16MHz externe kloksignaal op de on-board ATMega328p handhaaft, voert de microcontroller een 1 klokcyclus instructie in precies 62.5ns (1/16 MHz = 62.5ns). Aangezien we hoeveel klokcycli elke instructie vinden kunnen, kunnen we exact bepalen hoeveel instructies we moeten genereren ons signaal.
Zoals we eerder zagen, om te verzenden een 1 tot en met de WS281X-chip moeten we een signaal dat blijft bij een maximale (hoge) waarde voor 0.8μs, en vervolgens blijft bij een minimumwaarde (laag) voor 0.45μs doorgeeft. Dus we willen schrijven een lijst van instructies die:
-Set digitale pin op hoog
-Wacht 0.8μs
-De digitale pin sets op laag
-Wacht 0.45μs
In assembler, kan dit worden bereikt door de volgende code:
ASM vluchtige (
Instructie klok beschrijving fase bits verzonden
"sbi %0, %1\n\t" / / 2 PIN HIGH (T = 2)
"rjmp. + 0\n\t" / / 2 nop nop (T = 4)
"rjmp. + 0\n\t" / / 2 nop nop (T = 6)
"rjmp. + 0\n\t" / / 2 nop nop (T = 8)
"rjmp. + 0\n\t" / / 2 nop nop (T = 10)
"rjmp. + 0\n\t" / / 2 nop nop (T = 12)
"nop\n\t" // 1 nop (T = 13)
'cbi %0, %1\n\t' / / 2 PIN laag (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"rjmp. + 0\n\t" / / 2 nop nop (T = 19)
"nop\n\t" // 1 nop (T = 20) 1
::
Input operanden
"I" (_SFR_IO_ADDR(PORT)), //%0
"I" (PORT_PIN) //%1
);
Instructie
De eerste kolom bevat de instructie van de vergadering gevolgd door een regelinvoer en tab-tekens, waardoor de uiteindelijke assembler listing gegenereerd door de compiler beter leesbaar.
Klok
De tweede kolom toont het aantal klokcycli die elke instructie. Voor deze set van eenvoudige instructies is er slechts één mogelijke waarde, we zullen zien hoe sommige instructies (bijvoorbeeld voorwaardelijke) wellicht 1, 2 of 3 mogelijke waarden. Vergeet niet dat elke klokcyclus op de 16 MHz Arduino Uno 62.5ns is.
Beschrijving
De derde kolom geeft een zeer korte beschrijving van wat elke bewerking doet.
Fase
De term een beetje losjes, we gebruiken het om aan te geven de cumulatieve som van klokcycli genomen door de instructies die tot nu toe zijn uitgevoerd.
Om het verzenden van een enkele 255 waarde — 11111111 in binaire — naar de WS281X moeten we herhalen deze set van instructies 8 keer. Bovendien, als we een 50μs (of hoger) invoegen pauze tussen de verzending van de 8-bit-reeks, de WS281X zakken de toegezonden gegevens aan het register van de uitvoer. Zodra de gegevens zijn vergrendeld, moet de eerste LED (groen) van de WS281X inschakelen op het niveau van een maximale helderheid. De Arduino schets binnen bitbang_255.zip toont deze bewerking.
We moeten veranderen van de code die een 1 produceert door het verminderen van de tijd gedurende welke het signaal een hoge (maximale) waarde heeft en de intensivering van de tijd gedurende welke het signaal op een laag (minimaal is) voor het verzenden van een 0. Bovendien moeten we constateren dat de waarden voor elke LED altijd moeten worden opgegeven met behulp van 8 bits. Bijvoorbeeld, als we wilden een waarde van 105 verzenden — 1101001 in binaire — wij zou moeten voor het verzenden van de 8 bits 01101001 met inbegrip van de eerste 0. De code die een 0 produceert ziet er als:
ASM vluchtige (
Instructie klok beschrijving fase bits verzonden
"sbi %0, %1\n\t" / / 2 PIN HIGH (T = 2)
"rjmp. + 0\n\t" / / 2 nop nop (T = 4)
"rjmp. + 0\n\t" / / 2 nop nop (T = 6)
'cbi %0, %1\n\t' / / 2 PIN laag (T = 8)
"rjmp. + 0\n\t" / / 2 nop nop (T = 10)
"rjmp. + 0\n\t" / / 2 nop nop (T = 12)
"rjmp. + 0\n\t" / / 2 nop nop (T = 14)
"rjmp. + 0\n\t" / / 2 nop nop (T = 16)
"rjmp. + 0\n\t" / / 2 nop nop (T = 18)
"rjmp. + 0\n\t" / / 2 nop nop (T = 20) 0
::
Input operanden
"I" (_SFR_IO_ADDR(PORT)), //%0
"I" (PORT_PIN) //%1
);
We kunnen de Arduino schets binnen bitbang_105.zip gebruiken voor het genereren van het signaal wiens beeld kan worden gezien op de oscilloscoop scherm vangt die zijn gekoppeld aan deze stap.
Nu, voor de WS281X om weer te geven van de witachtig kleur die wij willen, moeten we niet één maar drie 255 waarden verzenden — in welk geval ons signaal bestaat uit 24 degenen — voordat op de 50μs voor de gegevens om in te haken. We kunnen dit doen door kopiëren-plakken de elf montage-instructies boven 23 keer (kunt u het een poging geven de schets van de bitbang_255.ino wijzigen). Maar de code zou onpraktisch voor het verzenden van waarden aan meer dan één WS281X chips. Een betere oplossing zou zijn om te schrijven een lus die de 8-bits waarden doorlopen zou totdat alle drie van hen zijn verzonden.
De schets binnen bitbang_whitish.zip bevat een duidelijke beschrijving van de stappen om het gewenste resultaat te bereiken. De main-sectie, geschreven in de logica hierboven beschreven, vergadering ziet er als volgt uit:
ASM vluchtige (
Instructie klok beschrijving fase
"nextbit:\n\t" // - label (T = 0)
"sbi %0, %1\n\t" / / 2 signaal HIGH (T = 2)
"sbrc %4, 7\n\t" / / 1-2 als MSB ingesteld (T =?)
"mov %6, %3\n\t" / / 0-1 tmp zult signaal hoog ingesteld (T = 4)
"dec %5\n\t" / / 1 afname bitcount (T = 5)
"nop\n\t" / / 1 nop (stationair 1 klokcyclus) (T = 6)
"st % a2, %6\n\t" / / 2 set PORT aan tmp (T = 8)
"mov %6, %7\n\t" / / 1 reset tmp lage (standaard) (T = 9)
"breq nextbyte\n\t" / / 1-2 als bitcount == 0 -> nextbyte (T =?)
"rol %4\n\t" / / 1 verschuiven MSB zijde (T = 11)
"rjmp. + 0\n\t" / / 2 nop nop (T = 13)
'cbi %0, %1\n\t' / / 2 signaal laag (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"nop\n\t" // 1 nop (T = 18)
"rjmp nextbit\n\t" / / 2 bitcount! = 0 -> nextbit (T = 20)
"nextbyte:\n\t" // - label -
"ldi %5, 8\n\t" / / 1 reset bitcount (T = 11)
"ld %4, %a8+\n\t" // 2 val = *p++ (T = 13)
'cbi %0, %1\n\t' / / 2 signaal laag (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"nop\n\t" // 1 nop (T = 18)
"dec %9\n\t" / / 1 afname bytecount (T = 19)
"Brně nextbit\n\t" / / 2 als bytecount! = 0 -> nextbit (T = 20)
::
);
De beste manier om te begrijpen van de werking van deze sectie is te overwegen verschillende case scenario's, en de montage-code regel voor regel te volgen. We weten bijvoorbeeld dat om te verzenden een waarde van 255, we afgeven van 8 bits moeten met een tijdschema dat overeenkomt met een 1. Met andere woorden, de digitale Pin verbonden met de WS281X hoog moet blijven voor 13 cycli (0.8125μs), en laag voor 7 (0.4375μs). De bovenstaande code bereiken? Laten we eens kijken wat er gebeurt wanneer we voor het eerst verzenden:
ASM vluchtige (
"nextbit:\n\t" / / Dit is alleen een label voor de regie van de sprongen hieronder.
"sbi %0, %1\n\t" / / het signaal is ingesteld op hoog, instructie gebruikt 2 cycli.
"sbrc %4, 7\n\t" / / True. Verzenden 255 impliceert huidige MSB is 'set' (= 1).
"mov %6, %3\n\t" / / Dit is uitgevoerd. "tmp" is ingesteld op hoog.
"dec %5\n\t" / / Bit wordt overgebracht, verlagen van de bit-teller.
"nop\n\t" / / moet actief om de 13 klokcycli.
"st % a2, %6\n\t" / / de waarde "tmp" schrijven naar de poort (pin nog hoog).
"mov %6, %7\n\t" / / Set "tmp" te laag voor de volgende pass door de lus.
"breq nextbyte\n\t" / / False. Beetje teller is niet 0, 1 cyclus gebruiken en wilt doorgaan.
"rol %4\n\t" / / verschuiven de bytewaarde MSB zijde.
"rjmp. + 0\n\t" / / Idle voor 2 klokcycli. Fase bereikt T = 13.
'cbi %0, %1\n\t' / / signaal is ingesteld op laag.
"rjmp. + 0\n\t" / / Idle voor 2 klokcycli.
"nop\n\t" / / Idle voor 1 klokcyclus.
"rjmp nextbit\n\t" / / Bit teller was niet 0 zo ga naar volgende beetje. T = 20.
);
Dus de instructies die daadwerkelijk worden uitgevoerd genereren een signaal op de data pin thats 13 cycli hoog (0.8125μs) en 7 LOW (0.4375μs), dus een beetje sturen met een waarde van 1 naar de WS281X. Als we blijven bestuderen wat de code doet als de rest van de bits worden verzonden, en wat het doet als andere waarden dan 255 worden gebruikt, zullen we een dieper begrip van deze specifieke uitvoering van bitbanging.
Persoonlijk hoop ik dat u deze tutorial nuttig vinden om aan de slag met uw eigen communicatie protocollen telkens wanneer het noodzakelijk is bitbanging!