poniedziałek, 19 grudnia 2016

[5] STM32F7 - CubeMx - Wyświetlacz Cz. 1. - Obsługa wyświetlacza

Ten post będzie otwierał serie dotyczącą wyświetlacza umieszczonego na mikrokontrolerze STM32F7. Finalny projekt ma zawierać możliwość obsługi biblioteki STemWin. Aplikacja będzie tworzona od początku w środowisku CubeMx, a następnie w SystemWorkbench Ac6. Sterowniki oraz pozostałe potrzebne elementy pobrałem z przykładu dla wyświetlacza tego układu.

W tym poście opiszę jedynie odpalenie samego wyświetlacza oraz dołączenie wyszystkich potrzebnych elementów dotyczących wyświetlacza, touchpada oraz biblioteki STemWin. Samo włączenie touchpada oraz biblioteki graficznej zostawię na następny raz.

Przydatne linki:


Przykłady od firmy ST (do pobrania na dole w linku);
STM32F7 - Datasheet (Dokumentacja mikrokontrolera wykorzystywanego w płytce Discovery);
SDRAM - Datasheet (MT48LC4M32B2 Micron);
Touchpad (FT5336);
Niestety dokumentacji wyświetlacza nie udało mi się znaleźć w internecie, aby ją zdobyć należy napisać do firmy Rocktech.

Cube Mx:


Ta cześć będzie poświęcona generowaniu projektu krok po kroku w środowisku CubeMx oraz krótkiemu opisowi części sprzętowej,

Pierwszym etapem jest stworzenie projektu. Najlepiej wykonać go nie dla procesora a dla płytki discovery ponieważ wszystkie wykorzystywane elementy zostały na niej zintegrowane:

Poniżej przedstawię wszystkie kroki jakie będą niezbędne do włączenia wszystkich elementów dla obsługi wyświetlacza:

Uruchomienie RCC:

Standardowo na początku należy uruchomić potrzebne zegary. Wykorzystuje do tego celu kwarc zewnętrzny.



Pełna konfiguracja zegarów znajduje się na samym dole sekcji dla CubeMx w tym poście.

Uruchomienie FMC:

FMC odpowiada za uzyskanie dostępu do pamięci SDRAM, którą opisywałem krótko w jednym z poprzednich postów na tym blogu. Dla przypomnienia pamięć umieszczona na płytce została wyprodukowana przez Microna jest to model MT48LC4M32B2B5-6A o pojemności 128Mbit. Przez microkontroler wykorzystywane są niższe 16 bitów, czyli uzyskuje się dostęp do połowy pamięci (64 Mbit). Linie od DQ16 do DQ31 zostały połączone rezystorami 1% 10k do masy.


FMC_SDCKE0 oznacza Clock Enable. Aktywacja przebiega poprzez podanie stanu wysokiego, deaktywacja natomiast za pomocą stanu niskiego sygnału zegarowego. FMC_SDNE0 jest to Chip Select. Za jego pomocą jest włączany (stan niski) bądź wyłączany (stan wysoki) dekoder rozkazów w pamięci.  Z tego powodu, że te linie są wykorzystywane należy je wybrać przy włączaniu FMC. Zresztą tylko one będą dostępne jeśli wybierze się konfiguracje układu na podstawie płytki discovery.

Jak wspomniałem w wcześniejszym poście do dyspozycji są 4 banki pamięci wybierane poprzez ustawienie pinów FMC_BA0 oraz FMC_BA1.

Podłączenie do linii danych, dla wszystkich czterech banków pamięci, wymaga używania wszystkich sprzętowo podłączonych linii tzn od FMC_A0 do FMC_A11. Każdy z wspomnianych banków pamięci jest złożony z 256 komun i 4096 rzędów do tego każda z komórek składa się z 32 bitów. Aby uzyskać do nich dostęp, to wykorzystuje się linie od DQ0 do DQ15, podłączone do odpowiadającym im komórkom FMC (FMC_D0 do FMC_D15).

Na podstawie wspomnianych wcześniej połączeń przygotowuje się ustawienia w programie CubeMx.


Jeśli chodzi o czasy dostępu do pamięci to należy posłużyć się dokumentacją do mikokontrolera (link na górze strony) lub wykorzystać przykład przygotowany przez ST, gdzie są one już umieszczone w odpowiedniej funkcji. Delikatnie się będą one różnić jednak w bardzo małym stopniu od tych przedstawionych tutaj (dokładnie tylko jednym parametrem).

Sam sygnał taktujący FMC jest wartością częstotliwości HCLK/2 czyli Internal AHB. HCLK jest to częstotliwość taktowania całego sytemu. Ja wybrałem częstotliwość prawie maksymalną czyli 200 MHz( max dla tego mikrokontrolera bez podkręcania to 216MHz ). Wobec tego sygnał taktujący FMC będzie wynosił 100 MHz.

Wobec tego czas na opis ustawień. Pierwszy z nich czyli CAS Latency, oznacza opóźnienie pomiędzy wprowadzeniem komendy odczytu a otrzymaniem danych zwrotnych. Do ustawienia są możliwe wartości od 1 do 3. Każda z nich będzie działała poprawnie.

Ostatnim parametrem w tej sekcji jest Read Pipe Delay, czyli opóźnienie w cyklach pomiędzy zmianami adresu odczytywanych danych. Ta wartość może zostać ustawiona na 0, ponieważ adres może być ponownie ustawiony w każdym cyklu zegara.

