Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

7.4. Oheisrautaa.

LCD-näyttö, Liquid Crystal Display


10.01.2008 pva, kuvat jma

”Useat niistä,
jotka ovat pyrkineet maailman valoksi,
ovat löytäneet itsensä lyhtypylväästä.”
- tuntematon

Sisältö
Yleistä
LCD-näytön toiminta
LCD-näytön ohjelmointi
Mitä on tiedettävä lcd-raudasta ennen ohjelmointiin ryhtymistä:
Katsotaan mitä lcd_tat.h sisältää
lcd_1.c, esimerkki
Toinen esimerkki
printf()-funktio
Merkkijono
Merkkijonon esittely
fdevopen ja sen käyttö lcd-näytön kanssa

Yleistä

Kuten PC-ympäristössä, myös pienissä sulautetuissa järjestelmissä tarvitaan


numeroiden ja kirjaimien (merkkijonojen) käsittelyä. Tosin paljon harvemmin ja
pienemmissä mittasuhteissa. Miten muuten voidaan tulostaa lämpömittaritietoja,
kellonaika, varoituksia, jne.?
LCD-näyttö on tyypillinen sulautetun systeemin näyttö. Sillä voidaan tulostaa ASCII-
merkkejä, siis tavallisia numeroita ja kirjaimia sekä erilaisia yksinkertaisia graafisia
kuvioita. Varsinaisten kuvien tulostukseen tarvitaan erityinen graafinen lcd-näyttö.
Tässä käsitellään vain alfanumeerisen eli merkkinäytön perustekniikkaa.

1
Toiminta

Nestekide on eräitten orgaanisten yhdisteiden nestemäisen ja kiinteän olomuodon


välivaihe. Se on kuin neste, mutta sähköisesti ja optisesti kiteen kaltainen.
Sähkökentän avulla nestekiteen molekyylien asentoon voidaan vaikuttaa ja asento taas
vaikuttaa valon läpäisyyn. LCD-näyttötekniikoita on toiminnaltaan useita erilaisia.

Yksinkertaisin toiminnaltaan ja ohjaukseltaan on sulautetuissa yleisimmin käytetty


mustavalkoinen merkkinäyttö. Siinä kahden lasilevyn väliin on pantu ohut, noin 10
um:n nestekidekalvo. Lasilevyjen pintaan, siis kumpaankin, tehdään näkymättömillä
hyvin ohuilla johtimilla ruudutus, pistematriisi tai segmenttejä. Johtimiin tuodaan
jännite, joilla saadaan aikaan levyjen välille sähkökenttä.

Ohjaamalla sähkökenttää, vaikutetaan nestekidemolekyylien asentoon. Asento


vaikuttaa valon läpäisyyn, yhdessä asennossa valo pääsee läpi, toisessa ei. Jos
panemme näytön takareunaan peilin, niin valoa läpäisevä piste näkyy kirkkaana ja
läpäisemätön tummana.
Parempi kontrasti saadaan aikaan, jos peilin tilalle asennetaan valolähde, pieni
loisteputki tai LED-diodeita. Ilman taustavaloa, tehontarve on vähäinen. Rakenneidea
on yksinkertaisen nerokas. Monimutkainen siitä tulee, jos halutaan saada aikaan
värejä. Vaikeudet kasvavat edelleen, jos näytöistä halutaan tehdä suurikokoisia.

Paremmat laatuisissa lcd-näytöissä käytetään polarisoituja laseja estämään tai


päästämään valonsäteet eteenpäin. Segmenttien valonläpäisyä ohjataan sähkökenttien
avulla. Ohjauksesta huolehtii näytön sisäinen CPU ja sen elektroniikka.

Merkkinäytön ohjaus

Merkkinäytöt tehdään tänä päivänä pääasiassa Hitachin 44780 normin mukaiseksi.


Kaikki sen liitäntänastat ovat aina samannumeroisia ja toimintaperiaate on siis sama
riippumatta valmistajasta tai liittimen/liitännän mekaanisesta muodosta.

Kuva 7.4.1. Hitachi 44780 lohkotasolla.

2
44780-normin näyttö sisältää:
- lcd-moduulin
- mikro-ohjaimen, joka ohjaa itse näyttötapahtumaa
- RAM-muisti, johon tulostettavat merkit tallennetaan ja
- ROM-merkkimuistin, josta etsitään ASCII-merkkiä vastaavat ohjaussignaalit

