Skip to main content
Associate III
November 6, 2024
Solved

TouchGFX on Custom Board with no TE pin

  • November 6, 2024
  • 9 replies
  • 4846 views

I have a custom board, which uses STM32H747IXH6 and MB1166 LCD screen from the related discovery board. I am trying to get TouchGFX up and running on this custom setup, but in my PCB design, I missed out the dedicated TE pin in the connector (I'm following the RaspberryPi DSI convention of DSI CLK, Data Lanes and I2C only). I can initialise the display, but I cannot get my TouchGFX application running. The process at the moment is as follows:

Init DSI Host

Reset the LCD display
Config DSI PLL
Config Host Timeouts
Config PHY Timers
Config LPCommand
Config AdaptedCommand Mode
Invert clock and data pins (to correct for PCB error)
Register busIO for OTM8009A
Init OTM8009A (as per Discovery examples)
Config Flow Control
Force RX Low Power

Init LTDC

Active low VSYNC and HSYNC polarity
803x483 total width and height (800x480 + porches)
Format RGB888
Double frame buffer
Start address 0xD0000000

Init FMC (IS42S32800J)
Init as Bank 2

Init TouchGFX

Start TouchGFX Thread 

 

Now when TouchGFX starts, taskEntry sits at OSWrappers::waitForVSync(); I assume that this VSync should be called from HAL_DSI_TearingEffectCallback in TouchGFXHAL, but this callback never fires. It appears a tearing effect is requested earlier in taskEntry by LCD_ReqTear, which in the discovery code writes OTM8009A_CMD_TEEON to the LCD driver. I have modified LCD_ReqTear to send OTM8009A_CMD_WRTESCN with a param of 533 (0x2,0x15) instead, as per the CMDMode_TearingEffect example, but the TearingEffectCallback is still not triggered.

What am I missing here? I can't attach the full project for confidentiality reasons, but I'm happy to share main.c and TouchGFXHAL.cpp 

I'm aware also that TearingEffectCallback and EndOfRefreshCallback still contain code that assumes the display is split in two (as per both the examples I have borrowed from), I wouldn't have thought this was the issue however, as neither of these callbacks ever fire.

Edit

I have updated taskEntry to enable the DSI interrupts and call a DSI_Refresh before the first wait for Vsync, but the  callbacks still don't fire. Examining the SFRs shows that the DSI transfer hangs on BUSY (DSI_WISR), so presumably the callback doesn't fire due to the refresh never finishing. What could be preventing the transfer from occurring?

Best answer by Iris-DM

Sorted! I got it working with the example and then copied the TouchGFXHAL code to my custom project. The colours are wrong, but we're off to a good start!

9 replies

Iris-DMAuthor
Associate III
November 7, 2024

Post updated to add more information

Iris-DMAuthor
Associate III
November 7, 2024

Update:

I have moved the OTM8009A initialisation code to the end of the LTDC initialisation routine. The general flow is now:

Reset LCD via GPIO
Init DSI
Init LTDC
Start DSI
Init OTM8009A via DSI
Modify DSI flow control & low power RX 
Request scan

uint8_t ScanLineParams[2];
uint16_t scanline = 500;
ScanLineParams[0] = scanline >> 8;
ScanLineParams[1] = scanline & 0x00FF;

HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0x44, ScanLineParams);
HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, OTM8009A_CMD_TEEON, 0x00);

Turn the display on and call a refresh

HAL_DSI_ShortWrite(&hdsi,0,DSI_DCS_SHORT_PKT_WRITE_P0,OTM8009A_CMD_DISPON,0x00);
HAL_DSI_Refresh(&hdsi);

Then start the TouchGFX task

HAL_DSI_EndOfRefreshCallback is now called once, but no tearing effect callback is generated, thus VSync does not fire and TouchGFX doesn't take over the management of the process. Based on AN46860, I have enabled the bus turnaround request and the tearing effect acknowledge request.

ST Employee
November 7, 2024

I'm not sure where this goes wrong, but I can try to explain what I would do in this case, maybe there is a missing step somewhere in that process.

First, since you should now be getting TE signals on the bus rather than on a GPIO, set that under DSIHOST in CubeMX:

DSIHOST.png

Now,when your DSI interrupt fires, it should be able to determine if a TE signal has been received:DSIIRQHANDLER.png

