1.8 インチ TFT LCD シールド (3)

STmicroelectronics の STM32Cube では、MCU のファミリ別に HAL ライブラリが構成されています。
その中で F0 / F1 / F3 / F4 シリーズのライブラリしかダウンロードしていないのですが、いずれのライブラリにも Adafruit 1.8 インチ TFT LCD シールド用の BSP (Board Support Package) が含まれています。
各 HAL ライブラリは、STM32CubeMX アプリケーションの

Help / Updater Settings... / Repository Folder

で指定する「リポジトリ」のフォルダの下に格納されます。
STM32CubeF4 の V1.16.0 の場合を例に取ると、リポジトリ・フォルダの下に「STM32Cube_FW_F4_V1.16.0」というフォルダが作成され、その中に F4 用の HAL ライブラリが収められます。
その中から LCD に関係するソース・ファイルを抜き出すと下のようになります。

\Drivers\BSP\Adafruit_Shield\stm32_adafruit_lcd.c
\Drivers\BSP\Adafruit_Shield\stm32_adafruit_lcd.h
\Drivers\BSP\Components\Common\lcd.h
\Drivers\BSP\Components\st7735\st7735.c
\Drivers\BSP\Components\st7735\st7735.h
\Drivers\BSP\STM32F4xx-Nucleo\stm32f4xx_nucleo.h
\Drivers\BSP\STM32F4xx-Nucleo\stm32f4xx_nucleo.c

この中で、「stm32_adafruit_lcd.c / .h」が最も上位、つまりアプリケーションに最も近いレベルのレイヤーで、2D グラフィクスの「プリミティブ」として、下に示すような「BSP_LCD_」というプリフィクスで始まる関数が用意されています。

uint8_t  BSP_LCD_Init(void);
uint32_t BSP_LCD_GetXSize(void);
uint32_t BSP_LCD_GetYSize(void);
 
uint16_t BSP_LCD_GetTextColor(void);
uint16_t BSP_LCD_GetBackColor(void);
void     BSP_LCD_SetTextColor(__IO uint16_t Color);
void     BSP_LCD_SetBackColor(__IO uint16_t Color);
void     BSP_LCD_SetFont(sFONT *fonts);
sFONT    *BSP_LCD_GetFont(void);

void     BSP_LCD_Clear(uint16_t Color);
void     BSP_LCD_ClearStringLine(uint16_t Line);
void     BSP_LCD_DisplayStringAtLine(uint16_t Line,
                                     uint8_t *ptr);
void     BSP_LCD_DisplayStringAt(uint16_t Xpos,
                                 uint16_t Ypos, 
                                 uint8_t *Text,
                                 Line_ModeTypdef Mode);
void     BSP_LCD_DisplayChar(uint16_t Xpos, uint16_t Ypos, 
                             uint8_t Ascii);

void     BSP_LCD_DrawPixel(uint16_t Xpos, uint16_t Ypos,
                           uint16_t RGB_Code);
void     BSP_LCD_DrawHLine(uint16_t Xpos, uint16_t Ypos,
                           uint16_t Length);
void     BSP_LCD_DrawVLine(uint16_t Xpos, uint16_t Ypos,
                           uint16_t Length);
void     BSP_LCD_DrawLine(uint16_t x1, uint16_t y1,
                          uint16_t x2, uint16_t y2);
void     BSP_LCD_DrawRect(uint16_t Xpos, uint16_t Ypos,
                          uint16_t Width, uint16_t Height);
void     BSP_LCD_DrawCircle(uint16_t Xpos, uint16_t Ypos,
                            uint16_t Radius);
void     BSP_LCD_DrawPolygon(pPoint Points,
                             uint16_t PointCount);
void     BSP_LCD_DrawEllipse(int Xpos, int Ypos,
                             int XRadius, int YRadius);
void     BSP_LCD_DrawBitmap(uint16_t Xpos, uint16_t Ypos,
                            uint8_t *pBmp);
void     BSP_LCD_FillRect(uint16_t Xpos, uint16_t Ypos,
                          uint16_t Width, uint16_t Height);
void     BSP_LCD_FillCircle(uint16_t Xpos, uint16_t Ypos,
                            uint16_t Radius);
void     BSP_LCD_FillPolygon(pPoint Points,
                             uint16_t PointCount);
void     BSP_LCD_FillEllipse(int Xpos, int Ypos,
                             int XRadius, int YRadius);

void     BSP_LCD_DisplayOff(void);
void     BSP_LCD_DisplayOn(void);

フレーム・バッファからの読み出しを必須とはしていないので、多角形の輪郭線を描いて「内部」の点を指定し「塗りつぶす」ような使い方はできません。
「FillPolygon」関数は用意されていますが、描画の段階で多角形を 3 角形に分割し、塗りつぶされた 3 角形を描くことにより塗りつぶされた多角形を実現しています。
「stm32_adafruit_lcd.c」の中では、「グラフィクス・コントローラ・ドライバ」との結合は、下のような「関数へのポインタ」をメンバとする構造体を利用して行なっています。

typedef struct
{
  void     (*Init)(void);
  uint16_t (*ReadID)(void);
  void     (*DisplayOn)(void);
  void     (*DisplayOff)(void);
  void     (*SetCursor)(uint16_t, uint16_t);
  void     (*WritePixel)(uint16_t, uint16_t, uint16_t);
  uint16_t (*ReadPixel)(uint16_t, uint16_t);
  
   /* Optimized operation */
  void     (*SetDisplayWindow)(uint16_t, uint16_t, uint16_t, uint16_t);
  void     (*DrawHLine)(uint16_t, uint16_t, uint16_t, uint16_t);
  void     (*DrawVLine)(uint16_t, uint16_t, uint16_t, uint16_t);
  
  uint16_t (*GetLcdPixelWidth)(void);
  uint16_t (*GetLcdPixelHeight)(void);
  void     (*DrawBitmap)(uint16_t, uint16_t, uint8_t*);
  void     (*DrawRGBImage)(uint16_t, uint16_t, uint16_t, uint16_t, uint8_t*);
} LCD_DrvTypeDef;    

「stm32_adafruit_lcd.c」(および「st7735.h」) の中では、下に示すように外部シンボルとして「st7735_drv」を定義しており、「stm32_adafruit_lcd.c」のコンパイル時点では「st7735_drv」は「未定義」のまま処理されます。
「st7735_drv」は ST7735 ドライバである「st7735.c」の中で定義されており、最終的なリンクの時点で未定義参照が解決されます。

/* in "st7735.h" */

extern LCD_DrvTypeDef   st7735_drv;

/* in "stm32_adafruit_lcd.c" */

static LCD_DrvTypeDef  *lcd_drv; 

uint8_t BSP_LCD_Init(void)
{ 
  . . . . . <中略> . . . . .

  lcd_drv = &st7735_drv;

  /* LCD Init */   
  lcd_drv->Init();
  
  /* Clear the LCD screen */
  BSP_LCD_Clear(LCD_COLOR_WHITE);
  
  /* Initialize the font */
  BSP_LCD_SetFont(&LCD_DEFAULT_FONT);
 
  . . . . . <中略> . . . . .
}

LCD コントローラ・ドライバである「st7735.c」では、実際に LCD コントローラ・チップの st7735 へのコマンド/データ書き込みが必要ですが、「st7735.h」では下のように「LCD_IO_」プリフィクスから始まる関数を外部参照しており、「st7735.c」の内部では定義していません。

/* LCD IO functions */
void     LCD_IO_Init(void);
void     LCD_IO_WriteMultipleData(uint8_t *pData, uint32_t Size);
void     LCD_IO_WriteReg(uint8_t Reg);

この「LCD_IO_」プリフィクスを持つ関数は、各ボード固有の BSP 内で記述されており、Nucleo-F411RE の場合は、「stm32f4xx_nucleo.c」の中にあります。
したがって、ST7735 と MCU インターフェース仕様やコントローラ・コマンド体系が異なる LCD コントローラを利用するには、少なくとも

  • 「stm32f4xx_nucleo.c」内の「LCD_IO_」関数群の置き換え
  • 「st7735.c / .h」に代わる LCD コントローラ・ドライバの新規作成

が必要になります。
「stm32_adafruit_lcd.c」は上位のレイヤーなので、本来は変更の必要がないはずですが、下に示す 1 ヵ所だけ ST7735 依存の箇所があるので、そこだけは修正する必要があります。

  /* Remap Ypos, st7735 works with inverted X in case of bitmap */
  /* X = 0, cursor is on Top corner */
  if(lcd_drv == &st7735_drv)
  {
    Ypos = BSP_LCD_GetYSize() - Ypos - height;
  }