Näytön dataväylä on 8-bittinen ja sen kautta näytölle menevät merkit ovat joko
käskyjä tai dataa. Ohjauspulssien avulla käskyt (esim. kertoo merkin paikan) menevät
käskyrekistereihin ja näytettävä data (ASCII-koodi) datarekisterin kautta
näyttömuistiin ja edelleen näyttöön.
Merkit voivat olla kirjaimia, numeroita tai erikoismerkkejä. Niitä voi tehdä itsekin.
ASCII-koodia vastaava bittikuvio löytyy merkki-ROMmista ja näytön sisäinen CPU
hoitaa sen näytöllä oikeaan riviin ja sarakkeeseen.

LCD-näytön ohjaussignaalit
LCD:n Nimitys Merkitys
pinninumero
1 Vss Power Supply, GND
2 Vcc Power Supply, +5 V
3 Vee GND tai pot. liuku, Kontrasti
4 RS Register Select.
0 = Instruction input
1 = Data input
5 RW Read/Write,
0 = Write to LCD
1 = Read from LCD
6 E Enable, 1>>0
7 DB0 GND, jos 4-bitin ohjaus, LSB
8 DB1 GND, jos 4-bitin ohjaus
9 DB2 GND, jos 4-bitin ohjaus
10 DB3 GND, jos 4-bitin ohjaus
11 DB4 Data 4
12 DB5 Data 5
13 DB6 Data 6
14 DB7 Data 7, MSB
15 Anode Taustavalo +V,
16 Cathode Taustavalo GND

Taulukko 7.4.1. 44780-normin mukaisen LCD-näytön liitäntä.

Mikro-ohjain lähettää lcd-näytölle 8-bittisen ohjaus- tai datatavun. Tavallisimmin


kuitenkin käytetään 4-bittistä toimintomoodia, jolloin voidaan samasta portista (8
bittiä) hoitaa komennot ja datan siirto. Silloin ohjauskoodista tulee hieman
monimutkaisempi koska data pitää lähettää kahdessa osassa (nibble).

Ohjauskomentojen ja datan siirtäminen on ajoituksen kannalta vaativaa puuhaa, koska


lcd:n ohjaus vaatii aikaa. Se on hidas. Siksi komentojen välillä on käytettävä
viivesilmukoita.
Jos näytössä on taustavalo, sitä ohjataan pinneistä 15 = anodi ja 16 = katodi.

3
LCD-näytön liittäminen AVR-mikro-ohjaimeen
Japanilaisen Hitachin LCD-näytön ohjaustapa HD44780 on vallitseva normi
sulautettuihin soveltuvissa merkkinäytöissä. Valmistaja saa olla kuka tahansa, sen
ohjaus tapahtuu aina samalla tavalla.

Kuva 7.4.2. Hitachi 44780-lcd-näytön 4-bitin liitäntä

LCD-näytön käyttöönotto

Kun käyttöjännite on kytketty LCD-näyttöön, niin ensimmäinen tehtävä on näytön


kontrastin säätäminen. Se on tarpeen tehdä vain kerran. Säädä trimmeripotentio-
metrillä kontrastijännite sellaiseen arvoon, että näyttöön tulee (vähintään) yksi rivi (tai
osa siitä) grafiikkapisteistä muodostuneita tummia suorakaiteita. Nämä näkyvät ilman
datan ohjaustakin.
Kun ajat toimivaa ohjelmaa, niin säädä uudelleen kontrastia - nyt sellaiseen arvoon,
jossa merkit näkyvät parhaiten. Tämän jälkeen ei säätöön enää tarvitse koskea.

LCD-näytön ohjaaminen on hyvin monimutkaista. Siksi aluksi kannattaa tyytyä


valmiisiin malleihin ja taitojen kehittyessä, tehdä omia sovelluksia.

LCD-näytön ohjelmointi

Tommi Tilli on tehnyt mainion LCD-näytön (Hitachi 44780) ohjausohjelman. Ja mikä


parasta, hän on luovuttanut kaiken vapaaseen opetuskäyttöön.
Kiitos.
Tiedostot ovat, versiopäivitys 15.8.2007:
lcd_tat.h
lcd_tat.c

Header-tiedosto sisältää tarvittavat näyttöön ja porttiin liittyvät määritykset ja


funktioiden esittelyt. Funktioiden määritykset (sisällöt) löytyvät lcd_tat.c tiedostosta.

Tee työkansio lcd-näytön harjoituksille ja kopioi sinne lcd_tat.c ja lcd_tat.h tiedostot.


