Skocz do zawartości

Kurs STM32L4 – #11 – kolorowy wyświetlacz TFT (SPI)


Pomocna odpowiedź

  • 1 miesiąc później...
  • 10 miesiące później...
  • 5 tygodnie później...
(edytowany)

Witam. Czy próbował ktoś już tak zmodyfikować kod, aby przy użyciu funkcji hagl_put_text wypisać na wyświetlaczu liczbę zmiennoprzecinkową zamiast ciągu znaków? Osobiście próbowałem m.in. prostych konwersji z float na const char*. Widziałem, że podobny problem był wcześniej zgłaszany w tym temacie z tym, że wtedy chodziło o liczbę całkowitą. Istnieje może na to jakiś prosty trick?

Edytowano przez fidelty

Najprostszym rozwiązaniem jest użycie funkcji swprintf, dokładnie tak samo jak w przypadku wartości typu int i podobnie do metody opisywanej w odcinku poświęconym UART (tam używaliśmy sprintf ponieważ używaliśmy typu char do reprezentacji znaków, teraz używamy wchar_t, stąd zmiana na wsprintf.

Przykładowy fragment kodu mógłby wyglądać następująco:

  float value = 3.14f;
  wchar_t msg[16];

  swprintf(msg, 16, L"pi = %f :)", value);
  hagl_put_text(msg, 40, 55, YELLOW, font6x9);

Używanie sprintf lub swprintf spowoduje niestety zwiększenie zużycia pamięci flash, ale dostajemy bardzo proste i wygodne rozwiązanie. Gdybyśmy kiedyś chcieli optymalizować program to na pewno warto zacząć od zastanowienia się, czy użycie typu float jest na pewno niezbędne.

  • Lubię! 1
  • Pomogłeś! 1

@Elvis Dzięki wielkie, faktycznie zastosowanie instrukcji swprintf rozwiązało problem. Może przy dalszym rozwijaniu programu faktycznie będzie można pomyśleć o optymalizacji. Dobra robota 🙂

  • Lubię! 1
  • 4 tygodnie później...
(edytowany)

Potrzebna pomoc! 😞

 

void lcd_put_pixel(int x, int y, uint16_t color)
{
	frame_buffer[x + y * LCD_WIDTH] = color;
}

void lcd_copy(void)
{
	lcd_set_window(0, 0, LCD_WIDTH, LCD_HEIGHT);
	lcd_cmd(ST7735S_RAMWR);
	HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi2, (uint8_t*)frame_buffer, sizeof(frame_buffer), HAL_MAX_DELAY);
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);

w tablicy frame_buffer przechowujemy na odpowiednich miejscach 16 bitowe dane koloru, ale w funkcji HAL_SPI_Transmit jawnie rzutujemy dane 16 bitowe na 8 bitowe (bo funkcja ta spodziewa się 8 bitowych danych). Dobrze rozumiem, że po jawnym rzutowaniu bezpowrotnie tracimy 8 bitow z 16 bitow koloru ? czy możne funkcja Transmit dzieli je automatycznie na 2 x 8bit i wysyła pojedynczo ? Jeżeli tracimy to w jaki sposób jesteśmy w stanie rozpoznać kolor skoro definiuje go liczba 16 bitowa?

Edytowano przez vadmae
  • 1 miesiąc później...
Treker
Ta treść została wynagrodzona przez moderatora!

gtx otrzymał odznakę: "Korektor (za znalezienie błędu w artykule)"

@Treker@Elvis Drobna uwaga co do treści tego rozdziału kursu. Jest napisane tak:

Cytat

Możemy już napisać prostą funkcję, która wyśle komendę do wyświetlacza: na pinie DC ustawiamy stan niski, następnie taki sam stan ustawiamy na linii CS, co rozpocznie komunikację przez SPI. W kolejnym kroku możemy wykorzystać funkcję HAL_SPI_Transmit, dzięki której wyślemy bajt z kodem komendy, a po zakończeniu transmisji przywracamy stan wysoki na linii DC.

Natomiast kod wygląda tak:

static void lcd_cmd(uint8_t cmd)
{
	HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi2, &cmd, 1, HAL_MAX_DELAY);
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
}

Wygląda na to, że ostatnie słowo cytatu powyżej powinno brzmieć "CS", zamiast "DC". Warto to sprawdzić, bo próbowałem sam opis przerobić na kod 😉

  • Lubię! 1
  • Pomogłeś! 1

@gtx gratuluję spostrzegawczości i dziękuję za zgłoszenie, tysiące czytelników, a do tej pory nikt tego nie wyłapał. Już poprawione na blogu!

Dnia 3.10.2022 o 08:36, Treker napisał:

Uwaga: autor biblioteki hagl wprowadził do niej kilka modyfikacji, które sprawiają, że kody z tego odcinku kursu mogą nie działać poprawnie. W załączniku znajduje się starsza wersja biblioteki, która była używana podczas pisania kursu.

hagl.zip 64 kB · 23 downloads

Panowie, z innej beczki. Może warto dodać tą informację do kursu? "Biblioteka została zaktualizowana przez jej autora, link do starszej wersji biblioteki znajduje się na forum" lub najlepiej treść bloga uaktualnić, o czym za chwilę 😉

Jeśli ktoś chciałby jednak wykorzystać najnowszą wersję biblioteki HAGL to trzeba utworzyć trzy pliki. Pierwszy hagl_hal_color.h, w którym definiujemy typ koloru:

#ifndef INC_HAGL_HAL_COLOR_H_
#define INC_HAGL_HAL_COLOR_H_

#include <stdio.h>

typedef uint16_t color_t;

#endif /* INC_HAGL_HAL_COLOR_H_ */

Drugi już znamy, hagl_hal.h, w którym definiujemy parametry wyświetlacza. Nie trzeba już w nim jednak nadpisywać funkcji hagl_hal_put_pixel. Plik ten może wyglądać tak:

#ifndef INC_HAGL_HAL_H_
#define INC_HAGL_HAL_H_

#include "lcd.h"
#include "hagl/backend.h"

#define DISPLAY_WIDTH 	(LCD_WIDTH)
#define DISPLAY_HEIGHT 	(LCD_HEIGHT)
#define DISPLAY_DEPTH 	16

void hagl_hal_init(hagl_backend_t *backend);

#endif /* INC_HAGL_HAL_H_ */

Widać w nim prototyp funkcji inicjalizacyjnej, którą definiujemy w trzecim pliku, hagl_hal.c:

#include "hagl/backend.h"
#include "hagl_hal.h"

void hagl_hal_init(hagl_backend_t *backend)
{
    backend->width = DISPLAY_WIDTH;
    backend->height = DISPLAY_HEIGHT;
    backend->depth = DISPLAY_DEPTH;
    backend->put_pixel = lcd_put_pixel;
}

Widać tutaj, że nasza funkcja lcd_put_pixel() będzie wykorzystywana do rysowania pojedynczego pixela. Żeby jednak "ominąć" błędy i ostrzeżenia kompilatora funkcja ta musi się troszkę zmienić:

void lcd_put_pixel(void *_surface, int16_t x, int16_t y, uint16_t color)
{
	frame_buffer[x+y*LCD_WIDTH] = color;
}

Przyznaję bez bicia, że nie wiem po co jest to void *_surface, ale może dlatego, że nie czytałem dokumentacji 😉

Teraz pozostaje już tylko zainicjować LCD oraz bibliotekę przed pętlą while():

/* USER CODE BEGIN WHILE */
lcd_init();
hagl_backend_t *display = hagl_init();

Nie umiem ocenić, czy jest to najbardziej poprawne/eleganckie/optymalne rozwiązanie, ale hagl_draw_rounded_rectangle oraz hagl_put_text działają:

for (int i = 0; i < 8; i++)
{
  hagl_draw_rounded_rectangle(display,2+i, 2+i, 158-i, 126-i, 8-i, rgb565(0, 0, i*16));
}