Czas dostępu do pamięci będzie wynosił dwa cykle (Load mode Register to active delay). Czyli po wgraniu informacji do rejestrów pamięci, należy odczekać dwa cykle na wykonanie wprowadzonych rozkazów. Dopiero po tym czasie wprowadzone zmiany zostaną zaakceptowane i zakończy się ich wykonywanie.

Kolejnym parametrem jest Exit self refresh delay, czyli czas jaki jest wymagany do wyjścia z jednego stanu do drugiego. W dokumentacji producenta pamięci zdefiniowany jest on na 70 ns (strona 21), czyli dla częstotliwości 100 MHz będzie to wynosić 7 cykli.

Następny w kolejności jest self refresh time. Musi on wynosić przynajmniej czas odpowiadający wartości RAS (strona 20 tabela). Jest on w niej zdefioniowany na 42 ns minimum. Wobec tego wartość czterech cykli powinna być odpowiednia.

SDRAM common row cycle delay - czyli opóźnienie pomiędzy dostępem do kolejnych kolumn w pamięci. Ten czas powinien wynosić pomiędzy 60 ns czyli 6 cykli. Jeśli w programie CubeMx chce się zdefiniować wybraną przez ST'ma wartość 7 wtedy należy także zmienić kolejny parametr na liście na wartości 3. Wtedy dopiero program pozwoli na kontynuowanie przygotowywania projektu.

Write recovery time czyli czas na zapisanie danych do pamięci. W dokumentacji ten czas wynosi 14ns więc dwa cykle zegara.

Następna wartość SDRAM common row precharge delay odnosi się do czasu potrzebnego na przesłanie i wykonanie komendy. Opóźnienie powinno wynosić dwa cykle zegara czyli 20 ns.

No i ostatnia wartość mianowicie Row to column delay czyli opóznienie pomiędzy komendami read/write. Czyli opóźnienie pomiędzy komendami zapisującymi/odczytującymi z odpowiednich miejsc w pamięci. Ten czas powinien wynosić około 20 ns.

Wygenerowana funkcja zostanie wprowadzona do pliku main.c i wygląda następująco:

  1. static void MX_FMC_Init(void)
  2. {
  3.   FMC_SDRAM_TimingTypeDef SdramTiming;
  4.   /** Perform the SDRAM1 memory initialization sequence
  5.   */
  6.   hsdram1.Instance = FMC_SDRAM_DEVICE;
  7.   /* hsdram1.Init */
  8.   hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  9.   hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
  10.   hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
  11.   hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  12.   hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  13.   hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_1;
  14.   hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  15.   hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_DISABLE;
  16.   hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;
  17.   hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  18.   /* SdramTiming */
  19.   SdramTiming.LoadToActiveDelay = 2;
  20.   SdramTiming.ExitSelfRefreshDelay = 7;
  21.   SdramTiming.SelfRefreshTime = 4;
  22.   SdramTiming.RowCycleDelay = 6;
  23.   SdramTiming.WriteRecoveryTime = 2;
  24.   SdramTiming.RPDelay = 2;
  25.   SdramTiming.RCDDelay = 2;
  26.   if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  27.   {
  28.     Error_Handler();
  29.   }
  30. }

Jest ona delikatnie inna niż ta stworzona w plikach BSP przez ST. Ta natomiast wygląda następująco:

  1. uint8_t BSP_SDRAM_Init(void)
  2. {
  3.   static uint8_t sdramstatus = SDRAM_ERROR;
  4.   /* SDRAM device configuration */
  5.   sdramHandle.Instance = FMC_SDRAM_DEVICE;
  6.    
  7.   /* Timing configuration for 100Mhz as SD clock frequency (System clock is up to 200Mhz) */
  8.   Timing.LoadToActiveDelay    = 2;
  9.   Timing.ExitSelfRefreshDelay = 7;
  10.   Timing.SelfRefreshTime      = 4;
  11.   Timing.RowCycleDelay        = 7;
  12.   Timing.WriteRecoveryTime    = 2;
  13.   Timing.RPDelay              = 2;
  14.   Timing.RCDDelay             = 2;
  15.   sdramHandle.Init.SDBank             = FMC_SDRAM_BANK1;
  16.   sdramHandle.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_8;
  17.   sdramHandle.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_12;
  18.   sdramHandle.Init.MemoryDataWidth    = SDRAM_MEMORY_WIDTH;
  19.   sdramHandle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  20.   sdramHandle.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_2;
  21.   sdramHandle.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  22.   sdramHandle.Init.SDClockPeriod      = SDCLOCK_PERIOD;
  23.   sdramHandle.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;
  24.   sdramHandle.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0;
  25.   /* SDRAM controller initialization */
  26.   BSP_SDRAM_MspInit(&sdramHandle, NULL); /* __weak function can be rewritten by the application */
  27.   if(HAL_SDRAM_Init(&sdramHandle, &Timing) != HAL_OK)
  28.   {
  29.     sdramstatus = SDRAM_ERROR;
  30.   }
  31.   else
  32.   {
  33.     sdramstatus = SDRAM_OK;
  34.   }
  35.   /* SDRAM initialization sequence */
  36.   BSP_SDRAM_Initialization_sequence(REFRESH_COUNT);
  37.   return sdramstatus;
  38. }

Jak widać z porównania różnica jest głównie w wartości niektórych z parametrów.

Uruchomienie DMA2D:

Następnym elementem jest kooprocesor graficzny DMA2D, który został już umieszczony w pierwszym modelu płytki z wyświetlaczem graficznym czyli STM32F429i. Jest to specjalnie wykonany kanał DMA który samodzielnie realizuje wiele operacji graficznych.

Aby go uruchomić wystarczy zaznaczyć Active w zakładce DMA2D.

Z ustawień jedyne co trzeba dobrać to kolory, ja wybrałem RGB888 24 bitowy, ponieważ taki tryb wybiorę w dla kontrolera LTDC.


Uruchomienie LTDC:

LTDC jest to sterownik wyświetlaczy TFT. Na płytce umieszczono taki wyświetlacz o przekątnej 4.3 cala 480x272 piksele. Niestety dokumentacji do niego nie można pobrać z internetu, ponieważ nie została ona udostępniona.

Jeśli chodzi o opis wyprowadzeń to wygląda on następująco:


Jak można zaobserwować do dyspozycji są 24 linie danych. Wyprowadzenia I2C dotyczą panelu dotykowego. Dodatkowo znajdują się piny odpowiedzialne za podświetlenie czy wybranie trybu pracy.

Piny które są podłączone do wyświetlacza są opisane następująco:


  • LCD_CLK - linia sygnału zegarowego 
  • LCD_HSYNC - synchronizacja pionowa
  • LCD_VSYNC - synchronizacja pozioma
  • LCD_DE - włączenie transmisji danych
  • LCD_R[7:0] - 8-bit czerwone
  • LCD_G[7:0] - 8 bit zielone
  • LCD_B[7:0] - 8 bit niebieskie


Jeśli nie będą one przez niego wykorzystywane to możne je używać do innych celów.

Pin Reset podłączony jest do wspólnego resetu całego urzadzenia. Podświetlanie podłączone do układu STLD40DPUR czyli układu do zasilania diod led, służącego do poświetlania dużych ekranów LCD (Datasheet). Wzór do obliczenia wartości tej pamięci wygląda następująco:

Wymagana pamięć = (BPP (bity na pixel) * Szerokość * Wysokość) / 8.

Każdy wykorzystywany rodzaj wyświetlacza posiada pewną ilość miejsca jaka jest wymagana aby poprawnie go obsłużyć.

Jeśli chodzi o opóźnienia i pozostałe parametry to do wprowadzania do CubaMx odnośnie sterownika , to zaufam ustawieniom udostępnionym w przykładach dla tego układu.

Poszczególne czasy oraz wartości odpowiadają wykorzystywanemu wyświetlaczowi oraz jego rozdzielczości.

Funkcja przygotowana przez ST w BSP wygląda następująco:

  1. uint8_t BSP_LCD_Init(void)
  2. {    
  3.   /* Select the used LCD */
  4.   /* The RK043FN48H LCD 480x272 is selected */
  5.   /* Timing Configuration */
  6.   hLtdcHandler.Init.HorizontalSync = (RK043FN48H_HSYNC - 1);
  7.   hLtdcHandler.Init.VerticalSync = (RK043FN48H_VSYNC - 1);
  8.   hLtdcHandler.Init.AccumulatedHBP = (RK043FN48H_HSYNC + RK043FN48H_HBP - 1);
  9.   hLtdcHandler.Init.AccumulatedVBP = (RK043FN48H_VSYNC + RK043FN48H_VBP - 1);
  10.   hLtdcHandler.Init.AccumulatedActiveH = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP - 1);
  11.   hLtdcHandler.Init.AccumulatedActiveW = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP - 1);
  12.   hLtdcHandler.Init.TotalHeigh = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP + RK043FN48H_VFP - 1);
  13.   hLtdcHandler.Init.TotalWidth = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP + RK043FN48H_HFP - 1);
  14.   /* LCD clock configuration */
  15.   BSP_LCD_ClockConfig(&hLtdcHandler, NULL);
  16.   /* Initialize the LCD pixel width and pixel height */
  17.   hLtdcHandler.LayerCfg->ImageWidth  = RK043FN48H_WIDTH;
  18.   hLtdcHandler.LayerCfg->ImageHeight = RK043FN48H_HEIGHT;
  19.   /* Background value */
  20.   hLtdcHandler.Init.Backcolor.Blue = 0;
  21.   hLtdcHandler.Init.Backcolor.Green = 0;
  22.   hLtdcHandler.Init.Backcolor.Red = 0;
  23.   /* Polarity */
  24.   hLtdcHandler.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  25.   hLtdcHandler.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  26.   hLtdcHandler.Init.DEPolarity = LTDC_DEPOLARITY_AL;  
  27.   hLtdcHandler.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  28.   hLtdcHandler.Instance = LTDC;
  29.   if(HAL_LTDC_GetState(&hLtdcHandler) == HAL_LTDC_STATE_RESET)
  30.   {
  31.     /* Initialize the LCD Msp: this __weak function can be rewritten by the application */
  32.     BSP_LCD_MspInit(&hLtdcHandler, NULL);
  33.   }
  34.   HAL_LTDC_Init(&hLtdcHandler);
  35.   /* Assert display enable LCD_DISP pin */
  36.   HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_SET);
  37.   /* Assert backlight LCD_BL_CTRL pin */
  38.   HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_SET);
  39. #if !defined(DATA_IN_ExtSDRAM)
  40.   /* Initialize the SDRAM */
  41.   BSP_SDRAM_Init();
  42. #endif
  43.    
  44.   /* Initialize the font */
  45.   BSP_LCD_SetFont(&LCD_DEFAULT_FONT);
  46.   return LCD_OK;
  47. }

Gdzie zdefiniowane wartości wynoszą:

  1. #define  RK043FN48H_HSYNC            ((uint16_t)41)   /* Horizontal synchronization */
  2. #define  RK043FN48H_HBP              ((uint16_t)13)   /* Horizontal back porch      */
  3. #define  RK043FN48H_HFP              ((uint16_t)32)   /* Horizontal front porch     */
  4. #define  RK043FN48H_VSYNC            ((uint16_t)10)   /* Vertical synchronization   */
  5. #define  RK043FN48H_VBP              ((uint16_t)2)    /* Vertical back porch        */
  6. #define  RK043FN48H_VFP              ((uint16_t)2)    /* Vertical front porch       */

Poniżej przygotowana konfiguracja w programie CubeMx:


Po wygenerowaniu kodu delikatnie zmieniamy ustawienia tak aby były one zgodne z tymi przedstawionymi przez STM'a

  1. static void MX_LTDC_Init(void)
  2. {
  3.   LTDC_LayerCfgTypeDef pLayerCfg;
  4.   LTDC_LayerCfgTypeDef pLayerCfg1;
  5.   hltdc.Instance = LTDC;
  6.   hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  7.   hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  8.   hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  9.   hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  10.   hltdc.Init.HorizontalSync = 40;
  11.   hltdc.Init.VerticalSync = 9;
  12.   hltdc.Init.AccumulatedHBP = 53;
  13.   hltdc.Init.AccumulatedVBP = 11;
  14.   hltdc.Init.AccumulatedActiveW = 533;
  15.   hltdc.Init.AccumulatedActiveH = 283;
  16.   hltdc.Init.TotalWidth = 565;
  17.   hltdc.Init.TotalHeigh = 285;
  18.   hltdc.Init.Backcolor.Blue = 0;
  19.   hltdc.Init.Backcolor.Green = 0;
  20.   hltdc.Init.Backcolor.Red = 0;
  21.   if (HAL_LTDC_Init(&hltdc) != HAL_OK)
  22.   {
  23.     Error_Handler();
  24.   }
  25.   pLayerCfg.WindowX0 = 0;
  26.   pLayerCfg.WindowX1 = 0;
  27.   pLayerCfg.WindowY0 = 0;
  28.   pLayerCfg.WindowY1 = 0;
  29.   pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB888;
  30.   pLayerCfg.Alpha = 0;
  31.   pLayerCfg.Alpha0 = 0;
  32.   pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  33.   pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  34.   pLayerCfg.FBStartAdress = 0;
  35.   pLayerCfg.ImageWidth = 0;
  36.   pLayerCfg.ImageHeight = 0;
  37.   pLayerCfg.Backcolor.Blue = 0;
  38.   pLayerCfg.Backcolor.Green = 0;
  39.   pLayerCfg.Backcolor.Red = 0;
  40.   if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
  41.   {
  42.     Error_Handler();
  43.   }
  44.   pLayerCfg1.WindowX0 = 0;
  45.   pLayerCfg1.WindowX1 = 0;
  46.   pLayerCfg1.WindowY0 = 0;
  47.   pLayerCfg1.WindowY1 = 0;
  48.   pLayerCfg1.PixelFormat = LTDC_PIXEL_FORMAT_RGB888;
  49.   pLayerCfg1.Alpha = 0;
  50.   pLayerCfg1.Alpha0 = 0;
  51.   pLayerCfg1.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  52.   pLayerCfg1.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  53.   pLayerCfg1.FBStartAdress = 0;
  54.   pLayerCfg1.ImageWidth = 0;
  55.   pLayerCfg1.ImageHeight = 0;
  56.   pLayerCfg1.Backcolor.Blue = 0;
  57.   pLayerCfg1.Backcolor.Green = 0;
  58.   pLayerCfg1.Backcolor.Red = 0;
  59.   if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg1, 1) != HAL_OK)
  60.   {
  61.     Error_Handler();
  62.   }
  63. }

Uruchomienie I2C:

Magistrala I2C odpowiada za komunikację pomiędzy touchpadem a mikrokontrolerem. Ona w zasadzie nie musi zostać włączona, gdyż dla panelu dotykowego będę wykorzystał funkcje z BSP, a tam jej włączenie zostało już zdefiniowane. Ale nie zaszkodzi go uruchomić:


Wykorzystywany układ touchpada wykorzystuje siedmio-bitowy adres do rozpoczęcia komunikacji. Pozwala na wygenerowanie przerwań oraz na reset. Jego adres wynosi 112 dziesiętnie. Co daje konfiguracje:


Uruchomienie DMA dla I2C:

Kolejnym etapem konfiguracji jest DMA dla I2C. Tak jak powyżej, jest właściwie niepotrzebne.


Ustawienie CRC:


CRC (Cyclic Redundancy Check) czyli cykliczny kod nadmiarowy musi zostać włączony dla biblioteki STemWin. Należy go uruchomić w kodzie zanim biblioteka rozpocznie swoje działanie. Pozwala na wykrywanie błędów jakie mogą się pojawić podczas przesyłania błędów.

Uruchomienie w programie polega na wywołaniu wygenerowanej funkcji, po czym następuje włączenie zegara dla CRC. Dopiero po tym można włączyć bibliotekę STemWin.

