wtorek, 8 sierpnia 2017

[19] STM32F7 - Discovery - Przygotowanie wyświetlacza oraz panelu dotykowego w oparciu o CubeMx

Ten post chciałbym poświęcić na przygotowanie wyświetlacza na płytce discovery. Program będę przygotowywał w oparciu o biblioteki HAL'a bez bibliotek BSP. Przedstawię tutaj funkcje graficzne, służące do rysowania elementów na ekranie oraz wypisywanie tekstu. Przygotowane funkcje, w pewnej części będą się opierać na bibliotece udostępnionej przez ST.

Link do pobrania niezbędnych elementów znajduje się na dole strony.

[http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]

Cube Mx:


Jak zwykle rozpocznę od przygotowania niezbędnych elementów za pomocą środowiska CubeMx.

Iinimalizacja jest standardowa i bardzo podobna do tej przedstawionej w poprzednich postach do wyświetlacza.


Konfiguaracja LTDC:


Włączenie DMA2D:


Ustawienie kontrolera FMC:


Program został wygenerowany dla środowiska Keil, natomiast nie ma problemu aby go wykorzystać np. AC6 pod Eclipsem.

Kod programu dla wyświetlacza oraz pamięci SDRAM:


Do wygenerowanego projektu należy dołożyć kilka elementów.

Na samym początku należy pamiętać aby uruchomić układ pamięci RAM czyli MT48LC4M32B2 od Microna.

Procedura inicjalizacyjna wygenerowana przez program CubeMx wygląda następująco:

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

Kolejna funkcja jest odpowiedzialna za procedurę startową pamięci, gdzie należy przesłać do pamięci komendę konfiguracji zegara, zamknięcie wierszy w podanym banku pamięci (precharge all), włączenie trybu automatycznego odświeżania, ustawienia trybów pracy oraz wybrania szybkości odświeżania.

  1. uint8_t MT48LC4M32B2_SDRAM_Initial(SDRAM_HandleTypeDef *hsdram)
  2. {
  3.     __IO uint32_t tmpmrd=0;
  4.     /* Configure a clock configuration enable command */
  5.     command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
  6.     command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  7.     command.AutoRefreshNumber = 1;
  8.     command.ModeRegisterDefinition = 0;
  9.     halStatus = HAL_SDRAM_SendCommand(hsdram,&command,SDRAM_TIMEOUT);
  10.     if(halStatus != HAL_OK) { return 0x00; }
  11.     /* Delay at least 100us */
  12.     HAL_Delay(1);
  13.     /* Configure a PALL (precharge all) command */
  14.     command.CommandMode = FMC_SDRAM_CMD_PALL;
  15.     command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  16.     command.AutoRefreshNumber = 1;
  17.     command.ModeRegisterDefinition = 0;
  18.     halStatus = HAL_SDRAM_SendCommand(hsdram,&command,SDRAM_TIMEOUT);
  19.     if(halStatus != HAL_OK) { return 0x00; }
  20.     /* Configure an Auto Refresh command */
  21.     command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
  22.     command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  23.     command.AutoRefreshNumber = 8;
  24.     command.ModeRegisterDefinition = 0;
  25.     halStatus = HAL_SDRAM_SendCommand(hsdram,&command,SDRAM_TIMEOUT);
  26.     if(halStatus != HAL_OK) { return 0x00; }
  27.     /* Configure an Auto Refresh command */
  28.     tmpmrd = (uint32_t) SDRAM_MODEREG_BURST_LENGTH_1 |
  29.                         SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
  30.                         SDRAM_MODEREG_CAS_LATENCY_2 |
  31.                         SDRAM_MODEREG_OPERATING_MODE_STANDARD |
  32.                         SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
  33.     command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
  34.     command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  35.     command.AutoRefreshNumber = 1;
  36.     command.ModeRegisterDefinition = tmpmrd;
  37.     halStatus = HAL_SDRAM_SendCommand(hsdram,&command,SDRAM_TIMEOUT);
  38.     if(halStatus != HAL_OK) { return 0x00; }
  39.     /* Set the device refresh rate */
  40.     HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
  41.     return 1;
  42. }

Kolejnym elementem jest inicjalizacja wyświetlacza za pomocą funkcji wygenerowanych przez program CubeMx.

  1.   MX_DMA2D_Init();
  2.   MX_FMC_Init();
  3.   MX_I2C3_Init();
  4.   MX_LTDC_Init();
  5.   MX_SDMMC1_SD_Init();
  6.   MX_FATFS_Init();

Prace z wyświetlaczem należy rozpocząć od ustawienia bufora w pamięci RAM, gdzie dane będą przerzucane.

  1.   HAL_LTDC_SetAddress(&hltdc,(uint32_t)0xC0000000,0);

Dalej wykorzystuje się przygotowane funkcje które pozwolą na rysowanie obiektów na ekranie, wypisanie tekstu itp. W bibliotece są jeszcze zdefiniowane funkcje dla zestawu kolorów RGB565 natomiast wymagają one zmiany koloru podczas inicjalizacji. Poniżej znajdują się funkcje które wykorzystują DMA2D.

Wypełnienie ekranu podanym kolorem:

  1. void ROCKTECH_FillScreen_DMA2D(uint32_t color)
  2. {
  3.   hdma2d.Init.Mode = DMA2D_R2M; /* DMA2D register to memory transfer mode */
  4.   hdma2d.Init.OutputOffset = 0; /* Min_Data = 0x0000 and Max_Data = 0x3FFF */
  5.   if((HAL_DMA2D_Init(&hdma2d) == HAL_OK) &&
  6.      (HAL_DMA2D_Start(&hdma2d, color, hltdc.LayerCfg[0].FBStartAdress,hltdc.LayerCfg[0].ImageWidth,
  7.                                                              hltdc.LayerCfg[0].ImageHeight) == HAL_OK))
  8.   {
  9.       /*  Polling for transfer complete or CLUT loading. */
  10.       HAL_DMA2D_PollForTransfer(&hdma2d, 10);
  11.   }
  12. }

