Uwaga! Ta strona wysyła Ci "ciasteczko".

Artykuł pochodzi ze strony XYZ HOBBY ROBOT (abxyz.bplaced.net)

Kurs AVR-GCC Wyświetlacz LCD od nokii 3310

26.04.11 ABXYZ

Jak mówią, jeden obraz wart jest tysiąca słów, dlatego warto zainteresować się wyświetlaczami graficznymi. W tym artykule zajmiemy się wykorzystaniem wyświetlaczy LCD od popularnych niegdyś telefonów komórkowych Nokia 3310/3410, na allegro można taki kupić w cenie 5..7zł.

Wyświetlacz LCD od telefonu Nokia 3310 posiada monochromatyczny ekran o rozdzielczość 84x48 (wymiary czynnego pola ekranu 30x20mm). Wyposażony jest w sterownik PCD8544(lub podobny) z szeregowym interfejsem SPI. Dostępny jest wyłącznie tryb graficzny, ale można łatwo programowo zaaranżować tryb tekstowy; przykładowo można pokazać sześć linii tekstu, każda po 14 znaków wielkości 6x8 pikseli.

obrazek
Wyświetlacz od telefonu nokia 3310 wraz z oryginalną ramką. Identycznie wygląda wyświetlacz od noki 3410.
obrazek
Wyświetlacz od telefonu nokia 3310 wydobyty z ramki.

Wyświetlacz od Nokii3410 wygląda identycznie jak wyświetlacz od Nokii3310, ten sam układ wyprowadzeń złącza, ale ma nieco większą rozdzielczość: 96x65 (można pokazać 8 linii tekstu, każda po 16 znaków 6x8). Wyświetlacz od Nokii3410 wyposażony jest w kontroler "Philips OM6206", który programuje się prawie w ten sam sposób jak kontroler PCD8544 z Nokii3310. Do obsługi obu typów wyświetlaczy będziemy wykorzystywać te same funkcje napisane w języku C.

LCD Nokia 3310 LCD Nokia 3410
Rozdzielczość 84x48 96x65
kontroler PCD8544 OM6206
Wyświetlacze LCD od telefonów Nokia 3310/3410

Podłączenie wyświetlacza do mikrokontrolera AVR

Wyświetlacz jest delikatnym elementem, dlatego lepiej jest pozostawić go w jego oryginalnej ramce. Ramkę można odpowiednio przyciąć i wraz z wyświetlaczem przykręcić wkrętami do jakieś płytki, albo przykleić.

Na tylnej stronie ramki wyświetlacza dostępne jest złącze; w telefonie miedziane styki złącza przylegają do ścieżek na płytce drukowanej.

obrazek
Złącze na tylnej stronie wyświetlacza.
Nr styku Sygnał kierunek Opis
1 VDD zasilanie Zasilanie 2.7 .. 3.3 V
2 SCLK wejście Serial clock. Sygnał zegarowy taktujący dane na linii SDIN
3SDINwejścieWejście danych synchronizowane sygnałem SCLK
4 D/C wejście Wejście wyboru rodzaju danych wprowadzanych do sterownika (wyświetlane-1,sterujące -0)
5 SCE wejście Wejście aktywujące interfejs szeregowy(aktywny stan niski)
6 GND zasilanie Masa zasilania
7 VOUT zasilanie Ouptut voltage. Kondensator elektrolityczny 1-10 uF między VOUT i GND.
8 /RES input Reset LCD (aktywny stan niski)
Opis wyprowadzeń w złączu wyświetlacza od noki 3310/3410

Najprostszym sposobem przyłączenia wyświetlacza jest przylutować przewody bezpośrednio do miedzianych styków złącza, bez rozbierania ramki. Ale trzeba lutować szybko i zdecydowanie, inaczej plastykowa listwa, na której osadzone są miedziane styki złącza może się pod wpływem ciepła roztopić i odkształcić.

obrazek
Najprostszym sposobem przyłączenia wyświetlacza jest przylutować przewody do miedzianych styków złącza, bez wyciągania wyświetlacza z oryginalnej ramki.
obrazek
Wyświetlacz z przewodami przystosowanymi do podłączenia do płytki stykowej.

Wyświetlacz wymaga napięcia zasilania 2.7-3.3 VDC, zatem najprościej jest zasilać całość: mikrokontroler i wyświetlacz napięciem 3.3V. W takim przypadku wejścia sygnałowe wyświetlacza można podłączyć bezpośrednio do wyprowadzeń portów we/wy mikrokontrolera. Na schematach wejścia sygnałowe wyświetlacza połączone są do wyprowadzeń mikrokontrolera skojarzonych ze sprzętowym interfejsem SPI AVRa atmega. W przykładowych programach, które będziemy dalej uruchamiać, można wybrać sprzętowe lub programowe SPI; w przypadku wyboru programowego SPI, sygnały sterujące wyświetlaczem można przyłączyć do dowolnych porów we/wy AVRa.