Ne hoitavat lcd-näytön tarvitsemat ajurikoodit, näin oma työmme helpottuu.
Jos teet projektin, joka käyttää LCD-näyttöä, on projektikansioon kopioitava
molemmat ajurit.

4
PV-EMOssa on (kun siihen on liitetty PV-M32-kortti) A-porttiin kytkettynä 2*16
merkin LCD-näyttö. LCD:n taustavaloa ohjataan ON/EI C-portin PC.0-bitillä.

Mitä on tiedettävä lcd-raudasta ennen ohjelmointiin ryhtymistä?


- portti johon lcd-näyttö on asennettu
- miten lcd-näyttö on liitetty porttiin, 4/8-bittinen, pinnit
- millainen näyttö, rivien määrä ja merkkien määrä rivillä

Nämä tiedot on koottu erityiseen header-tiedostoon, tässä tapauksessa lcd_tat.h.


Kaikki tämän dokumentin koodit on tehty tuon headerin oletuksella, eli:
2*16 merkin näyttö A-portissa.

Katsotaan mitä lcd_tat.h sisältää


LCD:n liittäminen mikro-ohjaimen porttiin:

Comments: LCD (Hitachi HD44780 -based) control functions.


An LCD must be connected like the table below shows:
PORT
Bit 0 -> LCD-4 (RS)
Bit 1 -> LCD-5 (RW)
Bit 2 -> LCD-6 (EN)
Bit 4 -> LCD-11 (D4)
Bit 5 -> LCD-12 (D5)
Bit 6 -> LCD-13 (D6)
Bit 7 -> LCD-14 (D7)

LCD-näytön käyttöönottoa varten on seuraava funktio:


LCD_init(_Bool fCharfix, _Bool fCursor, _Bool fBlink)

-function initializes the LCD for operation.


First argument tells whether the character fix will be enabled or
not. Character fix is needed to display some characters
(ASCII > 127) properly. If second argument is true (1), a cursor will
be shown on the LCD. If third argument is true, the cursor will blink
if it is enabled.

Koodiin tulee esim.


LCD_init(1, 0, 0);
Korjattu (mukana myös erikoismerkit) näyttömoodi, ei kursoria, eikä se vilku.

Portin, johon lcd-näyttö asennetaan, määrittely:

/*A PORT where the LCD is connected and the Data Direction Register
* of that PORT.*/
#define LCD_PORT PORTA
#define LCD_PIN PINA
#define DIRECTION_REGISTER DDRA

Jos lcd-näyttö asennetaan johonkin muuhun kuin A-porttiin, on ylläoleva määritys


päivitettävä. Siis esim. B-portin kyseessä ollen: PORTB, PINB, DDRB.

5
Näytön liittäminen AVR:n porttiin:
/**
* Bit connections between the LCD and the PORT
*/
#define LCD_4_RS 0x01 // Bit 0, Register Select line
#define LCD_5_RW 0x02 // Bit 1, ReadWrite control line
#define LCD_6_EN 0x04 // Bit 2, Enable
//#define LCD_11_D4 0x10 // Bit 4
//#define LCD_12_D5 0x20 // Bit 5
//#define LCD_13_D6 0x40 // Bit 6
#define LCD_14_D7 0x80 // Bit 7

Seuraavaksi tulee erilaisia määrityksiä.

LCD:n ongelma on hitaus, siksi ohjainohjelmassa tarvitaan erilaisia viiveitä.

//** Delays needed to use the LCD **//

/**
* A delay between commands. The delay should be about 450ns (might
vary
* a little between different LCDs).
*/
#define COMMAND_DELAY_US 1

// The delay should be at least 120us.


#define SHORT_DELAY_US 128

// Delay should be at least 15ms.


#define STARTDELAY_MS 16

// Second delay used in the initialization of the LCD.


// Delay should be about 5ms.
#define STARTDELAY_SHORT_MS 5

Näytön ohjaamiseen tarvittavien funktioiden esittelyt, prototyypit, on listattu


seuraavaksi. Itse funktiot löytytävät lcd_tat.c-tiedostosta.
//***********************//
//* Function prototypes *//
//***********************//

Sitten koodin tekoon.