Rysowanie piksela na ekranie:

  1. void ROCKTECH_DrawPixel_DMA2D(uint16_t Xpos, uint16_t Ypos, uint32_t color)
  2. {
  3.     *(volatile uint32_t*) (hltdc.LayerCfg[0].FBStartAdress +
  4.         (4*(Ypos*hltdc.LayerCfg[0].ImageWidth + Xpos))) = color;
  5. }

Wypełnienie ekranu kolorem:

  1. void ROCKTECH_FillScreen_DMA2D(uint32_t color)
  2. {
  3.   hdma2d.Init.Mode = DMA2D_R2M; /* DMA2D register to memory transfer mode */
  4.   hdma2d.Init.OutputOffset = 0; /* Min_Data = 0x0000 and Max_Data = 0x3FFF */
  5.   if((HAL_DMA2D_Init(&hdma2d) == HAL_OK) &&
  6.      (HAL_DMA2D_Start(&hdma2d, color, hltdc.LayerCfg[0].FBStartAdress,hltdc.LayerCfg[0].ImageWidth,
  7.                                                              hltdc.LayerCfg[0].ImageHeight) == HAL_OK))
  8.   {
  9.       /*  Polling for transfer complete or CLUT loading. */
  10.       HAL_DMA2D_PollForTransfer(&hdma2d, 10);
  11.   }
  12. }

Rysowanie linii:

  1. void ROCKTECH_DrawLine_DMA2D(uint16_t start_x, uint16_t start_y,
  2.         uint16_t end_x, uint16_t end_y, uint32_t color)
  3. {
  4.     int dx=0;
  5.     int dy=0;
  6.     int err=0;
  7.     int ystep;
  8.     int steep = abs(end_y-start_y)>abs(end_x-start_x);
  9.     if (steep){
  10.         SWAPVARIABLES(start_x,start_y);
  11.         SWAPVARIABLES(end_x,end_y);
  12.     }
  13.     if(start_x>end_x){
  14.         SWAPVARIABLES(start_x,end_x);
  15.         SWAPVARIABLES(start_y,end_y);
  16.     }
  17.     dx=end_x-start_x;
  18.     dy=abs(end_y-start_y);
  19.     err=dx/2;
  20.     if(start_y<end_y){
  21.         ystep = 1;
  22.     }
  23.     else{
  24.         ystep = -1;
  25.     }
  26.     for (;start_x<=end_x;start_x++)
  27.     {
  28.         if (steep){
  29.             ROCKTECH_DrawPixel_DMA2D(start_y,start_x,color);
  30.         }
  31.         else{
  32.             ROCKTECH_DrawPixel_DMA2D(start_x,start_y,color);
  33.         }
  34.         err-=dy;
  35.         if (err<0)
  36.         {
  37.             end_y += ystep;
  38.             err+=dx;
  39.         }
  40.     }
  41. }

Rysowanie prostokąta z wypełnieniem:

  1. void ROCKTECH_FillRectangle_DMA2D(uint16_t start_x, uint16_t start_y,
  2.                     uint16_t end_x, uint16_t end_y, uint32_t color)
  3. {
  4.   uint32_t addr = 0;
  5.   if(start_x>end_x){ SWAPVARIABLES(start_x,end_x); }
  6.   if(start_y>end_y){ SWAPVARIABLES(start_y,end_y); }
  7.   addr = (hltdc.LayerCfg[0].FBStartAdress) + 4*(start_y*hltdc.LayerCfg[0].ImageWidth + start_x);
  8.   hdma2d.Init.Mode = DMA2D_R2M;
  9.   hdma2d.Init.OutputOffset = hltdc.LayerCfg[0].ImageWidth-(end_x-start_x);
  10.   if((HAL_DMA2D_Init(&hdma2d) == HAL_OK) &&
  11.      (HAL_DMA2D_Start(&hdma2d, color, addr, end_x-start_x, end_y-start_y) == HAL_OK))
  12.   {
  13.       HAL_DMA2D_PollForTransfer(&hdma2d, 10);
  14.   }
  15. }

Rysowanie pustego prostokąta:

  1. void ROCKTECH_DrawClearRectangle(uint16_t start_x, uint16_t start_y,
  2.                                  uint16_t end_x, uint16_t end_y, uint16_t color)
  3. {
  4.     ROCKTECH_DrawLine_DMA2D(start_x,start_y,end_x,start_y,color);
  5.     ROCKTECH_DrawLine_DMA2D(end_x, start_y, end_x, end_y, color);
  6.     ROCKTECH_DrawLine_DMA2D(start_x, start_y, start_x, end_y, color);
  7.     ROCKTECH_DrawLine_DMA2D(start_x, end_y, end_x, end_y, color);
  8. }

Rysowanie pustego koła:


  1. void ROCKTECH_DrawCircle_DMA2D(uint16_t x_start, uint16_t y_start, int Rad, uint32_t color)
  2. {
  3.     int f = 1-Rad;
  4.     int ddF_x=1;
  5.     int ddF_y=-2*Rad;
  6.     int x = 0;
  7.     int y = Rad;
  8.     ROCKTECH_DrawPixel_DMA2D(x_start,y_start+Rad,color);
  9.     ROCKTECH_DrawPixel_DMA2D(x_start,y_start-Rad,color);
  10.     ROCKTECH_DrawPixel_DMA2D(x_start+Rad,y_start,color);
  11.     ROCKTECH_DrawPixel_DMA2D(x_start-Rad,y_start,color);
  12.     while (x<y)
  13.     {
  14.         if (f>=0)
  15.         {
  16.             y--;
  17.             ddF_y+=2;
  18.             f+=ddF_y;
  19.         }
  20.         x++;
  21.         ddF_x+=2;
  22.         f+=ddF_x;
  23.         ROCKTECH_DrawPixel_DMA2D(x_start+x,y_start+y,color);
  24.         ROCKTECH_DrawPixel_DMA2D(x_start-x,y_start+y,color);
  25.         ROCKTECH_DrawPixel_DMA2D(x_start+x,y_start-y,color);
  26.         ROCKTECH_DrawPixel_DMA2D(x_start-x,y_start-y,color);
  27.         ROCKTECH_DrawPixel_DMA2D(x_start+y,y_start+x,color);
  28.         ROCKTECH_DrawPixel_DMA2D(x_start-y,y_start+x,color);
  29.         ROCKTECH_DrawPixel_DMA2D(x_start+y,y_start-x,color);
  30.         ROCKTECH_DrawPixel_DMA2D(x_start-y,y_start-x,color);
  31.   }
  32. }

