radzio.dxp.pl

Komunikacja mikrokontrolera z wyświetlaczem.
Interfejs wyświetlacza zajmuje 13 wyprowadzeń mikrokontrolera. Szyna danych zajmuje cały dowolnie wybrany port, natomiast sygnały sterujące zajmują 5 wyprowadzeń drugiego dowolnie wybranego portu. Przypisanie sygnałów sterujących poszczególnym wyprowadzeniom jest dowolne. Schemat połączeń zawarty jest w pliku schemat.pdf Napięcie ujemne konieczne do zasilania wyświetlacza wytwarzane jest przez układ MAX232 (w przypadku zastosowania wyświetlacza z wbudowanym generatorem napięcia ujemnego można ten układ pominąć).

W kodzie programu umieszczamy definicje odpowiednich sygnałów sterujących :

// port szyny danych
#define LCD_DATA_PORT PORTA
#define LCD_DATA_PIN PINA
#define LCD_DATA_DDR DDRA
// port sygnałów sterujących
#define LCD_CTRL_PORT PORTC
#define LCD_CTRL_PIN PINC
#define LCD_CTRL_DDR DDRC
// sygnały sterujące
#define LCD_CS1P PC6
#define LCD_CS2P PC5
#define LCD_EN PC0
#define LCD_RW PC1
#define LCD_RS PC2

Następnie zdefiniujmy kilka podstawowych makroinstrukcji kontrolujących stan sygnałów sterujących :

// makroinstrukcje ustawienia stanu na linii CS1
#define SET_CS1() (LCD_CTRL_PORT |= (1 << LCD_CS1P))
#define CLR_CS1() (LCD_CTRL_PORT &= ~(1 << LCD_CS1P))
// makroinstrukcje ustawienia stanu na linii CS2
#define SET_CS2() (LCD_CTRL_PORT |= (1 << LCD_CS2P))
#define CLR_CS2() (LCD_CTRL_PORT &= ~(1 << LCD_CS2P))
// makroinstrukcje ustawienia stanu na linii EN
#define SET_EN() (LCD_CTRL_PORT |= (1 << LCD_EN))
#define CLR_EN() (LCD_CTRL_PORT &= ~(1 << LCD_EN))
// makroinstrukcje ustawienia stanu na linii RW
#define SET_RW() (LCD_CTRL_PORT |= (1 << LCD_RW))
#define CLR_RW() (LCD_CTRL_PORT &= ~(1 << LCD_RW))
// makroinstrukcje ustawienia stanu na linii RS
#define SET_RS() (LCD_CTRL_PORT |= (1 << LCD_RS))
#define CLR_RS() (LCD_CTRL_PORT &= ~(1 << LCD_RS))
// makroinstrukcje ustawiajace odpowiednią kombinację sygnałów CS1 i CS2
#define LCD_CS0() CLR_CS1();SET_CS2();
#define LCD_CS1() SET_CS1();CLR_CS2();
#define LCD_NOCS() SET_CS1();SET_CS2();

Ponieważ niemożliwy jest odczyt z wyświetlacza aktualnej pozycji kursora należy zadeklarować globalne zmienne przechowujące współrzędne kursora :

unsigned char lcd_x, lcd_y;

Funkcje pomocnicze
Pierwszą funkcją będzie funkcja konfigurująca porty wykorzystywane do komunikacji z wyświetlaczem w tryb wyjściowy. Wyświetlacz LM12864A nie wymaga dodatkowej inicjalizacji jak to miało miejsce w wyświetlaczu alfanumerycznym.

void lcdInit(void)
{
LCD_DATA_DDR = 0xFF;
LCD_CTRL_DDR = 0xFF;
}

Kolejna funkcja wprowadza opóźnienie wymagane przez sterownik wyświetlacza :

void delay(void)
{
asm("nop");asm("nop");
}

Dokumentacja sterownika określa minimalny czas trwania stanu na linii EN na 450ns. Tak więc dwie instrukcje nop łącznie z wywołaniem i powrotem z funkcji zapewniają spełnienie tego warunku. Przeprowadzone przeze mnie eksperymenty wykazały że w rzeczywistości dodawanie opóźnień jest zbędne. Wyświetlacz poprawnie współpracował z mikrokontrolerem taktowanym sygnałem o częstotliwości 16MHz bez żadnych dodatkowych opóźnień. Niemniej jednak, aby mieć pewność poprawnego działania należy spełnić wymogi czasowe transmisji zalecane przez producenta kontrolera.

Sprawdzanie zajętości kontrolera
Ponieważ kontroler wyświetlacza do czasu zakończenia wykonywania poprzedniej instrukcji ignoruje kolejne instrukcje, należy przed zapisaniem instrukcji sprawdzić flagę zajętości kontrolera. W tym celu zdefiniujmy następującą funkcję :

void lcdWait(void)
{
LCD_DATA_DDR = 0x00; // ustawienie portu danych w tryb wejściowy
CLR_RS(); // niski stan na linii RS -> odczyt rejestru statusu
SET_RW(); // wysoki stan na linii RW -> odczyt z wyświetlacza
do { //pętla
delay(); // opóźnienie
SET_EN(); // ustaw linię EN
delay(); // opóźnienie
CLR_EN(); // wyzeruj linię EN
} while((LCD_DATA_PIN & DISPLAY_STATUS_BUSY)); // powtarzaj do
// wyzerowania flagi BUSY
}

Zapis instrukcji
Zapis instrukcji odbywa się przy niskim stanie linii RS i RW. Opadające zbocze na linii E zatrzaskuje dane w rejestrze wejściowym.

void lcdWriteCmd(unsigned char x)
{
lcdWait();
CLR_RS();
CLR_RW();
LCD_DATA_DDR = 0xFF;
LCD_DATA_PORT = x;
SET_EN();
delay();
CLR_EN();
}

