跳轉到主要內容

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 (RGB565 (16位元) - DSI模式)
  • 層設定
    • Number of layers (層數)設定為1 layer (1層)
    • Windows Position (視窗位置)、Frame Buffer Line Length (影像緩衝區行長度)及Frame Buffer Number of Lines (影像緩衝區行數)設定螢幕解析度
    • Layer 0 - Pixel Format (第0層 - 像素格式)設定為RGB565
    • Layer 0 - Alpha constant for blending (第0層 - 用於混合的Alpha常數)設定為255

      LTDC設定

  • NVIC設定
    • LTDC global interrupt (LTDC全域中斷)及LTDC global error interrupt (LTDC全域錯誤中斷)都不需要且應停用。

      LTDC NVIC設定

DSIHOST設定

  • 模式
    • DSIHost設定為_*Adapted Command Mode with TE Pin**(適性指令模式搭配TE腳位)
  • 顯示介面
    • Color Coding (色彩編碼)設定為RGB565 (16 bits) - DSI mode (RGB565 (16位元) - DSI模式)
    • Maximum Command Size (最大指令大小)設定為顯示寬度順序的數字
    • The Refresh of the Display Frame Buffer is Triggered (觸發顯示影像緩衝區的刷新頻率)設定為manually by Enabling the LTDC (啟用LTDC手動進行)
    • 剩餘設定需視選取的LCD硬體而定

      DSIHOST設定

  • NVIC設定
    • 啟用DSI global interrupt (DSI全域中斷)

      DSIHOST NVIC設定

STM32CubeMX - TouchGFX Generator

  • 模式
    • 啟用Graphics Application (圖形應用程式)
  • TouchGFX Generator
    • Display / Interface (顯示/介面)設定為Parallel RGB (LTDC)(平行RGB (LTDC)),因為這仍是應用程式需要通訊的控制器。
    • Application Tick Source (應用時標源)設定為Custom (客製)

      TouchGFX Generator設定

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顯示在應用程式中渲染第一幀之前開啟的一種方法是保護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將視窗寫入至其中。 視窗尺寸可設定為整個螢幕區域:

  static void MX_LTDC_Init(void)
{
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中的像素值設定像素以逐行更新螢幕。 為了避免螢幕出現撕裂效應,GRAM的每個像素值都必須在顯示器將其繪製至螢幕前進行更新。 顯示器更新期間可將值寫入GRAM,只要更新速度能夠跟上掃描即可。 顯示器可安全更新GRAM時會發出撕裂效應訊號, 以下是在高度為800像素的螢幕情況下,啟用TE訊號且將其設定在顯示最後一行的程式碼:

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