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 16x2 HD44780 – jak poprawnie wyświetlić tekst na drugiej linii?

HUGO 02 Paź 2006 09:55 4996 27
REKLAMA
  • #1 3077296
    HUGO
    Poziom 15  
    Posty: 220
    Pomógł: 3
    Ocena: 14
    Witam serdecznie

    Mam taki problem mam wyświetlacz LCD oparty na HD44780 są to dwie linie po 16 znaków jednak w projekcie którym go stosuje on nie działą jest w programie tak skonfigurowany pierwsza linia 00h druga 40h no i wszystko było by ok ale on mi wyświtla tylko pierwsza linie czy jest możliwosc skonfigurowania drugiej lini jakos inaczej ? mozecie mi pomoc ?

    z góry badzoe dziekuje i pozdrawiam

    Andrzej Lembicz
  • REKLAMA
  • #2 3077830
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    ale czy on wyświetla znaki w pierwszej lini, czy tylko "zaczernione kwadraty" ? w jakim języku piszesz? wklej tutaj najlepiej kod programu (odpowiedzialny za lcd).
  • #3 3077902
    HUGO
    Poziom 15  
    Posty: 220
    Pomógł: 3
    Ocena: 14
    Witam

    Tzn. tak kod jest ok bo kolega robil urzadzenie w oparciu o ten soft i wszystko gra ja sie obawiam ze lcd co ja uzywam ma jakas inna konfiguracje a niemam do niego noty, dokumentacji. Wyswietla tylko pierwsza linie a w drugiej nic a ma byc cos wyswietlane niewiem moze ten lcd potrzebuje jakiegos specjalnego zabiegu w aktywacji jego. program jest w C napisany jest on dla PIC16F873.

    Pozadrawiam

    Andrzej
  • #4 3085257
    HUGO
    Poziom 15  
    Posty: 220
    Pomógł: 3
    Ocena: 14
    Temat zamkniety wszystko dziala


    Pozdrawiam

    Andrzej
  • #5 3896796
    campnou
    Poziom 11  
    Posty: 15
    Witam
    Ja mam dokładnie ten sam problem co HUGO.
    Mam wyświetlacz LCD 2x16.
    W pierwszej linii wszystko jest wyświetlane tak jak chcę.
    Natomiast nie mogę nic wyświetlić w drugiej linii.
    Wysyłanie komendy 0x40 nic nie daje, a jak każę mu wyświetlić bardzo długi tekst (np 100 znaków) to i tak wyświetlane jest to tylko w 1. linii.

    Może mam coś źle w konfiguracji LCD i jest on widziany jako 1x16.

    write_command(0x38); // interfejs 8-bitów, 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
  • #6 3896886
    migol
    Poziom 18  
    Posty: 313
    Pomógł: 4
    Ocena: 5
    HUGO, podziel się rozwiązaniem problemu :)
  • REKLAMA
  • #7 3896888
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    Komenda, instrukcja czyli sygnał na RS=0 (nie dana) przejścia do następnej lini to 0xC0 a nie jak piszesz 0x40. czyli po wyświetleniu tekstu w pierwszej linii ustawiasz stan niski na RS, wysyłasz 0xC0 potem RS=1 i wysyłasz tekst do wyświetlenia w drugiej linii
  • #8 3897026
    campnou
    Poziom 11  
    Posty: 15
    prokopcio napisał:
    Komenda, instrukcja czyli sygnał na RS=0 (nie dana) przejścia do następnej lini to 0xC0 a nie jak piszesz 0x40. czyli po wyświetleniu tekstu w pierwszej linii ustawiasz stan niski na RS, wysyłasz 0xC0 potem RS=1 i wysyłasz tekst do wyświetlenia w drugiej linii


    Niestety jak używam komendy 0xC0 a potem każę mu wyświetlić tekst to dalej nic nie jest wyświetlane w drugiej linii.
  • REKLAMA
  • #9 3897462
    jj
    Poziom 19  
    Posty: 145
    Pomógł: 42
    campnou napisał:

    Może mam coś źle w konfiguracji LCD i jest on widziany jako 1x16.

    write_command(0x38); // interfejs 8-bitów, 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
    Czy to cała inicjalizacja? To problem może być tutaj. Według dokumentacji jeśli czas narastania napięcia zasilającego nie mieści się w przedziale 0.1ms - 10ms to nie działa wewnętrzny reset i trzeba przeprowadzić inicjalizację programową. Patrz rozdział "Initializing by Instruction" dokumentacji HD44780.

    Pozdrawiam,
    Jacek
  • #10 3897617
    campnou
    Poziom 11  
    Posty: 15
    jj napisał:

    Czy to cała inicjalizacja? To problem może być tutaj. Według dokumentacji jeśli czas narastania napięcia zasilającego nie mieści się w przedziale 0.1ms - 10ms to nie działa wewnętrzny reset i trzeba przeprowadzić inicjalizację programową. Patrz rozdział "Initializing by Instruction" dokumentacji HD44780.

    Pozdrawiam,
    Jacek


    Cała inicjacja wygląda w ten sposób:
    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; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x3F; //
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    waitms(1); // czekaj 1ms
    write_command(0x38); // interfejs 8-bitów, 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
    }


    I po tej inicjacji mogę bez problemu wypisywać tekst w 1. linii.
    Ale nie potrafie nic wyświetlić w 2. linii.
    Czy to może być źle przeprowadzona inicjacja?
  • #11 3898842
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    Jak włączasz zasilanie to jeszcze przed zainicjowaniem LCD w pierwszej linijce powinny pojawić się zaciemnione prostokąty (pełne znaki 16szt) oczywiście przy właściwie ustawionym kontraście - jak zainicjujesz prawidłowo wyświetlacz jako dwu-liniowy to te prostokąty powinny zniknąć. czy tak się dzieje u Ciebie? Jak nie masz pewności to dodaj opóźnienie powiedzmy 1s przed inicjacją LCD. Jeśli tak się nie dzieje to by oznaczało, że błąd leży w inicjowaniu LCD.

    Dodano po 15 [minuty]:

    wklej jeszcze podprogram o nazwie write_command.

    sugeruję n apoczątek również użyć opóźnienia po ustawieniu sygnału enable na 1 (a raczej przed jego zmianą na 0)
  • #12 3899031
    jj
    Poziom 19  
    Posty: 145
    Pomógł: 42
    Domyślam się, że zmienna LCD reprezentuje port mikrokontrolera do którego dołączone są linie danych wyświetlacza. W takim razie zastanawia mnie dlaczego wpisujesz tam wartość tak: a nie tak:

    Ale nie do tego chciałem się przyczepić. Według mnie bruździ linia:
    LCD &= 0x2E; //

    Przestawia ona wyswietlacz na 4-bitową szynę danych. Później wprawdzie
    write_command(0x38); // interfejs 8-bitów, 2-linie, znak 5x7
    wraca do szyny 8-bitowej, ale ten rozkaz robi tylko to! Nie ustawia on dwóch linii danych ani rozmiaru znaku.

    Spróbuj bez fragmentu
    SET_E; // E = 1 
    LCD &= 0x2E; //
    CLR_E; // E = 0


    No i pytanie, czy procedura "write_command" faktycznie robi to co sugeruje jej nazwa...

    Jacek
  • REKLAMA
  • #13 3899111
    Dr_DEAD
    Poziom 28  
    Posty: 829
    Pomógł: 126
    Ocena: 3
    SET_E; // E = 1 
    LCD &= 0x2E; //
    CLR_E; // E = 0


    Panowie tu też tkwi problem, pwinno być tak:

    
    LCD = 0x2E; //
    SET_E; // E = 1
    //Dodatkowe opuźnienie 
    CLR_E; // E = 0
    


    Czyli najpierw ustawiamy lnie danych D0-D8 na następnie "strobujemy" linią "E"
  • #14 3899180
    jj
    Poziom 19  
    Posty: 145
    Pomógł: 42
    Dr_DEAD napisał:

    Panowie tu też tkwi problem, pwinno być tak:

    
    LCD = 0x2E; //
    SET_E; // E = 1
    //Dodatkowe opuźnienie 
    CLR_E; // E = 0
    


    Czyli najpierw ustawiamy lnie danych D0-D8 na następnie "strobujemy" linią "E"

    Zapis danych następuje przy zboczu opadającym na linii E, i podczas tego zbocza dane muszą być stabilne. A dokładniej minimum 80 ns przed wystąpieniem zbocza opadającego, i minimum 10 ns po zboczu. Polecam lekturę rysunku "Write Operation" z dokumentacji HD44780.

    Jacek
  • #15 3899273
    Dr_DEAD
    Poziom 28  
    Posty: 829
    Pomógł: 126
    Ocena: 3
    jj napisał:

    Zapis danych następuje przy zboczu opadającym na linii E, i podczas tego zbocza dane muszą być stabilne. A dokładniej minimum 80 ns przed wystąpieniem zbocza opadającego, i minimum 10 ns po zboczu. Polecam lekturę rysunku "Write Operation" z dokumentacji HD44780.
    Jacek


    No fakt. Ja sie sugerowałem tym obrazkiem:
    LCD 16x2 HD44780 – jak poprawnie wyświetlić tekst na drugiej linii?
    A ten obrazek jest tylko poglądowy.
  • #16 3899290
    campnou
    Poziom 11  
    Posty: 15
    Mój program wygląda w ten sposób:
    #include <avr/io.h>
    //
    #define LCD  PORTA
    #define E  7
    #define RW 6                     
    #define RS  5
    //
    #define SET_E   PORTC |= _BV(E)
    #define CLR_E   PORTC &= ~_BV(E)
    
    #define SET_RS  PORTC |= _BV(RS)
    #define CLR_RS  PORTC &= ~_BV(RS)
    
    #define SET_RW  PORTC |= _BV(RW)
    #define CLR_RW  PORTC &= ~_BV(RW) 
    
    // 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
      for(b = 10; b > 0; --b) // a ta 10 razy
    	for(a = 100; a > 0; --a) // natomiast ta 100 razy
    	__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
    asm("nop");
    LCD = x;
    CLR_E;
    waitms(1); // czekaj 1ms
    }
    
    // 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; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD = 0x3F; //
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    
    LCD = 0x2E; //
    SET_E; // E = 1
    CLR_E; // E = 0
    waitms(1); // czekaj 1ms
    write_command(0x38); // interfejs 8-bitow, 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
    }
    
    int main(void)
    {
    // konfiguracja portów we/wy
    DDRA = 0xFF;
    DDRC = 0xFF;
    PORTA = 0xFF;
    PORTC = 0xFF;
    
    CLR_RW;
    
    lcd_init();
    
    write_text("Hello ");
    write_command(0x40);
    write_text("World");
    
    while(1);
    return 0;
    }


    Skopiowalem gotowy przyklad ze strony: http://mikrokontrolery.net/avr_c_08.htm
    gdzie pozmienialem porty i konfigurację.

    Przed inicjacją w pierwszej linii są "kwadraty" a druga jest pusta, więc z kontrastem chyba wszystko OK.

    Chcialem wyświetlić w 1. linijce slowo "Hello" a w 2. "World".
    A wyświetla się tylko "Hello" w 1 linii.

    Więc domyślam się że coś jest nie tak w inicjacji (konfiguracji) i wyświetlacz działa jako 1x16.
  • #18 3899475
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    już to było testowane...

    campnou napisał:
    prokopcio napisał:
    Komenda, instrukcja czyli sygnał na RS=0 (nie dana) przejścia do następnej lini to 0xC0 a nie jak piszesz 0x40. czyli po wyświetleniu tekstu w pierwszej linii ustawiasz stan niski na RS, wysyłasz 0xC0 potem RS=1 i wysyłasz tekst do wyświetlenia w drugiej linii


    Niestety jak używam komendy 0xC0 a potem każę mu wyświetlić tekst to dalej nic nie jest wyświetlane w drugiej linii.


    Skoro po zainicjowaniu znikają kwadraty z pierwszej linii to znaczy, że został skonfigurowany jako dwu-liniowy. tu niema błędu.

    Dodano po 48 [minuty]:

    A jaki stosujesz wogóle zegar ?

    Spróbuj czy to zadziała (pozmieniałem jak powinno być wg mnie) są bardzo wydłużone czasy bo niewiem jaki masz zegar, jak nie zadziała to ja już niewiem co robić :-( jak zadziała to pozmniejszasz czasy i będzie ok.

    #include <avr/io.h> 
    // 
    #define LCD  PORTA 
    #define E  7 
    #define RW 6                      
    #define RS  5 
    // 
    #define SET_E   PORTC |= _BV(E) 
    #define CLR_E   PORTC &= ~_BV(E) 
    
    #define SET_RS  PORTC |= _BV(RS) 
    #define CLR_RS  PORTC &= ~_BV(RS) 
    
    #define SET_RW  PORTC |= _BV(RW) 
    #define CLR_RW  PORTC &= ~_BV(RW) 
    
    // 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 
      for(b = 10; b > 0; --b) // a ta 10 razy 
       for(a = 100; a > 0; --a) // natomiast ta 100 razy 
       __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) 
    { 
    LCD = x; 
    SET_E; // ustaw na E stan wysoki 
    waitms(10); // czekaj 10ms 
    CLR_E; 
    waitms(10); // czekaj 10ms 
    } 
    
    // 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(100); // czekaj 100ms na ustabilizowanie się napięcia zasilającego 
    CLR_RS; // RS = 0 
    char i; // zmianna licznikowa 
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji 
      { 
      SET_E; // E = 1 
      LCD = 0x30; // 
    waitms(10); // czekaj 10ms 
      CLR_E; // E = 0 
    waitms(10); // czekaj 10ms 
      } 
    
    SET_E; // E = 1 
    waitms(10); // czekaj 10ms 
    write_command(0x3C); // interfejs 8-bitow, 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 
    } 
    
    int main(void) 
    { 
    // konfiguracja portów we/wy 
    DDRA = 0xFF; 
    DDRC = 0xFF; 
    PORTA = 0xFF; 
    PORTC = 0xFF; 
    
    CLR_RW; 
    
    lcd_init(); 
    
    write_text("Hello "); 
    write_command(0xC0); 
    write_text("World"); 
    
    while(1); 
    return 0; 
    }
  • #19 3900066
    campnou
    Poziom 11  
    Posty: 15
    Wielkie dzięki.
    Zmniejszylem już czasy do takich jakie miałem wcześniej i wszystko działa;)

    Pozdrawiam
  • #20 3900153
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    wg. mnie to najprawdopodobniej zamieszanie zrobiły w bloku inicjacji linie które skasowałem w pierwszej kolejności:

    LCD = 0x2E; //
    SET_E; // E = 1
    CLR_E; // E = 0
    waitms(1); // czekaj 1ms


    co to miało być ?
  • #21 4589224
    ser1983
    Poziom 12  
    Posty: 116
    Pomógł: 3
    Ocena: 7
    Dopisuję się do wątku, gdyż mam podobny problem. Tylko tym razem sterowanie 4-bitowe. Nie powinien to być problem hardwarowy, gdyż pod Bascomem obsługuje mi dwie linie bez problemu. Tutaj, gdy wyśle komende 0x40 po napisie "Ala ma kota :D" pojawia mi sie jakiś krzak i pierwsza litera drugiego zdania czyli "K". Gdy wysyłam komendę 0xC0, nic się nie dzieje. Kod programu:

    
    #include <avr/io.h>
    //
    #define LCD  PORTC
    #define E  4
    #define RS  5
    //
    #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; // zmnienne licznikowe
    for( ; x > 0; --x) // ta pętla zostanie wykonana x-razy
      for(b = 10; b > 0; --b) // a ta 10 razy
    	for(a = 100; a > 0; --a) // natomiast ta 100 razy
    	__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;
    LCD=(LCD & 0xF0) | (x >>4);
    CLR_E;
    SET_E;
    LCD=(LCD & 0xF0) | (x & 0x0F);
    CLR_E;
    waitms(1); // czekaj 1ms
    }
    
    // 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; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      
      LCD =0x3F;//&= 0x3F; //
      SET_E; // E = 1
      waitms(5);
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    LCD = 0x2E; //
    SET_E; // E = 1
    
    waitms(5);
    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)
    {
    DDRC = 0xFF;
    PORTC = 0xFF;
    lcd_init();
    write_text("Ala ma kota :D");
    write_command(0xc0);
    write_text("Kot ma AIDS");
    while(1);
    return 0;
    }
    


    Z góry dziękuję za pomoc!

    Aha! Konfiguracja portu:
    DB4- PORTC.0
    DB5- PORTC.1
    DB6- PORTC.2
    DB7- PORTC.3
    E- PORTC.4
    RS- PORTC.5
  • #22 4592453
    Konto nie istnieje
    Konto nie istnieje  
  • #23 4594429
    webmortiz
    Poziom 20  
    Posty: 467
    Pomógł: 24
    Ocena: 15
    Korzystalem z tego samego kodu. Z poczatku cos nie smigalo wyswietlacz mrugal i dzialy sie dziwne rzeczy, pozniej nie jestem pewnien czy czegos nieswiadomie nie zmienialem ale zaczelo dzialac dobrze. Jak co wstawiam swoj kod to sobie porownasz. Dodalem tez 2 swoje funkcje.

    plik HD44780.h
    
    
    #include <avr/io.h>
    #include <util/delay.h>
    #define LCD_RS_DIR		 DDRC
    #define LCD_RS_PORT     	PORTC
    #define LCD_RS			    (1 << PC0)
    
    #define LCD_E_DIR		  DDRD
    #define LCD_E_PORT		 PORTD
    #define LCD_E			     (1 << PD5)
    
    #define LCD_DB4_DIR		DDRD
    #define LCD_DB4_PORT	       PORTD
    #define LCD_DB4			   (1 << PD6)
    
    #define LCD_DB5_DIR		DDRD
    #define LCD_DB5_PORT	       PORTD
    #define LCD_DB5			   (1 << PD7)
    
    #define LCD_DB6_DIR		DDRG
    #define LCD_DB6_PORT	       PORTG
    #define LCD_DB6			   (1 << PG0)
    
    #define LCD_DB7_DIR		DDRG
    #define LCD_DB7_PORT	       PORTG
    #define LCD_DB7			   (1 << PG1)
    
    
    #define HD44780_CLEAR					0x01
    
    #define HD44780_HOME					0x02
    
    #define HD44780_ENTRY_MODE				0x04
    	#define HD44780_EM_SHIFT_CURSOR		0
    	#define HD44780_EM_SHIFT_DISPLAY	1
    	#define HD44780_EM_DECREMENT		0
    	#define HD44780_EM_INCREMENT		2
    
    #define HD44780_DISPLAY_ONOFF			0x08
    	#define HD44780_DISPLAY_OFF			0
    	#define HD44780_DISPLAY_ON			4
    	#define HD44780_CURSOR_OFF			0
    	#define HD44780_CURSOR_ON			2
    	#define HD44780_CURSOR_NOBLINK		0
    	#define HD44780_CURSOR_BLINK		1
    
    #define HD44780_DISPLAY_CURSOR_SHIFT	0x10
    	#define HD44780_SHIFT_CURSOR		0
    	#define HD44780_SHIFT_DISPLAY		8
    	#define HD44780_SHIFT_LEFT			0
    	#define HD44780_SHIFT_RIGHT			4
    
    #define HD44780_FUNCTION_SET			0x20
    	#define HD44780_FONT5x7				0
    	#define HD44780_FONT5x10			4
    	#define HD44780_ONE_LINE			0
    	#define HD44780_TWO_LINE			8
    	#define HD44780_4_BIT				0
    	#define HD44780_8_BIT				16
    
    #define HD44780_CGRAM_SET				0x40
    
    #define HD44780_DDRAM_SET				0x80
    
    void LCD_WriteCommand(unsigned char);
    void LCD_WriteData(unsigned char);
    void LCD_WriteText(char *);
    void LCD_GoTo(unsigned char, unsigned char);
    void LCD_Clear(void);
    void LCD_Home(void);
    void LCD_Initalize(void);
    int  wysrodkuj(char* text);
    void LCD_WriteTextXY(unsigned char x, unsigned char y, char * text);
    


    plik HD44780.c
    
    
    #include <String.h>
    #include "HD44780.h"
    
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja wystawiająca półbajt na magistralę danych
    //
    //-------------------------------------------------------------------------------------------------
    void _LCD_OutNibble(unsigned char nibbleToWrite)
    {
    	if(nibbleToWrite & 0x01)
    		LCD_DB4_PORT |= LCD_DB4;//ustawienie portu na 1??
    	else
    		LCD_DB4_PORT  &= ~LCD_DB4;//ustawienie portu na 0??
    
    	if(nibbleToWrite & 0x02)
    		LCD_DB5_PORT |= LCD_DB5;
    	else
    		LCD_DB5_PORT  &= ~LCD_DB5;
    
    	if(nibbleToWrite & 0x04)
    		LCD_DB6_PORT |= LCD_DB6;
    	else
    		LCD_DB6_PORT  &= ~LCD_DB6;
    
    	if(nibbleToWrite & 0x08)
    		LCD_DB7_PORT |= LCD_DB7;
    	else
    		LCD_DB7_PORT  &= ~LCD_DB7;
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja zapisu bajtu do wyświetacza (bez rozróżnienia instrukcja/dane).
    //
    //-------------------------------------------------------------------------------------------------
    void _LCD_Write(unsigned char dataToWrite)
    {
    
    	LCD_E_PORT |= LCD_E;//1
    	_LCD_OutNibble(dataToWrite >> 4);
    	LCD_E_PORT &= ~LCD_E;//0
    	LCD_E_PORT |= LCD_E;//1
    	_LCD_OutNibble(dataToWrite);
    	LCD_E_PORT &= ~LCD_E;//0
    	_delay_us(50);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja zapisu rozkazu do wyświetlacza
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_WriteCommand(unsigned char commandToWrite)
    {
    	LCD_RS_PORT &= ~LCD_RS;//0
    	_LCD_Write(commandToWrite);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja zapisu danych do pamięci wyświetlacza
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_WriteData(unsigned char dataToWrite)
    {
    	LCD_RS_PORT |= LCD_RS;//1
    	_LCD_Write(dataToWrite);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja wyświetlenia napisu na wyswietlaczu.
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_WriteText(char * text)
    {
     	while(*text) LCD_WriteData(*text++);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja ustawienia współrzędnych ekranowych
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_GoTo(unsigned char x, unsigned char y)
    {
    	LCD_WriteCommand(HD44780_DDRAM_SET | (x + (0x40 * y)));
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja czyszczenia ekranu wyświetlacza.
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_Clear(void)
    {
    	LCD_WriteCommand(HD44780_CLEAR);
    	_delay_ms(2);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja przywrócenia początkowych współrzędnych wyświetlacza.
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_Home(void)
    {
    	LCD_WriteCommand(HD44780_HOME);
    	_delay_ms(2);
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Procedura inicjalizacji kontrolera HD44780.
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_Initalize(void)
    {
    	unsigned char i;
    	LCD_DB4_DIR |= LCD_DB4; // Konfiguracja kierunku pracy wyprowadzeń
    	LCD_DB5_DIR |= LCD_DB5; //czyli wyjscie z atmegi
    	LCD_DB6_DIR |= LCD_DB6; //
    	LCD_DB7_DIR |= LCD_DB7; //
    	LCD_E_DIR 	|= LCD_E;   //
    	LCD_RS_DIR 	|= LCD_RS;  //
    	KEY_ENA_DIR |= KEY_ENA;
    	KEY_ROW0_DIR|= KEY_ROW0;
    	_delay_ms(15); // oczekiwanie na ustalibizowanie się napiecia zasilajacego
    	KEY_ENA_PORT  |= KEY_ENA;//ENABLE i ROW0 na 1 czyli blokada odczytu z klawiatury
    	KEY_ROW0_PORT |= KEY_ROW0;
    	LCD_RS_PORT &= ~LCD_RS; // wyzerowanie linii RS
    	LCD_E_PORT &= ~LCD_E;  // wyzerowanie linii E
    
    	for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
    	{
    		LCD_E_PORT |= LCD_E; //  E = 1
    		_LCD_OutNibble(0x03); // tryb 8-bitowy
    		LCD_E_PORT &= ~LCD_E; // E = 0
    		_delay_ms(5); // czekaj 5ms
    	}
    
    	LCD_E_PORT |= LCD_E; // E = 1
    	_LCD_OutNibble(0x02); // tryb 4-bitowy
    	LCD_E_PORT &= ~LCD_E; // E = 0
    
    	_delay_ms(1); // czekaj 1ms 
    	LCD_WriteCommand(HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT); // interfejs 4-bity, 2-linie, znak 5x7
    	LCD_WriteCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF); // wyłączenie wyswietlacza
    	LCD_WriteCommand(HD44780_CLEAR); // czyszczenie zawartosći pamieci DDRAM
    	_delay_ms(2);
    	LCD_WriteCommand(HD44780_ENTRY_MODE | HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT);// inkrementaja adresu i przesuwanie kursora
    	LCD_WriteCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK); // włącz LCD, bez kursora i mrugania
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja zwraca wartosc pola x tak aby tekst byl wysrodkowany.
    //
    //-------------------------------------------------------------------------------------------------
    int wysrodkuj(char* text)
    {
    	int ilosc=strlen(text);
    	
    	if ((ilosc % 2)==0) return (16-ilosc)/2; else
    	return (15-ilosc)/2;
    }
    //-------------------------------------------------------------------------------------------------
    //
    // Funkcja wyświetlenia napisu na wyswietlaczu o wspolrzednych ekranowych(dla x>15 tekst wysrodkowany).
    //
    //-------------------------------------------------------------------------------------------------
    void LCD_WriteTextXY(unsigned char x, unsigned char y, char * text)
    {
    	if (x>15) LCD_GoTo(wysrodkuj(text),y); else LCD_GoTo(x,y);
    	LCD_WriteText(text);
    }
    
    


    Pozniej w main korzystasz np. z mojej funkcji
    LCD_WriteTextXY(4,1,"HELLO"); jezeli pierwsza wsp dasz > 15 tekst bedzie wysrodkowany. Mam nadzieje ze Ci to pomoze.

    P.S. Przypomnialo mi sie ze trza dodac ze jest to kontrola w trybie 4 bitowym.
  • #24 4609199
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    ser1983 napisał:
    Dopisuję się do wątku, gdyż mam podobny problem. Tylko tym razem sterowanie 4-bitowe. Nie powinien to być problem hardwarowy, gdyż pod Bascomem obsługuje mi dwie linie bez problemu. Tutaj, gdy wyśle komende 0x40 po napisie "Ala ma kota :D" pojawia mi sie jakiś krzak i pierwsza litera drugiego zdania czyli "K". Gdy wysyłam komendę 0xC0, nic się nie dzieje.......


    Wydaje mi się, że jest dobrze... tzn prawie bo nie wiem jaka prędkość procka. jedyne co mi przychodzi do głowy to wpisanie przerwy po zmianie stanu na RS...
  • #25 4609356
    ser1983
    Poziom 12  
    Posty: 116
    Pomógł: 3
    Ocena: 7
    Wstawiłem opóźnienie 5ms, ale nic to nie zmienia. Co więcej próbowałem write_command() dla zakresu 0x00-0xFF, w żadnym przypadku nie byłem w drugiej linii.
  • #26 4609688
    prokopcio
    Poziom 29  
    Posty: 2027
    Pomógł: 39
    Ocena: 143
    oj - dopiero teraz zauważyłem - zmień w procedurze inicjującej 0x28 na 0x2C i będzie dobrze ;-)

    pozdrawiam i życzę Wesołych Świąt.
  • #27 4610622
    ser1983
    Poziom 12  
    Posty: 116
    Pomógł: 3
    Ocena: 7
    prokopcio napisał:
    oj - dopiero teraz zauważyłem - zmień w procedurze inicjującej 0x28 na 0x2C i będzie dobrze ;-)


    Zmieniłem. Niewiele pomogło, może jeszcze źle przechodzę do następnej linii? Próbowałem:

    write_command(0x40);

    write_command(0xC0);

    oraz procedurą:
    void locate(char w, char k)
    {
    write_command((w*0x40+k)|0x80); 
    }


    0x40 po prostu wstawiło mi spację między pierwszym wyrazem a drugim (tym który powinien być w 2 linii).
    0xC0 wstawiło jeden z japońskich krzaków (dokładnie ten pod adresem 0xC0) oraz spację a następnie drugi wyraz.
    Procedura locate dała ten same efekt co wysłanie komendy 0x40.
  • #28 4611871
    ser1983
    Poziom 12  
    Posty: 116
    Pomógł: 3
    Ocena: 7
    No i działa! :) Dziękuję wszystkim za pomoc i życzę Wesołych Świąt!

    A dla potomności:
    PORTC zmieniłem na PORTD, zachowałem kolejność tzn. DB4-PIND4, DB5-PIND5, DB6-PIND6, DB7-PIND7, RS-PIND2, E-PIND3.
    Mój kod źródłowy dalej nie działał, ale zaczerpnąłem trochę wiadomości z tematu: https://www.elektroda.pl/rtvforum/topic331234.html
    I ruszył mi kod Pitusia Bajdusia. Zamieszczam go poniżej.

    #define F_CPU 4000000
    
    #include <avr/io.h>
    #include <avr/delay.h>
    
    #define DDR_DB4  DDRD
    #define PORT_DB4 PORTD
    #define DB4      PD4
    
    #define DDR_DB5  DDRD
    #define PORT_DB5 PORTD
    #define DB5      PD5
    
    #define DDR_DB6  DDRD
    #define PORT_DB6 PORTD
    #define DB6      PD6
    
    #define DDR_DB7  DDRD
    #define PORT_DB7 PORTD
    #define DB7      PD7
    
    
    #define DDR_RS   DDRD
    #define PORT_RS  PORTD
    #define RS       PD2
    
    #define DDR_E    DDRD
    #define PORT_E   PORTD
    #define E        PD3
    
    #define SET_DB4 PORT_DB4 |= _BV(DB4)
    #define CLR_DB4 PORT_DB4 &= ~_BV(DB4)
    
    #define SET_DB5 PORT_DB5 |= _BV(DB5)
    #define CLR_DB5 PORT_DB5 &= ~_BV(DB5)
    
    #define SET_DB6 PORT_DB6 |= _BV(DB6)
    #define CLR_DB6 PORT_DB6 &= ~_BV(DB6)
    
    #define SET_DB7 PORT_DB7 |= _BV(DB7)
    #define CLR_DB7 PORT_DB7 &= ~_BV(DB7)
    
    #define SET_E PORT_E |= _BV(E)
    #define CLR_E PORT_E &= ~_BV(E)
    
    #define SET_RS PORT_RS |= _BV(RS)
    #define CLR_RS PORT_RS &= ~_BV(RS)
    
    // funkcja wystawiająca na szynę danych połówkę bajtu
    // pod uwagę brana jest młodsza połówka parametru
    void out_nibble(char x)
    {
    if(x & _BV(0)) SET_DB4; else  CLR_DB4;
    if(x & _BV(1)) SET_DB5; else  CLR_DB5;
    if(x & _BV(2)) SET_DB6; else  CLR_DB6;
    if(x & _BV(3)) SET_DB7; else  CLR_DB7;
    }
    
    // funkcja zapisująca do LCD bajt danych
    void write_to_lcd(char x)
    {
    SET_E;
    out_nibble(x >> 4); // wystawienie na szynę danych starszej połówki bajtu
    CLR_E; // opadające zbocze na E powoduje zapis do wyświetlacza
    SET_E;
    out_nibble(x); // wystawienie na szynę danych młodszej połówki bajtu
    CLR_E; // opadające zbocze na E powoduje zapis do wyświetlacza
    _delay_ms(1); // opóźnienie ok 1 ms
    }
    
    // funkcja zapisująca do LCD rozkaz
    void write_command(char x)
    {
    CLR_RS; // niski stan na RS -> zapis instrukcji
    write_to_lcd(x); // zapis do LCD
    }
    
    // funkcja zapisujaca do LCD znak
    void write_char(char x)
    {
    SET_RS; // wysoki stan na RS -> zapis danej
    write_to_lcd(x); // zapis do LCD
    }
    
    // funkcja 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)
      }
    }
    
    // funkcja inicjująca LCD
    void lcd_init(void)
    {
    // ustawienie wszystkich linii sterujących LCD jako wyjścia
    DDR_DB4 |= _BV(DB4);
    DDR_DB5 |= _BV(DB5);
    DDR_DB6 |= _BV(DB6);
    DDR_DB7 |= _BV(DB7);
    DDR_E |= _BV(E);
    DDR_RS |= _BV(RS);
    //
    _delay_ms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      out_nibble(0x03);
      CLR_E; // E = 0
      _delay_ms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    out_nibble(0x02);
    CLR_E; // E = 0
    _delay_ms(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
    }
    
    void LCDxy(char x,char y)
    {
       unsigned char com=0x80;
       com|=(x|(y<<6));
       write_command(com);
    } 
    int main(void)
    {
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    write_text("1234");
    LCDxy(0,1);
    write_text("ABCD");
    // petla nieskończona
    while(1);
    return 0;
    }


    Tak naprawdę nie wiem do końca co było przyczyną, ważne że działa!

Podsumowanie tematu

✨ Dyskusja dotyczy problemu z wyświetlaniem tekstu na drugiej linii wyświetlacza LCD 16x2 opartego na sterowniku HD44780. Użytkownicy zgłaszali, że pomimo poprawnej inicjalizacji i konfiguracji (adresy DDRAM 0x00 dla pierwszej linii i 0x40 dla drugiej), tekst pojawia się tylko w pierwszej linii. Wskazano, że do przejścia do drugiej linii należy wysłać komendę ustawienia adresu DDRAM z bitem 0x80, czyli 0xC0 (0x80 + 0x40), a nie sam 0x40. Problemy mogły wynikać z błędów w inicjalizacji, niewłaściwego ustawiania linii danych, złej kolejności sygnałów na linii E (enable) oraz braku odpowiednich opóźnień czasowych. Zalecano dokładne przestrzeganie procedury inicjalizacji zgodnej z dokumentacją HD44780, w tym stabilizację napięcia zasilającego, prawidłowe ustawienie trybu 8-bitowego lub 4-bitowego oraz poprawne sterowanie liniami RS, RW i E. Wskazano także, że adresowanie DDRAM wymaga dodania 0x80 do adresu linii. Po poprawkach w kodzie, m.in. zmianie portów i korekcie opóźnień, problem został rozwiązany i możliwe jest wyświetlanie tekstu na obu liniach. W dyskusji pojawiły się przykłady kodu w języku C dla mikrokontrolerów PIC16F873 i AVR, a także uwagi dotyczące sygnałów sterujących i timingów zgodnych z dokumentacją HD44780.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA