MIPI-DSIコマンド・モード
このセクションでは、コマンド・モード用のMIPI DSIインタフェースの設定方法と、TouchGFX Generatorでこの設定を使用する方法について説明します。 この記事で使用する例は、16ビットRGB565のフレームバッファ・フォーマットに対応するもので、STM32CubeMXでは一般的に以下の設定で実行されます。また、コード例を示します。
- LTDC IP
- DSI-HOST IP
- TouchGFX Generator
STM32CubeMX - ハードウェア設定
LTDC
- モード
Display Type
をRGB565(16 bits)- DSI Modeに設定
- レイヤの設定
Number of layers
を1 layerに設定- スクリーン解像度を
Windows Position
、Frame Buffer Line Length
、およびFrame Buffer Number of Lines
に設定 Layer 0 - Pixel Format
をRGB565に設定Layer 0 - Alpha constant for blending
を255に設定
- NVICの設定
LTDC global interrupt
とLTDC global error interrupt
は両方とも不要なため、無効にする必要があります。
DSIHOSTの設定
- モード
DSIHost
をAdapted Command Mode with TE Pinに設定
- ディスプレイ・インタフェース
Color Coding
をRGB565(16 bits)- DSI modeに設定Maximum Command Size
をディスプレイ幅相当の数値に設定The Refresh of the Display Frame Buffer is Triggered
をmanually by Enabling the LTDCに設定- 残りの設定は選択したLCD HWに応じて異なる
- NVICの設定
DSI global interrupt
を有効にする
STM32CubeMX - TouchGFX Generator
- モード
- Graphics Applicationを有効にする
- TouchGFX Generator
Display / Interface
をParallel RGB(LTDC)に設定(これはアプリケーションが通信する必要があるコントローラであるため)Application Tick Source
をCustomに設定
DSIHOST / LTDCの初期化シーケンス
MX_DSIHOST_DSI_Init()
の呼出しは、MX_LTDC_Init()
の前に行う必要があります。 これはCubeMXで処理します。使用しているLCDコントローラに固有の必要な初期化コードを
MX_LTDC_Init()
関数の最後に追加する必要があります。これは通常、DSI HAL APIのHAL_DSI_ShortWrite()
およびHAL_DSI_LongWrite()
に基づいています。/**
* @brief LTDC Initialization Function
* @param None
* @retval None
*/
static void MX_LTDC_Init(void)
{
...
/* USER CODE BEGIN LTDC_Init 2 */
__HAL_LTDC_DISABLE(&hltdc);
HAL_DSI_Start(&hdsi);
// Specific LCD controller's initialization code
...
// Turn display off until first frame
HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_DISPLAY_OFF, 0x00);
__HAL_LTDC_ENABLE(&hltdc);
/* USER CODE END LTDC_Init 2 */
...
}
DSIコマンド・モード用に更新されたTouchGFXHALクラス
アプリケーションで最初のフレームの描画を完了するまでMIPI DSIディスプレイがオンになるのを防ぐ1つの方法は、関数TouchGFXHAL::endFrame
をガードして、TouchGFXが最初のフレームを描画するまでディスプレイをオフにしておくことです。 TouchGFXHAL::endFrame()
を以下のように更新すると、PWM出力用に設定されたHWタイマを通して、LCDとそのバックライトを有効にできます。
void TouchGFXHAL::endFrame()
{
if (!display_on)
{
display_on = true;
/* Enable the LCD, Send Display on DCS command to display */
HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_DISPLAY_ON, 0x00);
/* Start PWM Timer channel */
(void)HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
/* Enable Backlight by setting Brightness to 100% */
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, 2U * 100);
}
TouchGFXGeneratedHAL::endFrame();
}
DSIコマンド・モードでは、ディスプレイの内部GRAMは、LTDCを使用してウィンドウをそれに書き込むことにより更新されます。 ウィンドウのサイズはスクリーン領域全体に設定できます。
void LCD_SetUpdateRegion()
{
uint8_t pCols[4] = {0x00, 0x00, 0x01, 0xDF}; /* 0 -> 480 */
uint8_t pRows[4] = {0x00, 0x00, 0x03, 0x3F}; /* 0 -> 800 */
HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_CASET, pCols);
HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_PASET, pRows);
}
ディスプレイでは、GRAMのピクセル値に基づいてピクセルを設定することで、スクリーンが1行ずつ更新されます。 スクリーンでのティアリングを防ぐには、GRAMの各ピクセル値を更新してからスクリーンに描画する必要があります。 更新がスキャンラインに追いつけるかぎり、ディスプレイの更新中に値をGRAMに書き込むことができます。 GRAMの更新を安全に開始できる場合は、ディスプレイからティアリング効果信号が発せられます。 TE信号を有効にしてディスプレイの最終行に設定するコードは、高さが800ピクセルのスクリーンの場合、次のようになります。
/**
* Request TE at scanline 800.
*/
void LCD_ReqTear(void)
{
static uint8_t ScanLineParams[2];
uint16_t scanline = 800;
ScanLineParams[0] = scanline >> 8;
ScanLineParams[1] = scanline & 0x00FF;
HAL_DSI_LongWrite(&hdsi, LCD_OTM8009A_ID, DSI_DCS_LONG_PKT_WRITE, 2, 0x44, ScanLineParams);
// set_tear_on
HAL_DSI_ShortWrite(&hdsi, LCD_OTM8009A_ID, DSI_DCS_SHORT_PKT_WRITE_P1, 0x35, 0x00);
}
TE信号は割込みをトリガし、これはLTDC経由での転送の開始に使用されます。 転送が完了したら、別の割り込みがトリガされます。 この例では、ダブル・フレームバッファを使用して、一方のバッファに描画しながら、もう一方をGRAMに転送できるようにします。
void HAL_DSI_TearingEffectCallback(DSI_HandleTypeDef* hdsi)
{
GPIO::set(GPIO::VSYNC_FREQ);
HAL::getInstance()->vSync();
OSWrappers::signalVSync();
if (HAL::getInstance())
{
// Swap frame buffers immediately instead of waiting for the task to be scheduled in.
// Note: task will also swap when it wakes up, but that operation is guarded and will not have
// any effect if already swapped.
HAL::getInstance()->swapFrameBuffers();
}
// Transfer frame buffer data
HAL_DSI_Refresh(hdsi);
}
void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef* hdsi)
{
//Point LTDC base address to the new frame buffer:
__HAL_DSI_WRAPPER_DISABLE(hdsi);
LTDC_LAYER(&hltdc, 0)->CFBAR = ((uint32_t)currFbBase);
__HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(&hltdc);
__HAL_DSI_WRAPPER_ENABLE(hdsi);
GPIO::clear(GPIO::VSYNC_FREQ);
}