obrazek
Schemat 1 Sposób przyłączenia wyświetlacza od nokii3310/3410 do interfejsu SPI mikrokontrolera atmega8(88) zasilanego napięciem 3.3V. Kliknij w obrazek, żeby powiększyć.

Nie wszystkie wersje AVR mogą być zasilane napięciem 3.3V, schemat nr 2 pokazuje, jak można podłączyć wyświetlacz zasilany napięciem 3.3V do mikrokontrolera zasilanego napięciem 5V. Wyświetlacz od Nokii 3310 zużywa minimalne ilości prądu, napięcie 3.3V do jego zasilania można uzyskać stosując dzielnik napięcia: rezystor i dioda zenera 3.3V. Na schemacie wejścia sterujące wyświetlacza połączono z portami we/wy AVRa poprzez bufor 74ls07. Scalony układ 74LS07 zawiera sześć cyfrowych buforów z wyjściami typu otwarty kolektor, wyjścia te zostały podciągnięte rezystorami 4k7 do napięcia 3.3V.

obrazek
Schemat 2 Sposób przyłączenia wyświetlacza od nokii3310/3410 zasilanego napięciem 3.3V do mikrokontrolera zasilanego napięciem 5V. Kliknij w obrazek, żeby powiększyć.

Komunikacja z wyświetlaczem

Wyświetlacz od Nokii 3310 wyposażony jest kontroler PCD8544 z szeregowym interfejsem SPI(Serial Peripheral Interface). Interfejs posiada cztery wejścia:

Komunikacja przebiega tylko w jednym kierunku, od mikrokontrolera do wyświetlacza. Zależnie od stanu linii D/C, bajty danych wysyłane do wyświetlacza, mogą być interpretowane przez kontroler jako komendy do wykonania albo dane zapisywane do pamięci RAM obrazu; stan wysoki na linii D/C sygnalizuje daną, stan niski komendę.

Rys.1 pokazuje przebieg transmisji jednego bajtu danych od mikrokntrolera do wyświetlacza Komunikację rozpoczyna się od ustawienia linii /SCE w stan niski, co aktywuje interfejs SPI. Jeśli wysyłany bajt jest komendą, linię D/C ustawia się w stan niski, a jeśli zwykłą daną - w stan wysoki Następnie, linią SDIN, szeregowo(bit po bicie) przesyła się 8 bitów danej, zaczynając od bitu najbardziej znaczącego. Transmisja szeregowa jednego bitu przebiega w następujący sposób: Wpierw na linii danych SDIN ustawia się stan niski lub wysoki, zależnie od wartości przesyłanego bitu; następnie na linii SCLK podaje się impuls: 0-1-0. Zmiana na linii /SCE stanu niskiego na wysoki sygnalizuje zakończenie transmisji.

obrazek
Rys.1 Przesłanie jednego bajtu do wyświetlacza przez SPI.

A oto funkcja "lcd_write_byte" realizująca w sposób programowy przesłanie jednego bajtu z mikrokontrolera do wyświetlacza:

void lcd_write_byte(unsigned char c_d, unsigned char data )
{
    unsigned char m; 

    LCD_CE_CLR

    if(c_d)  
       LCD_DC_SET
    else 
       LCD_DC_CLR

    for(m=0x80; m; m>>=1)
    {        
       if(data & m)
          LCD_DATA_SET 
       else 
          LCD_DATA_CLR 
        
       LCD_CLK_SET
       LCD_NOP
       LCD_CLK_CLR
    }

    LCD_CE_SET
}
Listing nr 1. Funkcja przesyłająca szeregowo jeden bajt danych z mikrokontrolera do wyświetlacza LCD poprzez interfejs SPI zrealizowany programowo.

Pierwszy argument funkcji "lcd_write_byte" wskazuje czy wysyłana jest komenda, czy bajt danych (0-komenda, 1-bajt danych); drugi argument to kod komendy lub bajt danych. Użyte w funkcji makrodefinicje: LCD_x_SET, LCD_x_CLR ustawiają na liniach sygnałowych stan wysoki lub niski, a makro LCD_NOP to krótkie opóźnienie w programie.