HAL_DSI_TearingEffectCallback() is weak, so you can define that yourself, which it seems from what you write is already done in TouchGFXHAL.

TECALLBACK.png

Are you still running adapted command mode, or are you in video mode?

Have you looked at the code that split the display in two? I get that that can be a very confusing solution, but what basically happens is that the LTDC frame buffer pointer is moved, and then an LTDC transfer to the screen is triggered. If a transfer is never triggered, I'm not sure there would be a callback, but I'm not sure wether you actually ever get to that point?

Iris-DMAuthor
Associate III
November 7, 2024

I'm definitely running in adapted command mode, yes. I've also worked out how the code that splits the display in two works and have adapted my DSI EOR callback and LTDC setup to match.

At the end of my peripheral init (User code 2 in main), I call the following

HAL_DSI_Stop(&hdsi);

	DSI_LPCmdTypeDef LPCmd = {0};
	LPCmd.LPGenShortWriteNoP = DSI_LP_GSW0P_ENABLE;
	LPCmd.LPGenShortWriteOneP = DSI_LP_GSW1P_ENABLE;
	LPCmd.LPGenShortWriteTwoP = DSI_LP_GSW2P_ENABLE;
	LPCmd.LPGenShortReadNoP = DSI_LP_GSR0P_ENABLE;
	LPCmd.LPGenShortReadOneP = DSI_LP_GSR1P_ENABLE;
	LPCmd.LPGenShortReadTwoP = DSI_LP_GSR2P_ENABLE;
	LPCmd.LPGenLongWrite = DSI_LP_GLW_ENABLE;
	LPCmd.LPDcsShortWriteNoP = DSI_LP_DSW0P_ENABLE;
	LPCmd.LPDcsShortWriteOneP = DSI_LP_DSW1P_ENABLE;
	LPCmd.LPDcsShortReadNoP = DSI_LP_DSR0P_ENABLE;
	LPCmd.LPDcsLongWrite = DSI_LP_DLW_ENABLE;
	LPCmd.LPMaxReadPacket = DSI_LP_MRDP_ENABLE;
	LPCmd.AcknowledgeRequest = DSI_ACKNOWLEDGE_DISABLE;
	if (HAL_DSI_ConfigCommand(&hdsi, &LPCmd) != HAL_OK)
	{
		Error_Handler();
	}

	HAL_DSI_Start(&hdsi);

 uint8_t ScanLineParams[2];
	uint16_t scanline = 500;
	ScanLineParams[0] = scanline >> 8;
	ScanLineParams[1] = scanline & 0x00FF;

	HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0x44, ScanLineParams);
	//HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, OTM8009A_CMD_TEEON, 0x00);

	HAL_DSI_ShortWrite(&hdsi,
			0,
			DSI_DCS_SHORT_PKT_WRITE_P0,
			OTM8009A_CMD_DISPON,
			0x00);

	HAL_DSI_Refresh(&hdsi);

Then go on to start the touchGFX task as per usual. I would have thought this command would have generated a TE on the DSILink via the Refresh call, which I believe kicks off the LTDC transfer. As touchGFX has not started at this point, I'd expect to just see the random noise of the uninitialised buffer on screen (which I do), but still no TE interrupt generated.

Interestingly, as soon as I call DSI_Start, the GPRDE bit is set in the DSI ISR1 register. Could this be an indicator for something? I also get constant LTDC interrupts with no flags in the LTDC ISR, so I assume this is cause by GPRDE.

ST Employee
November 8, 2024

The reference manual for H747 has this detail about the GPRDE bit:

REFMAN.png

In the original TouchGFX project, the LTDC is turned off when the DSI_Start command is called. It may lead to some undefined behaviour if the DSI is in the middle of a transfer when the DSI host starts up, so I would try to call

__HAL_LTDC_DISABLE(&hltdc) before starting DSI and __HAL_LTDC_ENABLE(&hltdc) after.

You might also have noticed that there is a 1 ms delay in the original HAL_DSI_EndOfRefreshCallback. I do not actually know why it is there, but I do know that the whole thing stops working if it is removed. My guess is that it ensures that the DSI bus or LTDC is ready for the modification that will happen because of the half-screen thing, so you could also just try to add a delay before starting the DSI host.

Iris-DMAuthor
Associate III
November 14, 2024

I've made progress, but still no complete working solution. I'm now using a timer to trigger vsync every 20ms from main:

IrisDM_0-1731581952695.png