lcd_1.c, esimerkki
/**********************************************************
Project : lcd_1.c
Hardware: PV-M32, (4 MHz) + PV-EMO eli LCD on PORTA
Software : WinAVR-20071221
Date : 26.9.2007 + 10.1.2008
Author : pva
Comments: tulostaa tekstiä ja numeroita lcd-näyttöön

Kopioi lcd_tat.c ja lcd_tat.h tiedostot projektin kansioon.

6
Klikkaa hiiren oikealla (vasemmalla olevan hakemistopuun)
Source Files, Add Existing Source Files, lisää lcd_tat.c
- tällä kerrotaan C-kääntimelle,
että sen pitää kääntää myös lcd_tat.c
**********************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include "lcd_tat.h"
#define RIVI 0
#define SARAKE 0

// prototyyppi
void wait(uint16_t time);

int main(void)
{
LCD_init(1, 0, 0); // lcd ON, kursori EI, kursori EI VILKU
uint16_t numero = 0;

while(1)
{
LCD_Clear(); // näytön tyhjennys ja kursori "kotiin"
LCD_WriteString("AVR MCU");
LCD_SetCursorXY(SARAKE, RIVI+1);
LCD_WriteUINT(numero);
wait(200);
numero++;
}
}

// *** Primitive wait() ***


void wait(uint16_t time)
{
volatile uint16_t i;

for(i=0;i<2000;i++)
_delay_loop_2(time);
}

Analysointi
Ensin pitää kopioida lcd_tat.c ja lcd_tat.h tiedostot projektikansioon, kuten
kommenteissa kerrotaan.
AVRStudiossa lcd_tat.c tiedosto otetaan mukaan painamalla hiiren oikealla napilla
Source Files ja sitten Add Existing Source Files... (editorissa vasemmalla
tiedostoikkunassa).

Sitten pääohjelmalle kerrotaan, että otamme lcd-näytön käyttöön, se tapahtuu


kirjoittamalla koodiin rivi
LCD_init(1, 0, 0); // lcd ON, kursori EI, kursori EI VILKU

Tämän koodi toimii kuten kommentissa on kerrottu. Sen tarkempi sisältö löytyy
tiedostosta lcd_tat.c.

Seuraava rivi tekee kuten jo funktion nimestä selviää ja kommentti vielä vahvistaa.
LCD_Clear(); // näytön tyhjennys ja kursori "kotiin"
LCD_WriteString("AVR MCU");

7
Sitten näyttöön kirjoitetaan merkkijono. Lainausmerkkien väliin voi laittaa kaikkia
näyttömme osaavia merkkejä. Tekstin pituus on riippuvainen näytöstä.
LCD_SetCursorXY(SARAKE, RIVI+1);
LCD_Write_UINT(numero);

Ja sitten siirretään kursori seuraavalle riville ja kirjoitetaan näyttöön numero-


muuttujan arvo. Tommin tekemän ajuriohjelmiston hyvä puoli on se, että hän on
käyttänyt funktoiden nimenä hyvin toimintaa kuvaavia nimiä. Otetaan mallia! Ei
tarvitse välttämättä paljon kommenttia kirjoittaa, kun itse koodi kertoo mitä tehdään.

Toinen esimerkki
/**********************************************************
Project : lcd_yksitellen_m32.c
Hardware: PV-M32, (4 MHz) + PV-EMO eli LCD on PORTA
Software : WinAVR-20071221
Date : 10.1.2008
Author : pva
Comments: tulostaa tekstiä lcd-näyttöön merkki kerrallaan
Kopioi lcd_tat.c ja lcd_tat.h tiedostot projektin kansioon.
Klikkaa hiiren oikealla (vasemmalla olevan hakemistopuun)
Source Files, Add Existing Source Files, lisää lcd_tat.c
- tällä kerrotaan C-kääntimelle,
että sen pitää kääntää myös lcd_tat.c
**********************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include "lcd_tat.h"
#define RIVI 0
#define SARAKE 0

// *** Primitive wait() ***


void wait(uint16_t time)
{
volatile uint16_t i;

for(i=0;i<2000;i++)
_delay_loop_2(time);
}

int main(void)
{
uint8_t i;
uint8_t merkkijono[] = "Upotettu_AVR";
LCD_init(1, 0, 0);

while(1)
{
LCD_Clear(); // näytön tyhjennys ja kursori "kotiin"

for(i=0; i<12; i++)


{
LCD_SetCursorXY(SARAKE+i, RIVI);
LCD_WriteChar(merkkijono[i]);
// merkkitaulukon kirjaimet yksi kerrallaan ->lcd
wait(200);
}
}
}

8
Analysointi
Edellisen koodin toiminnan kun ymmärtää, niin tämäkin on ”selvää tekstiä”.

Muotoileva tulostus printf()-funktio


PC-koneissa tulostus ohjataan ulostulo-tiedostoon, joka on yleensä menee näyttöön.
Kaikkien C-kielikurssien ensimmäinen opittava ohjelma tulostaa PC-näytölle printf-
funktiolla ”Hello World!”. Mutta miksi se on ongelma sulautettujen C-kielessä?
Koska sitä on vaikea saada mahdutettua mukaan.

Sulautettujen näyttönä on tavallisimmin pieni, 1- tai useampirivinen lcd-merkkinäyttö.


Toisinaan on tarve ohjata tulostus sarjaporttiin ja sitä kautta eteenpäin vaikka PC:n
terminaaliohjelmaan tai muulle oheislaitteelle (modeemi, Nokian kännykkä, jne.).
Silloin suppeastakin printf-funktiosta on hyötyä, koska se kykenee muotoilemaan
tulostettavaa dataa käyttäjän haluamalla tavalla.

Printf on ns. formatoitu tulostuskomento, jossa c-käännin hoitaa kaiken:


- tulostuksen
- tulostuksen muotoilun
- tyyppimuunnokset, jne.

Kaupallisissa AVR-C-kääntimissä on printf()-funktio ja löytyy se avr-gcc:stäkin.


Valitettavasti se on tehtävä hyvin suppealla ja epästandardilla ’virityksellä’, sillä
oikea printf()-funktio vaatii muistitilaa toteutuksesta riippuen 4 – 30 kB. Ja kun
meidän käyttämissä mikro-ohjaimissa on muistia yleensä niin vähän, ettei sinne
’oikea’ printf() mahdu. Siksi yleensä päädytään ratkaisuun, jolla hoidetaan
merkkijonot muilla keinoin.

Toisaalta kuitenkin on hyvä tietää, että AVR-GCC-yhteisön tuottamana yleisessä


jakelussa on printf:n lisäksi hyvin monenlaisia merkkijonon tulostukseen käytettäviä
funktioita. Joten, jos välttämättä sellaista kaipaa, se vaatii tutustumista kääntimen
kirjaston dokumenttiin, tai nettisurffailua, tai kääntymistä kavereitten puoleen.

Jos ’lainaat’ koodia jostain PC-ympäristöön tarkoitetusta C-kielen oppikirjasta,


niin sulautetuissa järjestelmissä muutkin funktiot kuin printf esiintyvät yleensä
hyvin rajoitetuin ominaisuuksin (koska käytössä on hyvin vähän muistia ja
prosessori on pienitehoinen).
Sulautetun järjestelmän C-käännin (muukin kuin avr-gcc) ei välttämättä
’ymmärrä’ kaikkea ANSI C:n mukaista koodia.

Ennen LCD-printf-tulostukseen tutustumista perehdytään merkkijonoihin. Sen jälkeen


tulostus lcd-näyttöön tai myöhemmin RS-232-sarjaporttiin, ei tuota ongelmia.

Anyway, we do not recommend the printf() family for small devices (at least not
the version in the library -- feel free to use the source code, and cut down your
own customized version). Instead, there are simpler conversion functions
available as well.

9
Merkkijono

Lisää merkkijonoista (mm. kirjastofunktiot) sarjaportin (USART) yhteydessä.

C-kielessä merkkijono ei ole oma tietotyyppi, vaan on käytettävä taulukkoa. Se on


yhden tai useamman merkin muodostama jono, joka talletetaan peräkkäisiin
muistipaikkoihin. Peräkkäiset muistipaikat (kuten edellä opit) muodostavat vektorin,
eli yksiuloitteisen taulukon.

Merkkijono on taulukon erikoistapaus ja sen alkiotyyppi on 8-bittinen char. Jonon


päättyminen ilmaistaan nollamerkillä, joka merkitään '\0', eli NULL, eli null,
character. Se ei ole numero nolla, vaan tulostumaton merkki. Sen ASCII-arvo on 0.
Desimaalinollan ASCII-merkkihän on 0x30.

Ilman lopettavaa nollamerkkiä, kirjaimia sisältävä taulukko on merkkitaulukko, ei


merkkijono. Siitä C-käännin tietää mihin merkkijono loppuu. Käännin laittaa NULL-
merkin automaattisesti merkkijonon loppuun.
Taulukoita käytetään nimellä, samoin siis myös merkkijonoja. Merkkijonon nimi on
osoite merkkijonon ensimmäiseen merkkiin.

Jos merkkijonoa käytetään funktioiden kanssa, esim. printf-funktio, merkkijono


kokonaisuudessaan ei välity funktion kutsussa, vaan kutsussa välittyy ainoastaan
merkkijonon osoite. Siis kutsuva funktio välittää kutsuttavalle funktiolle paikan
muistissa, johon voi kirjoittaa, tai josta voidaan lukea.

Vektori
on samantyyppisten muuttujien kokoelma jolla on yhteinen nimi.

Merkkitaulukko
on char-tyyppisten alkioiden muodostama taulukko.

Merkkijono
on char-tyyppisten alkioiden muodostama taulukko,
joka päättyy ’\0’-merkkiin.

Merkkijonon esittely
char-tyyppinen muuttuja voi tallentaa vain yksittäisen merkin. Koska merkkijonolle ei
ole erityistä tietotyyppiä, muodostetaan merkeistä taulukko.

char jono[6] = {'A', 't', 'm', 'e', ’l’, '\0'};


tai
char jono[6 ] = "Atmel"; // parempi tapa
char sana[] = {'j', 'o', 'n', 'o', '\0'};
char motor[] = "Moottori ON”;

Lainausmerkit eivät sisälly jonoon, niitä käytetään jonon rajojen merkitsemiseen.


Heittomerkkejä käytetään yksittäisten merkkien erottamiseen. C-kielen standardi-
kirjastossa on runsaasti valmiita funktioita merkkien ja merkkijonojen käsittelyyn. Jos
ne eivät riitä, niin osaava tekee tarpeisiinsa sopivan itse.

10
”jono tekstiä”
”” // nollajono
” ” // tyhjää
”a-b+c;” // ei tehdä mitään
”/* ei kommenttia */”
”jono \” juu”