Mikrokontrolery atmega wyposażone są w sprzętowy interfejs SPI, który możemy wykorzystać do sterowania naszym wyświetlaczem. Poniżej znajduje się listing drugiej wersja funkcji "lcd_write_byte", która wysyła do wyświetlacza jeden bajt z wykorzystaniem sprzętowego interfejsu SPI.

/**/
SPCR =(1<<SPE)|(1<<MSTR)|(1<<SPR0);


/**/
void lcd_write_byte(unsigned char c_d, unsigned char data )
{
    LCD_CE_CLR

    if(c_d)  
       LCD_DC_SET
    else 
       LCD_DC_CLR

    SPDR = data;
    while(!(SPSR & (1<<SPIF)));

    LCD_CE_SET

}
Listing nr 2. Funkcja przesyłająca szeregowo jeden bajt danych z mikrokontrolera do wyświetlacza poprzez sprzętowy interfejs SPI AVRa atmega.

Do kontroli sprzętowego interfejsu SPI AVRa atmeaga wykorzystuje się trzy rejestry IO :

Sprzętowy interfejs SPI mikrokontrolera atmega ma szerokie możliwości konfiguracji, ale do sterowanie naszym wyświetlaczem pasują ustawienia domyślne. Pozostaje tylko wybrać szybkość działania interfejsu SPI, tzn. częstotliwość sygnału taktującego na linii SCLK. Służą do tego celu bity SPR0, SPR1 rejestru SPCR(SPI Control Register) oraz bit SPI2X rejestru SPSR(SPI -Status Register), tabela poniżej:

SPI2X SPR1 SPR0 SCK Frequency
0 0 0 fosc/4
0 0 1 fosc/16
0 1 0 fosc/64
0 1 1 fosc/128
1 0 0 fosc/2
1 0 1 fosc/8
1 1 0 fosc/32
1 1 1 fosc/64
Wybór częstotliwości sygnału taktującego na linii SCLK interfejsu SPI mikrokontrolera atmega

W naszych przykładach transmisja przez sprzętowy interfejs SPI będzie przebiegać z częstotliwością fosc/16, czyli przy częstotliwości pracy mikrokontrolera 16MHz sygnał taktujący na wyjściu SCLK będzie mieć częstotliwość 1MHz.

Ustawienie bitów SPE i MSTR w rejestrze SPCR(SPI Control Register) włącza interfejs SPI AVRa w trybie MASTER. Wysłanie bajtu następuje po zapisaniu danej do rejestru SPDR(SPI Data register), a ustawiony bit SPIF w rejestrze SPSR(SPI Status Register) sygnalizuje zakończenie transmisji.

Inicjalizacja wyświetlacza

Poniżej znajduje się listing funkcji inicjującej wyświetlacz. Na początku funkcji program resetuje wyświetlacz ustawiając linie /RES w stan niski na ok 15ms, po resecie funkcja wysyła kilka instrukcji inicjujących wyświetlacz.


/* Inicjuje wyświetlacz */
void lcd_init(void)
{
    LCD_RST_CLR; 
       
    //   < 30ms
    _delay_ms(15);    
    
    LCD_RST_SET

    LCD_CE_SET

    lcd_write_byte(LCD_CMD, 0x21); // Function set - extended instruction set
    lcd_write_byte(LCD_CMD, 0x13); // Bias - 1:48
    lcd_write_byte(LCD_CMD, 0x06); // Temperature Control
    lcd_write_byte(LCD_CMD, 0xa5); // Set Vop
    lcd_write_byte(LCD_CMD, 0x20); // Function set - basic instruction set, horizontal addressing
    lcd_write_byte(LCD_CMD, 0x0C); // Display control - normal mode 
}
Listing nr 3. Funkcja inicjująca wyświetlacz.

Pamięć RAM obrazu wyświetlacza

