Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

MSP430 i lcd 2x16 HD44780

Forrest1988 24 Apr 2010 16:10 5754 9
  • #1
    Forrest1988
    Level 10  
    Witam.

    Mam problem z obsługą wyświetlacza 2x16 zgodnego z HD44780. Pracuje on w trybie 4-bit podłączony pod port P1 mikrokontrolera MSP430.

    Schemat połączeń:
    P1.7 - D7
    P1.6 - D6
    P1.5 - D5
    P1.4 - D4
    P1.3 - EN
    P1.2 - x
    P1.1 - RW
    P1.0 - RS

    Poniżej zamieszczam kod programu, który do tej pory napisałem w IAR.


    
    #include "io430.h"
    
    /** Prototypy */
    void wait(unsigned int i);  // opóźnienie
    void initLcd(void);         // inicjalizuje wyświetlacz
    void lcdClock();            // strobowanie linii E
    void writeComm(int comm);   // wpisuje komendę do wyświetlacza
    
    /** Główna funckja */
    int main( void )
    {
      WDTCTL = WDTPW + WDTHOLD;
      P1DIR = 0xFF;
      wait(50000);
      initLcd();
    }
    
    /** Funkcja opóźniająca */
    void wait(unsigned int i)
    {
      unsigned int j;
      for(j=0;j<i;j++);
    }
    
    /** Inicjalizacja wyświetlacza */
    void initLcd(void)
    {
      writeComm(0x30);        // 4 - bitowe operacje, 2-wierszowy wyświetlacz, czcionka 5x8 kropek
      writeComm(0x08);        // wyłączenie wyświetlacza, kursora i migotania
      writeComm(0x01);        // kasowanie wyświetlacza
      writeComm(0x0E);        // załączenie wyświeltacza i kursora
    }
    
    /** Strobowanie stanem E */
    void lcdClock()
    {
      wait(5000);
      P1OUT |= 0x08;          // E w stan wysoki
      wait(5000);
      P5OUT &= ~0x08;           // E w stan niski
      wait(5000);
    }
    
    /** Wysyła komendę do wyświetlacza 2x4bit */
    void writeComm(int comm)
    {
      int help;
      
      // operacje na starszej części
      help = comm & 0xF0;
      P1OUT = help;          
      P1OUT |= 0x08;          // E <- 1
      lcdClock();
      
      // operacje na młodszej części
      help = (comm << 4) & 0xF0;
      P1OUT = help; 
      P1OUT |= 0x08;          // E <- 1
      lcdClock();
    }
    


    Nie miałem do czynienia z tego typu wyświetlaczami wcześniej więc prozę o pomoc i wyrozumiałość. Kod, który jest powyżej miał wyświetlić kursor na ekranie zanim zacznę wprowadzać dane.

    Pozdrawiam
  • #2
    tadzik85
    Level 38  
    Czy czarne prostokąty w 1 linii znikają po inicjacji?

    Dodano po 1 [minuty]:

    Nie widzę obsługi linii RS ani RW.
  • #3
    Forrest1988
    Level 10  
    to co napisałeś mnie zastanowiło więc pokręciłem sobie potencjometrem do ustawiania kontrastu i zauważyłem, że był praktycznie skręcony na min.
    Wiem, że mój program wykonuje się tylko raz więc po każdym "twardym" resecie kursor pojawia się na moment począwszy od lewej przemieszcza się w prawo (1 pozycja / reset),
    Czy to znaczy, że zainicjalizowany został poprawnie i teraz mogę się zabierać za przesyłanie znaków?
    A co do linii RS i RW przez cały czas inicjalizacji były ustawione na 0, ale wydawało mi się, że tak ma być.
  • Helpful post
    #4
    tadzik85
    Level 38  
    Jeśli pojawił ci się kursor więc jest wszystko w porządku. Lecz popraw swój program. Szczególnie w kwestii linii RS. RW może być zwarta do masy ale po każdej instrukcji musisz odczekać chwilę.
  • #5
    Forrest1988
    Level 10  
    Zmodyfikowałem nieco swój program i dodałem komentarze (może okazać się, że jestem jakimś trollem mikrokontrolerowym więc dopisałem co chciałem zrobić, w której linii, żeby ułatwić ew. wyłapanie tych błędów. Teraz wszystko ładnie się wyświetla, ale ... nie tak jak powinno. Zamiast alfabetu widzę zera, strzałki i inne krzaczki. Proszę o pomoc ;]

    
    #include "io430.h"
    
    /** Prototypy */
    void wait(unsigned int i);  // opóźnienie
    void initLcd(void);         // inicjalizuje wyświetlacz
    void lcdClock();            // strobowanie linii E
    void writeComm(int comm);   // wpisuje komendę do wyświetlacza
    void lcdClear(void);        // Czyści ekran wyświetlacza
    void lcdOutc(char str);     // Wypisuje znak na pole LCD
    void lcdOuts(char* str);    // Wypisuje na ekran łańcuch znaków
    
    
    int chars=0;              // zmienna pomocnicza, określa pozycję w wierszu
    int row=0;                // zmienna pomocnicza, określa wiersz
    int i=1;                  // zmienna pomocnicza, określa aktualną pozycję w alfabecie
    char* alphabet[]={"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
    
    /** Główna funckja */
    int main( void )
    {
      WDTCTL = WDTPW + WDTHOLD;     // zatrzymanie watchdoga
      P1DIR = 0xFF;         // kierunek portów do wyświetlacza na wyjście
      P6DIR = 0xF0;         // piny z diodami jako wyjście, piny z przyciskami jako wejście
      wait(50000);          // oczekiwanie na stabilizację napięcia
      initLcd();            // inicjalizacja wyświetlacza
      lcdClear();           // czyszczenie wyświetlacza
      for( ; ; )
      {
        if( (P6IN & 0x08) != 0)     // jeśli wciśnięty zostanie klawisz 4
        {
          lcdClear();               // czyszczenie wyświetlacza
        }
        if( (P6IN & 0x04) != 0)     // jeśli wciśnięty zostanie klawisz 3
        {
          P6OUT = 0x80;             // zapalenie diody 3
          lcdOuts(alphabet[i]);   // wyświetlenie znaku z pozycji i alfabetu i inkrementacja i
          writeComm(0x10);          // cofnięcie kursora
          chars--;                  // dekrementacja pozycji w wierszu
          wait(5000);              // oczekiwanie
        }
        if( (P6IN & 0x02) != 0)     // jeśli wsciśnięty zostanie klawisz 2
        {
          P6OUT = 0x40;             // zapalenie diody 2
          writeComm(0x14);          // przesunięcie w prawo
          i=0;                      // alfabet wyświetlany od początku
          wait(5000);              // oczekiwanie
        }
        if( (P6IN & 0x01) != 0)     // jeśli wciśnięty zostanie klawisz 1
        {
          P6OUT = 0x20;             // zapalenie diody 1
          writeComm(0x10);          // cofnięcie kursora
          chars--;                  // dekrementacja pozycji w wierszu
          wait(5000);              // oczekiwanie
        }
        if ((P6IN & 0x0F) == 0x00)  // jeśli nic nie zostanie wciśnięte
        {
          P6OUT = 0x00;             // zgaszenie wszystkich diod
        }
      }
    }
    
    /** Funkcja opóźniająca */
    void wait(unsigned int i)
    {
      unsigned int j;
      for(j=0;j<i;j++);             // i operacji w pętli, jakieś 1us na każde wykonanie
    }
    
    /** Inicjalizacja wyświetlacza */
    void initLcd(void)
    {
      writeComm(0x30);        // 4 - bitowe operacje, 2-wierszowy wyświetlacz, czcionka 5x8 kropek
      writeComm(0x0E);        // włączenie wyświetlacza, kursora
      writeComm(0x06);        // kursor na prawo bez przesunięcia
    }
    
    /** Miganie stanem E */
    void lcdClock()
    {
      wait(5000);             // odczekiwanie
      P1OUT |= 0x08;          // E w stan wysoki
      wait(5000);             // odczekiwanie
      P5OUT &= ~0x08;         // E w stan niski
      wait(5000);             // odczekiwanie
    }
    
    /** Wpisuje komendę do wyswietlacza */
    void writeComm(int comm)
    {
      int help;             // zmienna pomocnicza do operacji na bajtach
      
      // operacje na starszej części
      help = comm & 0xF0;         // starsza część
      P1OUT = help;               // starsza część na wyjście
      P1OUT |= 0x08;              // E <- 1
      lcdClock();                 // strobowanie
      
      // operacje na młodszej części
      help = (comm << 4) & 0xF0;    // młodsza część przesunięta na starszą
      P1OUT = help;                 // młodsza część na wyjście
      P1OUT |= 0x08;                // E <- 1
      lcdClock();                   // strobowanie
    }
    
    /** Czyści ekran wyświetlacza */
    void lcdClear(void)
    { 
      row = 0;                    // wiersz = 0
      chars = 0;                  // pozycja = 0
      writeComm(0x01);            // wysłanie komendy kasowania
      wait(5000);                // oczekiwanie
    }
    
    /** Wypisuje znak na pole LCD */
    void lcdOutc(char str)
    {
      int help;           // zmienna pomocnicza
      
      // operacje na starszej części
      help = str & 0xF0;        // starsza część kodu znaku
      P1OUT = help;             // starsza część na wyjście
      P1OUT |= 0x09;            // E <- 1, RS <-1
      lcdClock();               // strobowanie
      
      // operacje na młodszej części
      help = (str << 4) & 0xF0;   // młodsza część przesunięta na starszą
      P1OUT = help;               // młodsza część na wyjście
      P1OUT |= 0x09;              // E <- 1, RS <- 1
      lcdClock();                 // strobowanie
      
      chars++;                    // inkrementacja pozycji do wyświetlenia
      if(chars == 16)             // jeśli przekroczona zostanie długość wiersza
      {
        row ^= 1;                 // zmiana wiersza z 1 na 2 i 2 na 1
        chars = 0;                // początek wiersza
        if (row == 1)             
        {
          writeComm(0xC0);        // skok do drugiej części pamięci (dla drugiego wiersza)
        }
        else
        {
          lcdClear();             // czyszczenie wyświetlacza jeśli został przekroczony drugi wiersz
        }
      }
    }
    
    /** Wypisuje na ekran łańcuch znaków */
    void lcdOuts(char* str)
    {
      while(*str != 0)
      {
        lcdOutc(*str++);          // wyświetla znaki z alfabetu
      }
    }
    
  • #6
    tadzik85
    Level 38  
    Przeanalizuj co zamiast czego jest wyświetlane. Być może mylisz półbajty i odwrotnej kolejności je wysyłasz.
  • #7
    Forrest1988
    Level 10  
    kolejność jest na pewno dobra, najpierw starsze potem młodsze. Ale zauważyłem, że jak wystawię 4 bity na wyjście ustawiam od razu E na 1, potem wchodzę w procedurę, odczekuję chwilę, ustawiam jeszcze raz E na 1 czekam ustawiam E na 0, czekam i wracam do pozostałych 4, młodszych bitów. Czy może być jakiś problem w tym "podwójnym" ustawianiu E na 1? Nie mam w tej chwili jak tego sprawdzić bo jestem poza domem :/
  • #8
    tadzik85
    Level 38  
    Hm. nie mam pojęcia. Ja korzystałem z bibliotek radzia lecz musiałem je troszkę zmodyfikować do pracy z kwarcem 11.059. Poza tym działają doskonale.

    char* alphabet[]={"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; 

    A to co za tablica dziwna?
  • Helpful post
    #9
    rusala
    Level 22  
    no wlasnei o co chodzi z ta tablica? wyglada jak podwojna a jej funkcja ma byc co? wyliczenie liter alfabetu? to nie prosciej napisac sobie np. ('A' + i) jak chcesz je po kolei wyswietlac ?

    Dwuktrotne ustawienie na 1 nic nie zmienia - dalej jest 1 wiec to nie to. Ja bym skupil sie na opoznieniach bo to czesto jest przyczyna bledow w tych wyswietlaczach.
  • #10
    Forrest1988
    Level 10  
    Napisałem program jeszcze raz, tym razem w oparciu o bibliotekę Radzia. Teraz już nic się nie wyświetla :/. Nie mam pojęcia w czym może być problem. Dodałem opóźnienia. W miejscach gdzie jest // <-- nawet nadmiarowo, żeby sprawdzić czy to coś zmieni, ale ... nic się nie dzieje :|. Przejrzałem jeszcze raz opisy inicjalizacji wyświetlacza z datasheeta i teoretycznie wszystko jest poprawnie. Kursor powinien być widoczny i migać, a tymczasem nic nie widzę. Sprawdziłem w pracy krokowej i inicjalizacja jest wykonywana, po wciśnięciu przycisku program wchodzi do funkcji wyświetlającej na ekran, ale nic się nie dzieje na wyświetlaczu. Dodam, że opóźnienia z racji tego, że nie mogłem znaleźć żadnej funkcji do MSP430, która za nie odpowiada zrobiłem własną procedurą. Na każde i przekazane do niej procesor potrzebuje 4 cykli. Przy taktowaniu 1MHz daje to ok 4us. Opóźnienia jakie dałem zatem powinny być na poziomie 200ms (są mniejsze, ale nie dużo po ocenie czasu świecenia diody przy czyszczeniu wyświetlacza. Próbowałem zmniejszyć opóźnienia bo myślałem, że może przesadziłem. Dałem takie, które według moich obliczeń powinny być na poziomie 20ms. Nic się nie zmieniło w działaniu - wyświetlacz pusty. Proszę osoby, które korzystały z tej biblioteki, albo wiedzą co może być nie tak o radę.
    Zaznaczam, że nie ma już tej "nieszczęsnej" tablicy, a mimo to sytuacja wydaje się jeszcze gorsza.

    
    #include "io430.h"
    
    // ----------------------------------------------------------------
    
    /** Definicje stałych */
    #define RS 0x01           // RS = P1.0
    #define RW 0x02           // RW = P1.1
    #define EN 0x08           // EN = P1.3
    #define D4 0x10           // D4 - P1.4
    #define D5 0x20           // D5 - P1.5
    #define D6 0x40           // D6 - P1.6
    #define D7 0x80           // D7 - P1.7
    
    // ----------------------------------------------------------------
    
    /** Prototypy */
    void lcd_OutNibble(unsigned char nibbleToWrite);      // funkcja wystawiająca półbajt na magistralę danych
    void lcd_Write(unsigned char dataToWrite);            // zapisanie bajtu do wyświetlacza (bez rozróżnienia instrukcja / dane)
    void lcd_WriteCommand(unsigned char commandToWrite);  // funkcja zapisująca rozkaz do wyświetlacza
    void lcd_WriteData(unsigned char dataToWrite);        // funkcja zapisu danych do pamięci wyświetlacza
    void lcd_WriteText(char* text);                       // funkcja wyświetlenia napisu na wyświetlaczu
    void lcd_GoTo(unsigned char x, unsigned char y);      // funkcja ustawienia współrzędnych ekranowych
    void lcd_Clear(void);                                 // funkcja czyszczenia ekranu wyświetlacza
    void lcd_Home(void);                                  // funkcja przywrócenia początkowych współrzędnych wyświetlacza
    void lcd_Initialize(void);                            // procedura inicjalizacji wyświetlacza
    void wait(unsigned int i);                            // funkcja wprowadzająca opóźnienie
    
    // ----------------------------------------------------------------
    
    /** Funkcja main */
    int main(void)
    {
       WDTCTL = WDTPW + WDTHOLD;      // zatrzymanie watchdoga 
       P6DIR = 0xF0;                  // piny z diodami jako wyjście, piny z przyciskami jako wejście
       lcd_Initialize();
       for( ; ;)
       {
         if( (P6IN & 0x08) !=0)       // jeśli wciśnięty klawisz 4
         {
           lcd_Clear();               // czyszczenie wyświetlacza
         }
         if( (P6IN & 0x01) != 0)      // jeśli wciśnięty klawisz 1
         {
           P6OUT = 0x80;              // zapalenie diody 1
           lcd_WriteText("TEST");     // wyświetlenie napisy TEST
         }
         if( (P6IN & 0x0F) == 0x00)   // jeśli nic nie jest wciśnięte
         {
           P6OUT = 0x00;              // zgaszenie diod
         }
       }
    }
    
    // -----------------------------------------------------------------
    
    /** Funkcja wystawiająca półbajt na magistralę danych */
    void lcd_OutNibble(unsigned char nibbleToWrite)
    {
      if(nibbleToWrite & 0x01)    // testowanie D4
      {
        P1OUT |= D4;            // ustawienie 1 na D4
      }
      else
      {
        P1OUT &= ~D4;           // zerowanie D4
      }
      if(nibbleToWrite & 0x02)    // testowanie D5
      {
        P1OUT |= D5;            // ustawienie 1 na D5
      }
      else
      {
        P1OUT &= ~D5;           // zerowanie D5
      }
      if(nibbleToWrite & 0x04)    // testowanie D6
      {
        P1OUT |= D6;            // ustawienie 1 na D6
      }
      else
      {
        P1OUT &= ~D6;           // zerowanie D6
      }
      if(nibbleToWrite & 0x08)    // testowanie D7
      {
        P1OUT |= D7;            // ustawienie 1 na D7
      }
      else
      {
        P1OUT &= ~D7;           // zerowanie D7
      }
    }
    
    /** Zapisanie bajtu do wyświetlacza (bez rozróżnienia instrukcja / dane) */
    void lcd_Write(unsigned char dataToWrite)
    {
      P1OUT |= EN;                        // E = 1
      lcd_OutNibble(dataToWrite >> 4);    // wysłanie półbajtu starszego
      P1OUT &= ~EN;                      // E = 0
      wait(10000);
      P1OUT |= EN;                      // E = 1
      lcd_OutNibble(dataToWrite);         // wysłanie półbajtu młodszego
      P1OUT &= ~EN;                      // E = 0
      wait(50000);
    }
    
    /** Funkcja zapisująca rozkaz do wyświetlacza */
    void lcd_WriteCommand(unsigned char commandToWrite)
    {
      P1OUT &= ~RS;               // RS = 0
      lcd_Write(commandToWrite);    // wpisanie rozkazu do wyświetlacza
    }
     
    /** Funkcja zapisu danych do pamięci wyświetlacza */
    void lcd_WriteData(unsigned char dataToWrite)
    {
      P1OUT |= RS;                // RS = 1
      lcd_Write(dataToWrite);       // wpisanie danych do pamięci wyświetlacza
    }
    
    /** Funkcja wyświetlenia napisu na wyświetlaczu */
    void lcd_WriteText(char* text)
    {
      while(*text)
      {
        lcd_WriteData(*text++);     // wypisuje kolejne znaki
      }
    }
      
    /** Funkcja ustawienia współrzędnych ekranowych */
    void lcd_GoTo(unsigned char x, unsigned char y)
    {
      lcd_WriteCommand(0x80 | (x + (0x40 * y) ) );    // 0x80 - DDRAM Set
      wait(50000);
    }
      
    /** Funkcja czyszczenia ekranu wyświetlacza */
    //-------------------------------------------------------------------------------------------------
    void lcd_Clear(void)
    {
      lcd_WriteCommand(0x01);             // 0x01 - Clear
      wait(50000);
    }
      
    /** Funkcja przywrócenia początkowych współrzędnych wyświetlacza */
    void lcd_Home(void)
    {
      lcd_WriteCommand(0x02);             // 0x02 - Home
      wait(50000);
    }
    
    /** Procedura inicjalizacji wyświetlacza */
    void lcd_Initialize(void)
    {
      unsigned char i;
      P1DIR = (D7 | D6 | D5| D4 | EN | RS);    // konfiguracja kierunku wyprowadzeń
      wait(50000);                // oczekiwanie na stabilizację napięcia
      P1OUT &= ~RS;             // RS = 0
      P1OUT &= ~EN;             // E = 0
      for( i = 0; i < 3; i++)     // trzykrotne wyświetlenie bloku instrukcji
      {
        P1OUT |= EN;            // E = 1
        lcd_OutNibble(0x03);      // tryb 8-bit
        P1OUT &= ~EN;           // E = 0
        wait(10000);
      }
      P1OUT |= EN;              // E = 1
      lcd_OutNibble(0x02);        // tryb 4-bit
      P1OUT &= ~EN;             // E = 0
      wait(10000);
      lcd_WriteCommand(0x20 | 4 |  8 | 0);    // 0x20 - Function Set | 4 - Font 5x7 | 8 - dwie linie | 0 - tryb 4-bit
      lcd_WriteCommand(0x08 | 0);             // 0x08 - Display ON/OFF | 0 - DISPLAY OFF
      lcd_WriteCommand(0x01);                 // 0x01 - Clear DDRAM
      wait(50000);
      lcd_WriteCommand(0x04 | 0 | 0);         // 0x04 - Entry Mode | 0 - EM Shift Cursor | 0 - EM INCREMENT
      lcd_WriteCommand(0x08 | 4 | 2 | 1);     // 0x08 - Display ON/OFF | 4 - Display ON | 0 - Cursor ON | 0 - Cursor Blink
    }
    
    /** Funkcja wprowadzająca opóźnienie */
    void wait(unsigned int i)
    {
      int j;
      for(j=0;j<i;j++);             // i operacji w pętli, jakieś 1us na każde wykonanie
    }