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

Inicjalizacja LCD HY-1601C4 z ATmegą16 - brak wyświetlania tekstu

Mikemtb 12 Lip 2007 22:04 2164 9
REKLAMA
  • #1 4076464
    Mikemtb
    Poziom 12  
    Posty: 128
    Ocena: 22
    Witam
    Jestem poczatkujący z uP i po "zabawie" z migającymi diodami i klawiaturką chcę odpalić LCD. Czytam tu już setny post o tym, ale ciągle nie wychodzi. Mam zestaw uruchomieniowy "nowy Elektronik 300-k". Programuje pod ATmegą16. LCD tam jest HY-1601C4 1x16, który poniekad jest zgodny z HD44780. Przebrnąłem juz przez podłaczenie i teraz cche go zainicjalizować i jest problem. Mam te osiem pół zaczernionych i postępuję wg. instrukcji z datasheet HD44780 i wg. pdfa znalezionego na elektrodzie ( z "elektronika dla wszystkich"). Steruije w trybie 8bit. Moj program wygląda astepująco: (szyna danych podłączona do portu D, RS do B0, R/W do B1, E do B2.

    #include <avr/io.h>
    #include <util/delay.h>

    void Inicjalizacja(void)
    {
    DDRB=0xFF; // Ustawiam port B jako wyjścia
    DDRD=0xFF; // Ustawiam port D jako wyjścia

    }
    void _delay_s(unsigned int __s) //opoznienie w sekundach
    {
    unsigned int iles;
    for(iles=1;iles<=(__s*5);iles++) _delay_ms(200);
    }


    int main(void)
    {
    Inicjalizacja(); // Wywołanie funkcji inicjującej port B i D
    _delay_ms(50);
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_ms(10);
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_us(150);
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_ms(20);
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    PORTD &=~_BV(3);
    PORTD &=~_BV(2);
    _delay_ms(20);
    PORTD &=~_BV(4);
    PORTD &=~_BV(5);
    PORTD |=_BV(3);
    _delay_ms(20);
    PORTD &=~_BV(3);
    PORTD |=_BV(0);
    _delay_ms(20);
    PORTD &=~_BV(0);
    PORTD |=_BV(2);
    PORTD |=_BV(1);
    PORTD |=_BV(0);

    }

    No i po tej procedurze dalej jest te 8 pól zaczernionych- nic się nie dzieje. I pytanie czemu? Podczas inicjalizacji w ogóle trzeba coś robić z E? Co robię źle?(może nie wszystko;-))
  • REKLAMA
  • Pomocny post
    #2 4076539
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    No to coś kiepsko tą dokumentację czytałeś...
    Linia E steruje wysyłaniem danych do LCD...
    Opadające zbocze zatrzaskuje dane na szynie...
  • #3 4078630
    Mikemtb
    Poziom 12  
    Posty: 128
    Ocena: 22
    Ok, co racja to racja, czytałem to , ale nie wiem czemu myślałem, że to tylko trzeba robić przy wpisywaniu znakow, a nie tez przy inicjalizacji. No ale doczytałem i dalej kaplica- osiem pół dalej tylko zaczernionych, choc jak program się wykonuje, to mam wrażenie że troszkę migają. Program teraz wygląda następująco ( tak wiem co to sa pętle, ale na razie chce to wszystko łopatologicznie aby nie popełnić "byka" w innym miejscu :) )

    #include <avr/io.h>
    #include <util/delay.h>
    #define RS1 PORTB |=_BV(0); //stan wysoki RS
    #define RS0 PORTB &=~_BV(0); //stan niski RS
    #define RW1 PORTB |=_BV(1); //stan wysoki RW
    #define RW0 PORTB &=~_BV(1); //stan niski RW
    #define E1 PORTB |=_BV(2); //stan wysoki enable
    #define E0 PORTB &=~_BV(2); //stan niski enable

    void Inicjalizacja(void)
    {
    DDRB=0xFF; // Ustawiam port B jako wyjścia
    DDRD=0xFF; // Ustawiam port D jako wyjścia

    }
    int main(void)
    {
    Inicjalizacja(); // Wywołanie funkcji inicjującej port B i D
    E0;
    _delay_ms(30); //stabilizacja napiecia,
    //****** 1(function set):
    RS0;
    RW0;
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + minimum 4,1ms
    //******** 2(function set):
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(1);// Th + minimum 100us
    //******** 3(function set):
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    //********4(function set z N i F):
    PORTD |=_BV(4);
    PORTD |=_BV(5);
    PORTD &=~_BV(3);
    PORTD &=~_BV(2);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    //*****5(display off):
    PORTD |=_BV(3);
    PORTD &=~_BV(4);
    PORTD &=~_BV(5);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    //*****6(display on):
    PORTD |=_BV(0);
    PORTD &=~_BV(3);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    //*********7(entry mode set):
    PORTD |=_BV(2);
    PORTD |=_BV(1);
    PORTD |=_BV(0);
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    }
  • REKLAMA
  • Pomocny post
    #4 4079916
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    ten kod jest po części troche bez sensu: przeważa ustawianie bitów:
    PORTD |= _BV(...);
    1/ nie uwzględnia to poprzedniego stanu bitów portu - poprostu ustawia wybrany bit
    2/ bity po zapisaniu same się niekasują...
    najłatwiej zamiast ustawiać bity w ten sposób - najzwyczajniej przypisać pełną wartość dla PORTD:
    int main(void) 
    { 
    	Inicjalizacja(); // Wywolanie funkcji inicjujacej port B i D 
    	E0; 
    	_delay_ms(30); //stabilizacja napiecia, 
    	//****** 1(function set): 
    	RS0; 
    	RW0; 
    	PORTD = 0x30;	// <<<<==== szyna 8 bitów, 1 linia znaków, znaki 5x8
    	_delay_us(1);// Tas 
    	E1; 
    	_delay_us(3); //Ten 
    	E0; 
    	_delay_ms(10);// Th + minimum 4,1ms 
    	//******** 2(function set): 
    /* ETC... */
    }


    Dodatkowo warto napisać funkcję do zapisu bajtu polecenia lub bajtu danych do wyświetlacza - zdecydowanie kod jest czytelniejszy i łatwiejszy do modernizacji..
  • #5 4080962
    Mikemtb
    Poziom 12  
    Posty: 128
    Ocena: 22
    OK, faktycznie będzie tak wygodniej przypisując cały port D. Ale to jedno a drugie, że dalej nie działało. Próbowałem już chyba wszystkiego a tam ciągle te 8 pól tylko się świeciło :P. No ale coś mnie naszło, żeby zmienić format z 5x7 na 5x10 no i od razu poszło :D Już mi działa- kursor miga:D. Choć oczywiście jest "ale".... :) Ale nic nie chce się wyświetlić. Tak na początek zrobiłem procedurę, która na sztywno ma wpisać literkę 'a' czyli 97 ascii ->11000001 (bin)-> 61 (hex), ale jak można się spodziewać to nic się nie wyświetla- kursor jak migał tak miga w stałej pozycji. Program wygląda teraz tak:

    #include <avr/io.h>
    #include <util/delay.h>
    #define RS1 PORTB |=_BV(0); //stan wysoki RS
    #define RS0 PORTB &=~_BV(0); //stan niski RS
    #define RW1 PORTB |=_BV(1); //stan wysoki RW
    #define RW0 PORTB &=~_BV(1); //stan niski RW
    #define E1 PORTB |=_BV(2); //stan wysoki enable
    #define E0 PORTB &=~_BV(2); //stan niski enable



    void Inicjalizacja(void)
    {
    DDRB=0xFF; // Ustawiam port B jako wyjścia
    DDRD=0xFF; // Ustawiam port D jako wyjścia

    }

    void _delay_s(unsigned int __s) //opoznienie w sekundach
    {
    unsigned int iles;
    for(iles=1;iles<=(__s*5);iles++) _delay_ms(200);
    }

    void init_lcd(void)
    {
    E0;
    _delay_ms(30); //stabilizacja napiecia,

    //****** 1(function set):
    RS0;
    RW0;
    PORTD = 0x30;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + minimum 4,1ms

    //******** 2(function set):
    PORTD = 0x30;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + minimum 100us

    //******** 3(function set):
    PORTD = 0x30;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    //********4(function set z N=0 (1 linia) i F=1 (znaki 5x10):
    PORTD = 0x38;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    //*****5(display on):
    PORTD = 0x08;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    //*****6(clear display):
    PORTD = 0x01;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    //*********7(entry mode set):
    PORTD = 0x07;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    //*********8(dispaly ON, cursor OFF, blink ON):
    PORTD = 0x0D;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    }

    void lcd_wpisz_a(void)
    {
    RW0;
    RS1;
    PORTD = 0x61;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_s(1);// Th + rezerwa
    }


    int main(void)
    {
    Inicjalizacja(); // Wywołanie funkcji inicjującej port B
    init_lcd();
    lcd_wpisz_a();
    _delay_s(5);
    while(1)
    {
    }
    }

    Jedno to mam wątpliwości czy procedura "Inicjalizacja" nic nie psuje.
    A drugie, to czasem czytałem, że podłącza się RW do masy ( jezeli chce się tylko wykonywać operacje wypisywania na LCD), jak ja tak zrobię to powyższy program jak jest wykonywany nic nie wypisuje ale kursor przesuwa się o jedno pole ( nie wazne czy mu dam, zeby wypisał 'a' czy cokolwiek innego). Chyba też mi brakuje w init lcd jakiejś części o resetowaniu LCD bo pozycja kursora jest "pamietana" i przy kolejnym zaprogramowaniu przesuwa się na kolejne pozycje ( ale czemu jak jest pozycja 6 w init_lcd: clear display). No i kolejny mankament, to że kursor się przesuwa tylko do 8 pola a potem znika, tak że chyba dalej tylko 8 pól działa a nie 16 ( choć w sumie jak reguluje kontrast, to wszystki 16 pól się zaciemnia) , tylko czemu?:(
    Aha- i czy ten model (HY-1601C) tak ma, że znaki 5x7 nie pójdą?
    No i najwazniejsze pytanie - czemu mi nic nie wypisuje?:(

    Dodano po 4 [godziny] 16 [minuty]:

    OK- jestem krok dalej. Napis jest no i oczywiście "ale" :)

    Okazało się ( po raz kolejny nie wiem czemu) ze przy ustawieniu entry mode set nie można dać 1 na D0 ( S- przesuwanie napisu, choć jak dam 0 to i tak sie przesuwa :P ), wiec mamy w tej sekcji zmianę:

    //*********7(entry mode set):
    PORTD = 0x06;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa

    Dolozylem tez 3 funkcje:

    void lcd_wpisz_a(void)
    {
    RW0;
    RS1;
    PORTD = 0x61;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(10);// Th + rezerwa
    }

    void do_LCD(char x)
    {
    RW0;
    RS1;
    PORTD = x;
    _delay_us(1);// Tas
    E1;
    _delay_us(3); //Ten
    E0;
    _delay_ms(200);// Th + rezerwa
    //czas taki dlugi zeby wszystko dokladnie bylo widac
    }

    void napis(char * s)
    {
    while(*s) // do napotkania 0
    {
    do_LCD(*s); // zapisz znak wskazywany przez s na LCD
    s++; // zwiększ s (przygotuj nastepny znak)
    }
    }


    no i main teraz wyglada tak:

    int main(void)
    {
    char *n;
    n="12345";
    Inicjalizacja(); // Wywołanie funkcji inicjującej port B
    init_lcd();
    while(1)
    {
    napis(n);
    //do_LCD('1'); <-- to samo by sie stalo
    //do_LCD('2');
    //do_LCD('3');
    //do_LCD('4');
    //do_LCD('5');
    }
    }

    A problem jest taki, że na wyswietlaczu jakos to dziwnie się wyświetla: tzn jest wypisane 12345123 ( pierwsze osiem pól- zajmuje to jakies 1,4 sekundy- sa specjalnie duze opoznienia, zeby wszystko bylo widac), potem nastepuje przerwa na okolo 6,74 sekundy i na nastepnych 8 polach pojawia sie napis 12345123. Czemu tak się dzieje?

    Dodano po 5 [minuty]:

    No i przesuwanie jednak dziala, ale tez dziwnie :) Tzn. najpierw jest dluuuuga przerwa- nic sie nie wyswietla. potem napis zanczyna sie wyswietlać na ośmiu polach , zrobi ze 3 pętle, a potem dołącza się kolejne 8 pół. :)) Proszę niech mi to ktoś wytłumaczy :)
  • REKLAMA
  • #6 4081705
    szod
    Poziom 33  
    Posty: 1663
    Pomógł: 215
    Ocena: 120
    Spróbuj inicjalizację zrobić inaczej. Najpierw trzy razy wyślij 0x30, potem
    0x30 co oznacza tryb 1 liniowy i znak 5 x 7, następnie 0x08 czyli wyłączenie
    wyświetlacza, następnie 0x01 co włączy wyświetlacz (niestandardowa
    instrukcja), i na koniec 0x07 czyli inkrementacja wskaźnika adresu i brak
    przesuwania zawartości DDRAM. To powinno wystarczyć żeby poprawnie
    zainicjalizować wyświetlacz. Co do linii RW to musi być do masy jak nie
    korzystasz z flagi busy.
  • #7 4081921
    Mikemtb
    Poziom 12  
    Posty: 128
    Ocena: 22
    No tylko, że t ostandardowa inicjalizacj która próbowałem jako pierwszą i juz piałem, że w trybie 5x7 lcd w ogole nie chce startować. Dalej jestem w miejscu gdzie działa tylko pierwsze 8 pól LCd, a drugie 8 zaczyna działać dopiero potem jezeli jest napis jest przewijany, jezeli nie ejst to wcale nie wypisuje więcej niż 8 znaków, chybam, że dam to w pętli nieskonczonej, to wypisze 8 znaków potem przerwa i po chwili dopisuje dalej, ale oczywiście nie cały wyraz tylko troche środkowych literek traci tak jak by pisał gdzieś indziej, tylko gdzie, przecież jest jedn linia zainicjaklizowana, no ale może ten wyświetlacz jest jakiś dziwny?

    Dodano po 41 [minuty]:

    Znalazłem błąd u siebie- mam inicjalizacje dla 2 linii, ale to tylko dlatego ze w trybie jednoliniowym nie chce pójśc- moze jakiś dziwny wyswietlacz mam. No bo teraz mam funtion set:

    PORTD = 0x3C; // 00111100
    _delay_us(100);// Tas
    E1;
    _delay_us(200); //Ten
    E0;
    _delay_ms(100);// Th + rezerwa

    a dla jednej lini powinno być:
    PORTD = 0x34; // 00110100

    A wtedy jakas kaszana jest- pierwsze 8 pol jest zaczernionych ale troche mniej niz przy braku inicjalizacji, a drugie osiem pól ma zaczernionych tak z 2-3 piksle od góry i coś tam pod światło widać że się się zmienia, ale ogólnie kaszana i nie tak to powinno być. Może cos z moim wyświetlaczem?
  • REKLAMA
  • Pomocny post
    #8 4082180
    szod
    Poziom 33  
    Posty: 1663
    Pomógł: 215
    Ocena: 120
    Dla trybu jednej linii i znaku 5 x 7 powinno być 0x30 jak pisałem. Sprawdź
    dokładnie opóźnienia. Niewykluczone że jest coś nie tak. Może µC pracuje na
    innej częstotliwości niż myślisz. Fusebity sprawdziłeś?
    Co do wyświetlacza to możesz mieć nietypowy bo są takie 1 x 16 które trzeba
    inicjalizować jako 2 liniowe. Pierwsze osiem znaków jest jakby pierwszą linią
    (początek 00h) a drugie osiem drugą (początek 40h). Możliwe że masz właśnie
    taki wyświetlacz.
  • #9 4082603
    Mikemtb
    Poziom 12  
    Posty: 128
    Ocena: 22
    No to chyba jest jeden z tych dziwnych wyświetlaczy, bo zainicjalizować na jedną linię się za nic nie chce. Sprawdzalem czestotliwosc jest dobra i czasy też.
    Jeszcze tylko jedna prośba. Co to znaczy, ze zaczyna sie od 0x40? chyba tak jest faktycznie, bo zrobilem testy z wyswietlaniem i pierwsze 8 znakow jest ok, potem 'gubi' się 32 znaki i na kolejnych 8 polach pojawiaja sie znaki 40-47. Tylko jak to teraz zrobić, żeby dobrze się wyświetlało?
  • #10 4082691
    szod
    Poziom 33  
    Posty: 1663
    Pomógł: 215
    Ocena: 120
    Po prostu znak na pozycji 9 ma adres 0x40 a ostatni 16 to adres 0x47. Poza
    tym jak wpisujesz znaki po kolei to na wyświetlaczu wyświetla się pierwszych
    osiem znaków a pozostałe nie giną. Są zapisywane w DDRAM. Spróbuj załadować
    długi napis i przesuń go. Pojawią się te niby zgubione znaki. Ten wyświetlacz
    jest 2 liniowy po 8 znaków tylko te dwie linie są ustawione po kolei a nie pod
    sobą. W pierwszej połowie linii będzie widocznych 8 znaków a pozostałych
    32 będą dostępne dopiero jak przesuniesz napis. To samo w drugiej połowie
    linii. W sumie możesz zapisać 80 znaków ale widocznych dopiero po operacjach
    przesuwania. Zrób kilka prób i wszystko stanie się jasne.

