fbpx

W ostatniej >>części<< zająłem się zbadaniem czasu odświeżania wyświetlacza LCD 16×2 na STM32 za pomocą 4-bitowej magistrali komunikacyjnej oraz z ustalonym odgórnie czasem oczekiwania na przetworzenie dostarczonych danych. Dzisiaj sprawdzę jak na czas odświeżania wpłynie czytanie przez MCU tzw. flagi zajętości. Lecimy!

Magistrala 4-bitowa ze sprawdzaniem flagi zajętości

Aby odczytać busy flag z kontrolera wyświetlacza potrzebuję zmodyfikować schemat połączenia oraz kod. Modyfikacje które wykonałem polegają na:

    1. Pin nr 5 czyli RW będzie tym razem podłączony do STM32, który będzie sterował kierunkiem przepływu danych na magistrali. Dzięki temu możliwe będzie czytanie danych z LCD.
  1. Zmiana kierunku GPIO przeznaczonych do komunikacji z wyświetlaczem “w locie”. Biblioteka ma za zadanie robić to samodzielnie.

Konfigurację GPIO dla pinów LCD_DB4…LCD_DB7 jako Input lub Output wykonam w sposób jaki proponuje SMT32CubeMX. W odpowiedni sposób zmodyfikowałem też funkcje przesyłu i odbioru danych.

Jako bazy użyję Nucleo F401RE.

