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
- 将
- NVIC 设置
LTDC全局中断
和LTDC全局错误中断
均不需要,应禁用。
DSIHOST 配置
- 模式
- 使用TE Pin将
DSIHost
设置为_*自适应命令模式**
- 使用TE Pin将
- 显示接口
- 将
色彩格式
设置为“RGB888(16位)- DSI模式” - 根据显示宽度设置
Maximum Command Size
- 通过启用LTDC将显示帧缓冲区的
刷新设置为手动触发
- 其余配置取决于所选的LCD HW
- 将
- NVIC 设置
- 启用
DSI全局中断
- 启用
STM32CubeMX - TouchGFX Generator
- 模式
- 启用图形应用程序
- TouchGFX Generator
- 将
显示/接口
设置为并行RGB(LTDC),因为这仍是应用程序与之通信的控制器。 - 将
应用计时源
设置为自定义
- 将
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);
}