Podsumowanie tematu

✨ Dyskusja dotyczy problemów z inicjalizacją wyświetlacza LCD HY-1601C4 (1x16) pod mikrokontroler ATmega16 w trybie 8-bitowym. Użytkownik stosował standardowe procedury inicjalizacji zgodne z HD44780, jednak wyświetlacz nie wyświetlał tekstu, a jedynie świeciło osiem pól. Po zmianie formatu z 5x7 na 5x10 kursor zaczął migać, lecz znaki nie pojawiały się na ekranie. Wskazano, że linia E steruje zapisem danych i należy poprawnie generować opadające zbocze sygnału. Zasugerowano przypisywanie pełnej wartości do portu danych zamiast ustawiania pojedynczych bitów, co poprawia czytelność i stabilność kodu. Problemem okazał się nietypowy charakter wyświetlacza HY-1601C4, który choć fizycznie jednowierszowy, wymaga inicjalizacji i obsługi jak wyświetlacz dwuwierszowy, gdzie pierwsze 8 znaków to linia 1 (adres DDRAM 0x00), a kolejne 8 znaków to linia 2 (adres DDRAM 0x40). W efekcie wyświetlacz posiada 16 znaków w dwóch "wirtualnych" liniach, a wyświetlanie dłuższych napisów wymaga przesuwania zawartości DDRAM. Zalecane jest stosowanie odpowiednich opóźnień, sprawdzenie fusebitów i częstotliwości taktowania mikrokontrolera. Ostatecznie problem rozwiązano przez dostosowanie inicjalizacji do trybu dwuliniowego i obsługę adresacji DDRAM zgodnie z nietypową konstrukcją wyświetlacza.
Wygenerowane przez model językowy.
REKLAMA