Jedyne wyjaśnienie jakie udało mi się znaleźć odnośnie tego bloku jest takie, że biblioteka sprawdza czy CRC jest włączone zawsze przed swoją inicjalizacją. Dzięki temu wiadomo czy jest ona wykorzystywana przez odpowiedni procesor czyli STM'a a nie jakiś zupełnie inny układ.

Włączenie go polega na wykonaniu tych samych operacji co dla DMA2D.



Ustawienie TIM6:

On będzie potrzebny do obsługi touchpada, tak aby nie był on wykonywany w pętli while programu głównego.

Jego włączenie oraz ustawienie zostało przedstawione na obrazku poniżej:



W wyniku końcowym po wygenerowaniu kodu należy jeszcze do niego dopisać jedną linijkę odpowiedzialną za jego włączenie i rozpoczęcie zliczania. Całe włączenie wygląda następująco:

  1. static void MX_TIM6_Init(void)
  2. {
  3.   TIM_MasterConfigTypeDef sMasterConfig;
  4.   htim6.Instance = TIM6;
  5.   htim6.Init.Prescaler = 10000;
  6.   htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  7.   htim6.Init.Period = 400;
  8.   if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  9.   {
  10.       Error_Handler();
  11.   }
  12.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
  13.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  14.   if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  15.   {
  16.     Error_Handler();
  17.   }
  18.   HAL_TIM_Base_Start_IT(&htim6);
  19. }

Inicjalizację najlepiej zrobić na samym końcu po włączeniu reszty, ponieważ może przeszkadzać przy inicjalizacji niektórych bibliotek. Druga opcja to włączenie go wcześniej ale wyłączenie możliwości generowania przerwań na ten czas.

Konfiguracja Zegarów:

Poniżej przedstawiam screena z przygotowaną konfiguracją zegara:

Projekt Ac6:


Ta część została poświęcona na przygotowanie projektu w środowisku System Workbench Ac6. Na podstawie wygenerowanego projektu w CubeMx oraz bibliotek BSP i STemWin. Tych drugich nie trzeba dołączać do wykonywania podstawowych operacji na wyświetlaczu, jednak dołączę je już teraz, ponieważ będą one opisywane w drugiej części.

Całość po wygenerowaniu i dołączeniu projektu do środowiska System Workbench należy dołączyć wymagane biblioteki. Poniżej znajduje się ich lista wraz z informacją skąd je pobrać z przykładów od ST. Link do ich ściągnięcia znajduje się na samej górze strony.

Do projektu trzeba dołożyć pliki takie jak BSP, znajdują się w folderze Drivers pobranych przykładów od ST. Należy skopiować pliki odpowiedzialne za ogólne ustawienia, wyświetlacza, panel dotykowy oraz pamięć RAM. Struktura w projekcie:

  • Drivers
    • BSP:
      • inc
        • stm32746g_discovery_lcd.h
        • stm32746g_discovery_sdram.h
        • stm32746g_discovery_ts.h
        • stm32746g_discovery.h
      • src
        • stm32746g_discovery_lcd.c
        • stm32746g_discovery_sdram.c
        • stm32746g_discovery_ts.c
        • stm32746g_discovery.c
To są właściwie wszystkie elementy potrzebne do obsługi wyświetlacza oraz panelu dotykowego bez biblioteki STemWin. Do niej należy dołożyć trochę więcej plików:

  • Inc
    • GUI.h
    • GUIConf.h
    • LCDConf.h
  • Src
    • GUI_X.c
    • GUIConf.c
    • LCDConf.c
  • STEMWIN
    • inc (pliki z folderu: ...\Middlewares\ST\STemWin\inc)
    • lib
      • STemWin532_CM7_GCC.a

Ostatnim elementem są czcionki:

  • Drivers
    • Utilities
      • Fonts (tutaj cala zawartość folderu \Utilities\Fonts z pliku od ST. )

Do tego należy dołożyć te pliki oraz biblioteki tak żeby program wiedział że zostały dodane do projektu:


Oczywiście napewno pojawią się różnego rodzaju błędy podczas kompilacji. Czasem może nie widzieć dołączonych plików wtedy należy je wprowadzić bezpośrednio do folderów Inc oraz Src. Dodatkowo napewno pojawią się różnego rodzaju błędy dotyczące wielokrotnej definicji niektórych plików. W takim wypadku  trzeba nie które z nich zakomentować lub usunąć. Jednym z takich przypadków będą funkcje:

void HAL_DMA2D_MspInit(DMA2D_HandleTypeDef* hdma2d)
void HAL_DMA2D_MspDeInit(DMA2D_HandleTypeDef* hdma2d)
void HAL_LTDC_MspInit(LTDC_HandleTypeDef* hltdc)
void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef* hltdc)

które należy zostawić w pliku stm32f7xx_hal_msp.c, a zakomentować w LCDConf.c.

Kolejne błędy jakie mogą się pojawić będą w bibliotece, objawiać się to będzie brakiem definicji niektórych struktur, rozwiązuje się to poprzez dodanie odpowiednich plików nagłówkowych. Dobrze zapamiętać jakich, ponieważ każde wygenerowanie ponownie pliku poprzez CubeMx spowoduje usunięcie danych które nie będą wprowadzone w przygotowanych przez program komentarzach z miejscem na kod użytkownika.