Zapis danych
Dane zapisywane są przy wysokim stanie linii RS i niskim stanie linii RW. Opadające zbocze na linii E zatrzaskuje dane w rejestrze wejściowym. W zależności od aktualnej wartości adresu Y odpowiednio ustawiana jest kombinacja sygnałów CS1 i CS2.

void lcdWriteData(unsigned char data)
{
if(lcd_x < 64) // jeśli współrzędna x wyświetlacza < 64
{LCD_CS0()} // to zapisujemy do pierwszego kontrolera
else // w przeciwnym razie
{LCD_CS1()} // zapisujemy do drugiego kontrolera
lcdWait(); // oczekiwanie na gotowość kontrolera
SET_RS(); // wysoki stan na linii RS -> dane
CLR_RW(); // niski stan na linii RW -> zapis
LCD_DATA_DDR = 0xFF; // port danych -> wyjście
LCD_DATA_PORT = data; // wystawienie na port danej
SET_EN(); // wysoki stan na linii EN
delay(); // opóźnienie
CLR_EN(); // niski stan na linii EN
lcd_x++; // zwiększenie współrzędnej x wyświetlacza (pomocniczej)
if(lcd_x > 127) // jesli koniec ekranu
lcd_x = 0; // to wyzeruj współrzędną x
LCD_NOCS();
}

Odczyt danych
Dane odczytywane są przy wysokim stanie linii RS i RW. Aby poprawnie odczytać dane z wyświetlacza należy operację odczytu wykonać dwukrotnie.

unsigned char lcdReadData(void)
{
unsigned char data;
if(lcd_x < 64) // jeśli współrzędna x wyświetlacza < 64
{LCD_CS0()} // to odczytujemy z pierwszego kontrolera
else // w przeciwnym razie
{LCD_CS1()} // odczytujemy z drugiego kontrolera
lcdWait(); // oczekiwanie na gotowość kontrolera
SET_RS(); // wysoki stan na linii RS -> dane
SET_RW(); // wysoki stan na linii RW -> odczyt
SET_EN(); // wysoki stan na linii EN
delay(); // opóźnienie
LCD_DATA_DDR = 0x00; // ustawienie portu danych w tryb wejsciowy
data = LCD_DATA_PIN; // odczyt danych z portu
CLR_EN(); // niski stan na linii EN
lcd_x++; // zwiększenie współrzędnej x wyświetlacza
if(lcd_x > 127) // jesli koniec ekranu
lcd_x = 0; // to wyzeruj współrzędną x
LCD_NOCS();
return data;
}

Ustawienie współrzędnych wyświetlania

void lcdGoTo(unsigned char x, unsigned char y)
{
lcd_x = x; // przypisanie współrzędym globalnym nowych wartości
lcd_y = y;
if(lcd_x > 63) // jeśli współrzędna pozioma jest większa od 64 to
{
LCD_CS1(); // uaktywnienie drugiego kontrolera
lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis współrzędnej pionowej
lcdWriteCmd(DISPLAY_SET_Y | (lcd_x - 64)); // zapis współrzędnej poziomej
}
else // w przeciwnym razie
{
LCD_CS0(); // uatywnienie pierwszego kontrolera
lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis współrzędnej pionowej
lcdWriteCmd(DISPLAY_SET_Y | lcd_x); // zapis współrzędnej poziomej
LCD_CS1(); // uaktywnienie drugiego kontrolera
lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis współrzędnej pionowej
lcdWriteCmd(DISPLAY_SET_Y | 0 ); // wyzerowanie współrzędnej poziomej
}
LCD_CS0(); // uaktywnienie pierwszego kontrolera
lcdWriteCmd(DISPLAY_START_LINE | 0); //
LCD_CS1(); // uaktywnienie drugiego kontrolera
lcdWriteCmd(DISPLAY_START_LINE | 0);
LCD_NOCS();
}

Włączenie wyświetlacza
Włączenie wyświetlacza następuje po wykonaniu instrukcji Display On. Instrukcję należy zapisać do każdego kontrolera oddzielnie :

void lcdOn(void)
{
LCD_CS0(); // aktywny pierwszy kontroler
lcdWriteCmd(DISPLAY_ON_CMD | ON); // włączenie wyświetlania danych
LCD_CS1(); // aktywny drugi kontroler
lcdWriteCmd(DISPLAY_ON_CMD | ON); // włączenie wyświetlania danych
LCD_NOCS(); //
}

Wyłączenie wyświetlacza
Wyłaczenie wyświetlacza następuje po wykonaniu instrukcji Display Off

void lcdOff(void)
{
LCD_CS0(); // aktywny pierwszy kontroler
lcdWriteCmd(DISPLAY_ON_CMD | OFF); // wyłączenie wyświetlania danych
LCD_CS1(); // aktywny drugi kontroler
lcdWriteCmd(DISPLAY_ON_CMD | OFF); // wyłączenie wyświetlania danych
LCD_NOCS(); //
}

Czyszczenie wyświetlacza
Czyszczenie zawartości wyświetlacza polega na wypełnieniu pamięci danych ekranu bajtami o wartości zero :

void lcdCls(void)
{
unsigned char x, y; // pomocnicze zmienne
for (y = 0; y < 8; y++) // 8-krotne powtórzenie pętli
{
lcdGoTo(0,y); // ustawienie współrzędnej y wyświetlacza
for (x = 0; x < 128; x++) // 128-krotne powtórzenie pętli
lcdWriteData(0x00); // zapis do wyświetlacza
}
lcdGoTo(0,0); // ustawienie początkowych współrzędnych
}

Poprzednie : Rozkazy kontrolera KS0108 Następne : Tryb tekstowy

 
(c) Radosław Kwiecień