Każdemu pikselowi na ekranie wyświetlacza odpowiada jeden bit w pamięci RAM obrazu. Ekran wyświetlacza od Nokii 3310 ma rozdzielczość 84x48, zatem, aby zapamiętać cały obraz, kontroler wyświetlacza potrzebuje mieć 4032 bity pamięci RAM obrazu. Bity w pamięci obrazu pogrupowane są w komórki po 8 bitów(bajty), zapisując dane do pamięci obrazu zmieniamy od razu cały bajt(osiem pikseli na ekranie wyświetlacza). Niestety nie ma możliwości odczytu danych z pamięci RAM obrazu :( Komórki pamięci RAM obrazu są ponumerowane, numer komórki w pamięci nazywany jest jej adresem. Kolejnym ośmiu bitom każdej komórki pamięci RAM obrazu, odpowiada osiem kolejnych pikseli na ekranie LCD, ALE w kierunku pionowym, LSB na górze, MSB na dole ,patrz rys. 2. Przykładowo, jeśli gdzieś w pamięci RAM obrazu zapisana zostanie bajt o wartości 0xff, to w odpowiednim miejscu na ekranie wyświetlacza pojawi się pionowa kreska | o wysokości 8 pikseli, patrz rys.2.

obrazek
Rys.2 Tak można wyobrazić sobie pamięć obrazu wyświetlacza od nokii 3310, widzimy tablicę: 6 wierszy i 84 kolumny - każda kratka w tabeli to jeden bajt w pamięci RAM obrazu.

Dodatkowo, obok pamięci RAM obrazu, kontroler wyświetlacza posiada rejestr: licznik_adresu. Liczniku_adresu zawiera adres wskazujący na komórkę w pamięci RAM obrazu, gdzie zostanie zapisany następny bajt danych wysyłany do wyświetlacza, gdy na linii D/C występuje stan wysoki. Każdorazowo po zapisaniu bajtu danych w pamięci obrazu, licznik adresu zwiększa się automatycznie o jeden i wskazuje na następną komórkę w pamięci RAM obrazu. Wysyłając do wyświetlacza komendy: Set_Y_address_of_RAM i  Set_X_address_of_RAM można ustawić zawartość licznika_adresu tak, aby skazywał na dowolną komórkę w pamięci RAM obrazu.

Komendy kontrolera

Jak pisałem wcześniej, bajty danych wysyłane do wyświetlacza mogą być interpretowane przez kontroler wyświetlacza jako komendy do wykonania albo jako dane kopiowane do pamięci RAM obrazu - zależnie od stanu linii sygnału D/C. Bajty danych i komendy będziemy wysyłać do LCD omawianą wcześniej funkcją:

void lcd_write_byte(unsigned char c_d, unsigned char byte )

Parametr "c_d" to stan linii D/C, parametr "byte" to wysyłany bajt danych albo kod komendy.

Zbiór wszystkich komend wyświetlacza można znaleźć w dokumentacji kontrolera PCD8544. W naszych przykładach będziemy najczęściej wysyłać do LCD komendy Set_Y_address_of_RAM i Set_X_address_of_RAM", żeby ustawić zawartość licznika_adresu.

Wszystkie komendy kontrolera mają rozmiar jednego bajta(osiem bitów). Na bitach 6..0 kodu komendy Set_X_address_of_RAM kodowana jest współrzędna X, która może przyjmować wartości od 0 do 83, patrz rys.2.

1 X X X X X X X

Na bitach 2..0 kodu komendy Set_Y_address_of_RAM kodowana jest współrzędna Y, która może przyjmować wartości od 0 do 5, patrz rys.2

0 1 0 0 0 Y Y Y

W przypadku wyświetlacza od Nokii3410 z kontrolerem OM6206 można ustawić współrzędną Y na wartości 0..7, a współrzędną X na wartości 0..96

I to jest wszystko, co potrzebujemy wiedzieć, aby wykorzystać wyświetlacz od Nokii_3310. W dalszej części artykułu uruchomimy kilka przykładowych programików. Wszystkie przykłady są maksymalnie uproszczone, bo jak wiadomo, dobry przykład, to prosty przykład:)

Przykład 1. Jak wyświetlić obrazek

W programie MicroLCD narysowałem przykładowy obrazek, który zostanie pokazany na ekranie naszego wyświetlacza. Obrazek ma wymiary 84x48 - zajmie cały ekran wyświetlacza.

obrazek
Przykładowy obrazek do pokazania na wyświetlaczu narysowany w programie MicroLCD. Kliknij w obrazek, żeby powiększyć.

Dane obrazka wyeksportowałem do pliku tekstowego "hello_img.c", wybierając w menu programu MicroLCD opcję:

File->Export (.C hex file)

Plik z danymi obrazka zostaje dołączony do programu instrukcją preprocesora #include "hello_img.c", dane obrazka trafią do tablicy typu char.

unsigned char hello_img[] PROGMEM = {
#include "hello_img.c"
};

Słówko PROGMEM w deklaracji tablicy decyduje, że tablica zostanie utworzona w pamięci programu (we FLASHu AVRa).

Teraz, aby pokazać obrazek na ekranie, wystarczy przekopiować dane z tablicy we FLASHu AVRa do pamięci RAM obrazu wyświetlacza, robi to funkcja "lcd_image".