”abc” // merkkitaulukko, koko on 4, koska lopussa on \0


’a’ ja ”a” ovat eri asioita, jälkimmäisessä a ja \0

Merkkijonon esittelyssä SRAM-muistista varattu alue ei tyhjene,


vaan sinne jää aikaisempi sisältö.

Ongelma on vain siinä miten printf()-funktio saadaan käyttöön avr-gcc:n kanssa. Sitä
varten on opittava C-kielen tiedostojen käsittelyfunktio fopen.

fdevopen ja sen käyttö lcd-näytön kanssa

Yleistä
Kaikki ohjelmiemme käsittelemä data katoaa bittien taivaaseen heti kun ohjelman ajo
loppuu. Näin käy ellemme talleta sitä ennen johonkin pysyvään muistiin kuten esim.
kiintolevylle. Tallentamista varten meidän on luotava tiedosto, file.

Lukemista ja kirjoittamista varten tiedosto on ensin avattava (ja käytön jälkeen


suljettava). Se tehdään standardikirjaston (stdio.h) funktiolla fopen() ja tiedosto-
osoittimella (file or stream-pointer), jonka ko. funktio palauttaa.

Tiedoston käyttöönotto tapahtuu fopen-funktiolla, jonka muoto-formaatti on:


FILE *fopen(char *name, char *mode);