hagl_put_text(display, L"Hello World!", 40, 55, GREEN, font6x9);

 

  • Lubię! 1
  • Pomogłeś! 1
4 godziny temu, gtx napisał:

Panowie, z innej beczki. Może warto dodać tą informację do kursu? "Biblioteka została zaktualizowana przez jej autora, link do starszej wersji biblioteki znajduje się na forum" lub najlepiej treść bloga uaktualnić, o czym za chwilę 😉

@gtx tej zmiany niestety nie wprowadzę od ręki, bo nie mogę teraz, robić aż tak dużych różnic względem e-booka i książki drukowanej. Gdy będzie czas aktualizacji to od razu dodamy taką informację we wszystkich 3 miejscach. Na ten moment ograniczam się więc do przypięcie tej informacji w komentarzach.

8 godzin temu, Treker napisał:

@gtx tej zmiany niestety nie wprowadzę od ręki, bo nie mogę teraz, robić aż tak dużych różnic względem e-booka i książki drukowanej. Gdy będzie czas aktualizacji to od razu dodamy taką informację we wszystkich 3 miejscach. Na ten moment ograniczam się więc do przypięcie tej informacji w komentarzach.

@Treker Jasne, już rozumiem. Dzięki za świetny rozdział!

  • Lubię! 1

Obiecuję, że to już ostatni raz. Mam problem z funkcją lcd_is_busy() podczas wysyłania danych z użyciem DMA. Wszystkie ustawienia jak w kursie (poza HAGL, jak pisałem wyżej wykorzystuję najnowszą wersję). Postanowiłem stworzyć własną, prostą animację. Kod jest prosty:

	lcd_init();
	hagl_backend_t *display = hagl_init();
	color = RED;

	while (true)
	{
		for (int i=50;i>10;i--)
		{
			while (lcd_is_busy2()) {color = GREEN;}
			backgroudSolid(CYAN);
			hagl_fill_circle(display, 50, 50, i, color);
			lcd_copy();
		}

		while (lcd_is_busy2()) {}
		backgroudSolid(CYAN);
		lcd_copy();

		for (int i=50;i>10;i--)
		{
			while (lcd_is_busy()) {color = BLUE;}
			backgroudSolid(CYAN);
			hagl_fill_circle(display, 50, 50, i, color);
			lcd_copy();
		}

Taki kod miał za zadanie generowanie jednorodnego tła (backgroundSolid) oraz zanikających okręgów, najpierw zielonego, potem niebieskiego. Specjalnie wygenerowałem dwie funkcje lcd_is_busy() oraz lcd_is_busy2(). Fragment lcd.c jest następujący:

void lcd_transfer_done(void)
{
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, 1);
}

bool lcd_is_busy2(void)
{
	if (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
		return true;
	else
		return false;
}

bool lcd_is_busy(void)
{
	if (HAL_SPI_GetState(&hspi2) == HAL_SPI_STATE_BUSY)
		return true;
	else
		return false;
}

//wysyłanie danych do wyświetlacza po DMA
void lcd_copy(void)
{
	lcd_set_window(0,0,LCD_WIDTH,LCD_HEIGHT);
	lcd_cmd(ST7735S_RAMWR);
	HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, 1);
	HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, 0);
	HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)frame_buffer, sizeof(frame_buffer));
}

Wyświetlacz działa tak:

gifnotok.thumb.gif.c9f160d962b56eb731a1a120aec278b0.gif

Widać, że kod w ogóle nie zmienia koloru koła w drugim for-ze (sprawdzone także w trybie debuggera):

while (lcd_is_busy()) {color = BLUE;}

Tak jakby układ nigdy nie był w trybie BUSY. Udało mi się to sytuację poprawić wykorzystując wszędzie funkcję lcd_is_busy2() lub sprawdzając stan HAL_SPI_STATE_BUSY_TX zamiast HAL_SPI_STATE_BUSY. Wtedy wynik jest następujący:

gifok.thumb.gif.f5904b9c7b1bcbece30f91949734b9e4.gif

 

  • Lubię! 2

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...