logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

LCD 4x20 BC2004B, ster. 4 bity,jak przerobić z 2 na 4 linie?

87irek 31 Maj 2009 14:13 2084 7
REKLAMA
  • #1 6598453
    87irek
    Poziom 10  
    Witam. Programuję właśnie pulpit wyświetlacza.. albo raczej próbuję. Jestem w tym początkujący ale trochę się orientuję. Wyświetlacz jest w układzie pulpitu wyświetlającego komendy pochodzące od czujników, sterowany przez ATmege128, ale krótko.... nie trzeba się teraz nad tym rozpisywać. Porty mam na pewno podłączone dobrze, tzn.
    RS PORTC->1
    E PORTC->3
    DB(4..7) PORTC->4..7
    Pomijam R/W.
    Także do samego wyświetlacza też wszystko jest podłączone jak trzeba.
    Wykorzystuję na razie program napisany przez Pana R. Kwietnia.
    Nie wiem tylko jak z tego zrobić, aby mój program obsługiwał wszystkie 4 linie, może wprowadzić adresowanie. Ogólnie po uruchomieniu inicjalizuje się wyświetlacz, pojawia się migający kursor w lewym górnym rogu i to wszystko. Poniżej zamieszczam soft, proszę o jakieś podpowiedzi albo ukierunkowania. (od razu powiem, że naprawdę długo szukałem na forum i takiej wskazówki nie znalazłem, tzn konkretnych wyjaśnień).
    ---------------------------------------------------------

    
    #include <avr/io.h>
    //
    #define LCD  PORTC
    #define E  3
    #define RS  1
    //
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    //
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    // funkcja opóźniająca o x*1ms
    void waitms(char x)
    {
    unsigned char a, b;
    for( ; x > 0; --x) 
      for(b = 10; b > 0; --b) 
    	for(a = 100; a > 0; --a) 
    	__asm("nop"); // opóźnienie o 1 cykl
    	// razem to da opóźnienie ok. x * 1ms
    	// x od 0 do 255
    }
    
    // prcodedura zapisu bajtu do wyświetlacza LCD
    // bez rozróżnienia instrukcja/dana
    // najpierw zapisuje pierwszą połówkę bajtu, później drugą 
    void write_to_lcd(char x)
    {
    SET_E;
    LCD = ((LCD & 0x0F) | (x & 0xF0));
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    SET_E; 
    LCD = ((LCD & 0x0F) | ((x & 0x0F)<<4));
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    waitms(1);
    }
    
    // procedura zapisu instrukcji do wyświetlacza LCD
    void write_command(char x)
    {
    CLR_RS; // niski stan na RS -> zapis instrukcji
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu danej do wyświetlacza LCD
    void write_char(char x)
    {
    SET_RS; // wysoki stan na RS -> zapis danej
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu tekstu do wyświetlacza LCD
    void write_text(char * s)
    {
    while(*s) // do napotkania 0
      {
      write_char(*s); // zapisz znak wskazywany przez s na LCD
      s++; // zwiększ s (przygotuj nastepny znak)
      }
    }
    
    // procedura inicjalizacji wyświetlacza LCD
    void lcd_init(void)
    {
    waitms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmienna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x3F; //
      CLR_E; // E = 0
      waitms(5);
    }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    waitms(1); // czekaj 1ms
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    write_command(0x08); // wyłącz LCD, kursor i miganie
    write_command(0x01); // czyść LCD
    write_command(0x06); // bez przesuwania w prawo
    write_command(0x0C); // włącz LCD, bez kursora i mrugania
    }
    
    // program główny
    int main(void)
    {
    // konfiguracja portów we/wy
    DDRC = 0xff;
    PORTC = 0xff;
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    write_text("Ala ma kota");
    // petla nieskończona
    while(1);
    return 0;
    }
    
  • REKLAMA
  • #2 6599243
    jurex
    Poziom 12  
    Witam

    Wyswietlane znaki znajduja sie w buforze DDRAM.
    Każda komórka w tym buforze odpowiada jednej pozycji na wyświetlaczu.
    Dla wyswietlacza z 4 liniami adresy sa nastepujace:
    1 linia: 0x00 - 0x1F
    2 linia: 0x40 - 0x5F
    3 linia: 0x20 - 0x3F
    4 linia: 0x60 - 0x7F

    Po inicjalizacji wskaźnik na DDRAM jest rowny zero wiec wskazuje pierwszy znak w pierszej linii i jest zwiekszany o 1 z kazdym wpisanym znakiem (to zależy od inicjalizacji wyświetlacza).
    Można wybierac od ktorego miejsca chcemy zaczac wpisywanie tekstu. Do tego uzywam funkcji: LCD_SetXY(u08 x, u08 y). Gdzie x i y to wspolrzedne ekranu.

    
    #define LCD_SDDRAM 	0x80
    #define LCD_SDDRAM_MASK	0x7F
    
    
    void LCD_WriteByte(u08 byte)
    {
      SetDB(byte);
      E_Tick();
      SetDB(byte<<4);
      E_Tick();
      _delay_us(70);			// czekaj chwilę 
    }
    
    void LCD_WriteCmd(u08 byte)
    {
      ClrRS();
      LCD_WriteByte(byte);
    }
    
    
    void LCD_SetCDDRamAdr(u08 bAdr)
    {
      bAdr &= LCD_SDDRAM_MASK;
      bAdr |= LCD_SDDRAM;
    
      LCD_WriteCmd(bAdr);
    {
    
    void LCD_SetXY(u08 x, u08 y)
    {
      switch(y)
      {
        case 1: x += 0x40; break; // drugi wiersz
        case 2: x += 0x20; break; // trzeci wiersz
        case 3: x += 0x60; break; // czwarty wiersz
      }
      LCD_SetCDDRamAdr( x );	// ustaw adres RAM
    }
    
    void LCD_Putch(char ch)
    {
      SetRS();
      LCD_WriteByte((u08)ch);
    }
    
    
    void LCD_Putstr(char* s)
    {
      register char c;
      while ((c = *s++))		// dopóki nie napotkasz 0
       LCD_Putch(c);
    }
    
    


    Powodzenia
  • REKLAMA
  • #3 6599372
    87irek
    Poziom 10  
    Ok rozumiem ideę i to jak są adresowane linie. Tylko jeśli mógłbyś mi jeszcze powiedzieć jak mogę wykorzystać tą funkcję wysyłającą polecenie do adresowania, tzn:

    Cytat:

    void LCD_SetCDDRamAdr(u08 bAdr)
    {
    bAdr &= LCD_SDDRAM_MASK;
    bAdr |= LCD_SDDRAM;

    LCD_WriteCmd(bAdr);
    }


    Rozumiem, że wartość x to będzie uzyskana z funkcji określającej linię, tylko chodzi mi już tutaj dalej. Jak to jest z tym adresowaniem. Na początku zdefiniowałeś te wartości LCD_SDDRAM_MASK i LCD_SDDRAM - trochę nie bardzo wiem na czym to będzie polegało.

    Jak już mówiłem, staram się zrozumieć maksymalnie dużo żeby nie zawracać głowy tutaj na forum, ale wszystkiego jeszcze nie ogarniam.
  • REKLAMA
  • #4 6599850
    jurex
    Poziom 12  
    Wiec funkcją: LCD_SetCDDRamAdr(u08 bAdr) ustawiasz adres od ktorego poczawszy beda wpisywane znaki ASCII.
    Wiec jesli po resecie bedziesz po kolei dopisywal znaki to bedzie sie to zachowywalo nastepujaco:D
    Pierwsze 20 znakow bedzie w pierszej linii.
    Potem od 21 do 32 nie beda widoczne.
    Od 32 do 52 pojawia sie w 3 wierszu.
    Potem od 53 do 64 znowu niewidoczne.
    Od 65 do 85 pokaze sie w drugiej linii.
    Od 86 do 94 pokaze sie w czwartej linii.

    Widzisz ze przestrzen w buforze nie jest liniowa.
    Dlatego przed wpisaniem w okreslone miejsce tekstu ustalam poczatek bufora ta procedura a nawet latwiej procedura: LCD_SetXY(u08 x, u08 y), gdzie nie musze pamietac od jakiego adresu zaczyna sie konkretna linia.
    x i y to wspolrzedne poczatkowe:
    x - kolumna,
    y - wiersz czyli linia wyswietlacza.

    Wiec uzywasz to nastepujaco:
    
    
    LCD_SetXY(0,0); //poczatek pierwszej linii
    LCD_Putstr("1 linia");
     
    LCD_SetXY(0,1); //poczatek drugiej linii
    LCD_Putstr("2 linia");
    
    LCD_SetXY(0,2); //poczatek trzeciej linii
    LCD_Putstr("3 linia");
    
    LCD_SetXY(3,3); //czwarty wiersz czwartej linii
    LCD_Putstr("4 linia");
    
    


    Wiec reausumujac najpierw wybierasz wspolrzedne gdzie chcesz pisac a potem piszesz.

    Jesli chodzi o zdefiniowane wartosci LCD_SDDRAM i LCD_SDDRAM_MASK.
    Polecenie SetCDDRamAdr wymaga by najwyzszy bit byl ustawiony (LCD_SDDRAM) a adres byl ograniczony do 7 bitow (LCD_SDDRAM_MASK).
  • #5 6601218
    87irek
    Poziom 10  
    Hmmm, chyba ja się źle wyraziłem. To co napisałeś jest dla mnie zrozumiałe i to już ogarnąłem. Może po prostu wrzucę teraz kod jak ja sobie to ustawiłem i jeśli możesz powiedz mi co tu mam źle, tzn na pewno czegoś brakuje w funkcji lcd_init związanego właśnie z tym ustawieniem adresów. Pomijam plik z funkcją opóźniającą i z nagłówkowego tylko pokażę zdefiniowane zmienne.

    Nagłówkowy:
    #include <avr/io.h>
    //
    
    #define LCD  PORTC
    #define E  3
    #define RS  1
    //
    #define Wysoki_E   LCD |= _BV(E)
    #define Niski_E   LCD &= ~_BV(E)
    //
    #define Wysoki_RS  LCD |= _BV(RS)
    #define Niski_RS  LCD &= ~_BV(RS)
    
    #define Pamiec_LCD       0x80
    #define Pamiec_LCD_ogr   0x7F
    


    Plik żródłowy:
    
    #include "testhh.h"
    
    // prcodedura zapisu bajtu do wyświetlacza LCD
    // bez rozróżnienia instrukcja/dana
    // najpierw zapisuje pierwszą połówkę bajtu, poźniej drugą 
    void zapisz_do_lcd(char x)
    {
    Wysoki_E;
    LCD = ((LCD & 0x0f) | (x & 0xF0));
    Niski_E; // opadające zbocze na E -> zapis do wyświetlacza
    Wysoki_E; 
    LCD = ((LCD & 0x0f) | ((x & 0x0F)<<4));
    Niski_E; // opadające zbocze na E -> zapis do wyświetlacza
    delayms(1);
    }
    
    // procedura zapisu instrukcji do wyświetlacza LCD
    void zapisz_polecenie(char x)
    {
    Niski_RS; // niski stan na RS -> zapis instrukcji
    zapisz_do_lcd(x); // zapis do LCD
    }
    
    //procedury kontrolujące adresowanie dla 4 linii
    
    void ustaw_pamiec(int adres)
    {
      adres &= Pamiec_LCD_ogr;
      adres |= Pamiec_LCD;
    
      zapisz_polecenie(adres);
    }
    
    void ustaw_lokXY(int x, int y)
    {
      switch(y)
      {
        case 1: x += 0x40; break; // drugi wiersz
        case 2: x += 0x20; break; // trzeci wiersz
        case 3: x += 0x60; break; // czwarty wiersz
      }
      ustaw_pamiec( x );   // ustaw adres RAM
    } 
    
    // procedura zapisu danej do wyświetlacza LCD
    void zapisz_znak(char x)
    {
    Wysoki_RS; // wysoki stan na RS -> zapis danej
    zapisz_do_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu tekstu do wyświetlacza LCD
    void zapisz_tekst(char * s)
    {
    while(*s) // do napotkania 0
      {
      zapisz_znak(*s); // zapisz znak wskazywany przez s na LCD
      s++; // zwiększ s (przygotuj nastepny znak)
      }
    }
    
    // procedura inicjalizacji wyświetlacza LCD
    void lcd_init(void)
    {
    delayms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    Niski_E; // E = 0
    Niski_RS; // RS = 0
    char i; // zmienna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      Wysoki_E; // E = 1
      LCD &= 0x3F; //
      Niski_E; // E = 0
      delayms(5);
    }
    Wysoki_E; // E = 1
    LCD &= 0x2E; //
    Niski_E; // E = 0
    delayms(1); // czekaj 1ms
    zapisz_polecenie(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    zapisz_polecenie(0x08); // wyłącz LCD, kursor i miganie
    zapisz_polecenie(0x01); // czyść LCD
    zapisz_polecenie(0x06); // bez przesuwania w prawo
    zapisz_polecenie(0x0C); // włącz LCD, bez kursora i mrugania
    }
    
    // program główny
    int main(void)
    {
    // konfiguracja portów we/wy
    DDRC = 0x00;
    PORTC = 0x00;
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    zapisz_tekst("Halo");
    // petla nieskończona
    while(1);
    return 0;
    }
    
  • REKLAMA
  • #6 6606495
    jurex
    Poziom 12  
    Masz blad w procedurze inicjujacej:

    Powinno byc:
    
    
    // procedura inicjalizacji wyświetlacza LCD 
    void lcd_init(void) 
    { 
    delayms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego 
    Niski_E; // E = 0 
    Niski_RS; // RS = 0 
    char i; // zmienna licznikowa 
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji 
      { 
      Wysoki_E; // E = 1 
      // LCD &= 0x3F; // Moze byc problem przy powtornej inicjalizacji
      // powiedzmy ze poprzedni stan rejestru to 0x29, a Ty robisz and z 0x3F
      // i otrzymujesz 0x29 zamiast 0x39
      LCD = ((LCD & 0x0f) | 0x30); //!!!!!
      Niski_E; // E = 0 
      delayms(5); 
    } 
    Wysoki_E; // E = 1 
    //LCD &= 0x2E; // zerujesz niepotrzebnie zerowy bit 
     LCD &= 0x2F; // 
    Niski_E; // E = 0 
    delayms(1); // czekaj 1ms 
    zapisz_polecenie(0x28); // interfejs 4-bity, 2-linie, znak 5x7 
    zapisz_polecenie(0x08); // wyłącz LCD, kursor i miganie 
    zapisz_polecenie(0x01); // czyść LCD 
    zapisz_polecenie(0x06); // bez przesuwania w prawo 
    zapisz_polecenie(0x0C); // włącz LCD, bez kursora i mrugania 
    } 
    
    



    Nastepna sprawa to moj kawalek to mozna nieco uproscic:
    
    
    void ustaw_pamiec(int adres) 
    { 
      adres |= 0x80; 
      zapisz_polecenie(adres); 
    } 
    
    


    I jeszcze jedno. Musza byc opoznienia miedzy rozkazami zmieniajacymi stan na linii E.
  • #7 6622270
    87irek
    Poziom 10  
    Trochę źle się za to wszystko wziąłem. Tym razem robię wszystko patrząc na dobrą dokumentację wyświetlacza i wszystko powinno być ok. Biorę pod uwagę opóźnienia, ale nadal nie działa tak jak powinien. Zaczyna wariować, tzn migają wszystkie linie, a w dwóch miejscach tylko pojawia się zamiennie migający kursor. Już nie wspomnę o tym, że nie chce wyświetlać żadnego tekstu. Co ja robię nie tak??
    ps. opóźnienia zmieniałem na 1 lub więcej i nic to nie dawało.
    na razie nie zajmuję się przestawieniem tego na 4 linie, póki nie zacznie mi odpowiednio działać

    
    #include <avr/io.h>
    //
    #define LCD  PORTC
    #define E  3
    #define RW 2
    #define RS  1
    //
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    //
    #define CLR_RW  LCD &= ~_BV(RW)
    //
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    // funkcja opóźniająca o x*1ms
    void waitms(char x)
    {
    unsigned char a, b; // zmnienne licznikowe
     for( ; x > 0; --x) // ta pętla zostanie wykonana x-razy
    //ustawienia kolejnych dwóch pętli zostały dobrane dla zegara 16MHz 
      for(b = 10; b > 0; --b)
       for(a = 26; a > 0; --a)
        	__asm("nop"); // dodatkowa instrukcja opóźniająca o 1 cykl
    	// razem to da opóźnienie ok. x * 1ms
    	// x od 0 do 255
    }
    
    
    // pcodedura zapisu bajtu do wyświetlacza LCD
    // bez rozróżnienia instrukcja/dana
    void write_to_lcd(char x)
    {
    SET_E; // ustaw na E stan wysoki
    waitms(2);
    LCD = ((LCD & 0x0F) | (x & 0xF0)); // zapis pierwszej połówki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    waitms(2);
    SET_E; // ustaw na E stan wysoki
    waitms(2);
    LCD = ((LCD & 0x0F) | ((x & 0x0F) << 4)); // zapis drugiej połowki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    waitms(4); // czekaj 1ms
    }
    
    // procedura zapisu instrukcji do wyświetlacza LCD
    void write_command(char x)
    {
    CLR_RS; // niski stan na RS -> zapis instrukcji
    waitms(1);
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu danej do wyświetlacza LCD
    void write_char(char x)
    {
    SET_RS; // wysoki stan na RS -> zapis danej
    waitms(1);
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu tekstu do wyświetlacza LCD
    void write_text(char * s)
    {
    while(*s) // do napotkania 0
      {
      write_char(*s); // zapisz znak wskazywany przez s na LCD
      s++; // zwiększ s (przygotuj nastepny znak)
      }
    }
    
    // procedura inicjalizacji wyświetlacza LCD
    void lcd_init(void)
    {
    waitms(45); // czekaj 45ms na ustabilizowanie się napięcia zasilającego
    
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    CLR_RW;
    waitms(1);
    // pierwszy FUNCTION SET
      SET_E; // E = 1
      waitms(2);
      LCD = ((LCD & 0x0f) | 0x30);//
      CLR_E; // E = 0
      waitms(2); // czekaj 1ms
    
    //wszystko wg instrukcji 
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    write_command(0x0f); // display on/off control
    write_command(0x01); // czyść LCD
    waitms(3);
    write_command(0x06); // bez przesuwania w prawo
    }
    
    // program główny
    int main(void)
    {
    // konfiguracja portów we/wy
    DDRC = 0x00;
    PORTC = 0x00;
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    write_text("Ala ma kota :D");
    // petla nieskończona
    while(1);
    return 0;
    }
    


    Załączam instrukcję do wyświetlacza przesłaną mi bezpośrednio z serwisu technicznego producenta.
    Załączniki:
  • #8 6623834
    jurex
    Poziom 12  
    Widzę tu jeden zasadniczy błąd.
    Aby ustawić port C na wyjściowy trzeba wpisać do rejestru DDRC na odpowiednich bitach jedynki. A Ty wpisujesz zero, wiec wszystkie piny tego portu ustawione są jako wejściowe.
    Do wykrywania tego rodzaju błędów polecam używanie symulatora :D

    Moja propozycja jest następująca:
    
    // procedura inicjalizacji wyświetlacza LCD 
    void lcd_init(void) 
    { 
    // konfiguracja portów we/wy 
    CLR_E; // E = 0 
    CLR_RS; // RS = 0 
    CLR_RW; 
    
    DDRC = 0xF0 | _BV(E) | _BV(RW) | _BV(RS); 
    
    waitms(45); // czekaj 45ms na ustabilizowanie się napięcia zasilającego 
    
    
    waitms(1); 
    // pierwszy FUNCTION SET 
      SET_E; // E = 1 
      waitms(2); 
      LCD = ((LCD & 0x0f) | 0x30);// 
      CLR_E; // E = 0 
      waitms(2); // czekaj 1ms 
    
    //wszystko wg instrukcji 
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7 
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7 
    write_command(0x0f); // display on/off control 
    write_command(0x01); // czyść LCD 
    waitms(3); 
    write_command(0x06); // bez przesuwania w prawo 
    } 
    
    // program główny 
    int main(void) 
    { 
    // inicjalizacja LCD 
    lcd_init(); 
    // zapisz na LCD przykładowy tekst 
    write_text("Ala ma kota :D"); 
    // petla nieskończona 
    while(1); 
    return 0; 
    } 
    


    Następna sprawa to że nie używasz linii RW.
    Nie musisz jej używać jeśli nie chcesz odczytywać danych z wyświetlacza,
    ale wtedy po co zajmować sobie pin portu. Można przecież na stałe podłączyć stan niski. Należy wtedy jednak pamiętać by po każdym zapisie do LCD poczekać co najmniej 70µs przed kolejnym zapisem.
REKLAMA