Do obsługi wyświetlacza wykorzystuje się cały szereg funkcji przygotowanych przez ST w plikach BSP. Ich składnie można podglądnąć w plikach .c natomiast sam wykaz funkcji znajduje się w pliku nagłówkowym, tam także widnieje opis kolorów, pinów itp w postaci nagłówków #define.

Poniżej cała lista dostępnych funkcji:

  1. uint8_t  BSP_LCD_Init(void);
  2. uint8_t  BSP_LCD_DeInit(void);
  3. uint32_t BSP_LCD_GetXSize(void);
  4. uint32_t BSP_LCD_GetYSize(void);
  5. void     BSP_LCD_SetXSize(uint32_t imageWidthPixels);
  6. void     BSP_LCD_SetYSize(uint32_t imageHeightPixels);
  7. /* Functions using the LTDC controller */
  8. void     BSP_LCD_LayerDefaultInit(uint16_t LayerIndex, uint32_t FrameBuffer);
  9. void     BSP_LCD_LayerRgb565Init(uint16_t LayerIndex, uint32_t FB_Address);
  10. void     BSP_LCD_SetTransparency(uint32_t LayerIndex, uint8_t Transparency);
  11. void     BSP_LCD_SetTransparency_NoReload(uint32_t LayerIndex, uint8_t Transparency);
  12. void     BSP_LCD_SetLayerAddress(uint32_t LayerIndex, uint32_t Address);
  13. void     BSP_LCD_SetLayerAddress_NoReload(uint32_t LayerIndex, uint32_t Address);
  14. void     BSP_LCD_SetColorKeying(uint32_t LayerIndex, uint32_t RGBValue);
  15. void     BSP_LCD_SetColorKeying_NoReload(uint32_t LayerIndex, uint32_t RGBValue);
  16. void     BSP_LCD_ResetColorKeying(uint32_t LayerIndex);
  17. void     BSP_LCD_ResetColorKeying_NoReload(uint32_t LayerIndex);
  18. void     BSP_LCD_SetLayerWindow(uint16_t LayerIndex, uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height);
  19. void     BSP_LCD_SetLayerWindow_NoReload(uint16_t LayerIndex, uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height);
  20. void     BSP_LCD_SelectLayer(uint32_t LayerIndex);
  21. void     BSP_LCD_SetLayerVisible(uint32_t LayerIndex, FunctionalState State);
  22. void     BSP_LCD_SetLayerVisible_NoReload(uint32_t LayerIndex, FunctionalState State);
  23. void     BSP_LCD_Reload(uint32_t ReloadType);
  24. void     BSP_LCD_SetTextColor(uint32_t Color);
  25. uint32_t BSP_LCD_GetTextColor(void);
  26. void     BSP_LCD_SetBackColor(uint32_t Color);
  27. uint32_t BSP_LCD_GetBackColor(void);
  28. void     BSP_LCD_SetFont(sFONT *fonts);
  29. sFONT    *BSP_LCD_GetFont(void);
  30. uint32_t BSP_LCD_ReadPixel(uint16_t Xpos, uint16_t Ypos);
  31. void     BSP_LCD_DrawPixel(uint16_t Xpos, uint16_t Ypos, uint32_t pixel);
  32. void     BSP_LCD_Clear(uint32_t Color);
  33. void     BSP_LCD_ClearStringLine(uint32_t Line);
  34. void     BSP_LCD_DisplayStringAtLine(uint16_t Line, uint8_t *ptr);
  35. void     BSP_LCD_DisplayStringAt(uint16_t Xpos, uint16_t Ypos, uint8_t *Text, Text_AlignModeTypdef Mode);
  36. void     BSP_LCD_DisplayChar(uint16_t Xpos, uint16_t Ypos, uint8_t Ascii);
  37. void     BSP_LCD_DrawHLine(uint16_t Xpos, uint16_t Ypos, uint16_t Length);
  38. void     BSP_LCD_DrawVLine(uint16_t Xpos, uint16_t Ypos, uint16_t Length);
  39. void     BSP_LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
  40. void     BSP_LCD_DrawRect(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height);
  41. void     BSP_LCD_DrawCircle(uint16_t Xpos, uint16_t Ypos, uint16_t Radius);
  42. void     BSP_LCD_DrawPolygon(pPoint Points, uint16_t PointCount);
  43. void     BSP_LCD_DrawEllipse(int Xpos, int Ypos, int XRadius, int YRadius);
  44. void     BSP_LCD_DrawBitmap(uint32_t Xpos, uint32_t Ypos, uint8_t *pbmp);
  45. void     BSP_LCD_FillRect(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height);
  46. void     BSP_LCD_FillCircle(uint16_t Xpos, uint16_t Ypos, uint16_t Radius);
  47. void     BSP_LCD_FillPolygon(pPoint Points, uint16_t PointCount);
  48. void     BSP_LCD_FillEllipse(int Xpos, int Ypos, int XRadius, int YRadius);
  49. void     BSP_LCD_DisplayOff(void);
  50. void     BSP_LCD_DisplayOn(void);
  51. /* These functions can be modified in case the current settings
  52.    need to be changed for specific application needs */
  53. void     BSP_LCD_MspInit(LTDC_HandleTypeDef *hltdc, void *Params);
  54. void     BSP_LCD_MspDeInit(LTDC_HandleTypeDef *hltdc, void *Params);
  55. void     BSP_LCD_ClockConfig(LTDC_HandleTypeDef *hltdc, void *Params);

