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);            
}