/* */
void lcd_image(unsigned char img[],char x0,char y0,char w,char h)
{     
   unsigned int i,j,k;
    
   for(i=0,j=0,k=0; i<h; i++)       
   {  
      /* Komenda  LCD "Set Y address of RAM" */
      lcd_write_byte(LCD_CMD, 0x40|(i+y0));
      /* Komenda "Set X address of RAM"*/
      lcd_write_byte(LCD_CMD, 0x80|(x0));

      /* Kopiowanie z FLASH do pamięci obrazu LCD  */
      for(j=0; j<w ; j++,k++)
         lcd_write_byte(LCD_DATA, pgm_read_byte(&img[k]));
   } 
}
Listing nr 4. Funkcja wyświetlająca obrazek na ekranie LCD.

Pierwszy argument funkcji "lcd_image" to tablica we FLASHu z danymi obrazka; kolejne dwa argumenty (x0,y0), to położenie górnego lewego rogu obrazka na ekranie LCD; argument czwarty i piąty to szerokość i wysokość obrazka. Parametry: y0 i wysokość obrazka trzeba podać w bajtach (osiem pikseli pionowo, patrz rys.2); na przykład, jeśli obrazek ma wysokość 48 pikseli, to należy wstawić 6.

A oto główny pliku pierwszego przykładu. Najpierw funkcja "lcd_init" inicjuje wyświetlacz, następnie funkcja "lcd_image" kopiuje dane obrazka z tablicy we FLASHu do pamięci RAM obrazu wyświetlacza.

/*
          Plik "main.c"  
*/
#include <avr/io.h>
#include <avr/pgmspace.h>

#include "lcd.h"

/* Dołącza dane z obrazkiem, obrazek zostanie
umieszczony w pamięci programu, we FLASHu  */
unsigned char hello_img[] PROGMEM = {
#include "hello_img.c"
};


int main(void)
{
    /* Inicjuje wyświetlacz */    
    lcd_init();

    /* Wyswietla obrazek  */ 
    lcd_image(hello_img,0,0,84,6);

    /* Zatrzymanie programu */    
    while(1);

    return 0;
}
Listing nr 5. Główny plik przykładu pierwszego.
obrazek
Przykładowy obrazek pokazany na ekranie LCD od Nokii_3310

Katalog z plikami źródłowymi przykładu pierwszego można pobrać klikają w link: 001.zip; katalog zawiera także plik Makefile, który należy dostosować do "sprzętu", tzn. wybrać typ AVRa, częstotliwość pracy mikrokontrolera, programator -przykłady były testowane na atmega88 8MHz. Plik Makefile wygodnie jest edytować z pomocą programu MFile dostarczonego razem z pakietem WinAVR. Jeśli nie wiesz jak skompilować przykłady, to polecam artykuł "Szybki start z WinAVR"

Przykład 2. Tekst na ekranie LCD

Nasz wyświetlacz nie udostępnia trybu tekstowego, zatem aby napisać na ekranie tekst, trzeba samemu rysować litery. W programie MicroLCD przygotowałem obrazek ze wzorami liter, cyfr i innych znaków z tablicy kodów ASCII, każdy znak narysowany jest na polu o wymiarach 6x8 punktów. Dane obrazka ze wzorami znaków wyeksportowałem do pliku tekstowego i dołączyłem do programu komendą preprocesora #include "font6x8p.c". Wzory znaków trafią do tablicy o nazwie "font" umieszczonej w pamięci programu (we FLASHu)

obrazek
Obrazek ze wzorami znaków dla wyświetlacza. Kliknij w obrazek, żeby powiększyć.

Aby tekst pojawił się na ekranie wyświetlacza, program będzie kopiował wzory znaków z tablicy "font" do odpowiedniego miejsca w pamięci RAM obrazu wyświetlacza. Ekran wyświetlacza podzielony jest na linie tekstowe o wysokości ośmiu punktów, taka jest organizacja pamięci RAM obrazu naszego wyświetlacza, patrz rys.2. Żeby narysować jeden znak o wymiarach 6x8, trzeba skopiować sześć kolejnych bajtów; każdy bajt to osiem ułożonych pionowo punków na ekranie wyświetlacza. Wyświetlacz Noki_3310 ma rozdzielczość 84x48, zatem można na nim pokazać 6 linii teksu, po 14 znaków 6x8. A to jest funkcja pisząca na ekranie wyświetlacza tekst:

/* 
   Wyświetla tekst - znaki 6x8
   s  - ciąg znaków zakończony zerem
   x - pierwsza kolumna 0..84(96)
   y - wiersz 0..5(7)
*/
void lcd_text(char s[], unsigned char x, unsigned char y)
{
   unsigned int c,j;
   unsigned char i,k;

   /* Kody polskich literek z ogonkami */
   char pl[] = {'ą','ć','ę','ł','ń','ó','ś','ź','ż','Ą','Ć','Ę','Ł','Ń','Ó','Ś','Ź','Ż'};
   
 /* Ustawia położenia pierwszej litery tekstu na ekranie LCD */
   lcd_write_byte(LCD_CMD, LCD_SETY|(y));
   lcd_write_byte(LCD_CMD, LCD_SETX|(x));

   /* Rysuje znak po znaku  */
   for(k=0; (c = s[k]); k++)
   {
      /* Dopasowuje kody znaków z ogonkami */
      for(i=0; (i<18) && (pl[i]!=c); i++) ;
      if(i<18) c= 0x80+i; 

      /* Kopiuje jeden znak(6x8) z FLASH do pamięci obrazu LCD  */ 
      for(i=0, j=(c-32)*6; i<6; i++,j++)
           lcd_write_byte(LCD_DATA, pgm_read_byte(&font[j]));
   }
}
Listing nr 6. Funkcja wyświetlająca ekranie LCD tekst

Pierwszym argumentem funkcji lcd_text jest ciąg znaków zakończony zerem, tekst może zawierać polskie znaki z ogonkami. Drugi i trzeci argument to położenie tekstu na ekranie LCD; x może przyjmować wartości 0..84, y-wartości 0..5.

A to jest główny plik drugiego przykładu:

/*
    Plik "main.c"

    LCD od nokia3310 przykład 2
    KURS AVR-GCC www.abxyz.bplaced.net

    testowane na atmega8 (16MHz) 
*/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include "lcd.h"


/* */
int main(void)
{
    /* Inicjuje wyświetlacz */    
    lcd_init();
    /* Czyści ekran */
    lcd_clear();

    /* Wyświetla tekst */
    lcd_text("Kurs AVR-GCC", 1*6, 0);
    lcd_text("ABXYZ :)", 4*6, 1);
    lcd_text("Wyświetlacz", 1*6, 3);
    lcd_text("LCD", 5*6, 4);
    lcd_text("od Nokii 3310", 0*6, 5);

    /* Zatrzymanie programu */    
    while(1);

    return 0;
}
Listing nr 7. Główny plik przykładu drugiego.
obrazek
Przykład 2. Tekst na ekranie wyświetlacza od Nokii 3310.

Katalog z plikami źródłowymi przykładu drugiego można pobrać klikają w link 002.zip; katalog zawiera także plik Makefile, który należy dostosować do "sprzętu", tzn. wybrać typ AVRa, częstotliwość pracy mikrokontrolera, programator -przykłady były testowane na atmega88 8MHz. Plik Makefile wygodnie jest edytować z pomocą programu MFile dostarczonego razem z pakietem WinAVR. Jeśli nie wiesz jak skompilować przykłady, to polecam artykuł "Szybki start z WinAVR"

Przykład 3. Płynący napis.

W tym przykładzie, na górze ekranu LCD, pokazany jest obrazek, z użyciem funkcji "lcd_image", podobnie jak w przykładzie 1. W dolnej części ekranu, w linii nr 5, wyświetlany jest tekst, ale, żeby było nieco ciekawiej, tekst "płynący", robi to funkcja "scroll".

obrazek
Przykład 3. Obrazek na górze ekranu, płynący tekst na dole.
/*
    Plik "main.c"

    LCD od Nokii_3310 przykład 3
    KURS AVR-GCC www.abxyz.bplaced.net

    testowane na atmega8 16(MHz)
*/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdio.h>
#include "lcd.h"

// Dane obrazka "avr_gcc"
unsigned char screen[] PROGMEM = {
#include "avr_gcc.c"
};

// Wzory znaków 6x8 dla funkcji "scroll"
extern unsigned char font[] PROGMEM;

// Tekst płynącego napisu 
unsigned char tekst[] PROGMEM = "              Internetowy kurs programowania mikrokontrolerów AVR w języku C              ";