Jedną z ważniejszych funkcji w tym zestawieniu jest funkcja uruchamiająca sam wyświetlacz prezentuje się ona następująco. Została ona przezemnie odrobinę przerobiona tak aby obsługiwała sterownik przedstawiony w programie CubeMx.

  1. uint8_t BSP_LCD_Init(void)
  2. {
  3.        LTDC_LayerCfgTypeDef pLayerCfg;
  4.        LTDC_LayerCfgTypeDef pLayerCfg1;
  5.  
  6.        hLtdcHandler.Instance = LTDC;
  7.        hLtdcHandler.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  8.        hLtdcHandler.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  9.        hLtdcHandler.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  10.        hLtdcHandler.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  11.        hLtdcHandler.Init.HorizontalSync = 0;
  12.        hLtdcHandler.Init.VerticalSync = 0;
  13.        hLtdcHandler.Init.AccumulatedHBP = 43;
  14.        hLtdcHandler.Init.AccumulatedVBP = 12;
  15.        hLtdcHandler.Init.AccumulatedActiveW = 523;
  16.        hLtdcHandler.Init.AccumulatedActiveH = 284;
  17.        hLtdcHandler.Init.TotalWidth = 531;
  18.        hLtdcHandler.Init.TotalHeigh = 288;
  19.        hLtdcHandler.Init.Backcolor.Blue = 0;
  20.        hLtdcHandler.Init.Backcolor.Green = 0;
  21.        hLtdcHandler.Init.Backcolor.Red = 0;
  22.  
  23.        if (HAL_LTDC_Init(&hLtdcHandler) != HAL_OK)
  24.        {
  25.            Error_Handler();
  26.        }
  27.  
  28.        //Tlo domyslne
  29.        hLtdcHandler.Init.Backcolor.Blue = 0;
  30.        hLtdcHandler.Init.Backcolor.Green = 0;
  31.        hLtdcHandler.Init.Backcolor.Red = 0;
  32.  
  33.        pLayerCfg.WindowX0 = 0;
  34.        pLayerCfg.WindowX1 = 0;
  35.        pLayerCfg.WindowY0 = 0;
  36.        pLayerCfg.WindowY1 = 0;
  37.        pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;
  38.        pLayerCfg.Alpha = 0;
  39.        pLayerCfg.Alpha0 = 0;
  40.        pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  41.        pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  42.        pLayerCfg.FBStartAdress = 0;
  43.        pLayerCfg.ImageWidth = 0;
  44.        pLayerCfg.ImageHeight = 0;
  45.        pLayerCfg.Backcolor.Blue = 0;
  46.        pLayerCfg.Backcolor.Green = 0;
  47.        pLayerCfg.Backcolor.Red = 0;
  48.        if (HAL_LTDC_ConfigLayer(&hLtdcHandler, &pLayerCfg, 0) != HAL_OK)
  49.        {
  50.            Error_Handler();
  51.        }
  52.  
  53.        pLayerCfg1.WindowX0 = 0;
  54.        pLayerCfg1.WindowX1 = 0;
  55.        pLayerCfg1.WindowY0 = 0;
  56.        pLayerCfg1.WindowY1 = 0;
  57.        pLayerCfg1.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;
  58.        pLayerCfg1.Alpha = 0;
  59.        pLayerCfg1.Alpha0 = 0;
  60.        pLayerCfg1.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  61.        pLayerCfg1.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  62.        pLayerCfg1.FBStartAdress = 0;
  63.        pLayerCfg1.ImageWidth = 0;
  64.        pLayerCfg1.ImageHeight = 0;
  65.        pLayerCfg1.Backcolor.Blue = 0;
  66.        pLayerCfg1.Backcolor.Green = 0;
  67.        pLayerCfg1.Backcolor.Red = 0;
  68.        if (HAL_LTDC_ConfigLayer(&hLtdcHandler, &pLayerCfg1, 1) != HAL_OK)
  69.        {
  70.            Error_Handler();
  71.        }
  72.  
  73.        /* Initialize the LCD pixel width and pixel height */
  74.        hLtdcHandler.LayerCfg->ImageWidth  = RK043FN48H_WIDTH;
  75.        hLtdcHandler.LayerCfg->ImageHeight = RK043FN48H_HEIGHT;
  76.  
  77.        /* Assert display enable LCD_DISP pin */
  78.        HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_SET);
  79.  
  80.        /* Assert backlight LCD_BL_CTRL pin */
  81.        HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_SET);
  82.  
  83.         #if !defined(DATA_IN_ExtSDRAM)
  84.            BSP_SDRAM_Init();
  85.         #endif
  86.  
  87.         BSP_LCD_SetFont(&LCD_DEFAULT_FONT);
  88.  
  89.         return LCD_OK;
  90. }

Jej zadaniem jest ustawienie i przygotowanie sterownika