Rysowanie koła z wypełnieniem:


  1. void ROCKTECH_FillCircle_DMA2D(uint16_t x_start, uint16_t y_start, uint16_t Rad, uint32_t color)
  2. {
  3.   int32_t   change_pos;     /* Decision Variable */
  4.   uint32_t  x_current_pos;   /* Current X Value */
  5.   uint32_t  y_current_pos;   /* Current Y Value */
  6.   change_pos = 3 - (Rad << 1);
  7.   x_current_pos = 0;
  8.   y_current_pos = Rad;
  9.   while (x_current_pos <= y_current_pos)
  10.   {
  11.     if(y_current_pos > 0)
  12.     {
  13.         ROCKTECH_DrawLine_DMA2D((x_start - y_current_pos),
  14.                                 (y_start + x_current_pos),
  15.                                 (x_start - y_current_pos) + 2*y_current_pos,
  16.                                 (y_start + x_current_pos),
  17.                                 color);
  18.         ROCKTECH_DrawLine_DMA2D((x_start - y_current_pos),
  19.                                 (y_start - x_current_pos),
  20.                                 (x_start - y_current_pos) + 2*y_current_pos,
  21.                                 (y_start - x_current_pos),
  22.                                 color);
  23.     }
  24.     if(x_current_pos > 0)
  25.     {
  26.         ROCKTECH_DrawLine_DMA2D((x_start - x_current_pos),
  27.                                 (y_start - y_current_pos),
  28.                                 (x_start - x_current_pos) + 2*x_current_pos,
  29.                                 (y_start - y_current_pos),
  30.                                 color);
  31.         ROCKTECH_DrawLine_DMA2D((x_start - x_current_pos),
  32.                                 (y_start + y_current_pos),
  33.                                 (x_start - x_current_pos) + 2*x_current_pos,
  34.                                 (y_start + y_current_pos),
  35.                                 color);
  36.     }
  37.     if (change_pos < 0)
  38.     {
  39.         change_pos += (x_current_pos << 2) + 6;
  40.     }
  41.     else
  42.     {
  43.         change_pos += ((x_current_pos - y_current_pos) << 2) + 10;
  44.         y_current_pos--;
  45.     }
  46.     x_current_pos++;
  47.   }
  48.   ROCKTECH_DrawCircle_DMA2D(x_start, y_start, Rad, color);
  49. }

Rysowanie bitmapy:

  1. void ROCKTECH_DrawBitmap_DMA2D(uint32_t xpositon, uint32_t yposition, uint8_t *bmp_picture)
  2. {
  3.     uint32_t index=0;
  4.     uint32_t width=0;
  5.     uint32_t height=0;
  6.     uint32_t bit_pixel=0;
  7.     uint32_t address;
  8.    
  9.     /* Get bitmap data addres offset*/
  10.     index = *(__IO uint16_t *) (bmp_picture+10);
  11.     index |= (*(__IO uint16_t *) (bmp_picture+12)) << 16;
  12.    
  13.     /* Read bitmap width */
  14.     width = *(__IO uint16_t *) (bmp_picture+18);
  15.     width |= (*(__IO uint16_t *) (bmp_picture+20)) << 16;
  16.    
  17.     /* Read bitmap height */
  18.     height = *(__IO uint16_t *) (bmp_picture+22);
  19.     height |= (*(__IO uint16_t *) (bmp_picture+24)) << 16;
  20.    
  21.     /* Read bit/pixel */
  22.     bit_pixel = *(__IO uint16_t *) (bmp_picture+28);
  23.    
  24.     /* Set the addres*/
  25.     address = hltdc.LayerCfg[0].FBStartAdress+(((X_AXIS_SIZE*yposition) + xpositon)*4);
  26.    
  27.     /* Bypass the bitmap header */
  28.     bmp_picture += (index + (width * (height-1) * (bit_pixel/8)));
  29.     /* Get the layer pixel format */    
  30.     if((bit_pixel/8) == 4)      {   ROCKTECH_FillScreen_DMA2D(0xFFFF0000);  }   //ARGB8888
  31.     else if((bit_pixel/8) == 2) {   ROCKTECH_FillScreen_DMA2D(0xFF00FF00);  }   //RGB565
  32.     else                                        //if RGB888 then convert it into ARGB8888
  33.     {
  34.         for(index=0; index < height; index++)           /* Convert picture to ARGB8888 pixel format */
  35.         {
  36.             hdma2d.Init.Mode = DMA2D_M2M_PFC;
  37.             hdma2d.Init.ColorMode = DMA2D_ARGB8888;
  38.             hdma2d.Init.OutputOffset = 0;
  39.             hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
  40.             hdma2d.LayerCfg[1].InputAlpha = 0xFF;
  41.             hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB888;
  42.             hdma2d.LayerCfg[1].InputOffset = 0;
  43.             if(HAL_DMA2D_Init(&hdma2d) == HAL_OK){
  44.                 if(HAL_DMA2D_ConfigLayer(&hdma2d, 1) == HAL_OK){
  45.                     if(HAL_DMA2D_Start(&hdma2d, (uint32_t) bmp_picture, address, width, 1) == HAL_OK){
  46.                         HAL_DMA2D_PollForTransfer(&hdma2d, 10);
  47.                     }
  48.                 }
  49.             }
  50.             address += X_AXIS_SIZE*4;
  51.             bmp_picture -= width*(bit_pixel/8);
  52.         }
  53.     }
  54.     hdma2d.Init.Mode = DMA2D_M2M_BLEND;
  55.     hdma2d.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
  56.     hdma2d.Init.OutputOffset = 0;
  57.     hdma2d.LayerCfg[1].InputOffset = 0;
  58.     hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
  59.     hdma2d.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
  60.     hdma2d.LayerCfg[1].InputAlpha = 0;
  61.     hdma2d.LayerCfg[0].InputOffset = 0;
  62.     hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888;
  63.     hdma2d.LayerCfg[0].AlphaMode = DMA2D_REPLACE_ALPHA;
  64.     hdma2d.LayerCfg[0].InputAlpha = 0;
  65.     if (HAL_DMA2D_Init(&hdma2d) == HAL_OK)
  66.     {
  67.         HAL_DMA2D_ConfigLayer(&hdma2d, 0);
  68.         HAL_DMA2D_ConfigLayer(&hdma2d, 1);
  69.     }
  70.     bit_pixel = 0;
  71. }

