Dzisiaj chciałem przedstawić obsługę popularnego wyświetlacza LCD znakowego na przykładzie rodziny wyświetlaczy 44780. Ponieważ pracujemy na poziomie napięcia 3,3V musimy zakupić wyświetlacz mogący współpracować z takim poziomem sygnału dla przykładu CBC016002BS00-YIY-R. W przeciwnym przypadku musimy zastosować konwersję sygnału sterującego z 3,3V na 5V.
Poniżej przedstawiłem podłączenie elektryczne wyświetlacza do układu SAM4S.
Na początku zaczynamy od zdefiniowania pinów wejściowych i przypisania ich memoników ( zrozumiałych nazw w kodzie programu).
#define LCD_RS_s PIOC->PIO_SODR = (1<<4) #define LCD_RS_c PIOC->PIO_CODR = (1<<4) #define LCD_E_s PIOC->PIO_SODR = (1<<5) #define LCD_E_c PIOC->PIO_CODR = (1<<5) #define LCD_D4_s PIOA->PIO_SODR = (1<<16) #define LCD_D4_c PIOA->PIO_CODR = (1<<16) #define LCD_D5_s PIOC->PIO_SODR = (1<<7) #define LCD_D5_c PIOC->PIO_CODR = (1<<7) #define LCD_D6_s PIOA->PIO_SODR = (1<<15) #define LCD_D6_c PIOA->PIO_CODR = (1<<15) #define LCD_D7_s PIOC->PIO_SODR = (1<<6) #define LCD_D7_c PIOC->PIO_CODR = (1<<6)
Dzięki temu używamy bezpośrednio nazwy np. wejścia E wyświetlacza i jego stanu wysokiego ( LCD_RS_s) lun niskiego (LCD_RS_c).
Teraz potrzebujemy dodatkowe procedury umożliwiające odliczanie czasu, ponieważ wyświetlacz potrzebuje czas na odczytanie sygnału. Procedury są bardzo proste i testowane były tylko dla zegara 4Mhz. Odliczany przez nie czas nie jest dokładny. Procedura DelayUs – odlicza w przybliżeniu 1 ns, poprzez wykonanie dwóch instrukcji NOP – to jest pusta intrukcja + wymagany jest skok do samej instrukcji co zajmuje 2 cykle zegara. Wykonanie instrukcji DelayMs trwa w przybliżeniu 1 ms, poprzez wykonanie 800 razy instrukcji DelayUs, ale również wykonanie pętli zabiera dużą liczbę instrukcji.
void DelayUs(uint32_t us) { while(--us) { __NOP(); __NOP(); } } void DelayMs(uint32_t ms) { uint32_t ims; for(ims=0; ims<ms; ims++) DelayUs(800); }
Strona w budowie poniżej cały kod:
#include "sam.h" #define LEDA (1<<0) #define LEDB (1<<17) #define LCD_RS_s PIOC->PIO_SODR = (1<<4) #define LCD_RS_c PIOC->PIO_CODR = (1<<4) #define LCD_E_s PIOC->PIO_SODR = (1<<5) #define LCD_E_c PIOC->PIO_CODR = (1<<5) #define LCD_D4_s PIOA->PIO_SODR = (1<<16) #define LCD_D4_c PIOA->PIO_CODR = (1<<16) #define LCD_D5_s PIOC->PIO_SODR = (1<<7) #define LCD_D5_c PIOC->PIO_CODR = (1<<7) #define LCD_D6_s PIOA->PIO_SODR = (1<<15) #define LCD_D6_c PIOA->PIO_CODR = (1<<15) #define LCD_D7_s PIOC->PIO_SODR = (1<<6) #define LCD_D7_c PIOC->PIO_CODR = (1<<6) //#define INIT_DB4() GPIO_dir(PORT_DB4, DB4, 1); //#define SET_DB4() GPIO_set(PORT_DB4, DB4, 1); //#define CLR_DB4() GPIO_set(PORT_DB4, DB4, 0); #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 //#define Lcd(tekst) LcdWriteProgmem(PSTR(tekst)) #define Lcd1 LcdGoto(0,0) // skok do pierwszej linii #define Lcd2 LcdGoto(0,1) // skok do drugiej linii void DelayUs(uint32_t us) { while(--us) { __NOP(); __NOP(); } } void DelayMs(uint32_t ms) { uint32_t ims; for(ims=0; ims<ms; ims++) DelayUs(800); } void _lcd_Write(unsigned char dataToWrite) { if(dataToWrite & 0b00010000) LCD_D4_s; else LCD_D4_c; if(dataToWrite & 0b00100000) LCD_D5_s; else LCD_D5_c; if(dataToWrite & 0b01000000) LCD_D6_s; else LCD_D6_c; if(dataToWrite & 0b10000000) LCD_D7_s; else LCD_D7_c; LCD_E_s; DelayUs(1); LCD_E_c; if(dataToWrite & 0b00000001) LCD_D4_s; else LCD_D4_c; if(dataToWrite & 0b00000010) LCD_D5_s; else LCD_D5_c; if(dataToWrite & 0b00000100) LCD_D6_s; else LCD_D6_c; if(dataToWrite & 0b00001000) LCD_D7_s; else LCD_D7_c; LCD_E_s; DelayUs(1); LCD_E_c; DelayUs(40); } void LcdCommand(unsigned char commandToWrite) { LCD_RS_c; DelayUs(1); _lcd_Write(commandToWrite); } void LcdData(unsigned char dataToWrite) { LCD_RS_s; DelayUs(1); _lcd_Write(dataToWrite); } void LcdWrite(const char * text) { while(*text) LcdData(*text++); } void LcdNumber(uint32_t licz) { char arr[10]; itoa(licz,arr,10) ; LcdWrite(arr); } void LcdGoto(unsigned char x, unsigned char y) { LcdCommand(HD44780_DDRAM_SET | (x + (0x40 * y))); } void LcdClear(void) { LcdCommand(HD44780_CLEAR); DelayMs(2); } void LcdInit(void) { PIOC->PIO_PER = (1<<4); PIOC->PIO_OER = (1<<4); PIOC->PIO_PUDR = (1<<4); PIOC->PIO_PER = (1<<5); PIOC->PIO_OER = (1<<5); PIOC->PIO_PUDR = (1<<5); PIOA->PIO_PER = (1<<16); PIOA->PIO_OER = (1<<16); PIOA->PIO_PUDR = (1<<16); PIOC->PIO_PER = (1<<7); PIOC->PIO_OER = (1<<7); PIOC->PIO_PUDR = (1<<7); PIOA->PIO_PER = (1<<15); PIOA->PIO_OER = (1<<15); PIOA->PIO_PUDR = (1<<15); PIOC->PIO_PER = (1<<6); PIOC->PIO_OER = (1<<6); PIOC->PIO_PUDR = (1<<6); DelayMs(5); // oczekiwanie na ustalibizowanie się napiecia zasilajacego LCD_RS_c; LCD_E_c; DelayMs(1); for(uint8_t i = 0; i < 3; i++) { // trzykrotne powtórzenie bloku instrukcji LCD_E_s; _lcd_Write(0x03); // tryb 8-bitowy DelayMs(1); LCD_E_c; DelayMs(5); } LCD_E_s; _lcd_Write(0x02); // tryb 4-bitowy DelayMs(1); LCD_E_c; DelayMs(1); LcdCommand(HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT); // interfejs 4-bity, 2-linie, znak 5x7 LcdCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF); // wyłączenie wyswietlacza LcdCommand(HD44780_CLEAR); // czyszczenie zawartosći pamieci DDRAM DelayMs(2); LcdCommand(HD44780_ENTRY_MODE | HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT);// inkrementaja adresu i przesuwanie kursora LcdCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK); // włącz lcd, bez kursora i mrugania } int main(void) { /* Initialize the SAM system */ SystemInit(); WDT->WDT_MR = WDT_MR_WDDIS; PIOC->PIO_PER = LEDB; PIOC->PIO_OER = LEDB; PIOC->PIO_PUDR = LEDB; PIOA->PIO_PER = LEDA; PIOA->PIO_OER = LEDA; PIOA->PIO_PUDR = LEDA; LcdInit(); // inicjalizacja sterownika LCD LcdClear(); LcdWrite("Test 1"); // wyświetlenie napisu uint16_t liczbs, liczbm = 0; while (1) { LcdGoto(0,1); liczbs++; if (liczbs >= 59) { liczbs = 0; liczbm++; } LcdNumber(liczbs); LcdWrite(":"); LcdNumber(liczbm); //LcdData("A"); PIOA->PIO_SODR = LEDA; PIOC->PIO_CODR = LEDB; DelayMs(500); //LcdData("0x00"); PIOC->PIO_SODR = LEDB; PIOA->PIO_CODR = LEDA; DelayMs(500); } }
kh