Funktion fopen
- ensimmäinen argumentti on osoitin
o (ulkoiseen, käsiteltävänä olevaan) tiedostoon
- ja toinen argumentti, mode,
o mitä tiedostolle aiotaan tehdä (w – kirjoitus, r – luku, a – lisäys).
- funktion nimi kuvastaa sen osoitetta.
o tätä osoitetta kutsutaan funktion osoittimeksi, file pointer.

Tähän kaikkeen tarvitaan käyttäjärjestelmän apua. Mutta kun meillä ei ole


käyttöjärjestelmää (vielä), eikä edes kiintolevyä, niin toimitaan seuraavasti:

Fdevopen() on tarkoitettu tietovuon siirtämisen jollekin laitteelle. Se avaa datavirran


laitteeseen (kirjoitus tai luku tai molemmat), joten se tarvitsee funktion yksittäisen
merkin siirtämiseen. Sillä ei ole väliä siirretäänkö tekstiä tai binääridataa (inside avr-
libc).

11
Funktiolla on kaksi argumenttia
- eka: mihin merkki kirjoitetaan
- toinen: on osoitin tiedostoon

avr-libc-kirjastossa tuo fopen() on korvattu fdevopen()-funktiolla.


kts. avr-libc user manual.

( FILE_ fdevopen (int(_)(char, FILE _) put, int(_)(FILE _) get) )

• FILE _ fdevopen (int(_put)(char, FILE _), int(_get)(FILE _))

stdio.h
FILE_ fdevopen (int(_)(char, FILE _) put, int(_)(FILE _) get)

Kun ohjataan data esim. LCD-näytölle korvataan ed. funktio tällä:


// Otetaan prinft()-funktio käyttöön siten, että sen tulostus
// ohjataan LCD_kirjoitaMerkki()-funktioon, eli LCD-näytölle.
fdevopen((void *) LCD_WriteChar, NULL);

Näin saadaan prinf()-funktio käyttöön. Funktio on ”iso” ja sillä on laaja tulostuksen


muotoilua ohjaava argumenttilista.
printf("Hello: %s %d=%c", data, luku, luku);

The 3rd argument of fdevopen has been removed since a few avr-
libc versions, it was unused anyway.

Funktion nimi kuvastaa osoitetta, jossa funktio sijaitsee. Tähän osoitteeseen osoittaa
funktion osoitin-pointer. Fdevopen() mahdollistaa funktion osoittimen käytön
parametrina kun kutsutaan itse funktiota ja lähetetään argumentteja sille.

Fdevopen()-funktio ohjaa laitteeseen ( UART, LCD, tms.) kirjoittavan tai sieltä


lukevan funktion tietovirtaa. Ei ole eroa onko data tekstivirtaa tai binäärivirtaa.

Sarjaportin käsittelyyn printf() löytyy valmiina, mutta LCD-näytölle on kirjoitettava


oma. Jos osaat kirjoittaa funktion, joka kirjoittaa merkin suoraan UARTin
datarekisteriin (tai lcd-näyttöön), puolet hommasta on hoidossa. Sitten kirjoitetaan
funktio, joka ottaa argumenttina merkin (ja palauttaa integerin = always nolla).
Käytä tuon funktion nimeä argumenttina kirjoittavaan funktioon, nyt kaikki merkit
ohjataan suoraan läpi funktion. Initialisoi UART ja/tai lcd ensiksi.

Käytännön esimerkki
/**********************************************************
Project : printf_to_lcd.c
Hardware: PV-M32, (4 MHz) + PV-EMO eli LCD on PORTA
Software : WinAVR-20071221
Date : 10.1.2008
Author : Tomi Tilli & pva
Comments: tulostaa tekstiä lcd-näyttöön merkki kerrallaan
Opiskele fdevopen-dokumentti.
Kopioi lcd_tat.c ja lcd_tat.h tiedostot projektin kansioon.

12
Klikkaa hiiren oikealla (vasemmalla olevan hakemistopuun)
Source Files, Add Existing Source Files, lisää lcd_tat.c
- tällä kerrotaan C-kääntimelle,
että sen pitää kääntää myös lcd_tat.c
**********************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include "lcd_tat.h"

// *** Primitive wait() ***


void wait(uint16_t time)
{
volatile uint16_t i;

for(i=0;i<2000;i++)
_delay_loop_2(time);
}

int main(void)
{
uint16_t pieni = 123;
uint32_t iso = 123456789;
// Otetaan LCD käyttöön merkinkorjauksella ja ilman kursoria
LCD_init(1, 0, 0);

// Otetaan prinft()-funktio käyttöön siten, että tulostus


// ohjataan LCD_WriteChar()-funktioon = LCD-näytölle.
fdevopen((void *) LCD_WriteChar, 0);
printf("Hello AVR-World!");

while (1)
{
// Kirjoitetaan 16-bittinen luku 2.riville
LCD_SetCursorXY(0, 1);
printf("min: %d ", pieni);
wait(500);
// Standard C89/C99 feature:
// long integers require a `l' (letter `ell')
// specifier in integer formats.
LCD_SetCursorXY(0, 1);
// Kirjoitetaan 32-bittinen luku 2.riville
printf("max: %li",iso); // <--- did the trick!!
wait(500);
}
}

Analysointi

Miten saadaan printf-funktio käyttöön ja tulostettava data ohjattua lcd-näyttöön?


// Otetaan prinft()-funktio käyttöön siten, että tulostus
// ohjataan LCD_WriteChar()-funktioon = LCD-näytölle.
fdevopen((void *) LCD_WriteChar, NULL);

Sen suurin hyöty on erilaisten (char, int, hex, des...) numeroiden tulostuksessa.
Toinen huomioitava asia on suurten kokonaislukujen tulostus.
// Standard C89/C99 feature:
// long integers require a `l' (letter `ell')
// specifier in integer formats.

13
LCD_SetCursorXY(0, 1);
// Kirjoitetaan 32-bittinen luku 2.riville
printf("max: %li",iso); // <--- did the trick!!

LCD-näyttö ja liukulukutulostus

Vaikka liukulukujen, floating point, käyttö pienissä mikro-ohjaimissa ei ole


suositeltavaa (liian pienet resurssit), annetaan seuraavassa mallikoodi sen käytöstä.
Ihan vaan oppimisen iloksi.

Mallikoodi
/**********************************************************
Project : adc_floatingpoint.c
Hardware: PV-M32 (4 MHz) + PV-EMO
Software: WinAVR-20071221
Date : 10.01.2008
Author : pva
Comments: mittaa ADC-3-trimmerin liu'un jännitteen ja
tulostaa sen A-porttiin kytkettyyn LCD-näyttöön.
Laita 10 kohmin trim_potikka GND ja +5 V väliin,
liulle noin 100 kohmia ja
se ADC3-inputtiin, eli PA.3
Säädä potikkaa, seuraa lcd-näyttöä.

Kopioi lcd_tat.c ja lcd_tat.h tiedostot projektin kansioon.


Klikkaa hiiren oikealla (vasemmalla olevan hakemistopuun)
Source Files, Add Existing Source Files, lisää lcd_tat.c
- tällä kerrotaan C-kääntimelle,
että sen pitää kääntää myös lcd_tat.c
**********************************************************/
#include <avr/io.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "lcd_tat.h"
#define AIKA 600
#define WAIT(time) for(uint16_t i=0;i<2000;i++)_delay_loop_2(time);
#define TAUSTAVALO 1<<0

