跳转到主要内容

MIPI-DSI命令模式

本节介绍如何为命令模式配置MIPI DSI接口,以及如何在TouchGFX生成器中使用此配置。 本文使用的示例将用于16位RGB565帧缓冲区格式,通常会在STM32CubeMX和代码示例中进行以下配置。

  • LTDC IP
  • DSI-HOST IP
  • TouchGFX Generator

STM32CubeMX - 硬件配置

LTDC

  • 模式
    • 显示类型设置为RGB565(16位)-DSI模式
  • 层设置
    • 层数设置为1层
    • 窗口位置帧缓冲区行长度以及帧缓冲区行数中设置屏幕分辨率
    • 设置0层-像素格式设置为RGB565
    • 设置0层- Alpha混合常数255

      LTDC 配置

  • NVIC 设置
    • LTDC全局中断LTDC全局错误中断均不需要,应禁用。

      LTDC NVIC 设置

DSIHOST 配置

  • 模式
    • 使用TE Pin将DSIHost设置为_*自适应命令模式**
  • 显示接口
    • 色彩格式设置为“RGB888(16位)- DSI模式”
    • 根据显示宽度设置Maximum Command Size
    • 通过启用LTDC将显示帧缓冲区的刷新设置为手动触发
    • 其余配置取决于所选的LCD HW

      DSIHOST 配置

  • NVIC 设置
    • 启用DSI全局中断

      DSIHOST NVIC 设置

STM32CubeMX - TouchGFX Generator

  • 模式
    • 启用图形应用程序
  • TouchGFX Generator
    • 显示/接口设置为并行RGB(LTDC),因为这仍是应用程序与之通信的控制器。
    • 应用计时源设置为自定义

      TouchGFX Generator配置

DSIHOST / LTDC初始化顺序

  • MX_DSIHOST_DSI_Init()的调用必须在MX_LTDC_Init()之前完成。 应由CubeMX来处理。

  • 用户必须在MX_LTDC_Init()函数的末尾添加特定于所用LCD控制器所需的初始化代码,该代码通常基于DSI HAL APIs 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 Command模式下,通过LTDC向显示中写入一个窗口来更新显示中的内部GRAM。 窗口的大小可设置为整个屏幕区域:

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时,该显示会发出Tearing Effect信号。 对于高度为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);
}