Obsługa kolorowego wyświetlacza LCD-TFT 320×240 za pomocą mikrokontrolera STM32
TFT320240C256 jest kolorowym wyświetlaczem o rozdzielczości 320 pikseli (w poziomie) i 240 pikseli (w pionie) wykonanym w technologii TFT. Wbudowany sterownik zapewnia uzyskanie 8 bitowej głębi kolorów. Panel wyświetlacza jest podświetlany za pomocą wbudowanych białych diod LED i ma zintegrowany panel dotykowy. Obszar wyświetlanych informacji ma wymiary 56 x 73 mm.
Do sterowania segmentami wykorzystano sterownik zbudowany w oparciu o układ Actel Igloo. Jest to programowany układ logiczny FPGA z pamięcią Flash. Producent wyświetlacza nie podaje właściwie żadnych technicznych szczegółów pracy sterownika poza opisem wyprowadzeń i przebiegów czasowych na magistrali sterującej. Do tych przebiegów dodana jest tabelka z liczbowymi zależnościami czasowymi przy przesyłaniu danych po magistrali. Jako zamiennik klasycznej dokumentacji umieszczono w danych technicznych krótki program napisany w języku C dla mikrokontrolerów rodziny 8051. Muszę przyznać, że przyjąłem takie podejście z mieszanymi uczuciami. Z jednej strony klasyczna dokumentacja to jest to czego się spodziewałem i co w praktyce jest bardzo potrzebne. Z drugiej jednak strony procedury w C wyjaśniają właściwie wszystkie wątpliwości dotyczące sterowania wyświetlaczem na poziomie komunikacji z mikrokontrolerem. Ideałem byłoby połączenie klasycznego opisu sterownika z przykładowymi procedurami. Jednak w końcowym efekcie analiza prostych procedur w C dała wyczerpująca odpowiedź na wszystkie wątpliwości i można uznać, że dokumentacja spełnia swoje zadanie.
Interfejs komunikacyjny
Sterownik wyświetlacza do komunikacji z mikrokontrolerem wykorzystuje równoległą 8–bitową magistralę pracująca w przemysłowym standardzie Intel 8080. Przebiegi czasowe na magistrali zostały pokazane na rysunku 1.
Rys. 1. Przebiegi czasowe na magistrali wyświetlacza
Interfejs jest aktywowany po ustawieniu stanu niskiego na linii CS. Kierunkiem przesyłu danych na magistrali sterują linie WR (zapis do sterownika) i RD (odczyt ze sterownika). Dane są wpisywane z mikrokontrolera do sterownika wyświetlacza przy narastającym zboczy na linii WR, a odczytywane z pamięci obrazu sterownika przy opadającym zboczu na linii RD. Stan dodatkowej linii sterującej RS określa czy dane są przesyłane do pamięci obrazu sterownika (RS=1) ,czy do rejestrów sterujących (RS=0).
Programowa obsługa sterownika wyświetlacza
Trzy ośmiobitowe rejestry adresowe (RS=0) określają pozycję x, y na ekranie wyświetlacza (rejestr adresu x jest dwubajtowy bo zawiera liczbę z zakresu 0…319). Brak innych rejestrów oznacza, że nie trzeba sterownika wstępnie konfigurować. Wynika to pewnie z tego, jest to rozwiązanie zoptymalizowane dla zastosowanej matrycy i wszystko jest tu zoptymalizowane pod kątem właściwości użytej matrycy. To znacznie upraszcza sterowanie, bo nie trzeba pisać skomplikowanej czasami procedury inicjalizacji. Z drugiej strony nie ma tu na przykład sprzętowego wsparcia wyświetlania ograniczonych bitmap. To wsparcie przydaje do wyświetlania znaków w trybie tekstowym. Jednak jak pokażę dalej można sobie bez problemu z tym poradzić programowo.
Do testów wyświetlacza wykorzystałem moduł ewaluacyjny STM32F0Discovery. Jeżeli nie chcemy wyświetlać pełnowymiarowych bitmap, to jest to zestaw zupełnie wystarczający. Dla bitmap o pełnym wymiarze 240 x 320 pikseli potrzeba pamięci o pojemności 76 800 bajtów i to jest więcej niż całkowita pojemność pamięci Flash użytego mikrokontrolera. W aplikacjach wymagających wielu bitmap najlepiej jest je umieścić w zewnętrznej pamięci np. na karcie SD.
Obsługa magistrali jest realizowana programowo. Linie danych D0…D7 są połączone z liniami GPIOC0…GPIOC7. Jako linie sterujące są wykorzystane linie GPIOB8…GPIOB12 – tabela 1. Oprócz linii interfejsu komunikacyjnego umieszczono tu dodatkowa linię zerowania sterownika (aktywny stan L).
Tab. 1. Przykładowe połączenie magistrali sterującej
Port | Funkcja |
GPIOB8 | RESET (aktywny stan L) |
GPIOB9 | WR |
GPIOB10 | RD |
GPIOB11 | CS |
GPIOB12 | RS (oznaczona jako A0) |
GPIO0…GPIOC7 | D0…D7 |
Wszystkie linie portów w STM32 muszą być przed użyciem zainicjowane. Na listingu 1 pokazano inicjalizację linii GPIO0…GPIOC7, a na listingu 2 – inicjalizację linii sterujących.
List. 1. Inicjalizacja portu GPIOC
void BusInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 |GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |GPIO_Pin_6 | GPIO_Pin_7 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_InitStructure); }
List. 2. Inicjalizacja linii sterujących
void CtrlInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 |GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStructure); }
Do emulacji magistrali potrzebne będą procedury wysyłania 8-bitowej danej na port – listing 3 i manipulacja stanami pojedynczych linii – listing 4.
List. 3. Wystawienie 8-bitowej danej na GPIOC
void BusWrite(uint8_t PortVal) { GPIOC->ODR = PortVal; }
List. 4. Funkcje manipulacji liniami portów
//definicje linii GPIO_TypeDef* GPIO_PORT[LCDn] = {RST_GPIO_PORT, WR_GPIO_PORT, RD_GPIO_PORT, CS_GPIO_PORT, A0_GPIO_PORT}; const uint16_t GPIO_PIN[LCDn] = {RST_PIN, WR_PIN, RD_PIN, CS_PIN, A0_PIN}; //ustaw port void SetP(Port_TypeDef Port) { GPIO_PORT[Port]->BSRR = GPIO_PIN[Port]; } //zeruj port void ClrP(Port_TypeDef Port) { GPIO_PORT[Port]->BRR = GPIO_PIN[Port]; }