uint8_t lippu = 0;
uint16_t adc_data = 0;

// hypätään tänne, kun muunnos valmis


ISR(ADC_vect)
{
lippu = 1;
adc_data = ADCW;
// luetaan muunnostulos muuttujaan (10 bittia >> 0-1023)
}

14
void Tulosta(uint16_t jannite)
{
cli(); // kielletään keskeytykset
LCD_Clear();

double luku =0;


// float luku = 0;
luku = jannite*5000/1023;
// skaalataan muunnostulos 'volteiksi'
printf("%.2f", luku);
LCD_WriteString(" Volttia" );
LCD_SetCursorXY(0, 1);
LCD_WriteString("V-mittari");
lippu = 0;
WAIT(1000);
sei(); // globaali keskeytys sallittu
}

void ADC_init(void)
{
ADMUX |= 1<<REFS0;
// (1<<REFS0) AVCC=5V with external capacitor at AREF pin
ADMUX |= (1<<MUX1) | (1<<MUX0); // valitaan ADC.3-input
ADCSRA |= (1<<ADPS2) | (1<<ADPS0); // jakoluku 32
// 4000000/32 = 125 kHz
// oltava välillä 50 kHz ... 200 kHz
ADCSRA |= (1<<ADIE) | (1<< ADEN); // adc ON ja keskeytys ON
}

int main(void)
{
DDRC |= TAUSTAVALO;
PORTC |= TAUSTAVALO;
// Otetaan LCD käyttöön merkinkorjauksella ja ilman kursoria
LCD_init(1, 0, 0);

// Otetaan prinft()-funktio käyttöön siten, että sen tulostus


// ohjataan LCD_kirjoitaMerkki()-funktioon, eli LCD-näytölle.
fdevopen((void *) LCD_WriteChar, NULL);
ADC_init();
DDRA &= ~(1<<3); // ADC.3 input
sei(); // globaali keskeytys sallittu

while(1) // jatkuva mittaus


{
DDRA &= ~(1<<3); // ADC.3 input
// oltava tässä, koska "lcd sekoittaa asetukset"
ADCSRA |= (1<<ADSC);
// (1<< ADEN) ADC ON
// (1<<ADSC) Start conversion
WAIT(AIKA);

if(lippu==1)
Tulosta(adc_data);
}
}

/*
Oletuksena avr-libc-kirjaston printf-funktio ei osaa tulostaa
liukulukuja, desimaalilukuja.
Kun et käytä liukuluja, säästät muistia melkoisesti.

15
Jos kuitenkin haluat käyttää liukulukuja, meidän on maipuloitava
linkkeriä.
1) Avaa AVRStudion "Project -> Configuration Options"
2) Avaa "Libraries"-välilehti
3) Available Link Objects:
- Add Library: math library (libm.a)
- ja the floating-point printf library (libprintf_flt.a)
libprintf_flt.a täytyy olla ylin ja libm.a täytyy olla alin

4) Avaa "Custom Options" välilehti.


5) Maalaa-Highlight [Linker Options].
6) Kirjoita (copypastaa tästä) "Add" laatikkoon:
-Wl,-u,vfprintf -lprintf_flt -lm
7) Klikkaa "Add" ja OK
*/

16

You might also like