Wyświetlacz to fajna rzecz nie trzeba chyba przekonywać o tym nikogo. W świecie znakowych wyświetlaczy LCD najpopularniejszym wśród hobbystów standardem jest wyświetlacz oparty na sterowniku Hitachi HD44780. Tani jak barszcz wolny jak żółw ale frajdy daje co niemiara :) I dla tej frajdy spróbujmy zasterować ten wyświetlacz za pomocą mikrokontrolera PIC.
W tym mini-projekciku skorzystam z wyświetlacza 4x16 i 16-bitowego mikrokontrolera PIC24HJ128GP502 .
O ile w świecie napięć 3.3V królują wyświetlacze graficzne gdzie nie ma jednego słusznego standardu a różnorodność sterowników może przyprawić o ból głowy o tyle standard HD44780 zadomowił się w świecie 5 V.
Wydaje się , że taki mariaż wyświetlacza +5V i mikrokontrolera zasilanego +3.3 V może nam spowodować jakiś wybuch nuklearny. W praktyce jednak okazuje się, że nie ma z tym żadnego problemu a współpraca układa się idealnie. Oczywiście w takim układzie obawa jest o mikrokontroler zasilany z niższego napięcia ale nawet w przypadku podania +5V na wejście mikrokontrolera (jakimś nie zamierzonym cudem) nie zjaramy go :) Dlaczego ?? ano dlatego , że wspaniali konstruktorzy PIC-ów przystosowali kilka (7 szt w PIC24HJ128GP502) wejść I/O do przyjęcia na klatę +5V. Ale i tak w naszym mini-projekciku wejścia mikrokontrolera nie będą musiały nadstawiać tej klaty dla +5V. Jedyną niedogodnością prezentowanego rozwiązania jest konieczność zastosowania dwóch oddzielnych torów zasilania +5V i 3.3V.
Przyjmuję następujące założenia do projektu :
- wyświetlacz HD44780, 4x16 znaków,
- tryb sterowania (magistrala) - 4 bity,
- R/W nie wykorzystany (podłączamy do masy)
- E ---> RB11
- RS --> RB10
- DB4 --> RB9
- DB5 --> RB8
- DB6 --> RB7
- DB7 --> RB6
- mikrokontroler PIC 16-bitowy - PIC24HJ128GP502 : zegar wewnętrzny ok.40 MHz.
Wszystkie zastosowane w projekcie piny mikrokontrolera czyli od RB6 do RB11 są +5V tolerant, ale jak wspominałem ta właściwość będzie nam zbędna. Wyświetlacz będzie pracował na swoim zasilaniu +5V a mikrokontroler na swoim +3.3V. Sygnały podawane z mikrokontrolera na wyświetlacz będą w logice +3.3 V. Wyświetlacz nie wysyła w stronę mikrokontrolera żadnych poziomów napięciowych.
Schemat połączeń :
Na wykresie dioda LED1 nie jest potrzebna do projektu narysowana z rozpędu :)
Ja do budowy projektu użyłem dwóch standardowych płytek stykowych i jednego chińskiego zasilacza do płytek za ok.6 zł :), który ma dwa tory zasilania +5V i +3.3 V co pokrywa nam zapotrzebowanie na zasilanie w projekcie. Masy wewnątrz zasilacza są połączone z dwóch torów. Warto jednak rozejrzeć się za jakimiś nie chińskimi zasilaczami parafrazując przysłowie ludowe tanie zasilacze psy jedzą. Mając porządne zasilanie odpada wiele problemów w uruchamianych projektach.
Podstawową czynnością wstępną jest inicjalizacja wyświetlacza i tutaj najłatwiej jest się wyłożyć w szczególności jak pominiemy jakiegoś wymaganego delay-a. W/g mnie najłatwiej sprawdzić czy wyświetlacz się zainicjalizował poprzez ustawienie w rejestrze rozkazów trybu wyświetlania migającego kursora. Jeśli zobaczymy migający kursor to znaczy , że wyświetlacz jest zainicjalizowany do pracy.
Poniżej oficjalny obrazek na temat inicjalizacji wyświetlacza HD44780.
Pozdrawiam
picmajster.blog@gmail.com
*****************************************LINKI**********************************************
Link z tutorialem o sterowaniu HD44780 bardzo fajny :
Link z LCD Simulatorem :
Link z animacją efektów :
Link z blogiem o HD44780
Artykuł z Elektroniki Praktycznej
Kod programu :
***********************************main.c*****************************************
1 #include <xc.h> /* wykrywa rodzaj procka i includuje odpowiedni plik nagłówkowy "p24HJ128GP502.h"*/ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <stdint.h> /*dyrektywy uint8_t itp*/ 5 6 #define FCY 40000000UL /* podajemy wartość ustawionego zegara (40 MHz), ważne 7 aby przed includowaniem <libpic30.h>, potrzebne to jest do wyliczania delay-i*/ 8 #include <libpic30.h> // biblioteka dająca dostęp do delay-i. 9 #include "lcd.h" 10 11 #pragma config JTAGEN = OFF 12 #pragma config FWDTEN = OFF 13 #pragma config FNOSC = FRC // FOSCSEL-->FNOSC=0b000 (Fast RC Oscillator (FRC)) 14 #pragma config FCKSM = CSECMD //FOSC-->FCKSM=0b01 - włącz zegar 15 #pragma config OSCIOFNC = OFF //FOSC-->OSCIOFNC=1 - Fcy będzie na pinie OSCO 16 17 char napis1[] = "Kocham Monik""\x01"; /* "\x01" to zapis kodu ASCII dla zdefiniowanej 18 literki "ę". Dla własnych znaków mamy zarezerwowane kody ASCII od 0 do 7, 19 przyczym aby używać podanego sposobu wstawiania własnych znaków w stringa nie możemy 20 używać kodu ASCI 0*/ 21 22 int main(void) { 23 24 PLLFBD = 41 ; //M=43 (0 bit to 2 stąd 41 = 43 patrz w rejestrze), tutaj 3.685 x 43 = 158.455MHz 25 CLKDIVbits.PLLPRE=0 ; //N1=2, tutaj 7.37 MHz / 2 = 3.685 MHz 26 CLKDIVbits.PLLPOST=0 ; //N2=2, tutaj 158.455 MHz / 2 = 79.2275 MHz (Fosc) 27 28 __builtin_write_OSCCONH(0x01); //tutaj argumentem jest wartość z NOSC 29 __builtin_write_OSCCONL(0x01); 30 while(OSCCONbits.COSC !=0b001); 31 while(OSCCONbits.LOCK !=1) {}; 32 33 PMD1bits.AD1MD=1; //wyłączamy ADC 34 AD1PCFGL = 0x1E3F; //wyłączenie linii analogowych(wszystkie linie cyfrowe) 35 36 WlaczLCD(); //inicjalizacja wyświetlacza LCD 37 WpiszSwojeZnaki(); //wpisz do CGRAM-u definicję znaku "ę" 38 UstawKursorLCD(2,2); //wiersz 2, kolumna 2 39 WyswietlLCD(napis1); //wyświetl napis z tablicy napis1[] 40 41 while(1) 42 { 43 /*Główna Pętla Programu*/ 44 45 } 46 47 return 0; 48 } 49
****************************************lcd.h*************************************
2 #ifndef LCD_H 3 #define LCD_H 4 5 /*_TRISB6 --> TRISBbits.TRISB6*/ 6 #define TRIS_RS_LCD _TRISB11 7 #define TRIS_EN_LCD _TRISB10 8 #define TRIS_DB4_LCD _TRISB9 9 #define TRIS_DB5_LCD _TRISB8 10 #define TRIS_DB6_LCD _TRISB7 11 #define TRIS_DB7_LCD _TRISB6 12 /*_RB6 --> PORTBbits.RB6*/ 13 #define RS_LCD _RB11 14 #define EN_LCD _RB10 15 #define DB4_LCD _RB9 16 #define DB5_LCD _RB8 17 #define DB6_LCD _RB7 18 #define DB7_LCD _RB6 19 20 /* przyporządkowanie adresów pamięci DD-RAM do pól wyświetlacza*/ 21 /*Uwaga dla wyświetlacza 4x20 [0x00,0x40,0x14,0x54*/ 22 #define LCD_Line1 0x00 /*adres 1 znaku 1 wiersza */ 23 #define LCD_Line2 0x40 /*adres 1 znaku 2 wiersza */ 24 #define LCD_Line3 0x10 /*adres 1 znaku 3 wiersza */ 25 #define LCD_Line4 0x50 /*adres 1 znaku 4 wiersza */ 26 27 void Wyslij_do_LCD(unsigned char bajt); 28 void CzyscLCD(); 29 void WlaczLCD(); 30 void WyswietlLCD(char *napis); 31 void UstawKursorLCD(uint8_t y, uint8_t x); 32 void WpiszSwojeZnaki(void); 33 34 #endif /* LCD_H */
***************************************lcd.c*************************************
1 #include "xc.h" /*wykrywa rodzaj procka i includuje odpowiedni plik nagłówkowy "p24HJ128GP502.h"*/ 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <stdint.h> /*dyrektywy uint8_t itp*/ 6 #include "../string.h" 7 #define FCY 40000000UL /* podajemy wartość ustawionego zegara (40 MHz), ważne 8 aby przed includowaniem <libpic30.h>, potrzebne to jest do wyliczania delay-i*/ 9 #include <libpic30.h> // biblioteka dająca dostęp do delay-i. 10 #include "lcd.h" 11 /*deklaracje funkcji*/ 12 void Wyslij_do_LCD(unsigned char bajt); 13 void CzyscLCD(); 14 void WlaczLCD(); 15 void WyswietlLCD(char *napis); 16 void UstawKursorLCD(uint8_t y, uint8_t x); 17 void WpiszSwojeZnaki(void); 18 /*definicje funkcji*/ 19 void Wyslij_do_LCD(unsigned char bajt) 20 { 21 /*ustaw linię EN, przed wysyłką danych*/ 22 23 EN_LCD = 1; 24 /*wysłanie 4 najstarszych bitów danych*/ 25 if(bajt & 0x80) DB7_LCD = 1; else DB7_LCD = 0; 26 if(bajt & 0x40) DB6_LCD = 1; else DB6_LCD = 0; 27 if(bajt & 0x20) DB5_LCD = 1; else DB5_LCD = 0; 28 if(bajt & 0x10) DB4_LCD = 1; else DB4_LCD = 0; 29 __delay_us(1); 30 /*potwierdzenie wysłania danych (opadającym zboczem EN)*/ 31 EN_LCD = 0; 32 33 /*ustawienie EN*/ 34 __delay_us(1); 35 EN_LCD = 1; 36 /*wysłanie 4 najmłodszych bitów danych*/ 37 if(bajt & 0x08) DB7_LCD = 1; else DB7_LCD = 0; 38 if(bajt & 0x04) DB6_LCD = 1; else DB6_LCD = 0; 39 if(bajt & 0x02) DB5_LCD = 1; else DB5_LCD = 0; 40 if(bajt & 0x01) DB4_LCD = 1; else DB4_LCD = 0; 41 __delay_us(1); 42 /*potwierdź wysyłkę danych opadającym zboczem EN*/ 43 EN_LCD = 0; 44 45 __delay_us(37); 46 47 } 48 49 void CzyscLCD() 50 { 51 RS_LCD = 0; 52 Wyslij_do_LCD(1); 53 RS_LCD = 1; 54 /*czekaj minimum 1.64 ms*/ 55 __delay_ms(2); 56 } 57 58 void WlaczLCD() 59 { 60 /*ustawienie kierunku wyjściowego linii podłączonych do LCD*/ 61 TRIS_RS_LCD = 0; 62 TRIS_EN_LCD = 0; 63 TRIS_DB7_LCD = 0; 64 TRIS_DB6_LCD = 0; 65 TRIS_DB5_LCD = 0; 66 TRIS_DB4_LCD = 0; 67 68 /*zerowanie linii*/ 69 RS_LCD = 0; /*wskazuje na rejestr rozkazów*/ 70 EN_LCD = 0; 71 DB7_LCD = 0; 72 DB6_LCD = 0; 73 DB5_LCD = 0; 74 DB4_LCD = 0; 75 76 /*Start Inicjalizacji HD44780*/ 77 /*zaczekaj co najmniej 45 ms na ustabilizowanie się napięcia*/ 78 __delay_ms(45); 79 80 /*powtórz 3 x sekwencję startową 0011 (hex 0x30 i wpisz ją do rejestru rozkazów */ 81 /*(RS=0 ustawione przy zerowaniu linii)*/ 82 83 /*ustaw linię EN, przed wysyłką danych*/ 84 EN_LCD = 1; 85 /*załaduj sekwencję startową 0011*/ 86 DB7_LCD = 0; 87 DB6_LCD = 0; 88 DB5_LCD = 1; 89 DB4_LCD = 1; 90 91 __delay_us(1); 92 /*potwierdź wysyłkę danych opadającym zboczem EN*/ 93 EN_LCD = 0; 94 /*zaczekaj co najmniej minimum 4.1 ms*/ 95 __delay_ms(4.1); 96 97 /*ustaw linię EN, przed wysyłką danych*/ 98 EN_LCD = 1; 99 /*załaduj sekwencję startową 0011*/ 100 __delay_us(1); 101 /*potwierdź wysyłkę danych opadającym zboczem EN*/ 102 EN_LCD = 0; 103 /*zaczekaj 100 us*/ 104 __delay_us(100); 105 106 /*ustaw linię EN, przed wysyłką danych*/ 107 EN_LCD = 1; 108 /*załaduj sekwencję startową 0011*/ 109 __delay_us(1); 110 /*potwierdź wysyłkę danych opadającym zboczem EN*/ 111 EN_LCD = 0; 112 /*zaczekaj 100 us*/ 113 __delay_us(100); 114 115 /* *********************Koniec Inicjalizacji HD44780***************** */ 116 117 /* ***********************Start ustawień HD44780 ******************** */ 118 /*ustaw parametry wyświetlacza, wysłanie słowa operacyjnego do rejestru rozkazów, RS na 0 119 bit 7 = 0 (musi być 0) 120 bit 6 = 0 (musi być 0) 121 bit 5 = 1 (musi być 1) 122 bit 4 = 0 (słowo danych i interfejs ma 4 bity) 123 bit 3 = 1 (2 wiersze znaków) 124 bit 2 = 0 (matryca 5x8 pikseli) 125 bit 1 = 0 (bez znaczenia) 126 bit 0 = 0 (bez znaczenia) */ 127 /*Uwaga po każdym użyciu RS = 0 (wybór rejestru rozkazów), ustawiamy RS = 1*/ 128 /*(rejestr danych)*/ 129 130 RS_LCD = 0; /*stan niski na linię RS, wybieramy rejestr instrukcji*/ 131 Wyslij_do_LCD(0b00101000);//wysyłamy instrukcję do rejestru rozkazów 132 RS_LCD = 1; /*przełącz na rejestr danych */ 133 134 /*włącz/wyłącz wyświetlacz, wysłanie słowa operacyjnego, RS na 0 135 bit 7 = 0 (musi być 0) 136 bit 6 = 0 (musi być 0) 137 bit 5 = 0 (musi być 0) 138 bit 4 = 0 (musi być 0) 139 bit 3 = 1 (musi być 1) 140 bit 2 = 1 (wyświetlacz włączony) 141 bit 1 = 0 (kursor wyłaczony) 142 bit 0 = 0 (migotanie kursora wyłączone) */ 143 144 RS_LCD = 0; /*stan niski na linię RS, wybieramy rejestr instrukcji*/ 145 Wyslij_do_LCD(0b00001100);//wysyłamy instrukcję do rejestru rozkazów 146 RS_LCD = 1; /*przełącz na rejestr danych */ 147 148 CzyscLCD(); 149 150 /*ustaw tryb pracy wyświetlacza 151 bit 7 = 0 (musi być 0) 152 bit 6 = 0 (musi być 0) 153 bit 5 = 0 (musi być 0) 154 bit 4 = 0 (musi być 0) 155 bit 3 = 0 (musi być 0) 156 bit 2 = 1 (musi być 1) 157 bit 1 = 1 (inkremetacja) 158 bit 0 = 0 (wpis znaku od lewej) */ 159 160 RS_LCD = 0; /*stan niski na linię RS, wybieramy rejestr instrukcji*/ 161 Wyslij_do_LCD(0b00000110);/*wysyłamy instrukcję do rejestru rozkazów*/ 162 RS_LCD = 1; /*przełącz na rejestr danych */ 163 164 /*Koniec inicjalizacji i ustawień wyświetlacza HD44780*/ 165 } 166 167 void WyswietlLCD(char *napis) 168 { 169 while(*napis){ 170 Wyslij_do_LCD(*napis++); 171 } 172 173 } 174 175 void UstawKursorLCD(uint8_t y, uint8_t x) 176 { 177 uint8_t n ; 178 /*y (wiersze) = 1 do 4*/ 179 /*x (kolumna) = 1 do 16*/ 180 /*ustal adres początku znaku w wierszu*/ 181 switch(y) 182 { 183 case 1: y = LCD_Line1 ;break; 184 case 2: y = LCD_Line2 ;break; 185 case 3: y = LCD_Line3 ;break; 186 case 4: y = LCD_Line4 ;break; 187 188 } 189 /*ustal nowy adres pamięci DD RAM*/ 190 /*ustaw bajt do Set DDRAM adres*/ 191 /* x odejmujemy jeden aby przekonwertować z 0-15 na 1-16 */ 192 n = 0b10000000 + y + (x-1) ; 193 194 /*wyślij rozkaz ustawienia nowego adresu DD RAM*/ 195 RS_LCD = 0; /*stan niski na linię RS, wybieramy rejestr instrukcji*/ 196 Wyslij_do_LCD(n); 197 RS_LCD = 1; /*przełącz na rejestr danych */ 198 } 199 200 void WpiszSwojeZnaki(void) { 201 /*definicja własnych znaków maks 8 szt*/ 202 char znak1[]= {0,0,14,17,31,16,14,2}; /* definicja literki ę */ 203 int i; 204 /* adresy początku definicji znaku to wielokrotność osmiu DEC(0,8,16,24,32,40,48,56) 205 * ale uwaga ważne ! adresy kodowane są na 6 młodszych bitach dwa najstarsze bity 206 * to zawsze 01 (01AAAAAA-gdzie A adres).Uwzględniając wartość całego bajtu 207 * adresy początku będą wyglądać tak HEX(0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78) 208 * Aby wpisać do pamięci wyświetlacza zdefiniowany znak należy najpierw wysłać 209 * do rejestru rozkazów (RS na 0) adres początku definicji znaku 210 * a w drugim kroku wysyłamy 8 x bajt (tablicę) definjujący obraz znaku*/ 211 212 RS_LCD = 0 ;/*stan niski na linię RS, wybieramy rejestr instrukcji*/ 213 /*wysyłamy instrukcję do rejestru rozkazów (ustaw adres początkowy w CGRAM 214 na nasz znak w tym przypadku znak na pozycji drugiej) */ 215 Wyslij_do_LCD(0x48);/*wysyłamy instrukcję do rejestru rozkazów 216 (ustaw adres początkowy w CGRAM na nasz znak w tym przypadku znak na pozycji drugiej) */ 217 218 RS_LCD = 1 ;/*stan wysoki na linię RS, wybieramy rejestr danych*/ 219 /*wysyłamy 8 x bajt zdefiniowanego w tablicy znak1[] znaku*/ 220 for(i=0;i<=7;i++) 221 { 222 Wyslij_do_LCD(znak1[i]); 223 } 224 225 RS_LCD = 0 ;/*stan niski na linię RS, wybieramy rejestr instrukcji*/ 226 /*ustawiamy adres DDRAM na pierwszy znak w pierwszej linii, nie zapomnijmy 227 o tym ponieważ inaczej zostaniemy w pamięci CGRAM*/ 228 Wyslij_do_LCD(0x80); 229 }
Greetings from Idaho! I'm bired at work sso I decided to browse your site on my iphone during lunch
OdpowiedzUsuńbreak. I enjoy the knowledge you provide
here and can't wait to take a lookk when I get home. I'm shocked at how quick your blog loaded on my phgone ..
I'm not even using WIFI, just 3G .. Anyhow, wonderful
site!
Hello Idaho :) I am glad that your crazy iphone loads my blog so quickly.
OdpowiedzUsuńRegards
PICmajster