static void LCD_DataOut()
{
	GPIO_InitTypeDef GPIO_InitStruct;

	GPIO_InitStruct.Pin = LCD_DB4_Pin|LCD_DB5_Pin|LCD_DB6_Pin|LCD_DB7_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

static void LCD_DataIn()
{
	GPIO_InitTypeDef GPIO_InitStruct;

	GPIO_InitStruct.Pin = LCD_DB4_Pin|LCD_DB5_Pin|LCD_DB6_Pin|LCD_DB7_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

//
// Write byte to LCD
//
uint8_t LCD_ReadByte(void)
{
	uint8_t result = 0;
	LCD_DataIn();

	SET_LCD_RW;

	SET_LCD_E;
	result = (LCD_GetDataPort() << 4);
	RESET_LCD_E;

	SET_LCD_E;
	result |= LCD_GetDataPort();
	RESET_LCD_E;

	return result;
}

//
// Check Busy Flag
//
uint8_t LCD_CheckBusyFlag()
{
	RESET_LCD_RS;
	return LCD_ReadByte();
}

//
// Write byte to LCD
//
void LCD_WriteByte(uint8_t data)
{
	LCD_DataOut();

	RESET_LCD_RW;

	SET_LCD_E;
	LCD_SetDataPort(data >> 4);
	RESET_LCD_E;

	SET_LCD_E;
	LCD_SetDataPort(data);
	RESET_LCD_E;

	// HAL_Delay(1);
	// Delay_us(120); // Wait for data processing
	while((LCD_CheckBusyFlag() & (1<<7))); // Wait for data processing
}

Odpalam kod i… WOW! Mimo, że mikrokontroler robi masę operacji na pinach, to czas odświeżenia ramki wyniósł tylko 2,74 ms! Czy ta prędkość jest warta jeden pin GPIO? Myślę, że w wielu przypadkach zdecydowanie tak.

A może jeszcze uda się coś urwać? Kontroler LCD oferuje maksymalnie 8-bitową szerokość magistrali komunikacyjnej. Sprawdzę czy dwukrotne zwiększenie jednocześnie wysyłanych danych przyśpieszy dwukrotnie czas odświeżenia ramki. W pierwszej kolejności sprawdzę bez czytania flagi zajętości.

Magistrala 8-bitowa bez sprawdzania flagi zajętości

Ze względu na beznadziejnie długi czas odświeżania jaki osiągnąłem korzystając jedynie z wbudowanych w STM32HAL funckji delay, pominę rozważania tego sposobu dla 8-bitów. Pomiary zrealizuję jedynie używając funkcji działającej na timerze.

Zaczynam od podłączenia pozostałych 4 linii, czyli LCD_DB0…LCD_DB3.

Modyfikacja kodu polega na przypisaniu nowych funkcji odpowiednim pinom w STM32CubeMX

…oraz modyfikacji kodu pod szerszą magistralę. Zmianie ulegają funkcje dotyczące ustawienia pinów GPIO, przesłania jednego bajtu danych oraz funkcja inicjalizująca wyświetlacz, która teraz ustawia kontroler LCD w tryb 8-bitowy.

Uzyskany czas jest bez szału. W zasadzie prawie nic się nie skróciło porównując z wersją 4-bit. Cała ramka przesłała się w 7,11 ms.

Magistrala 8-bitowa ze sprawdzaniem flagi zajętości

Dla trybu 4-bitowego zamiana delay’ów na czytanie zajętości LCD wyszło rewelacyjnie – czas skrócił się znacząco. Sprawdzę czy dla 8-bitów wynik będzie równie spektakularny.

Przerabiam kod i… szczerze mówiąc spodziewałem się czegoś lepszego 🙁 Uzyskałem w ten sposób 2.74 ms, czyli wynik dokładnie taki sam, jak przy wykorzystaniu trybu 4-bitowego.

Dalsza optymalizacja?

Można jeszcze zamienić HALowe funkcje do sterowania GPIO na bezpośrednie działania na rejestrach. Czy warto iść dalej tą droga? Chyba nie. Powinno dać się jeszcze urwać może 0,2 ms ponieważ czas ustawiania GPIO niestety ma znikomy wpływ – czas oczekiwania na kontroler LCD jest zdecydowanie większy, a tego nie skrócę.

Patrząc na przebiegi można  też wywnioskować całkiem ciekawą rzecz. Otóż 1,5 ms na początku przesyłania trwa kasowanie wyswietlacza. Przy sterowaniu z obsługą busy flag jest to ponad połowa czasu potrzebnego na wyświetlenie dwóch pełnych wierszy! Może by tutaj spróbować ugryźć? Np. zamiast wysyłać komendę kasującą wpisać same spacje, hmm? Albo wysyłać wcześniej przygotowany kompletny bufor z pamięci RAM mikrokotrolera? Sposobów na buforowanie jest wiele i w przyszłości na pewno pojawi się na blogu wątek dotyczący tego tematu.

Podsumowanie

Dotarłem do końca 🙂 Zbadałem wszystkie 4 możliwe sposoby komunikacji z popularnym wyświetlaczem LCD z kontrolerem zgodnym z HD44780 wykorzystując mikrokontroler STM32F401RE. Wyniki badań przedstawia poniższa tabela.

TrybCzas
4-bit HAL_Delay
67,29 ms
4-bit bez BF
7,14 ms
4-bit z BF
2,74 ms
8-bit bez BF
7,11 ms
8-bit z BF
2,74 ms

Z którego trybu korzystać? Z miejsca można odrzucić sposób z wykorzystaniem wbudowanego HAL_Delay. Czas ten jest zdecydowanie nieakceptowalny! Szkoda życia, nie polecam.

Porównując dwa tryby 4-bitowe widać jak duży wpływ ma czytanie flagi zajętości. Nie trzeba zgadywać, kiedy kontroler jest gotowy na przyjęcie danych, wydłużając całą procedurę. Skrócenie czasu ponad 2.5x kosztuje jedynie jeden pin GPIO. Ten czas może być bardzo cenny w bardziej skomplikowanym projekcie.

A co z 8-bitami? Pic na wodę. Wyniki są prawie identyczne jak przy dwa razy węższej szynie danych. Co to oznacza? Zupełnie można pominąć ten tryb używając wyświetlaczy alfanumerycznych. Wchodzenie w tryb 8-bitowy jest marnowaniem czterech cennych pinów mikrokontrolera.

Jeżeli wyświetlacz Ci się spodobał, możesz nabyć go w różnych wariantach u mnie w sklepie.

Kod testowy wraz z biblioteką znajdziesz na moim GitHUB’ie: link

Na koniec zachęcam do dyskusji w komentarzach na temat obsługi wyświetlacza LCD na STM32. Uważasz, że popełniłem gdzieś błąd? Masz jakiś ciekawy pomysł co można ulepszyć? Podziel się tym w komentarzu! Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

5/5 - (4 votes)

Podobne artykuły

.

11 komentarzy

SaS · 05/04/2022 o 20:28

Zrealizowałem w praktyce komunikację z wyświetlaczem całkowicie na przerwaniach od timera. Czas zajętości CPU przy zegarze 16MHz praktycznie niemierzalny.

Zbigniew · 05/03/2022 o 21:55

Jak używać polskich znaków na tym wyświatlaczu?

    Mateusz Salamon · 07/03/2022 o 10:05

    Trzeba zdefiniować własne znaki. Jest miejsce na 8 własnych znaków i wtedy z nich korzystasz.

Wojciech Klimek · 11/07/2020 o 17:20

Gratuluję świetnego opracowania tematu. Uruchomiłem Twoją bibliotekę na F103 i F030. Miałem problem z włączeniem obsługi flagi zajętości. Po chwili pracy z debuggerem zorientowałem się, że port GPIO w funkcjach LCD_DataOut() i LCD_DataIn() jest wpisany na sztywno jako GPIOB, a ja próbowałem uruchomić wyświetlacz na porcie A. Mógłbyś dodać gdzieś w pliku nagłówkowym komentarz o tym porcie, bo nie każdemu będzie się chciało szukać co jest nie tak ?

    Mateusz Salamon · 11/07/2020 o 17:22

    Dzięki 🙂 Aaaa kojarzę ten błąd. Mam na liście poprawienie tego, aby biblioteka była bardziej uniwersalna 😉 Plus dla Ciebie za znalezienie tego samemu 🙂

      rezasurmar · 07/10/2020 o 11:29

      Sam dokładnie przechodziłem podobną rozkimnę, libsy są napisane w sposób nie uwzględniający podłączenia wyświetlacza pod różny port.
      Np. w przypadku shield LCD D1Robot, piny DB4-DB7, są rozdzielone między port B i A. Trzeba sobie rozbudować dwa rozkazy zmieniające kierunek portów.

      Mateusz Salamon · 07/10/2020 o 11:42

      @rezasurmar nom, dalej tego nie poprawiłem. Póki co mam inne rzeczy na głowie, ale jest to w mojej magicznej liście ToDo 🙂

Sas · 03/12/2019 o 11:01

Faktycznie w przypadku STM przy wysokim taktowaniu nie widać różnicy w prędkości działania w trybach 4 i 8 bit. Dla wolnych uC różnice są widoczne np STM32 taktowany wolnym zegarem w celu oszczędzania energii zwłaszcza gdy magistrala MOTOROLLI jest generowana programowo.
Tak samo tryb 4-bit będzie znacznie wolniejszy od 8-bit w przypadku komunikacji przez ekspander ale nie typowy I2C czy SPI tylko własna konstrukcja na rejestrze szeregowo-równoległym.

    Mateusz Salamon · 03/12/2019 o 11:04

    Aa no tak, nie rozpatrywałem bardzo niskich zegarów jeszcze. Plus dla Ciebie 🙂 widzę, że masz spore doświadczenie. Fajnie!

      SaS · 03/12/2019 o 11:58

      Alfanumeryczne LCD podłączałem do Z-80, 8051, 68000, AVR, LPC, STM32 na różne sposoby: 4-8bit, do magistrali 8080 (do Z-80, 8051, AVR) jak i 6800 (do 68000) jak i GPIO (8051, AVR, STM32, LPC). Dekoderem adresowym (magistrala 8080) był najczęściej GAL bo od razu wyrabiał E z WR i RD tylko raz, w projekcie na Z-80 był to 74HCT138 + 74HCT00.

      Mateusz Salamon · 03/12/2019 o 12:05

      To w takim razie powinienem się od Ciebie uczyć 🙂 super dorobek doświadczenia

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *