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
- 將
- NVIC設定
LTDC global interrupt
(LTDC全域中斷)及LTDC global error interrupt
(LTDC全域錯誤中斷)都不需要且應停用。
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硬體而定
- 將
- NVIC設定
- 啟用
DSI global interrupt
(DSI全域中斷)
- 啟用
STM32CubeMX - TouchGFX Generator
- 模式
- 啟用Graphics Application (圖形應用程式)
- TouchGFX Generator
- 將
Display / Interface
(顯示/介面)設定為Parallel RGB (LTDC)(平行RGB (LTDC)),因為這仍是應用程式需要通訊的控制器。 - 將
Application Tick Source
(應用時標源)設定為Custom (客製)
- 將
DSIHOST / LTDC初始化順序
對
MX_DSIHOST_DSI_Init()
的呼叫必須在MX_LTDC_Init()
之前完成, 並應由CubeMX處理。使用者必須將指定的必要初始化程式碼新增至使用的LCD控制器,位在
MX_LTDC_Init()
函數末端;這一般將依據DSI HAL APIHAL_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);
// 特定LCD控制器的初始化程式碼
...
// 在第一張畫面前關閉顯示器
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將視窗寫入至其中。 視窗尺寸可設定為整個螢幕區域:
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())
{
// 立刻交換影片緩衝區而不等待工作排程。
// 任務在喚醒時也會交換,但操作受到保護,如果已經交換,則不會產生任何影響。
HAL::getInstance()->swapFrameBuffers();
}
// 傳輸影片緩衝區資料
HAL_DSI_Refresh(hdsi);
}
void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef* hdsi)
{
// 將LTDC base address指向新的影片緩衝區:
__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);
}