When my code launches, the first framebuffer is written to the LCD. This is done by forcing a DSI Refresh from TouchGFXHAL::taskEntry(). The refresh that writes to the screen is on line 154.

IrisDM_1-1731582037343.png

This is fine, so long as my display is static. We get through backPorchExited() and go into the infinite loop, checking for vsync - although nothing ever actually changes in the framebuffer as there's nothing new to display.

If the framebuffer needs to be updated, either through user code in the view or putting an animated image in the display, the first refresh draws the initial framebuffer, but backPorchExited() never returns, it gets stuck on Model::Tick().

I have to assume that there is an issue with the updating of the framebuffer. Its address is allocated in CubeMX - does this need manually setting in TouchGFXHAL::initialize()?

IrisDM_2-1731582355937.png

And DMA2D is configured with a matching format

IrisDM_3-1731582385940.png

We're using external SRAM here, but FMC is configured and correctly as I can see the framebuffer in the memory monitor

IrisDM_4-1731582815757.png

What other issues could cause the hang when copying the new framebuffer? 

 

 

ST Employee
November 14, 2024

One thing that could lead to the tick method hanging is trying to measure MCU usage but not having the correct hooks in the OS to support this.

Do you have a call to enableMCULoadCalculation(true) somewhere? I believe removing that (or setting it to false in TouchGFXHAL::initialize()) would solve the problem so TouchGFX can execute.

I would advise using double buffers if you have space for that in RAM. In general, it is easier to have their address set by allocation than by manual address.

If set to by allocation, you can define what sector they go in with the linker script, you can see how that is handled for your particular compiler in most of the board setups that ship with the TouchGFX designer.

As you set it by address, it will be placed at that address.

You can see what the start address of the currently active framebuffer is in the LTDC->CFBAR register.

But with single frame buffer and no TE signal to synchronize, I would expect tearing on the display when anything moves.

 

Iris-DMAuthor
Associate III
November 14, 2024

I did have enableMCULoadCalculation(true) set, but I have now removed it, along with mcuInstr.init() and setMCUInstrumentation(&mcuInstr). Unfortunately, this doesn't change anything.

Iris-DMAuthor
Associate III
November 14, 2024

One difference from STM32H747DISCO is that I am using the IS42S32800J SRAM chip instead of the IS32S32160D on the DISCO board. I have half as much memory at 256Mb instead of 512, but the access parameters are all the same (except for one fewer address line). This is compensated for in the FMC init, so I wouldn't think it would cause a problem. 

Iris-DMAuthor
Associate III
November 14, 2024

This is the stack from the point that the task locks up

IrisDM_0-1731589199041.png

Without access to the HAL source, I can't probe this any further

Iris-DMAuthor
Associate III
November 14, 2024

I don't have an implementation of getTFTCurrentLine() in TouchGFXHAL, no.

 

I have tried going from an example - just the blankUI example in TouchGFX designer for the STM32H747I-DISCO. I have had to make the following modifications to get it up and running:

- Change DSI timings so OTM8009AInit() works correctly
- Change LCD reset pin to PE6
- Remove external flash from linker script (hardware implementation is incorrect)
-Disabled touch controller init
-20ms timer calling touchgfx_signalVSyncTimer();

The touchgfx task starts and cycles as expected, with flushframebuffer being called. BUT, because there is no TE pin, the tearing effect callback never gets called and the below code never executes - I'm back at square 1! What should trigger this code instead of the Tearing Effect?

IrisDM_0-1731596325521.png

 

 

 

Iris-DMAuthorBest answer
Associate III
November 14, 2024

Sorted! I got it working with the example and then copied the TouchGFXHAL code to my custom project. The colours are wrong, but we're off to a good start!

ST Employee
November 15, 2024

Good to hear!

As I said, you would probably be better off with two frame buffers, and you should definitely find a way to get a TE signal, but I guess you will just have to experiment a bit with the registers on the board. I would suggest enabling everything related to TE and seeing if you get something. You will get tearing regardless of your frame buffer strategy unless you can get the display and the MCU in sync.

Trying to initialize the display in video mode is also an option, then it will be synced by the LTDC V-blank signal.

The display on MB1166 is actually a portrait mode display, which is the reason for the "split" screen code. I would suggest initializing it as such, setting the LTDC and framebuffers to 480x800 and running a rotated UI in TouchGFX if you discard the split screen code.