Laboration: Teckenfönster Drivrutiner för att hantera teckenfönster. I denna laboration ska vi göra funktioner som på ett praktiskt sätt hanterar en tvåradig display av typ LM162XXX (se LCD.pdf). Displayen är utrustad med en dubbelriktad databuss och tre kontrollsignaler som är inkopplade enligt figuren till vänster. Dessutom finns en potentiometer fär att ställa in bästa kontrast. Förberedelser: Läs igenom databladen för teckenfönstret, LCD.pdf. Viktigt är att du förstår hur kommandon och tecken skickas och hur kontrollsignalerna manövreras vid skrivning och läsning. Teckenfönstret är inkopplat enligt figuren ovan och har alltså sin databuss ansluten till PORTC. Detta innebär att man måste se upp, så man inte läser ur teckenfönstret samtidigt som PORTC är ställd som utport. Lämna alltid PORTC inåtriktad när du inte skriver till displayen. Alla förberedelseuppgifter ska vara gjorda vid laborationens början. Uppgift 1. Ge ett bra skäl till varför man kan behöva läsa från displayen. Stefan Nyman, MICRONYM
Uppgift 2. Vilken styrsignal bestämmer om LCD ska ta emot ett tecken eller en instruktion? Uppgift 3. Skriv en fördröjningsloop i C. Uppgift 4. I databladet sidan 18 kan man se att nästan varje skrivning och läsning tar lite tid. Om man gör en skrivning eller läsning innan föregående är klar, blir det krångel i kommunikationen. För att göra varje skrivning till displayen helt säker, inleder vi den därför med en läsning av en flagga i ett statusregister BF, Busy Flag. Du skall göra en funktion som testar BF (den mest signifikanta biten i statusregistret) och inte återvänder förrän BF blivit = 0. Funktionen ska heta Test_busy_flag. Följande ska göras: 1. Se till att PORTC är inport. 2. Sätt RS = 0 (Instruction Register) och ställ R/W på läsning. 3. Sätt E = 1 (Enable). 4. Läs av BF (bit 7 i PORTC) och vänta tills den blivit = 0. 5. Sätt E = 0 (Disable). 2
Uppgift 5. Hur ska kontrollsignalerna RS och R/W vara satta för att data ska tolkas som ett kommando? Uppgift 6. Hur ska kontrollsignalerna RS och R/W vara satta för att data ska tolkas som tecken? Uppgift 7. Funktionen Put(ch) är en central funktion som ska sköta alla skrivningar till displayen. Styrsignalerna RS och R/W skall inte påverkas av denna funktion, men annars ska databladets figur 5 följas noggrant. 1. Det kommando/tecken som ska skrivas läggs i PORTC. 2. Gör PORTC till utport. 3. Sätt E = 1. 4. Sätt E = 0. 5. Ställ om PORTC till inport (för säkerhets skull). 3
Laborationsuppgifter: De funktioner vi skriver för LCD:n kommer att vara användbara i alla program där LCD:n används. Förutom själva funktionerna gör vi därför också en header-fil som ska utgöra en beskrivning av funktionerna. LCD-funktionerna skriver vi i LCD.c, och headerfilen kallar vi LCD.h. För att kunna skriva tecken var vi vill på LCD:n, ska vi först göra två enkla funktioner, Put_Command och Put_Character, som ska skicka ett kommando respektive tecken till displayen. Båda funktionerna har en inparameter med typen char. Funktionerna Put_Command och Put_Character ska se ut såhär: void Put_Command(char ch) { TestBF(); // Vänta tills BF=0 Hemuppgift 4????????; // Ställ in RS för kommando Hemuppgift 5 Put(ch); // Skriv till LCD:n Hemuppgift 7 } void Put_Character(char ch) { TestBF(); // Vänta tills BF=0 Hemuppgift 4????????; // Ställ in RS för tecken Hemuppgift 6 Put(ch); // Skriv till LCD:n Hemuppgift 7 } Uppgift 1. Test_busy_flag Skriv funktionen enligt hemuppgift 4. Kompilera och kontrollera att du följer databladets figur 6. Uppgift 2. Put (ch) Skriv funktionen enligt hemuppgift 7. Skriv ihop funktionerna Put_Command och Put_Character! Vi har nu ett programpaket klart för att på ett enkelt sätt hantera displayen. Vi kan skicka ett valfritt kommando och skriva ett tecken. Men innan vi kan använda displayen, måste vi starta den med ett par speciella kommandon. När strömmen slås på är den nämligen avstängd och inställd på att bara visa en rad. (Du kan se att den ena raden syns mer än den andra). 4
Uppgift 3. InitLCD Det första som ska göras med displayen, är att den ska initieras, d.v.s. ställas in så att den fungerar på önskat sätt. Med utgångspunkt från flödesplanen på sid. 5 i databladen får man följande initieringsfunktion: void InitLCD(void) { Wait_15ms(); Set_Command(); Put(0x38); // 8 bits, Lines=2, Font normal Wait_15ms(); Put(0x38); Wait_15ms(); Put(0x38); Put_Command(0x38); Put_Command(0x01); // Clear display Put_Command(0x06); // Cursor increment Put_Command(0x0F); // Disp on, cursor on Skriv själv funktionen som väntar c:a 15 ms. Vi ska nu slutligen konstruera två funktioner som ska göra kommunikationen med LCD:n enkel och behändig. Dels behöver vi en funktion som skriver ett tecken på en bestämd plats på displayen, och dels bör vi ha en funktion som skriver en textsträng på valfri plats. Uppgift 4. Write_Character Skriv en funktion som skriver ett tecken på displayen på en bestämd plats. Två parametrar ska funktionen ta emot: 1. pos bestämmer var på displayen tecknet ska skrivas. Vi ska alltså skicka ut kommandot "Set DD (Data Display) adress" enligt innehållet i pos. 2. ch är tecknet som skickas med hjälp av Put_Character. Testa! 5
Uppgift 5. Write_String Funktionen ska skriva en teckenstäng på valfri plats. Den påminner mycket om föregående funktion men med den skillnaden att adressen till en teckensträng (fält eller tabell med tecken) ska tas emot, och utskrift ska ske tills värdet 0 påträffas i strängen. Tips: Såhär kan en sträng initieras: const char String [] = "abc"; eller const char String [] = {0x41, B, 0x43, 0}; Om man skriver abc, hamnar värdet noll automatiskt som en slutmarkering. När man refererar till denna sträng, kan man antingen skriva: Write_String (&String[0]); eller Write_String (String); I båda fallen består parametern av adressen till det första värdet. Testa funktionen med ett program som skriver ut ditt namn på någon av raderna. Uppgift 8. Headerfil Avgör nu vilka av funktionerna som du tycker kan vara användbara av eventuella andra programdelar. Dessa ska tas med som externdeklarationer i filen LCD.h. 6