cancel
Showing results for 
Search instead for 
Did you mean: 

LTDC + DSI Video Mode - No Display

Iris-DM
Associate III

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:

  • Enabling LP transitions during all periods (seemingly required for sending commands in video mode)
  • Reducing the LTDC pixel clock, as I'm only aiming for 20Hz frame rate
  • Testing all combinations of HSYNC and VSYNC signal polarity
  • Testing Non-burst and burst modes of DSI video (all modes available in ILI9881C)

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.

8 REPLIES 8
MM..1
Chief III

MCU???

Apologies. It's STMH747. I'm using the discovery board for testing out a 3rd party screen.

Reducing LTDC clock is how .... next no info. Try RGB565 as first. 

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Iris-DM
Associate III

This is the datasheet for the panel I'm working on 

It doesn't seem to say whether or not the panel itself supports 20Hz, but the PHY timings for the DSI link in my firmware all conform to what is laid out. I presume that if they didn't, I wouldn't be able to write the initialisation commands after power up and reset.

For 2-lane MIPI @ 24bpp, p9 of the datasheet specifies a UI time of between 1.176ns (850Mbps) and 25ns.

IrisDM_1-1737107851874.png

This is my current setup in the clock tree

IrisDM_3-1737109403288.png

By my calculations, a lane byte clock of approx 62MHz should give me a UI of approx 2ns, which fits this criteria.

Onto LTDC...

The example code given to me by the panel vendor puts the panel scan clock frequency at 55MHz. They are using an RA8776 graphics engine IC to drive the panel, with an SSD2828 MIPI bridge. In my setup, I see this as an analog for the LTDC and DSIHOST peripherals, respectively.

The datasheet for the panel itself doesn't give values for HBP,HFP etc., so I have reverse engineered it from the driver IC's datasheet and the example code:

IrisDM_4-1737109771340.png

 

//// LCD Setup Code from Example ////
Command_Write(0x13,0x00); //HSYNC:Low VSYNC:Low DE:High Active
LCD_HorizontalWidth_VerticalHeight(720,1280)

LCD_Horizontal_Non_Display(12);            //HBP 
LCD_HSYNC_Start_Position(18);              //HFP 
LCD_HSYNC_Pulse_Width(2);                  //HSW

LCD_Vertical_Non_Display(14)               //VBP
LCD_VSYNC_Start_Position(8)                //VFP
LCD_VSYNC_Pulse_Width(2)                   //VSW

 

I have copied these settings for the LTDC in CubeMX as best I can, but data enable cannot be set to active high in DSI mode.

Using the formulae from the ILI9881C datasheet and the porch timings, I have calculated the bandwidths and frame rates as follows:

BRbps = 496 (62MHz * 8)
LaneNo = 2
VACT = 1280
VSA = 2
VBP = 14
VFP = 8
HACT = 720
HSA = 2
HBP = 12
HFP = 18

Frame Rate = (492x10e6 * 2)/(1304*752*24) ~= 42fps

This all points towards the bandwidth for both DSI and LTDC being sufficient, particularly if I clock the LTDC lower to aim for 20fps. Am I missing something here?

Your match miss. Start with master here LTDC. CLK on image 55M. This is real pixel speed. Result to simple two calc

1. data thr 1280x720x55Mx24bit = 1,24Gbps   ... over DSI capacity two 500Mbps lanes

2. screen refr. 55M / (1303x751) = 56Hz

 

Iris-DM
Associate III

Ok so by your calcs, reducing the LTDC clk to 25MHz should give the following:

Data throughput 1280x720x25Mx24bit = 553Mbps (Not sure if this is calculated right)

Screen refresh rate 25M/(1303x751) = 25.5Hz

Correct?

Iris-DM
Associate III

Interestingly, when debugging the program with the lower LTDC clock, I can see the LTDC ticking through the frame buffer by inspecting the LTDC->CPSR register, but looking at the DSI->GPSR, PWRFE and PRDFE never clear. Does this mean that the DSI host never receives the data from the LTDC?