STM32_08.TFT_STM32F407_RootDev

TFT User Interface Example

Projekt z interfejsem graficznym na TFT na STM32F407. Implementacja lokalnej prezentacji danych i obsługi interfejsu bez systemu operacyjnego.

STM32F4CHALTFTDisplayEmbedded UILVGLTouchscreenSD CardJSONWiFiLoRaBMP280

Informacje

  • Repozytorium: STM32_08.TFT_STM32F407_RootDev
  • Płytka / obszar: STM32F407 / TFT display
  • Kategoria: Display / UI
  • Status: Public repository

Opis techniczny

  • Kod jest ukierunkowany na obsługę wyświetlacza i warstwę prezentacji danych na mikrokontrolerze.
  • Warstwa wyświetlacza pracuje bezpośrednio na mikrokontrolerze, dlatego istotne są inicjalizacja panelu, sposób odświeżania i koszt rysowania elementów UI.
  • STM32F407 daje większy zapas zasobów niż małe MCU, co pasuje do projektu z ekranem.
  • STM32F407 zapewnia większy zapas RAM, flash i wydajności niż małe kontrolery z rodziny G0, co ma znaczenie przy pracy z grafiką.
  • Projekt rozszerza typowy firmware sterujący o warstwę lokalnego interfejsu użytkownika.
  • Zastosowano bibliotekę LVGL do zarządzania interfejsem graficznym, obsługę ekranu dotykowego SPI, kartę SD do przechowywania konfiguracji i danych.
  • System obsługuje komunikację przez WiFi (ESP8266), LoRa oraz czujnik BMP280 do pomiaru temperatury, ciśnienia i wilgotności.
  • Dane konfiguracyjne są przechowywane w plikach INI na karcie SD, a dane JSON są wykorzystywane do komunikacji z serwerem NTP.

Co dzieje się w kodzie

Wybrane najważniejsze mechanizmy z projektu.

  • Projekt wykorzystuje bibliotekę LVGL do zarządzania interfejsem graficznym z funkcjami takimi jak przyciski, pola tekstowe i elementy UI.
  • Obsługa ekranu dotykowego odbywa się przez SPI z wykorzystaniem XPT2046, z obsługą kalibracji i filtrowania współrzędnych.
  • Karta SD jest używana do przechowywania konfiguracji w plikach INI oraz danych systemowych.
  • Komunikacja WiFi odbywa się przez ESP8266 z obsługą NTP do synchronizacji czasu.
  • Komunikacja LoRa wykorzystuje moduł SX1276 z funkcjami wysyłania i odbierania wiadomości.
  • Czujnik BMP280 obsługuje pomiary temperatury, ciśnienia i wilgotności z obsługą stanów maszyny.
  • System wykorzystuje cJSON do parsowania danych JSON oraz DataTime do zarządzania czasem i datą.

Kluczowe fragmenty kodu

Kod jest skrócony do części istotnych dla zrozumienia projektu.

Inicjalizacja LVGL i ekranu dotykowego

Core/Src/main.c
lv_init();
  lv_port_disp_init();
  lv_port_indev_init();

  LCD_BL_ON();

  if(HAL_GPIO_ReadPin(GPIOC, CardDetectPin_Pin) == GPIO_PIN_RESET)
  {
    CardDetectedInSlot = true;
    InitializeSDCard();
  }

  // ...existing code...
  
  initTouchScr(); //lcdInit();
  lcdFillRGB(COLOR_BLACK);
  lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE);

Inicjalizacja LVGL i ekranu dotykowego odbywa się w głównym pliku. Inicjalizacja LCD, ustawienie orientacji oraz uruchomienie sterownika dotykowego.

Obsługa ekranu dotykowego SPI

Core/Src/SPITouchScreen.c
void Touch_ReadCoordinates(uint16_t* x, uint16_t* y)
{
    uint8_t tempXH, tempXL, tempYH, tempYL;

    Touch_Select(); // CS Low
    Touch_SPI_Transfer(XPT2046_CMD_X);  // Komenda dla odczytu X 0xD0
    tempXH = Touch_SPI_Transfer(0x00);  // Odczyt wyższego bajtu X
    tempXL = Touch_SPI_Transfer(0x00);  // Odczyt niższego bajtu X

    Touch_SPI_Transfer(XPT2046_CMD_Y);  // Komenda dla odczytu Y 0x90
    tempYH = Touch_SPI_Transfer(0x00);  // Odczyt wyższego bajtu Y
    tempYL = Touch_SPI_Transfer(0x00);  // Odczyt niższego bajtu Y

    Touch_Unselect(); // CS High

    *x = (tempXH << 8 | tempXL) >> 3;  // Usunięcie nieużywanych bitów
    *y = (tempYH << 8 | tempYL) >> 3;  // Usunięcie nieużywanych bitów
}

Funkcja odczytuje współrzędne dotykowe z czujnika XPT2046 przez magistralę SPI. Wartości są przetwarzane i zwracane jako współrzędne X i Y.

Obsługa karty SD i plików konfiguracyjnych

Core/Src/File_Handling.c
void InitializeSDCard()
{
  // ... initialization code ...
  
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.ClockDiv = 2;

  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  if(HAL_SD_Init(&hsd)!= HAL_OK)
  {
    // Error handling
  }
  
  // ... existing code ...
}

FRESULT Mount_SD (const TCHAR* path)
{
  fresult = f_mount(&fs, path, 1);
  if (fresult != FR_OK)
  {
    Debug ("ERROR!!! in mounting SD CARD...");
  }
  else
  {
    // Success message
  }
  return fresult;
}

Funkcje inicjalizują kartę SD i obsługują operacje montowania/odmontowywania systemu plików. Pliki konfiguracyjne są odczytywane z karty SD.

Komunikacja LoRa

Core/Src/LoRa.c
LoRa newLoRa(){
  LoRa new_LoRa;

  new_LoRa.frequency          = 433       ;
  new_LoRa.spredingFactor     = SF_7      ;
  new_LoRa.bandWidth          = BW_125KHz ;
  new_LoRa.crcRate            = CR_4_5    ;
  new_LoRa.power              = POWER_17db;
  new_LoRa.overCurrentProtection = 100       ;
  new_LoRa.preamble           = 8         ;

  return new_LoRa;
}

void LoRa_gotoMode(LoRa* _LoRa, int mode){
  uint8_t    read;
  uint8_t    data;

  read = LoRa_read(_LoRa, RegOpMode);
  data = read;

  if(mode == SLEEP_MODE){
    data = (read & 0xF8) | 0x00;
    _LoRa->current_mode = SLEEP_MODE;
  }else if (mode == STNBY_MODE){
    data = (read & 0xF8) | 0x01;
    _LoRa->current_mode = STNBY_MODE;
  }else if (mode == TRANSMIT_MODE){
    data = (read & 0xF8) | 0x03;
    _LoRa->current_mode = TRANSMIT_MODE;
  }else if (mode == RXCONTIN_MODE){
    data = (read & 0xF8) | 0x05;
    _LoRa->current_mode = RXCONTIN_MODE;
  }else if (mode == RXSINGLE_MODE){
    data = (read & 0xF8) | 0x06;
    _LoRa->current_mode = RXSINGLE_MODE;
  }

  LoRa_write(_LoRa, RegOpMode, data);
}

Funkcje inicjalizują i konfigurują moduł LoRa. Ustawiają parametry takie jak częstotliwość, szerokość pasma, współczynnik kodowania i moc wyjściową.

Pomiar danych z BMP280

Core/Src/BMP280.c
void ProcessBMPStateMachine()
{
    switch (bmpState)
    {
    	case BMP_INITIALIZE:
    		if((HAL_GetTick() - lastBMPSync) > DefaultTimeBMPFailSyncAfterMs)
    		{
				if(bmp280_init(&bmp280, &bmp280.params))
				{
					bmpState = BMP_MESURMENT;
					ErrorCount = 0;
					lastBMPSync = HAL_GetTick();
				}
				else
				{
					if(debugBMP)
					{
						Debug("BMP280 initialization failed...");
					}
					if(ErrorCount >9)
						bmpState = BMP_HW_RESET_REQUIRED;
					else
						ErrorCount++;
					lastBMPSync = HAL_GetTick();
				}
    		}
    		break;

    	case BMP_MESURMENT:
				if(HAL_GetTick() - lastBMPSync > CheckTSensorsBMPAfterMS)
				{
					if(bmp280_force_measurement(&bmp280) == true)
					{
						bmpState = BMP_DATA_READY;
					}
					else
					{
						ErrorCount++;
						lastBMPSync = HAL_GetTick();
					}
				}
				break;
    }
}

Maszyna stanów BMP280 obsługuje inicjalizację, pomiary i obsługę błędów. W zależności od stanu wykonywane są odpowiednie operacje.