Poniżej podstawowy program pozwalający na wygenerowanie przykładowego obrazu na wyświetlaczu.

  1. /* USER CODE BEGIN PV */
  2. /* Private variables ---------------------------------------------------------*/
  3. TS_StateTypeDef ts;
  4. uint8_t  status = 0;
  5. /* USER CODE END PV */
  6. //...
  7. //...
  8. //...
  9. char data[50];
  10. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  11. {
  12.     if(status == TS_OK)
  13.     {
  14.         BSP_TS_GetState(&ts);
  15.         if (ts.touchDetected)   //Touchpad wcisniety
  16.         {
  17.             TS_State.Pressed = ts.touchDetected;
  18.             TS_State.y = ts.touchY[0];
  19.             TS_State.x = ts.touchX[0];
  20.            
  21.             sprintf(data, "%u, %u", TS_State.y, TS_State.x); //Pozycja ostatniego wcisniecia
  22.             //Wyczyszczenie ekranu w miejscach zmian tekstu
  23.             BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
  24.             BSP_LCD_FillRect(10, 250, 200, 100);
  25.             BSP_LCD_FillRect(10, 200, 100, 100);
  26.             BSP_LCD_SetTextColor(LCD_COLOR_RED);            //Zmiana koloru czcionki
  27.             BSP_LCD_FillRect(TS_State.x, TS_State.y, 5, 5); //Dodaje opcje rysowania po ekranie
  28.             BSP_LCD_SetFont(&Font16);                       //Zmiana czcionki
  29.             //Wyswietlenie danych
  30.             BSP_LCD_DisplayStringAt(10, 200, (uint8_t*)data, LEFT_MODE);
  31.             BSP_LCD_DisplayStringAt(10, 250, (uint8_t*) "Wcisniety", LEFT_MODE);
  32.            
  33.             //Obsługa przykładowego przycisku
  34.             if(TS_State.y >= 80 && TS_State.y<=100 && TS_State.x >= 80 && TS_State.x <= 100)
  35.             {
  36.                 BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
  37.                 BSP_LCD_FillRect(200, 20, 280, 252);
  38.                 BSP_LCD_SetTextColor(LCD_COLOR_RED);
  39.             }
  40.         }
  41.         else
  42.         {
  43.             TS_State.Pressed = 0;
  44.             TS_State.y = -1;
  45.             TS_State.x = -1;
  46.             BSP_LCD_SetFont(&Font16);
  47.             BSP_LCD_DisplayStringAt(10, 250, (uint8_t*) "Nie wcisniety", LEFT_MODE);
  48.         }
  49.     }
  50. }
  51. int main(void)
  52. {
  53.  /* Enable I-Cache-------------------------------------------------------------*/
  54.   SCB_EnableICache();
  55.   /* Enable D-Cache-------------------------------------------------------------*/
  56.   SCB_EnableDCache();
  57.   /* MCU Configuration----------------------------------------------------------*/
  58.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  59.   HAL_Init();
  60.   /* Configure the system clock */
  61.   SystemClock_Config();
  62.   /* Initialize all configured peripherals */
  63.   MX_GPIO_Init();
  64.   MX_DMA_Init();
  65.   MX_CRC_Init();
  66.   MX_DMA2D_Init();
  67.   MX_FMC_Init();
  68.   MX_I2C3_Init();
  69.   MX_LTDC_Init();   //Wlasciwie zbedne funckja BSP_LCD_Init robi to samo
  70.   MX_USART6_UART_Init();
  71.   MX_USB_DEVICE_Init();
  72.   /* USER CODE BEGIN 2 */
  73.   status = BSP_TS_Init(480, 272);
  74.   BSP_SDRAM_Init(); //Wlasciwie zbedne funckja BSP_LCD_Init robi to samo
  75.    
  76.   BSP_LCD_Init();
  77.   BSP_LCD_LayerDefaultInit(0, (uint32_t)0xC0000000);
  78.   BSP_LCD_DisplayOn();
  79.   BSP_LCD_SelectLayer(0);
  80.   BSP_LCD_Clear(LCD_COLOR_BLUE);
  81.   BSP_LCD_SetTextColor(LCD_COLOR_RED);
  82.   BSP_LCD_SetFont(&Font12);
  83.   BSP_LCD_DisplayStringAt(0, 0, (uint8_t*) "Wyswietlacz przyklad", LEFT_MODE);
  84.   BSP_LCD_DisplayStringAt(0, 0, (uint8_t*) "Wysrodkowane", CENTER_MODE);
  85.   BSP_LCD_SetBackColor(LCD_COLOR_BLUE);
  86.   BSP_LCD_SetTextColor(LCD_COLOR_YELLOW);
  87.   BSP_LCD_DisplayChar(100, 20, 'X');
  88.   BSP_LCD_DrawCircle(80, 80, 20);
  89.   BSP_LCD_DrawEllipse(150, 80, 30, 40);
  90.   MX_TIM6_Init();
  91.   while (1)
  92.   {
  93.   /* USER CODE END WHILE */
  94.   /* USER CODE BEGIN 3 */
  95.   /* USER CODE END 3 */
  96.   }

Obsługę przycisku można wygenerować albo w pętli while albo w nadpisanej funkcji HAL_TIM_PeriodElapsedCallback. Co przedstawiłem powyżej.

Jeśli chodzi o sposób wygenerowania sterownika to aby funckja działała bez przeszkód z układem zdefiniowanym w BSP należałoby posłużyć sie tam zdefioniowanym handlerem (LTDC_HandleTypeDef  hLtdcHandler;) i przekazać go w definicji jako zewnętrzny do głównej funkcji w programie, no i oczywiście dodać w inicjalizacji w pętli main ustawienie wartości pikseli w osiach x i y oraz ustawienie pinów podświetlania i włączenia wyświetlacza w stan HIGH.

W kolejnym poście chciałbym opisać bibliotekę STemWin oraz touchpad w bibliotece STemWin.