LED Etch-a-Sketch (Full Instructable) (6 / 10 steg)
Steg 6: Programvaran
Som med alla C-program har vi först omfattar ut. Du behöver io.h och interrupt.h för detta projekt. Nästa, definierade jag några symboler så att det skulle vara lättare att arbeta med dem i programmet. Jag tycker det är lättare att veta vad jag gör när jag använder SPI_PORT istället för PORTB. Sedan har vi funktionen prototyper. inget alltför speciellt här. Slutligen i detta avsnitt har vi våra globala variabel definitioner. Det finns några jag vill få din uppmärksamhet till.
int currentRow = 0;
unsigned int kråka = 0, cCol = 0;
int rowRead, colRead, faktor.
int soft_prescaler = 0;
osignerade långa ritning [8] = {0}.
Variabeln currentRow, har som namnet antyder, värdet av den aktuella raden som vi visar upp. Du ser exakt vad det används till lite senare. Kråka och cCol håll markörens raden och kolumnen värden. rowRead och colRead hålla ADC läser värden från krukor. Dessa kommer att användas vid beräkningen av värdet på variablerna kråka och cCol. Soft_prescaler används för att ytterligare dela upp klockan med programvaran; Vi ska diskutera det senare. Slutligen, den viktigaste variabeln är en vektor av osignerade långa ints kallas ritning. Du kommer att märka denna matris innehåller åtta element-ett för varje rad. Detta gäller 32-bitar som styr lysdioderna för varje kolumn.
Nu ska vi gå till vissa funktioner. Vi börjar med huvudfunktionen. Efter att ha utfört uppsättningen upp timern, SPI, ingångar och utgångar in vi huvudprogrammet ögla. Den första tror inuti denna slinga är en om uttalande.
om (räknare < cykler)
{
rowReadings [counter] = Analog_Read(PC4);
colReadings [counter] = Analog_Read(PC5);
räknaren ++;
}
Detta helt enkelt ta en provtagning av ADC mätvärden över så många cykler. Detta används för att beräkna ett genomsnitt på ADC läsning och förhindrar problem med ritningen. Att ange en annan kommer att visa hur detta görs.
annat
{
Genomsnitt alla rad- och Col ADC avläsningar
colRead = CycAvg(colReadings);
rowRead = CycAvg(rowReadings);
räknare = 0;
Efter har vi ett urval av ADC värden för rad- och kolumner kan vi i genomsnitt dem med hjälp av funktionen CycAvg. Detta är bara en generisk genomsnitt funktion som summerar värdena och dividerar sedan med det totala antalet värden. Jag kommer inte prata om det här som du kan hitta det i koden. Tja, nu när vi har ADC avläsningarna vill vi från krukor, måste vi omvandla detta till en rad och en kolumn nummer så vi kan placera markören där. Hanteras detta nästa.
om (colRead / DIVISOR ! = cCol & & colRead / DIVISOR < DIMENSION)
{
cCol = colRead / DIVISOR;
}
Markören kolumnvärdet är rättframt att beräkna. Vi kontrollerar att det nya värdet är inte samma som det gamla värdet (eftersom det skulle vara nödvändigt att räkna om det) och vi se till att markören inte går utöver våra 16 x 16 ritning yta. Sedan tilldela vi värdet på ADC behandlingen dividerat med värdet av DIVISOR. NÄMNAREN är bara det maximala värdet av ADC dividerat med måtten på displayen (1024/16 = 64, så varje 64 vi är på en annan rad eller kolumn). Markören radens värde är lite mer komplicerat.
om (rowRead / DIVISOR ! = kråka & & rowRead / DIVISOR < DIMENSION)
{
om (rowRead / DIVISOR > = 8)
{
Kråka = rowRead / DIVISOR - 8.
faktor = 0;
}
annat
{
Kråka = rowRead / DIVISOR;
faktor = 16.
}
}
Så vad är med detta extra om uttalande? Detta går tillbaka till hur vi trådbunden upp displayerna som om det vore faktiskt en 8 x 32 skärm istället för en 16 x 16. De första 16 bitarna i vår ritning variabel motsvarar de första 16 kolumnerna på 8 x 32 displayen. De resterande 16 bitarna motsvarar de andra 16 kolumnerna. När vi gör displayen 16 x 16 vi flyttar de senast 16 kolumnerna och placera dem under granar 16 kolumner på konstgjord väg skapa 8 fler rader. Om vi vill beräkna raden markören på samma sätt som vi gjorde kolumnen markören kan vi sluta med ett värde mer än 8. Kom ihåg att vi bara har åtta rader eftersom våra displayen är tekniskt 8 x 32 så detta skapar ett problem. Så om resultatet av uppdelningen av ADC med DIVISOR är större än 8 måste vi dra ifrån 8 och använda den som raden, kan annars vi bara ta resultatet är. Vi måste också hålla reda på om ritningen som sker i de första 16 kolumnerna (rader 9-16) eller de senast 16 kolumnerna (rader 1-8). Det är där variabeln faktor kommer in. Detta borde göra mer känsla när vi faktiskt tilldelar ett värde till variabeln ritning nästa.
ritning [cRow] | = (0x80000000 >> (cCol + faktor));
Ganska lite är gjort här. Först ger vi matrisen våra markören kolumn som index och sedan ska vi bitvisa eller detta med nästa del. Vi använder bitvisa eller här eftersom vi vill hålla alla bitar som redan angetts, där de är och endast ange en motsvarande till den markörplaceringen. På höger sida av operatören, vi skifta en enda bit till sin rätta plats. Till exempel, anta att värdet på cCol är 4 och kråkan är 5. I det här scenariot ritning [5] | = (0x80000000 >> (4 + 16)) = 0b0000000000000000|0001000000000000. För att bättre klargöra vad bitar motsvarar vad, de 16 mest signifikanta bitarna motsvarar kolumnerna i rader 1-8, motsvarar de 16 minst signifikanta bitarna rader 9-16.
Den sista delen av denna loop kontrollerar om båda luta växlar är stängda på samma gång. Det är mycket enkel och bygger på att det finns en hög sannolikhet att två växlar stängs samtidigt när enheten skakas.
Rensa skärmen om tilt sensorer är utlöst.
om (bit_is_clear(PINC,0) & & bit_is_clear (PINC, 1))
{
clrscr();
}
Nu när ritningen är klar måste vi få det till displayen. Detta tas om hand av avbryta tjänsten rutin (ISR) som utlöses på varje timer overflow. Låt oss titta på.
ISR (TIMER0_OVF_vect)
{
soft_prescaler ++;
om (soft_prescaler == SFT_PRESCAL_MAX)
{
currentRow = (currentRow + 1) % 8.
Överföra Data på SPI
SPI_Transmit(Drawing[currentRow]);
PORTD = (1 << currentRow);
soft_prescaler = 0;
}
}
Det första vi gör i ISR är öka den mjuka prescaler. Den mjuka prescaler i grunden kan vi göra varje rad är visas något annat än vad vi kan med den ATmega hårdvara prescaler. Om man ser till funktionen Timer_Init() ser du vi prescale klockan 8 med hårdvara. Så blir vår 16 MHz klocka 2MHz. Med en mjuk prescaler med ett högsta värde på 15 kan vi få till tiden visas varje rad ner till ca 133 kHz. Detta skulle inte vara möjligt med en hårdvara prescaler. Hur som helst, låt oss gå vidare.
När vi når den mjuka prescaler max vi först bestämma vilken rad vill vi Visa. Varje gång avbryta bränderna visas bara en enda rad. Detta händer så snabbt att ögonen inte kan se ändringarna och så bilden ser solid. Denna teknik kallas multiplexing och det används för massor av applikationer. Så här anger du den aktuella raden lägger vi först till en till att öka på rad och sedan mod det med 8 så att värdet alltid kommer någonstans mellan 0-7. Sedan skickar vi ritningen för den aktuella raden till funktionen SPI_transmitt för att överföra uppgifterna till 595s. Efter att vi satt en av Port D stift hög motsvarande till den aktuella raden. Minns dessa stift är anslutna till ULN2803s. Slutligen, vi återställa den mjuka prescaler för att noll och gör det igen.
Det är ganska mycket det. Alla andra funktioner stöder bara dessa två. Definitivt kolla upp dem i den bifogade uppförandekoden.