//  Funkcja tworzy na ekranie LCD płynący napis 
void scroll(unsigned char txt[], unsigned char line)
{
   unsigned int j,l,k,n;
   unsigned char c,i,m;

   // Kody polskich literek z ogonkami
   unsigned char pl[] = {'ą','ć','ę','ł','ń','ó','ś','ź','ż','Ą','Ć','Ę','Ł','Ń','Ó','Ś','Ź','Ż'};

   // liczenie znaków w tekście
   for(n=0;pgm_read_byte(&txt[n]);n++);

   for(j=0; j<(n-(LCD_X/6))*6 ;j++)
   {
      LCD_GOTO(0, line) 
  
      for(i=0,l=j; i<LCD_X ;i++,l++)
      {
      c = pgm_read_byte(&txt[ (l/6) ]);

          // Dopasowuje kody polskich znaków z ogonkami
          for(m=0; (m<18) && (pl[m]!=c); m++) ;
          if(m<18) c= 0x80+m;

      k = (c-32)*6+(l%6);
          
      lcd_write_byte(LCD_DATA, pgm_read_byte(&font[k]));
      }
      _delay_ms(70);
   }
}


/*  MAIN  */
int main(void)
{
   // Inicjuje LCD    
   lcd_init();
    
   // Obrazek w górnej części ekranu 
   lcd_image(screen,0,0,84,5);
  
   // Płynący napis w linii nr 5
   while(1)
       scroll(tekst,5);

   return 0;
}
Listing nr 8. Główny plik przykładu trzeciego.

Katalog z plikami źródłowymi przykładu trzeciego można pobrać klikają w link 003.zip; katalog zawiera także plik Makefile, który należy dostosować do "sprzętu", tzn. wybrać typ AVRa, częstotliwość pracy mikrokontrolera, programator -przykłady były testowane na atmega88 8MHz. Plik Makefile wygodnie jest edytować z pomocą programu MFile dostarczonego razem z pakietem WinAVR. Jeśli nie wiesz jak skompilować przykłady, to polecam artykuł "Szybki start z WinAVR"

Przykład 4. Termometr z układem ds18b20

A teraz bardziej użyteczny przykład, termometr z układem ds18b20. Na środku ekranu wyświetlacza pokazywany jest wskaźnik cyfrowy, a po prawej wskaźnik analogowy - słupek cieczy. Podobny kity(zestaw do samodzielnego montażu) z mikrokontrolerem PIC dostępny jest w handlu.

obrazek
Przykład 4. Termometr z czujnikiem ds18b20

Na schemacie czujnik ds18b20 przyłączony jest do wyprowadzenia PD7 mikrokontrolera atmega, ale można wykorzystać dowolny port AVRa, w tym celu należy zmodyfikować makrodefinicje w pliku ds18b20.h

obrazek
Schemat 3. Sposób przyłączenia czujnika ds18b20.

W programie MicroLCD, przygotowałem tło, które będzie ładowane na ekran wyświetlacza jednorazowo, na początku programu funkcją "lcd_image"- podobnie jak w przykładzie pierwszym.

obrazek
Obrazek z tłem do przykładu 4. Na początku programu tło zostanie jednorazowo załadowane na ekran wyświetlacza. Kliknij w obrazek, żeby powiększyć.

Przygotowałem również obrazek ze wzorami cyfr dla wskaźnika cyfrowego. Każdy znak został narysowany na polu o wymiarach 8x16, czyli znaki cyferek mają wysokość dwóch linii tekstowych w pamięci RAM obrazu wyświetlacza.

obrazek
Obrazek ze wzorami cyfr o rozmiarze 8x16 do przykładu 4. Wzory cyferek zostaną wykorzystane do pokazania na ekranie wyświetlacza wskaźnika cyfrowego. Kliknij w obrazek, żeby powiększyć.

A poniżej znajduje się główny plik przykładu. W skrócie program działa następująco: Najpierw inicjowany jest wyświetlacz i na ekran ładowany jest obrazek z tłem; następnie, w pętli, odczytywana jest wartość temp z czujnika ds18b20, po czym na ekranie LCD rysowane są cyferki i słupek cieczy.

/*
    Plik "lcd.c"

    LCD od nokia3310 przykład 4
    KURS AVR-GCC www.abxyz.bplaced.net

    testowane na atmega8 16(MHz) 
*/
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <util/crc16.h>
#include <stdio.h>

#include "lcd.h"
#include "ds18b20.h"



// Obrazek z cyferkami dla wskaźnika cyfrowego 
unsigned char digits8x16[] PROGMEM = {
#include "dig8x16.c"
};

// Obrazek z tłem
unsigned char screen[] PROGMEM = {
#include "screen.c"
};

//
unsigned char tab[8];

// 9 bajtów odczytanych z ds18b20
unsigned char ds18b20_pad[9];


