2025-01-16 08:21 AM
I am attempting to display to an ILI9881C based panel. After successfully initialising and testing the panel using DSI only, I am working on DSI Video mode to pull data from a frame buffer via LTDC.
I have the DSI configured as follows
static void MX_DSIHOST_DSI_Init(void)
{
/* USER CODE BEGIN DSIHOST_Init 0 */
/* USER CODE END DSIHOST_Init 0 */
DSI_PLLInitTypeDef PLLInit = {0};
DSI_HOST_TimeoutTypeDef HostTimeouts = {0};
DSI_PHY_TimerTypeDef PhyTimings = {0};
DSI_VidCfgTypeDef VidCfg = {0};
/* USER CODE BEGIN DSIHOST_Init 1 */
/* USER CODE END DSIHOST_Init 1 */
hdsi.Instance = DSI;
hdsi.Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_ENABLE;
hdsi.Init.TXEscapeCkdiv = 4;
hdsi.Init.NumberOfLanes = DSI_TWO_DATA_LANES;
PLLInit.PLLNDIV = 119;
PLLInit.PLLIDF = DSI_PLL_IN_DIV3;
PLLInit.PLLODF = DSI_PLL_OUT_DIV2;
if (HAL_DSI_Init(&hdsi, &PLLInit) != HAL_OK)
{
Error_Handler();
}
HostTimeouts.TimeoutCkdiv = 1;
HostTimeouts.HighSpeedTransmissionTimeout = 0;
HostTimeouts.LowPowerReceptionTimeout = 0;
HostTimeouts.HighSpeedReadTimeout = 0;
HostTimeouts.LowPowerReadTimeout = 0;
HostTimeouts.HighSpeedWriteTimeout = 0;
HostTimeouts.HighSpeedWritePrespMode = DSI_HS_PM_DISABLE;
HostTimeouts.LowPowerWriteTimeout = 0;
HostTimeouts.BTATimeout = 0;
if (HAL_DSI_ConfigHostTimeouts(&hdsi, &HostTimeouts) != HAL_OK)
{
Error_Handler();
}
PhyTimings.ClockLaneHS2LPTime = 23;
PhyTimings.ClockLaneLP2HSTime = 33;
PhyTimings.DataLaneHS2LPTime = 14;
PhyTimings.DataLaneLP2HSTime = 26;
PhyTimings.DataLaneMaxReadTime = 100;
PhyTimings.StopWaitTime = 10;
if (HAL_DSI_ConfigPhyTimer(&hdsi, &PhyTimings) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_TCLK_POST, ENABLE, 35) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_TLPX_CLK, ENABLE, 120) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_THS_EXIT, ENABLE, 150) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_TLPX_DATA, ENABLE, 120) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_THS_TRAIL, ENABLE, 140) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_THS_PREPARE, ENABLE, 126) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetPHYTimings(&hdsi, DSI_TCLK_PREPARE, ENABLE, 100) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_ConfigFlowControl(&hdsi, DSI_FLOW_CONTROL_BTA) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetLowPowerRXFilter(&hdsi, 10000) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetLanePinsConfiguration(&hdsi, DSI_SWAP_LANE_PINS, DSI_CLOCK_LANE, ENABLE) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetLanePinsConfiguration(&hdsi, DSI_SWAP_LANE_PINS, DSI_DATA_LANE0, ENABLE) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetLanePinsConfiguration(&hdsi, DSI_SWAP_LANE_PINS, DSI_DATA_LANE1, ENABLE) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_ConfigErrorMonitor(&hdsi, HAL_DSI_ERROR_NONE) != HAL_OK)
{
Error_Handler();
}
VidCfg.VirtualChannelID = 0;
VidCfg.ColorCoding = DSI_RGB888;
VidCfg.LooselyPacked = DSI_LOOSELY_PACKED_DISABLE;
VidCfg.Mode = DSI_VID_MODE_BURST;
VidCfg.PacketSize = 720;
VidCfg.NumberOfChunks = 0;
VidCfg.NullPacketSize = 5;
VidCfg.HSPolarity = DSI_HSYNC_ACTIVE_LOW;
VidCfg.VSPolarity = DSI_VSYNC_ACTIVE_LOW;
VidCfg.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH;
VidCfg.HorizontalSyncActive = 5;
VidCfg.HorizontalBackPorch = 29;
VidCfg.HorizontalLine = 1795;
VidCfg.VerticalSyncActive = 2;
VidCfg.VerticalBackPorch = 14;
VidCfg.VerticalFrontPorch = 8;
VidCfg.VerticalActive = 1280;
VidCfg.LPCommandEnable = DSI_LP_COMMAND_DISABLE;
VidCfg.LPLargestPacketSize = 20;
VidCfg.LPVACTLargestPacketSize = 20;
VidCfg.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE;
VidCfg.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE;
VidCfg.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE;
VidCfg.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE;
VidCfg.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE;
VidCfg.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE;
VidCfg.FrameBTAAcknowledgeEnable = DSI_FBTAA_ENABLE;
if (HAL_DSI_ConfigVideoMode(&hdsi, &VidCfg) != HAL_OK)
{
Error_Handler();
}
if (HAL_DSI_SetGenericVCID(&hdsi, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DSIHOST_Init 2 */
if(HAL_DSI_Start(&hdsi)!= HAL_OK)
{
Error_Handler();
}
RCC_PeriphCLKInitTypeDef PeriphClkInit;
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_DSI;
PeriphClkInit.DsiClockSelection = RCC_DSICLKSOURCE_PHY;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
/* USER CODE END DSIHOST_Init 2 */
}
And the LTDC as follows
static void MX_LTDC_Init(void)
{
/* USER CODE BEGIN LTDC_Init 0 */
/* USER CODE END LTDC_Init 0 */
LTDC_LayerCfgTypeDef pLayerCfg = {0};
/* USER CODE BEGIN LTDC_Init 1 */
/* USER CODE END LTDC_Init 1 */
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 1;
hltdc.Init.VerticalSync = 1;
hltdc.Init.AccumulatedHBP = 13;
hltdc.Init.AccumulatedVBP = 15;
hltdc.Init.AccumulatedActiveW = 733;
hltdc.Init.AccumulatedActiveH = 1295;
hltdc.Init.TotalWidth = 751;
hltdc.Init.TotalHeigh = 1303;
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 720;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 1280;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB888;
pLayerCfg.Alpha = 255;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
pLayerCfg.FBStartAdress = 0xD0000000;
pLayerCfg.ImageWidth = 720;
pLayerCfg.ImageHeight = 1280;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
__HAL_DSI_WRAPPER_DISABLE(&hdsi);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_RESET); //Reset the driver chip
HAL_Delay(5);
HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_SET);
HAL_Delay(125);
/* Configure the audio driver */
IOCtx.Address = 0;
IOCtx.GetTick = BSP_GetTick;
IOCtx.WriteReg = DSI_IO_Write;
IOCtx.ReadReg = DSI_IO_Read;
ILI9881C_RegisterBusIO(&ILI9881CObj, &IOCtx);
ILI9881C_Init(&ILI9881CObj ,ILI9881C_FORMAT_RGB888, ILI9881C_ORIENTATION_LANDSCAPE); //Initialise
__HAL_DSI_WRAPPER_ENABLE(&hdsi);
//HAL_DSI_PatternGeneratorStart(&hdsi, 0, 0);
/* USER CODE END LTDC_Init 2 */
}
When the program runs, I can see the LTDC ticking in the SFR viewer, but no image is displayed on the screen, which is turned on, backlit and will have been initialised at the end of the LTDC startup code.
I have tried the following:
At this point I can't even narrow down where the issue lies! I believe my DSI PHY timings to be correct, as I assume I wouldn't be able to send commands if they weren't.
Another issue is that the DSI Read command does not work in Video mode. This means that I can't validate my settings before enabling the LTDC to start the video stream.
2025-01-16 08:37 AM
MCU???
2025-01-16 08:42 AM
Apologies. It's STMH747. I'm using the discovery board for testing out a 3rd party screen.
2025-01-16 08:49 AM
Reducing LTDC clock is how .... next no info. Try RGB565 as first.
2025-01-16 08:58 AM
My approach is to unpack and dump/decode the timings from the PLL thru the LTDC and DSI, so as to understand what I've actually built. This helps in both a) understanding the mechanics and gearing, but also b) as a quick sanity check or "will it blend" type test so as not to spend hours going down the wrong rabbit holes.
You should compute the bandwidth to the LTDC data generation. The bandwidth for that flood of data needs to be met or exceeded by the DSI settings. Being slower is not workable.
Does the panel support 20 Hz?
Usually they have a specific range where the pixel clock needs to live, and the pixel count for each frame, including the non-visible ones described by the porches. The syncs live within the porches.
For the panel data sheet *start* with the nominal settings for the clocks, porches, visible, and syncs. You can get creative later.
Make sure the panel controller has the same understanding, and that the panel is enabled there.
Find a proxy using the same panel / controller to review just how much configuration is needed, this could be datasheet code, drivers for assorted platforms, including Linux, etc.