Rysowanie elipsy:

  1. void ROCKTECH_LCD_DrawEllipse(int x_start, int y_start, int x_Rad, int y_Rad, uint32_t color)
  2. {
  3.   int x = 0;
  4.   int y = -y_Rad;
  5.   int err = 2-2*x_Rad;
  6.   int e2 = 0;
  7.   float k = 0;
  8.   float rad1 = 0;
  9.   float rad2 = 0;
  10.   rad1 = x_Rad;
  11.   rad2 = y_Rad;
  12.   k = (float)(rad2/rad1);
  13.   do {
  14.       ROCKTECH_DrawPixel_DMA2D((x_start-(uint16_t)(x/k)), (y_start+y), color);
  15.       ROCKTECH_DrawPixel_DMA2D((x_start+(uint16_t)(x/k)), (y_start+y), color);
  16.       ROCKTECH_DrawPixel_DMA2D((x_start+(uint16_t)(x/k)), (y_start-y), color);
  17.       ROCKTECH_DrawPixel_DMA2D((x_start-(uint16_t)(x/k)), (y_start-y), color);
  18.     e2 = err;
  19.     if (e2 <= x) {
  20.       err += ++x*2+1;
  21.       if (-== x && e2 <= y) e2 = 0;
  22.     }
  23.     if (e2 > y) err += ++y*2+1;
  24.   }
  25.   while (<= 0);
  26. }

Rysowanie elipsy z wypełnieniem:

  1. void ROCKTECH_LCD_FillEllipse_DMA2D(int x_start, int y_start, int x_Rad, int y_Rad, uint32_t color)
  2. {
  3.     int x = 0, y = -y_Rad, err = 2-2*x_Rad, e2;
  4.     float k = 0, rad1 = 0, rad2 = 0;
  5.     rad1 = x_Rad;
  6.     rad2 = y_Rad;
  7.     k = (float)(rad2/rad1);
  8.     do{
  9.         ROCKTECH_DrawLine_DMA2D((x_start-(uint16_t)(x/k)),
  10.                                 (y_start+y),
  11.                                 (x_start-(uint16_t)(x/k)) + (2*(uint16_t)(x/k) + 1),
  12.                                 (y_start+y),
  13.                                 color);
  14.         ROCKTECH_DrawLine_DMA2D((x_start-(uint16_t)(x/k)),
  15.                                 (y_start-y),
  16.                                 (x_start-(uint16_t)(x/k)) + (2*(uint16_t)(x/k) + 1),
  17.                                 (y_start-y),
  18.                                 color);
  19.         e2 = err;
  20.         if (e2 <= x)
  21.         {
  22.             err += ++x*2+1;
  23.             if (-== x && e2 <= y) e2 = 0;
  24.         }
  25.         if (e2 > y) err += ++y*2+1;
  26.     }while (<= 0);
  27. }

