U-Disp - The Digg (tm) display (Open Source) (16 / 18 steg)
Steg 16: Projektbeskrivning - Firmware
Firmware är skriven i AVR-GCC och ca 60% av den tillgängliga program plats 2 KB i ATtiny2313.Det är egentligen ganska enkel mjukvara. I princip kontrollera allt det gör det för att ta emot kommandot paket på serieporten på Tiny2313, om kommandot är för denna display, och om det är avkoda kommandot och lagra bitpattern in i minnet. Om kommandot är för en annan visning det kommer bara ge det vidare till nästa visning.
Firmware kan bli data överför båda som källa koden och som som förkompilerade hexfile i avsnittet ladda ner på projektets webbplats.
Meddelanden mellan Windows-tjänsten och mikrokontroller på displayerna är formaterade i ganska kompakt 10 byte paket. För att på ett tillförlitligt sätt upptäcka i början av varje paket den mest signifikanta biten [MSB] (stycke 7 som är) i den första byten sätts till noll, resten av byte är inställda på en.
Eftersom varje siffra har åtta lysdioder (de sju segment plus decimalkommat [DP]) som ska kontrolleras kan inte vi bara mappa varje siffra på en enda byte eftersom som skulle få MSB satt till noll (i början av packet indikator) om decimaltecknet var avstängd. Så up blir sin egen byte, men eftersom vi har åtta siffror, och således åtta DPs senaste DP måste placeras får någon annanstans eller vi in i samma problem igen. De senaste siffrorna DP är pålagd den första byten på lite sex...
Den första byten är är förutom signalering packet början och hålla de senaste siffrorna DP även fyra bitar (stycke 3 till 0) som anger vilka visas (enhet) detta meddelande är till. Den första visningen är numrerade 0 nästa 1 och sist visningen är nummer 15. När den första visningen i en kedja mottar ett meddelande kontrollerar enhetsnumret och ser om det är 0. Om det är meddelandet bearbetas det. Om det inte sedan display minskar värdet i enheten bits och passerar längs hela meddelandet till nästa visning i kedjan. Som visar också kontrollerar bitarna för noll och agerar därefter.
Lite nr | 7 6 5 4 3 2 1 0-------+---------------------------------------------Byte 0 | 0 d7p x x u3 u2 u1 u0 1 | 1 G F E D C B a (siffran 0) 2 | 1 G F E D C B a (siffran 1) 3 | 1 G F E D C B a (siffran 2) 4 | 1 G F E D C B a (siffran 3) 5 | 1 G F E D C B a (4 siffror) 6 | 1 G F E D C B a (digit 5) 7 | 1 G F E D C B a (siffran 6) 8 | 1 G F E D C B a (siffran 7) 9 | 1 d6p d5p d4p d3p d2p d1p d0p
Tabellen ovan visar formatet på meddelandet.
- d0p är siffran 0 decimaltecknet, d1p är Digit1 decimaltecknet och så vidare...
- U0, u1, u2, u3 är de bitar som berättar en att meddelandet är avsett för.
- A, B, C, D, E, F, G är segmenten av varje siffra.
- x är en oanvänd bit för framtida expansion.
Kommando tas emot
Ta emot data från datorn görs med avbryter. Varje inkommande karaktär generats ett avbrott och får hanteras av funktionen nedan. Det samlar tio tecken i en buffert, kontrollerar om kommandot är för denna display och avkodar det och lagrar den om det är. Om meddelandet är av en annan display det minskar destination nybble och skicka hela meddelande till nästa visning i kedjan i hopp om att en display senare blir det att hantera meddelandet.
Detta är avbrottshanterare för UART data received./// samla alla inkommande tecken i rxBuf-buffert tills / / vi fick alla tio charachets av meddelandet / / sedan kontrollera om meddelandet är för denna enhet. Om det är, avkoda den / / data och Lägg den i matrisen display. Othervise passera den på att den / / nästa skärm i chain.//ISR(SIG_USART0_RX) {unsigned char rx, rx = UDR; / / få inkommande data / / om MSB om tecknet inte anges då det är en start byte om (rx < 128) {rxPtr = 0; / / så nollställs buffert pekaren} rxBuf [rxPtr ++] = rx; / / Store karaktären i bufferten / / om vi fick hela meddelandet sedan bearbeta det. om (rxPtr == MSG_LENGTH) {/ / kontrollera om meddelandet är för oss om ((rxBuf [0] & 0x0F) == 0) {/ / Ja, avkoda och lagra i minnet för (rx = 0; rx < 8; rx ++) {siffror [rx] = (rxBuf [rx + 1] & 0x7F);} om (bit_is_set(rxBuf[0],6)) siffror [7] | = 0x80; för (rx = 0; rx < 7; rx ++) {om (bit_is_set(rxBuf[MSG_LENGTH-1],rx)) siffror [rx] | = 0x80;}} annat {/ / inte för oss, så vi bara vidarebefordra den rxBuf [0]--; / / minska enhet-counter / / kopiera hela meddelandet till sändnings bufferten för (int jag = 0; Jag txBuf [i] = rxBuf [i]; } txPtr = 1; Nästa tecken som ska skickas av ISR UDR = txBuf [0]; Skicka det första tecknet manuellt att utlösa ISR} rxPtr = 0; Nollställs ta emot buffert pekaren} / / se till att pekaren inte kan peka utanför bufferten om (rxPtr > = MSG_LENGTH) rxPtr = MSG_LENGTH - 1;}
Siffriga multiplexing
Eftersom siffrorna är multiplexed (endast en av siffrorna drivs i taget) firmware kontinuerligt lyser upp siffran efter siffran för några millisekunder i så ögat/hjärnan anser att alla siffror är på samtidigt,.
Om du vill spara jag/O-pins på mikrokontroller en speciell multiplexering teknik kallas Charlieplexing används. Det drastiskt minskar antalet nödvändiga stiften på mikrokontroller men kräver en mer komplex programvara styra denna.
Timer0 används för att generera avbrott för siffran multiplexing
Definiera de i/o stift som är anslutna till charlieplexed / / display. Suffixet B eller D gör det lättare att hålla koll / / hamnen lite tillhör. #define CHARLIE_1_B PB3 #define CHARLIE_2_B PB1 #define CHARLIE_3_D PD5 #define CHARLIE_4_D PD3 #define CHARLIE_5_D PD4 #define CHARLIE_6_B PB0 #define CHARLIE_7_B PB4 #define CHARLIE_8_B PB2 #define CHARLIE_9_D PD6uint8_t siffror [8]. Den array som innehåller siffror ska visas på den bildskärm / / / detta är den avbryta tjänsten rutin som ska få kallas / ofta tillräckligt för att inte Visa någon synlig flimrar på displays.//ISR (SIG_OVERFLOW0) {statisk uint8_t currentdigit = 0; / / håller reda på siffran ska visas uint8_t segment; / / bitpattern av den nuvarande siffran TCNT0 = 128; / / återställa TCNT0 halvvägs att få en högre freq / / få lite mönster av de nuvarande siffra = siffror [currentdigit]; / / Set alla LED-portpins som ingångar så motsvarande / / segment av skärmarna kommer att vara avstängd DDRB & = ~ (_BV(CHARLIE_1_B) | _BV(CHARLIE_2_B) | _BV(CHARLIE_6_B) | _BV(CHARLIE_7_B) | _BV(CHARLIE_8_B)); DDRD & = ~ (_BV(CHARLIE_3_D) | _BV(CHARLIE_4_D) | _BV(CHARLIE_5_D) | _BV(CHARLIE_9_D)); Ange låg nivå på alla LED-port stift. Förbereder detta segment / / att tändas om PIN-koden ändras till utgång senare. Den / / visar ar för common anod-typ (gemensamma positiva) så den segment / / behöver sjunkas marken vara påslagen. PORTB & = ~ (_BV(CHARLIE_1_B) | _BV(CHARLIE_2_B) | _BV(CHARLIE_6_B) | _BV(CHARLIE_7_B) | _BV(CHARLIE_8_B)); PORTD & = ~ (_BV(CHARLIE_9_D) | _BV(CHARLIE_3_D) | _BV(CHARLIE_4_D) | _BV(CHARLIE_5_D)); Ange portpins för segment ska tändas / / vi antar bara att varje segment har sin standard / / plats i anslutningar. Det speciala fallet hanteras / / i switch-uttalande nedan. om (bit_is_set(segments,0)) DDRB | = _BV(CHARLIE_1_B); om (bit_is_set(segments,1)) DDRB | = _BV(CHARLIE_2_B); om (bit_is_set(segments,2)) DDRD | = _BV(CHARLIE_3_D); om (bit_is_set(segments,3)) DDRD | = _BV(CHARLIE_4_D); om (bit_is_set(segments,4)) DDRD | = _BV(CHARLIE_5_D); om (bit_is_set(segments,5)) DDRB | = _BV(CHARLIE_6_B); om (bit_is_set(segments,6)) DDRB | = _BV(CHARLIE_7_B); om (bit_is_set(segments,7)) DDRB | = _BV(CHARLIE_8_B); Här gör vi det lite mygel som är nödvändigt att charlieplex. Eftersom en av segmenten (varje annan) för varje bildskärm / / flyttas till 9' th anslutningen måste vi ta hand om som. / / Beroende på vilken siffra som är aktiva nu vi behöver för att hantera / / situationen i sitt eget unika sätt. / / A segmentet på den första siffran är flyttade från raden 1' th / / man 9' th linje så i princip samma sak som i gäng / / tester ovan, men endast test för segmentet A och om det är upplyst / / vi slå på 9' th linje i stället för den första raden. Vi måste sedan aktivera transistorn som hanterar gemensamma / / anod för den första siffran. Transistor för första visning / / är ansluten till 1' th raden (där A-segmentet brukar gå). så vi vänder på utdata för att fästa och Ställ in det höga. / / Nästa gång denna rutin kallas vi göra samma sak med / / den andra siffran. Men vi sedan kontrollera om B-segmentet och så vidare... switch (currentdigit) {fall 0: om (segment & 0b00000001) DDRD | = _BV(CHARLIE_9_D); DDRB | = _BV(CHARLIE_1_B); PORTB | = _BV(CHARLIE_1_B); bryta; fall 1: om (segment & 0b00000010) DDRD | = _BV(CHARLIE_9_D); DDRB | = _BV(CHARLIE_2_B); PORTB | = _BV(CHARLIE_2_B); bryta; fall 2: om (segment & 0b00000100) DDRD | = _BV(CHARLIE_9_D); DDRD | = _BV(CHARLIE_3_D); PORTD | = _BV(CHARLIE_3_D); bryta; fall 3: om (segment & 0b00001000) DDRD | = _BV(CHARLIE_9_D); DDRD | = _BV(CHARLIE_4_D); PORTD | = _BV(CHARLIE_4_D); bryta; fall 4: om (segment & 0b00010000) DDRD | = _BV(CHARLIE_9_D); DDRD | = _BV(CHARLIE_5_D); PORTD | = _BV(CHARLIE_5_D); bryta; fall 5: om (segment & 0b00100000) DDRD | = _BV(CHARLIE_9_D); DDRB | = _BV(CHARLIE_6_B); PORTB | = _BV(CHARLIE_6_B); bryta; mål 6: om (segment & 0b01000000) DDRD | = _BV(CHARLIE_9_D); DDRB | = _BV(CHARLIE_7_B); PORTB | = _BV(CHARLIE_7_B); bryta; fall 7: om (segment & 0b10000000) DDRD | = _BV(CHARLIE_9_D); DDRB | = _BV(CHARLIE_8_B); PORTB | = _BV(CHARLIE_8_B); bryta; } / / Visa nästa siffra på nästa avbrottet. Detta kan inte göras / / överst i ISR eftersom variabeln siffra behövs / / i switch-uttalande senare. currentdigit ++; om (currentdigit > 7) currentdigit = 0;}