/*****************************************************
Project : SpinLED
Version : 1.0
Date : 2008-06-01
Author : Sara Hedfors, Niclas Evestedt & Simon Berthilsson
Chip type : ATmega16
Clock frequency : 16.000 000 MHz
*****************************************************/
// metoder:
void initHardware(); // Initierar processorn: Sätter in- respektive utportar,
// initialiserar timer 1 och 2, initialiserar interrupt-hanteringen,
// initialiserar UART-hanteringen samt initialiserar AD konverteraren
void nextByte(); // Hanterar inkommande paket från UART-kommunikationen, med hjälp av
// metoden getchar, avkodar dem och sätter rätt tillsånd samt
// rotation. Om data är del i en bild anropas metoden putByte och om
// del i en text anropas putchar. Metoden håller också koll på hur
// många paket som kommit.
void putByte(unsigned int pos, unsigned char byte); // Med hjälp av metoden spi_writebyte skrivs
// datat i parametern byte till adressen pos i det externa minnet
void putchar(char c); // Med hjälp av metoden spi_writebyte skrivs tecknet i parametern c
// till det externa minnet (som text). En global variabel posPutchar
// håller koll på vilken adress tecknet ska skrivas till.
void putTemp(int temp); // Gör om parametern temp till en sträng, och skriver sedan med hjälp
// av putchar tecknen i strängen till det externa minnet. Dessutom
// läggs ett specialtecken, °C, till sist i strängen.
int readTemp(); // Hämtar aktuell information från AD-omvandlaren och räkar om den
// till temperatur
void spi_writebyte(unsigned char byte); // Tar parametern byte, delar upp den i bitar och
// skickar dessa bitar en och
// en till det externa minnet, samtidigt som metoden genererar
// klockpulser till minnet.
// interrupts:
interrupt [USART_RXC] void usart_rx_isr(void); // Mjukvaruinterrupt från den inbyggda UART-
// mottagaren när nytt paket anländer. Sätter variabeln newByte till
// 1 för att markera att nytt paket anlänt, samt spara data i uartData
interrupt [EXT_INT0] void newLap(void); // Hårdvaruinterrupt från hallgivaren. Räknar ut
// föregående varvtid med hjälpa av timer 1, samt hur ofta en ny
// kolumn av bilden ska skiftas ut. Nollställer timer 2 och sätter
// dess compare match till den uträknade tiden. Här hanteras också
// eventuell rotation av bilden genom att flytta en pekare till
// bildens startposition framåt eller bakåt.
interrupt [TIM2_COMP] void updateImage(void); // Mjukvaruinterrupt från timer 2. Hanterar
// utskiftningen av en kolumn med hjälp av metoden spi_writebyte.
// Generering av klockpulser till minnet och till skiftregisterna
// talar om hur många bitar som ska skiftas ut.
// globala variabler:
unsigned char state = TEMP; // state anger vilket tillstånd
unsigned char lapsPerRotation = 0x00; // vrider 1 px vart lapsPerRotation varv
unsigned int posPutchar = 0; // position putchar använder (absolut plats i minnet där
// putchar skriver för tillfället)
unsigned int readPackets = 0; // håller reda på hur många packets som vi redan läst in.
unsigned int pointerWrite = 0; // plats i minnet som det skrivs till för tillfället
// (pekar på början av bilden)
unsigned int pointerRead = 0; // plats i minnet som det läses från för tillfället (pekar
// på början av bilden)
unsigned int pointer = 0; // plats i minnet som det läses från för tillfället (pekar
// på var exakt vi befinner oss i bilden)
unsigned int counter = 0; // håller koll på var vi befinner oss på varvet (fysiskt)
unsigned char num_interrupts = 3; // antalet interrupts som 8bitars timern måste cirkulera
// genom innan dax att uppdatera (räknas ned varje
//interrupt)
unsigned char fix_num_interrupts = 3; //antalet interrupts som 8bitars timern måste cirkulera
// genom innan dax att uppdatera (sätts 1 ggr/varv)
unsigned char remainder; // detta plus fix_num_interrupts*0xff motsvarar hur lång
// tid en pixel i sidled varar
unsigned char uartData; // används för att spara det som hämtas från UART
bit newByte = 0; // flagga om nytt UART-paket anlänt
unsigned int error = 0; // räknar antalet paket som anlänt utan att hunnit tagits
// om hand om
// globala konstanter:
const unsigned int asciiToLEDConverter[94][5] = // asci-tabell som putchar använder sig av
{{0x0000,0x0000,0x0000,0x0000,0},{0x0000,0x03fb,0x0000,0x0000,0},{0x0000,0x0300,0x0000,0x0300,0},
{0x0048,0x03fe,0x0048,0x03fe,0},{0x0300,0x0300,0x00fe,0x0101,0x00c6},
{0x0187,0x0198,0x0066,0x0386,0},{0x0019,0x0025,0x0025,0x0019,0},{0x0000,0x0380,0x0000,0,0},
{0x0000,0x00fc,0x0102,0x0201,0},{0x0000,0x0201,0x0102,0x00fc,0},{0x0140,0x0080,0x0140,0x0000,0},
{0x0020,0x0070,0x0020,0x0000,0},{0x0000,0x0003,0x0000,0x0000,0},{0x0020,0x0020,0x0020,0x0000,0},
{0x0000,0x0003,0x0003,0x0000,0},{0x0003,0x001c,0x00e0,0x0300,0},{0x01fe,0x0201,0x0201,0x01fe,0},
{0x0081,0x0101,0x03ff,0x0001,0},{0x0187,0x0219,0x0261,0x0181,0},{0x0102,0x0221,0x0251,0x018e,0},
{0x03e0,0x0020,0x0020,0x03ff,0},{0x03c6,0x0221,0x0221,0x021e,0},{0x01fe,0x0211,0x0211,0x010e,0},
{0x0300,0x0207,0x0238,0x03c0,0},{0x01de,0x0221,0x0221,0x01de,0},{0x01c2,0x0221,0x0221,0x01fe,0},
{0x0000,0x0022,0x0000,0x0000,0},{0x0000,0x0023,0x0000,0x0000,0},{0x0010,0x0028,0x0044,0x0082,0},
{0x0028,0x0028,0x0028,0x0028,0},{0x0082,0x0044,0x0028,0x0010,0},{0x0180,0x020d,0x0230,0x01c0,0},
{0x003e,0x0041,0x005d,0x003d,0},{0x01ff,0x0210,0x0210,0x01ff,0},{0x03ff,0x0211,0x0211,0x01ee,0},
{0x00fc,0x0102,0x0201,0x0201,0},{0x03ff,0x0201,0x0102,0x00fc,0},{0x03ff,0x0221,0x0221,0x0221,0},
{0x03ff,0x0220,0x0220,0x0220,0},{0x01fc,0x0202,0x0211,0x021f,0},{0x03ff,0x0020,0x0020,0x03ff,0},
{0x0201,0x03ff,0x0201,0x0000,0},{0x0206,0x0201,0x0201,0x03fe,0},{0x03ff,0x0030,0x00c8,0x0307,0},
{0x03ff,0x0001,0x0001,0x0001,0},{0x03ff,0x01c0,0x01c0,0x03ff,0},{0x03ff,0x00e0,0x001c,0x03ff,0},
{0x01fe,0x0201,0x0201,0x01fe,0},{0x03ff,0x0220,0x0220,0x01c0,0},{0x01fc,0x0202,0x0207,0x01fd,0},
{0x01ff,0x0230,0x0228,0x01c7,0},{0x01ce,0x0221,0x0221,0x011e,0},{0x0200,0x03ff,0x0200,0x0200,0},
{0x03fe,0x0001,0x0001,0x03fe,0},{0x03fc,0x0003,0x000c,0x03f0,0},{0x03fc,0x0007,0x000f,0x03fc,0},
{0x03cf,0x0030,0x0030,0x03cf,0},{0x03e2,0x0021,0x0021,0x03fe,0},{0x0207,0x0219,0x0261,0x0381,0},
{0x007f,0x0290,0x0090,0x007f,0},{0x027f,0x0090,0x0090,0x027f,0},{0x027e,0x0081,0x0081,0x027e,0},
{0x0080,0x0100,0x0200,0x0180,0},{0x0001,0x0001,0x0001,0x0001,0},{0x0200,0x0100,0x0080,0x0000,0},
{0x0012,0x0025,0x0025,0x001f,0},{0x03ff,0x0021,0x0021,0x001e,0},{0x001e,0x0021,0x0021,0x0012,0},
{0x001f,0x0021,0x0021,0x03ff,0},{0x001e,0x0029,0x0029,0x001a,0},{0x0010,0x00ff,0x0110,0x0080,0},
{0x0011,0x0029,0x0029,0x001e,0},{0x03ff,0x0010,0x0020,0x001f,0},{0x0020,0x00bf,0x0001,0x0000,0},
{0x0002,0x0001,0x00bf,0x0000,0},{0x03ff,0x0008,0x0014,0x0023,0},{0x0200,0x03fe,0x0001,0x0000,0},
{0x003f,0x0018,0x0008,0x001f,0},{0x003f,0x0010,0x0020,0x001f,0},{0x001e,0x0021,0x0021,0x001e,0},
{0x001f,0x0028,0x0028,0x0010,0},{0x0010,0x0028,0x0028,0x001f,0},{0x0021,0x003f,0x0021,0x0010,0},
{0x0012,0x0029,0x0025,0x0013,0},{0x0020,0x01ff,0x0021,0x0002,0},{0x003e,0x0001,0x0001,0x003f,0},
{0x003f,0x0001,0x0006,0x0038,0},{0x003e,0x0003,0x0007,0x003e,0},{0x0033,0x000c,0x000c,0x0033,0},
{0x0032,0x0009,0x0009,0x003e,0},{0x0023,0x0025,0x0029,0x0031,0},{0x0012,0x0025,0x00a5,0x001f,0},
{0x0012,0x00a5,0x0025,0x009f,0},{0x001e,0x00a1,0x0021,0x009e,0}
}; // specialtecken: $ är utbytt mot grader Celcius
// [ är utbytt mot Å
// \ är utbytt mot Ä
// ] är utbytt mot Ö
// { är utbytt mot å
// | är utbytt mot ä
// } är utbytt mot ö
/******* slut funktioner, makron, konstanter ***********************/
if(readPackets==0)
{ // paket nr 0:
// bit 7:6 - state
// bit 5:0 - rotationsinformation:
// bit5=1 => moturs, bit5=0 => medurs
// bit4:0 => rotationsfrekvens
rotation = (uartData & 0x3F); // bitmask för att ta fram lapsperrotation
state = uartData >> 6; // bitmask för att ta fram state
if(state == TEMP){ lapsPerRotation = rotation; }
else { readPackets++; }
}
else if(readPackets==1)
{ // paket nr 1:
// bit 7 - 0 => lägga ut befintlig bild som redan finns i minnet
// 1 => skriva in ny bild i minnet
// bit 6:0 - bildnummer
if(!(uartData & 0x80)) // om bara lägga ut bild som redan finns i minnet
{
readPackets = 0;
pointerRead = (uartData & 0x7F)*IMAGE_SIZE_BYTE; // hämtar bildnummer,
// konverterar till absolut adress i minnet
lapsPerRotation = rotation;
}
else // om vi bara ska skriva in ny bild i minnet
{
pointerWrite = (uartData & 0x7F)*IMAGE_SIZE_BYTE; // hämtar bildnummer,
// konverterar till absolut adress i minnet
posPutchar = pointerWrite; // sätter posPutchar till absolut adress i minnet
}
}
else if(state == IMAGE && readPackets < NUM_PACKETS)
{
putByte(pointerWrite + (readPackets-2), uartData);
readPackets++;
}
else if(state==TEXT && readPackets < NUM_TEXT_PACKETS)
{
putchar(uartData);
readPackets++;
}
else
{
readPackets = 0; // nytt set med data, nollställ readPackets och anropa metoden igen
nextByte();
}
}
void putByte(unsigned int pos, unsigned char byte) // skriver byte via SPI till minne
{
#asm("cli")
SPI_SS = 0; // initiatiera kommunikation genom att sätta ~SS låg
spi_writebyte(0x06); // op-kod för aktivera skriva till minne
SPI_SS = 1;
#asm("nop")
SPI_SS = 0;
spi_writebyte(0x02);
spi_writebyte((unsigned char)(pos >> 8)); // skriv första delen av adressen
spi_writebyte((unsigned char)(pos)); // skriv andra delen av aderssen
spi_writebyte(byte);
SPI_SS = 1; // avsluta kommunikation genom att sätta ~SS hög
#asm("sei")
}
void putchar(char c) // skriver tecken via SPI till minne
{
int i;
int j;
if (!(c < 0x7E && c > 0x1f)){ c = 0x20; } // om ogiltigt tecken, skriv space = 0x20
for (i=0; i<CHAR_WIDTH; i++)
{
#asm("cli")
SPI_SS = 0; // initiatiera kommunikation genom att sätta ~SS låg
spi_writebyte(0x06); // op-kod för aktivera skriva till minne
SPI_SS = 1;
#asm("nop")
SPI_SS = 0;
spi_writebyte(0x02); // op-kod för skriva till minne
spi_writebyte((unsigned char)(posPutchar >> 8)); // skriv första delen av adressen
spi_writebyte((unsigned char)(posPutchar)); // skriv andra delen av adressen
for (j=0; j<3; j++){ spi_writebyte(0x00); } // skicka 3 byte nollor
spi_writebyte((unsigned char)(asciiToLEDConverter[c-0x20][i] >> 8));// skicka översta
// delen av tecknet
spi_writebyte((unsigned char)(asciiToLEDConverter[c-0x20][i])); // skicka understa
// delen av tecknet
for (j=0; j<2; j++){ spi_writebyte(0x00); } // skicka 2 byte nollor
SPI_SS = 1; // avsluta kommunikation genom att sätta ~SS hög
#asm("sei")
posPutchar = posPutchar + BYTE_PER_KOLUMN;
}
}
int readTemp() // hämtar aktuell temperatur
{
int temp;
int temphel;
int temprest;
ADCSRA|=0x40; // Starta ADC genom att sätta bit 6 i ADCSRA till 1
while ((ADCSRA & 0x10) == 0); // Vänta på resultat
ADCSRA|=0x10; // Cleara genom att sätta bit 4 i ADCSRA till 1
temp = (ADCW)*11-2975; // (ger 125 ggr temperaturen)
temphel = temp/125;
temprest = temp%125;
if(temprest > 62){ return (temphel + 1); }
return temphel;
}
void putTemp(int temp) // skriver temp till utenhet
{
unsigned int pos;
char tempstr[4];
itoa(temp, tempstr);
if(tempstr[1] == 0) // ensiffrig temperatur
{
putchar(tempstr[0]); // lägg ut temperatur
putchar(0x24); // lägg till grader C
for(pos=0; pos<(KOLUMNS_PER_LAP/CHAR_WIDTH-15); pos++){ putchar(' '); } // fyll på med
// tomt
}
else // tvåsiffrig temperatur
{
putchar(tempstr[0]); // lägg ut temperatur siffra 1
putchar(tempstr[1]); // lägg ut temperatur siffra 2
putchar(0x24); // lägg till grader C
for(pos=0; pos<(KOLUMNS_PER_LAP/CHAR_WIDTH-16); pos++){ putchar(' '); } // fyll på med
// tomt
}
}
// hårdvaruinterrupt varje nytt varv från hallmätare
interrupt [EXT_INT0] void newLap(void)
{
static unsigned char lapcounter = 1;
static unsigned int lastpointer = 0;
unsigned int lastLapTime;
unsigned int pixeltime;
num_interrupts = pixeltime/0xFF; // Hur många gånger ska timern snurra runt innan dax för
// uppdatering
fix_num_interrupts = num_interrupts;
remainder = pixeltime%0xFF;
if(num_interrupts != 0){ OCR2=0xFF; } // Sätt compare match till rätt sak
else{ OCR2=remainder; }
if (1 == (lapsPerRotation & 0x1F)) // om vi ska vrida två px
{
if(lapsPerRotation & 0x20) // om bit5=1 => sätt pointer till två "kolumner framåt"
// => vrid motsols
{
if(lastpointer == (IMAGE_SIZE_BYTE - BYTE_PER_KOLUMN)){ pointer = BYTE_PER_KOLUMN; }
else if(lastpointer == (IMAGE_SIZE_BYTE - 2*BYTE_PER_KOLUMN)){ pointer = 0; }
else { pointer = lastpointer + 2*BYTE_PER_KOLUMN; }
lastpointer = pointer; // nollställ counter
}
else // om bit5=0 => sätt pointer till en två kolumner "bakåt" => vrid medsols
{
if(lastpointer == 0){ pointer = (IMAGE_SIZE_BYTE - 2*BYTE_PER_KOLUMN); }
else if(lastpointer == BYTE_PER_KOLUMN){ pointer = (IMAGE_SIZE_BYTE -
BYTE_PER_KOLUMN); }
else { pointer = lastpointer - 2*BYTE_PER_KOLUMN; }
lastpointer = pointer; // nollställ counter
}
}
else if (lapcounter == ((lapsPerRotation-1) & 0x1F)) // om vi ska vrida en px
{
lapcounter = 1; // nollställ lapcounter
if(lapsPerRotation & 0x20) // om bit5=1 => sätt pointer till en "kolumn framåt"
// => vrid motsols
{
if(lastpointer == (IMAGE_SIZE_BYTE - BYTE_PER_KOLUMN)){ pointer = 0; }
else { pointer = lastpointer + BYTE_PER_KOLUMN; }
lastpointer = pointer; // nollställ counter
}
else // om bit5=0 => sätt pointer till en kolumn "bakåt" => vrid medsols
{
if(lastpointer == 0){ pointer = (IMAGE_SIZE_BYTE - BYTE_PER_KOLUMN); }
else { pointer = lastpointer - BYTE_PER_KOLUMN; }
lastpointer = pointer; // nollställ counter
}
}
else // om ingen rotation ska ske
{
if(lapsPerRotation & 0x1F != 0){ lapcounter++; }
pointer = lastpointer;
}
counter = 0; // nollställ kolumnräknaren
}
// mjukvaruinterrupt från timer 2
interrupt [TIM2_COMP] void updateImage(void)
{
int i;
// interrupt från timer 2. Vi ska uppdatera LED vart num_interrupt:e interrupt, eftersom en
// ny kolumn ska skiftas ut mer sällan än vad timern räknar runt
if(num_interrupts != 0)
{
if(num_interrupts == 1){ OCR2=remainder; }
num_interrupts--;
}
else // om dags att skifta ut
{
// hanterar timer 2:s interrupt-återställning:
TCNT2=0x00;
num_interrupts = fix_num_interrupts;
if(num_interrupts != 0){ OCR2=0xFF; }
if(counter < KOLUMNS_PER_LAP) // om det finns kvar på bilden
{
SPI_SS = 0; // initiatiera kommunikation genom att sätta ~SS låg
spi_writebyte(0x03); // op-kod för läsa från minne (dvs be minne skicka ut till
// LED)
spi_writebyte((unsigned char)((pointerRead + pointer) >> 8)); // skriv första delen
// av adressen
spi_writebyte((unsigned char)(pointerRead + pointer)); // skriv andra delen
// av aderssen
LED_OUTPUT_DISABLE = 1;// släck alla LED under utskiftningen
for(i=0; i<57; i++) // vänta 57 klockcykler => klockar ut 56 ggr på LED
// (skiftregister "snor" en klockcykel)
{
SPI_CLK_MINNE = 1; // skicka klockpuls till minne
SPI_CLK_LED = 1; // skicka klockpuls LED (skiftregister)
#asm("nop")
SPI_CLK_MINNE = 0;
SPI_CLK_LED = 0;
}
LED_OUTPUT_DISABLE = 0; // tänd alla LED efter utskiftningen
SPI_SS = 1; // avsluta kommunikation genom att sätta ~SS hög
// om bilden ej är slut, räkna upp pointer
if (pointer < IMAGE_SIZE_BYTE - BYTE_PER_KOLUMN){ pointer = pointer +
BYTE_PER_KOLUMN; }
// om bilden är slut, nollställ pointer
else { pointer = 0; }
}
counter++; // räknar hur många gånger vi kommer hit per varv
}
}
/******* slut interrupts *******************************************/