Wypisanie znaku:

  1. void ROCKTECH_DrawChar(uint16_t xposition, uint16_t yposition, const uint8_t c)
  2. {
  3.   uint32_t i = 0, j = 0;
  4.   uint16_t height, width;
  5.   uint8_t offset;
  6.   uint8_t *pchar;
  7.   uint32_t line;
  8.   uint8_t loop = 127;
  9.   /* 127 - Ó - (uint8_t)co odpowiada 147 - (uint16_t)50067
  10.    * 128 - ó - (uint8_t)co odpowiada 179 - (uint16_t)50099
  11.    * 129 - Ą - (uint8_t)co odpowiada 132 - (uint16_t)50308
  12.    * 130 - ą - (uint8_t)co odpowiada 133 - (uint16_t)50309
  13.    * 131 - Ć - (uint8_t)co odpowiada 134 - (uint16_t)50310
  14.    * 132 - ć - (uint8_t)co odpowiada 135 - (uint16_t)50311
  15.    * 133 - Ę - (uint8_t)co odpowiada 152 - (uint16_t)50328
  16.    * 134 - ę - (uint8_t)co odpowiada 153 - (uint16_t)50329
  17.    * 135 - Ł - (uint8_t)co odpowiada 129 - (uint16_t)50561
  18.    * 136 - ł - (uint8_t)co odpowiada 130 - (uint16_t)50562
  19.    * 137 - Ń - (uint8_t)co odpowiada 131 - (uint16_t)50563
  20.    * 138 - ń - (uint8_t)co odpowiada 132 - (uint16_t)50564
  21.    * 139 - Ś - (uint8_t)co odpowiada 154 - (uint16_t)50586
  22.    * 140 - ś - (uint8_t)co odpowiada 155 - (uint16_t)50587
  23.    * 141 - Ź - (uint8_t)co odpowiada 185 - (uint16_t)50617
  24.    * 142 - ź - (uint8_t)co odpowiada 186 - (uint16_t)50618
  25.    * 143 - Ż - (uint8_t)co odpowiada 187 - (uint16_t)50619
  26.    * 144 - ż - (uint8_t)co odpowiada 188 - (uint16_t)50620
  27.    */
  28.   uint16_t PolishSignTable16b[18] = {
  29.     50067, 50099, 50308, 50309, 50310,
  30.     50311, 50328, 50329, 50561, 50562,
  31.     50563, 50564, 50586, 50587, 50617,
  32.     50618, 50619, 50620
  33.   };
  34.   uint16_t PolishSignTable8b[18] = {
  35.     147, 179, 132, 133, 134,
  36.     135, 152, 153, 129, 130,
  37.     131, 132, 154, 155, 185,
  38.     186, 187, 188
  39.   };
  40.   if((< 127 || c > 144) && c < 50067){
  41.       ch = &lcdprop.pFont->table[(- ' ') * lcdprop.pFont->Height * ((lcdprop.pFont->Width + 7) / 8)];
  42.       loop = 145;
  43.   }
  44.   for(uint8_t i = 0, loop = 127; loop < 145; i++, loop++)
  45.   {
  46.      if(== PolishSignTable16b[i])
  47.      {
  48.           ch = &lcdprop.pFont->table[(loop - ' ') * lcdprop.pFont->Height * ((lcdprop.pFont->Width + 7) / 8)];
  49.           break;
  50.      }
  51.      else if((uint8_t)== PolishSignTable8b[i])
  52.      {
  53.          if(== 2) { }
  54.          else{
  55.              ch = &lcdprop.pFont->table[(loop - ' ') * lcdprop.pFont->Height * ((lcdprop.pFont->Width + 7) / 8)];
  56.              break;
  57.          }
  58.       }
  59.   }
  60.   height = lcdprop.pFont->Height;
  61.   width = lcdprop.pFont->Width;
  62.   offset = 8 *((width + 7)/8) - width;
  63.   for(= 0; i < height; i++)
  64.   {
  65.         pchar = ((uint8_t *)ch + (width + 7)/8 * i);
  66.         switch((width + 7)/8)
  67.         {
  68.             case 1:
  69.                 line = pchar[0];
  70.             break;
  71.             case 2:
  72.                 line = (pchar[0]<< 8) | pchar[1];
  73.             break;
  74.             case 3:
  75.             default:
  76.                 line = (pchar[0]<< 16) | (pchar[1]<< 8) | pchar[2];
  77.             break;
  78.         }
  79.         for (= 0; j < width; j++)
  80.         {
  81.             if(line & (1 << (width- j + offset- 1)))
  82.             {
  83.                 ROCKTECH_DrawPixel((xposition + j), yposition, lcdprop.TextColor);
  84.             }
  85.             else
  86.             {
  87.                 ROCKTECH_DrawPixel((xposition + j), yposition, lcdprop.BackColor);
  88.             }
  89.         }
  90.         yposition++;
  91.   }
  92. }

Ta funkcja wymaga odrobiny wyjaśnienia. Na początku sprawdza czy podany znak jest znakiem polskim ( o sposobie generowania polskiej czcionki na dole strony ). Jeśli nie jest to rozpoczyna wypisanie znaku standardowego. Natomiast jeśli jest to znak z zakresu znaków polskich to przeszukuje i ustawia odpowiednią pozycję w tablicy z wygenerowaną czcionką.