// Funkcja tworzy z liczby ciąg cyfr w kodzie BCD
// jeden bajt - jedna cyfra  
void i2dig(int t, unsigned char tab[])
{
   // Część ułamkowa liczby
   unsigned char frac[] = { '0','1','1','2','2','3','4','4','5','6','6','7','8','8','9','9' };
   signed char i, s = 0;
    
   if(t < 0) { t = -t; s = 1; }
   
   for(i=0; i < 4 ; i++) tab[i] = '9'+5; // spacja

   tab[7] = 0;
   tab[6] = '9'+4;  // Znak 'C'
   tab[5] = '9'+3;  // Znak '°'
   tab[4] = frac[t & 0x0f];
   tab[3] = '9'+1;  // znak kropka
   tab[2] = '0'; 
   tab[1] = '9'+5;  // spacja
   tab[0] = '9'+5; 

   t >>= 4;
   for(i=2; t; i--, t/=10) tab[i] = t%10+'0';

   if(i == 2)  i-- ;
   
   if(s) tab[i] = '9'+2; // znak minus

}


// Funkcja wyświetla wskaźnik cyfrowy
void lcd_d_t(unsigned char *s, unsigned char x, unsigned char y)
{
   unsigned int i,j;
   unsigned char k;

   LCD_GOTO(x, y) 
   for(k=0; s[k]; k++)
   { 
     lcd_write_byte(LCD_DATA, 0);
     
     for(i=0, j=(s[k]-'0')*8; i<8; i++, j++)
          lcd_write_byte(LCD_DATA, pgm_read_byte(&digits8x16[j]));
   }

   LCD_GOTO(x, y+1) 
   for(k=0; s[k]; k++)
   {
      lcd_write_byte(LCD_DATA, 0);
      
      for(i=0, j=(s[k]-'0')*8; i<8; i++, j++)
          lcd_write_byte(LCD_DATA, pgm_read_byte(&digits8x16[j+15*8]));
   }
}

// Funkcja rysuje słupek cieczy 
void lcd_a_t(char v, unsigned char x)
{
   char y,t,b;
   
    
   v+=7; 
   for(y=5,t=0; y>=0; y--,t+=8)
   {
      if(v>=t+8)
         b = 0xff;
      else if(v>t)
         b= 0xff00>>(v%8);
      else
         b = 0;
 
      LCD_GOTO(x, y) 
      lcd_write_byte(LCD_DATA, b);
      lcd_write_byte(LCD_DATA, b);
      lcd_write_byte(LCD_DATA, b);
   }
}


// MAIN 
int main(void)
{    
   unsigned int  crc = 0;
   unsigned char i;

   int t; // wartość temperatury * 16

   // Inicjuje wyświetlacz
   lcd_init();
  // lcd_contrast(0x25);
  // lcd_clear();

   // Pokazuje obrazek z tłem     
   lcd_image(screen,0,0,84,6);
  
   while(1)
   {
       // Polecenie pomiaru 
       if(ds18b20_ConvertT())
       {
          // Zatrzymuje do czasu zakończenia pomiaru 
          while(!OneWireReadTimeSlot());       
          
          // Odczyt danych z ds18b20 
          ds18b20_Read(ds18b20_pad);       
          
         // Oblicza sumę kontrolną CRC danych z ds18b20
          for (i = 0, crc =0; i < 9; i++)
              crc = _crc_ibutton_update(crc, ds18b20_pad[i]);

          // Jeśli suma kontrolna się zgadza, 
          // pokazuje nowe wyniki pomiaru  
          if(!crc)
          {
             // Zmienna "t" zawiera wartość temperatury * 16
             t = (ds18b20_pad[1] << 8) | ds18b20_pad[0] ;
      
             i2dig(t,tab);      // tworzy z liczby ciąg cyfr  
             lcd_d_t(tab,0,2);  // rysuje wskaźnik cyfrowy
             lcd_a_t(t>>4, 68); // rysuje słupek cieczy 
          }
       }
   }

       return 0;
}
Listing nr 9. Główny plik przykładu 4.

Katalog z plikami źródłowymi przykładu czwartego można pobrać klikają w link 004.zip; katalog zawiera także plik Makefile, który należy dostosować do "sprzętu", tzn. wybrać typ AVRa, częstotliwość pracy mikrokontrolera, programator -przykłady były testowane na atmega88 8MHz. Najlepiej skompilować program otwierając projekt w edytorzy "Programers's Notepad" dostarczonym wraz z pakietem "WinAVR". Jeśli nie wiesz jak skompilować przykłady, to polecam artykuł "Szybki start z WinAVR"

26.04.11 ABXYZ


Copyright © 2009-2017 XYZ Hobby Robot - Wszelkie prawa zastrzeżone