Skocz do zawartości

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


Pomocna odpowiedź

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
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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
Link do komentarza
Share on other sites

@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
Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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ę! 1
Link do komentarza
Share on other sites

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...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.