Wypisanie ciągu znaków:

  1. void ROCKTECH_DisplayString(uint16_t xposition, uint16_t yposition, uint8_t *Text, Text_AlignModeTypdef Mode)
  2. {
  3.   uint16_t ref_column = 1;
  4.   uint16_t i = 0;
  5.   uint32_t size = 0;
  6.   uint32_t xsize = 0;
  7.   uint8_t *ptr = Text;
  8.   /* Calculate text size */
  9.   while(*ptr++) { size++;   }
  10.   xsize = (X_AXIS_SIZE/lcdprop.pFont->Width);
  11.   /* Set position on the screen */
  12.   switch(Mode)
  13.   {
  14.       case CENTER_MODE:{
  15.           ref_column = xposition + ((xsize - size) * lcdprop.pFont->Width) / 2;
  16.           break;
  17.       }
  18.       case LEFT_MODE:{
  19.           ref_column = xposition;
  20.           break;
  21.       }
  22.       case RIGHT_MODE:{
  23.           ref_column = - xposition + ((xsize - size) * lcdprop.pFont->Width);
  24.           break;
  25.       }
  26.       default:{
  27.           ref_column = xposition;
  28.           break;
  29.       }
  30.   }
  31.   if ((ref_column < 1) || (ref_column >= 0x8000))   {   ref_column = 1; }
  32.   while ((*Text != '\n') & (((X_AXIS_SIZE - (i*lcdprop.pFont->Width)) & 0xFFFF) >= lcdprop.pFont->Width))
  33.   {
  34.     ROCKTECH_DrawChar(ref_column, yposition, *Text++);
  35.     ref_column += lcdprop.pFont->Width;
  36.     i++;
  37.   }

Rysowanie bitmapy wykonuje się dwiema funkcjami. Najpierw należy ją otworzyć i załadować do pamięci, po czym z tej pamięci obraz jest ładowany na ekran:

  1. uint32_t OpenBMP(uint8_t *ptr, const char* fname)
  2. {
  3.     uint32_t ind=0,sz=0,i1=0,ind1=0;
  4.     static uint32_t bmp_addr;
  5.     if(f_open(&MyFile,fname,FA_READ)!=FR_OK)
  6.     {
  7.           ROCKTECH_FillScreen_DMA2D(LCD_COLOR_RED);
  8.     }
  9.     else
  10.     {
  11.         if(f_read(&MyFile,sect,30,(UINT *)&bytesread)!=FR_OK)
  12.         {
  13.             Error_Handler();
  14.         }
  15.         else
  16.         {
  17.             bmp_addr=(uint32_t)sect;
  18.             /*Get bitmap size*/
  19.             sz=*(uint16_t*)(bmp_addr + 2);
  20.             sz|=(*(uint16_t*)(bmp_addr + 4))<<16;
  21.             /*Get bitmap data address offset*/
  22.             ind=*(uint16_t*)(bmp_addr + 10);
  23.             ind|=(*(uint16_t*)(bmp_addr + 12))<<16;
  24.             f_close(&MyFile);
  25.             f_open(&MyFile,fname,FA_READ);
  26.             ind=0;
  27.             do
  28.             {
  29.                 if(sz<512)
  30.                 {
  31.                     i1=sz;
  32.                 }
  33.                 else
  34.                 {
  35.                     i1=512;
  36.                 }
  37.                 sz-=i1;
  38.                 f_lseek(&MyFile,ind1);
  39.                 f_read(&MyFile,sect,i1,(UINT *)&bytesread);
  40.                 memcpy((void*)(bmp1+ind1),(void*)sect,i1);
  41.                 ind1+=i1;
  42.             }
  43.             while(sz>0);
  44.             f_close(&MyFile);
  45.         }
  46.         ind1=0;
  47.     }
  48.     return 0;
  49. }

  1. void ROCKTECH_DrawBitmap_DMA2D(uint32_t xpositon, uint32_t yposition, uint8_t *bmp_picture)
  2. {
  3.     uint32_t index=0;
  4.     uint32_t width=0;
  5.     uint32_t height=0;
  6.     uint32_t bit_pixel=0;
  7.     uint32_t address;
  8.     /* Get bitmap data addres offset*/
  9.     index = *(__IO uint16_t *) (bmp_picture+10);
  10.     index |= (*(__IO uint16_t *) (bmp_picture+12)) << 16;
  11.     /* Read bitmap width */
  12.     width = *(__IO uint16_t *) (bmp_picture+18);
  13.     width |= (*(__IO uint16_t *) (bmp_picture+20)) << 16;
  14.     /* Read bitmap height */
  15.     height = *(__IO uint16_t *) (bmp_picture+22);
  16.     height |= (*(__IO uint16_t *) (bmp_picture+24)) << 16;
  17.     /* Read bit/pixel */
  18.     bit_pixel = *(__IO uint16_t *) (bmp_picture+28);
  19.     /* Set the addres*/
  20.     address = hltdc.LayerCfg[0].FBStartAdress+(((X_AXIS_SIZE*yposition) + xpositon)*4);
  21.     /* Bypass the bitmap header */
  22.     bmp_picture += (index + (width * (height-1) * (bit_pixel/8)));
  23.     /* Get the layer pixel format */
  24.     if((bit_pixel/8) == 4)      {   ROCKTECH_FillScreen_DMA2D(0xFFFF0000);  }   //ARGB8888
  25.     else if((bit_pixel/8) == 2) {   ROCKTECH_FillScreen_DMA2D(0xFF00FF00);  }   //RGB565
  26.     else                                        //if RGB888 then convert it into ARGB8888
  27.     {
  28.         for(index=0; index < height; index++)           /* Convert picture to ARGB8888 pixel format */
  29.         {
  30.             hdma2d.Init.Mode = DMA2D_M2M_PFC;
  31.             hdma2d.Init.ColorMode = DMA2D_ARGB8888;
  32.             hdma2d.Init.OutputOffset = 0;
  33.             hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
  34.             hdma2d.LayerCfg[1].InputAlpha = 0xFF;
  35.             hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB888;
  36.             hdma2d.LayerCfg[1].InputOffset = 0;
  37.             if(HAL_DMA2D_Init(&hdma2d) == HAL_OK){
  38.                 if(HAL_DMA2D_ConfigLayer(&hdma2d, 1) == HAL_OK){
  39.                     if(HAL_DMA2D_Start(&hdma2d, (uint32_t) bmp_picture, address, width, 1) == HAL_OK){
  40.                         HAL_DMA2D_PollForTransfer(&hdma2d, 10);
  41.                     }
  42.                 }
  43.             }
  44.             address += X_AXIS_SIZE*4;
  45.             bmp_picture -= width*(bit_pixel/8);
  46.         }
  47.     }
  48.     hdma2d.Init.Mode = DMA2D_M2M_BLEND;
  49.     hdma2d.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
  50.     hdma2d.Init.OutputOffset = 0;
  51.     hdma2d.LayerCfg[1].InputOffset = 0;
  52.     hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
  53.     hdma2d.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
  54.     hdma2d.LayerCfg[1].InputAlpha = 0;
  55.     hdma2d.LayerCfg[0].InputOffset = 0;
  56.     hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888;
  57.     hdma2d.LayerCfg[0].AlphaMode = DMA2D_REPLACE_ALPHA;
  58.     hdma2d.LayerCfg[0].InputAlpha = 0;
  59.     if (HAL_DMA2D_Init(&hdma2d) == HAL_OK)
  60.     {
  61.         HAL_DMA2D_ConfigLayer(&hdma2d, 0);
  62.         HAL_DMA2D_ConfigLayer(&hdma2d, 1);
  63.     }
  64.     bit_pixel = 0;
  65. }

Pozostałe funkcje są opisane w bezpośrednio w bibliotece.

Kod programu dla panelu dotykowego:

Panel dotykowy obsługiwany jest w przerwaniu od timera. Dzięki temu uzyskuje się płynniejsze działanie dotyku na wyświetlaczu.

Funkcje zostały przygotowane w oparciu o przykłady udostępnione przez firmę ST.

Inicjalizacja panelu dotykowego wygląda następująco:

  1. void Touch_FT5336_Initialization(void)
  2. {
  3.     uint8_t regValue = 0;
  4.     HAL_Delay(200);
  5.     if(Touch_ReadID(TS_I2C_ADDRESS) != FT5336_ID_VALUE){
  6.         Error();
  7.     }
  8.     tsOrientation = TS_SWAP_XY;
  9.     regValue = (FT5336_G_MODE_INTERRUPT_POLLING & (FT5336_G_MODE_INTERRUPT_MASK >> FT5336_G_MODE_INTERRUPT_SHIFT))
  10.                 << FT5336_G_MODE_INTERRUPT_SHIFT;
  11.     TS_IO_Write(TS_I2C_ADDRESS, FT5336_GMODE_REG, regValue);
  12.     ft5336_handle.i2cInitialized = FT5336_I2C_INITIALIZED;
  13. }



Obsługa dotyku odbywa się, jak już wspomniałem wcześniej, w przerwaniu od timera. Do testów można ją spokojnie wywołać w pętli while:

  1. /* Timer callback function */
  2. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  3. {
  4.     static uint16_t x=0;
  5.     static uint16_t y=0;
  6.     char str1[20];
  7.     if(CheckForUserInput() == 0)
  8.     {
  9.         TS_GetState(&TS_State);
  10.         if(TS_State.touchDetected) /* TS_State.touchDetected >= 1 */
  11.         {
  12.             if((!= TS_State.touchX[0]) && (!= TS_State.touchY[0]))
  13.             {
  14.                 x = TS_State.touchX[0];
  15.                 y = TS_State.touchY[0];
  16.                 /* Display cords on screen */
  17.                 ROCKTECH_SetFont(&Font12);
  18.                 ROCKTECH_SetTextColor(LCD_COLOR_WHITE);
  19.                 ROCKTECH_SetBackColor(LCD_BACKGROUND_DEFAULT);
  20.                 sprintf(str1,"1: x=%03d; y=%03d\n",x,y);
  21.                 ROCKTECH_DisplayString(0, 240, (uint8_t *)str1, LEFT_MODE, 1);
  22.             }
  23.         }
  24.     }
  25. }

Jeśli chce się wykorzystywać multitouch wtedy obsługa wygląda następująco:

  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  2. {
  3.     static uint16_t x=0;
  4.     static uint16_t y=0;
  5.     char str1[20];
  6.     static uint32_t tscnt[5]={0};
  7.     static uint16_t xstart[5]={0}, ystart[5]={0};
  8.    
  9.     while (CheckForUserInput()==0)
  10.     {
  11.         TS_GetState(&TS_State);
  12.         if(TS_State.touchDetected)
  13.         {
  14.             x = TS_State.touchX[0];
  15.             y = TS_State.touchY[0];
  16.             /* Display cords on screen */
  17.             ROCKTECH_SetFont(&Font12);
  18.             ROCKTECH_SetTextColor(LCD_COLOR_WHITE);
  19.             ROCKTECH_SetBackColor(LCD_BACKGROUND_DEFAULT);
  20.             sprintf(str1,"1: x=%03d; y=%03d\n",x,y);
  21.             ROCKTECH_DisplayString(0, 150, (uint8_t *)str1, LEFT_MODE, 1);
  22.             xstart[0]=x; ystart[0]=y;
  23.             tscnt[0]++;
  24.             if (TS_State.touchDetected >= 2)
  25.             {
  26.                 x = TS_State.touchX[1];
  27.                 y = TS_State.touchY[1];
  28.                 xstart[1]=x; ystart[1]=y;
  29.                 tscnt[1]++;
  30.                 sprintf(str1,"2: x=%03d; y=%03d\n",x,y);
  31.                 ROCKTECH_DisplayString(0, 180, (uint8_t *)str1, LEFT_MODE, 1);
  32.             }
  33.             if (TS_State.touchDetected >= 3)
  34.             {
  35.                 x = TS_State.touchX[2];
  36.                 y = TS_State.touchY[2];
  37.                 xstart[2]=x; ystart[2]=y;
  38.                 tscnt[2]++;
  39.                 sprintf(str1,"3: x=%03d; y=%03d\n",x,y);
  40.                 ROCKTECH_DisplayString(0, 210, (uint8_t *)str1, LEFT_MODE, 1);
  41.             }
  42.             if (TS_State.touchDetected >= 4)
  43.             {
  44.                 x = TS_State.touchX[3];
  45.                 y = TS_State.touchY[3];
  46.                 xstart[3]=x; ystart[3]=y;
  47.                 tscnt[3]++;
  48.                 sprintf(str1,"4: x=%03d; y=%03d\n",x,y);
  49.                 ROCKTECH_DisplayString(0, 230, (uint8_t *)str1, LEFT_MODE, 1);
  50.             }
  51.             if (TS_State.touchDetected >= 5)
  52.             {
  53.                 x = TS_State.touchX[4];
  54.                 y = TS_State.touchY[4];
  55.                 xstart[4]=x; ystart[4]=y;
  56.                 tscnt[4]++;
  57.                 sprintf(str1,"5: x=%03d; y=%03d\n",x,y);
  58.                 ROCKTECH_DisplayString(0, 250, (uint8_t *)str1, LEFT_MODE, 1);
  59.             }
  60.         }
  61.         else
  62.         {
  63.             tscnt[0]=0;tscnt[1]=0;tscnt[2]=0;tscnt[3]=0;tscnt[4]=0;
  64.         }
  65.     }
  66. }

W przypadku chęci wykorzystania przycisków można to zrobić w sposób następujący:

  1. /* Define button */
  2. typedef struct btn{
  3.     const uint16_t buttonNumber;            /*  ID, for button */
  4.     ButtonClickedHandler HandlerBtn;        /*  pointer to a handler function called when button is pressed */
  5.     const uint16_t positionTop;             /*  top edge of the button (in pixels, counted from top) x0*/
  6.     const uint16_t positionBot;             /*  bottom edge of the button (in pixels, counted from top) y0 */
  7.     const uint16_t positionLeft;            /*  left edge of the button (in pixels, counted from left) */
  8.     const uint16_t positionRight;           /*  right edge of the button (in pixels, counted from left) */
  9.     const sFONT * pfont;                    /*  font */
  10.     ButtonState_Typedef status_enable;      /*  block/unblocks button Handler function */
  11. } Button_Typedef;
  12. /* Example initialization */
  13. static Button_Typedef exampleBtn = {
  14.         .buttonNumber= 0x01,
  15.         .HandlerBtn= exampleBtnClickedHandler,
  16.         .positionTop= 0,        /* Top position y */
  17.         .positionBot= 50,       /* Bottm position y */
  18.         .positionLeft= 60,      /* Left position x */
  19.         .positionRight= 100,    /* Right position x*/
  20.         .pfont = &Font_ConsolasPL_18ptsBold,
  21.         .status_enable= ENABLED
  22. };
  23. static Button_Typedef * tpButtons[BTN_COUNT] = {
  24.         &entryBtn
  25. };
  26. /* Timer interrupt */
  27. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  28. {
  29.     static uint16_t x=0;
  30.     static uint16_t y=0;
  31.     char str1[20];
  32.     Button_Typedef * btn = NULL;
  33.     if(CheckForUserInput() == 0)
  34.     {
  35.         TS_GetState(&TS_State);
  36.         if(TS_State.touchDetected)
  37.         {
  38.             if((!= TS_State.touchX[0]) && (!= TS_State.touchY[0]))
  39.             {
  40.                 x = TS_State.touchX[0];
  41.                 y = TS_State.touchY[0];
  42.                 for(uint8_t i = 0; i < BTN_COUNT; i++)
  43.                 {
  44.                     if( (tpButtons[i]->status_enable== ENABLED) &&
  45.                         (< tpButtons[i]->positionRight) && (> tpButtons[i]->positionLeft) &&
  46.                         (> tpButtons[i]->positionTop) && (< tpButtons[i]->positionBot))
  47.                     {
  48.                         btn = tpButtons[i];
  49.                     }
  50.                 }
  51.                 if(btn != NULL) {
  52.                     countDelay = 0;
  53.                     btn->Handler(btn->buttonId);
  54.                 }
  55.                 ROCKTECH_SetFont(&Font12);
  56.                 ROCKTECH_SetTextColor(LCD_COLOR_WHITE);
  57.                 ROCKTECH_SetBackColor(LCD_BACKGROUND_DEFAULT);
  58.                 sprintf(str1,"1: x=%03d; y=%03d\n",x,y);
  59.                 ROCKTECH_DisplayString(0, 240, (uint8_t *)str1, LEFT_MODE, 1);
  60.             }
  61.         }
  62.     }
  63. }

Przygotowanie polskiej czcionki

Możliwe jest także wygenerowanie polskiej czcionki za pomocą programu Dot Factory (ja wykorzystywałem program w wersji 0.14).

Po włączeniu programu należy po lewej stronie wpisać następującą sekwencję znaków znajdującą się na obrazku poniżej.

Znaki polskie zostaną przerzucone na koniec pliku, z pominięciem znaków specjalnych, których nie definiowałem w czcionce.


W celu wygenerowania należy wybrać czcionkę:


Przed generacją należy przygotować ustawienia:



Aby czcionka poprawnie działała w całym projekcie to należy ustawić kodowanie dla projektu na UTF-8. Wykonuje się to poprzez wejście w Window -> Preferences -> Workspace i tam w groupboxie Text file encoding należy wybrać Other i UTF8. Jeśli nie wprowadzono żadnych zmian w projekcie to będzie on to kodowanie dziedziczył. Aby się upewnić należy kliknąć prawym przyciskiem myszy na projekt po czym wybrać Properties. Tam w zakładce Resource znajduje się wybór sposobu kodowania.

Po wygenerowaniu kodu dla czcionki należy dodać na początku danych w tablicy znak spacji. Jego rozmiar będzie zależny od rozmiaru czcionki. Poniżej przedstawiam przykładowe rozwiązanie:

  1. const uint8_t Font_ConsolasPL_34pts_Bold[] =
  2. {
  3.         /* ' ' (18 pixels wide) */
  4.         0x00, 0x00, 0x00, //
  5.         0x00, 0x00, 0x00, //
  6.         0x00, 0x00, 0x00, //
  7.         0x00, 0x00, 0x00, //
  8.         0x00, 0x00, 0x00, //      
  9.         0x00, 0x00, 0x00, //      
  10.         0x00, 0x00, 0x00, //        
  11.         0x00, 0x00, 0x00, //        
  12.         0x00, 0x00, 0x00, //        
  13.         0x00, 0x00, 0x00, //        
  14.         0x00, 0x00, 0x00, //      
  15.         0x00, 0x00, 0x00, //      
  16.         0x00, 0x00, 0x00, //        
  17.         0x00, 0x00, 0x00, //      
  18.         0x00, 0x00, 0x00, //        
  19.         0x00, 0x00, 0x00, //        
  20.         0x00, 0x00, 0x00, //        
  21.         0x00, 0x00, 0x00, //        
  22.         0x00, 0x00, 0x00, //      
  23.         0x00, 0x00, 0x00, //        
  24.         0x00, 0x00, 0x00, //
  25.         0x00, 0x00, 0x00, //
  26.         0x00, 0x00, 0x00, //        
  27.         0x00, 0x00, 0x00, //      
  28.         0x00, 0x00, 0x00, //      
  29.         0x00, 0x00, 0x00, //      
  30.         0x00, 0x00, 0x00, //
  31.         0x00, 0x00, 0x00, //
  32.         0x00, 0x00, 0x00, //
  33.         0x00, 0x00, 0x00, //
  34.         0x00, 0x00, 0x00, //
  35.         0x00, 0x00, 0x00, //
  36.         //...
  37.         //...
  38.         //...
  39. }

Struktura z danymi dla czcionki znajduje się w pliku z zdefiniowaną czcionką. Dane do niej powinny być zawarte w pliku fonts.h oraz w bibliotece do wyświetlacza jako extern.

  1. typedef struct _tFont{
  2.   const uint8_t *table;
  3.   uint16_t Width;
  4.   uint16_t Height;
  5. } sFONT;
  6. /* Example declaration, it is keep in every font file */
  7. sFONT Font_ConsolasPL_34ptsBold = {
  8.   Font_ConsolasPL_34pts_Bold,
  9.   18, /* Width */
  10.   32, /* Height */
  11. };

Funkcje odczytujące dane z czcionek zostały zawarte w bibliotece dla wyświetlacza.

Wyrysowanie znaku oraz znalezienie polskiego znaku zostało przedstawione we wcześniejszej części posta.

Cały projekt dla wyświetlacza wraz z wygenerowanymi czcionkami można pobrać z dysku Google pod tym linkiem. Znajduje się tam folder z czcionkami przygotowanymi przez ST oraz wygenerowanymi w programie przedstawionym powyżej.