2025-01-02 08:45 AM - edited 2025-01-03 06:33 AM
Hi Folks and Happy New Year!
I've been trying to track down a problem I'm having with my project for several months now. Essentially I'm getting these red-ish lines through portions of my buttons, but only when (I presume) they are drawn or redrawn - namely when the screen first loads, when they are pressed, or when as in this example, they have text drawn over them that updates.
In the case of the above picture, I've been able to get the lines to appear more consistently by layering some text over top of some of the buttons and then updating that text with each tick where it says "Tick Counter: xxxx". I managed to luck out and have it appear on the "Cancel" of the cancel button this time, though it probably only happens about 1 out of 10 times when the screen loads.
These lines have been appearing pretty consistently throughout my development on the project, and they seem (though I'm still not completely sure) to be happening regardless of my application code. The reason I say this is that I've commented out large portions of my application code and had them still appear. Frustratingly, they are not consistent. I can do a build, have them appear, and then insert a trivial line of code (even a NOP), and then they will disappear, only to reappear again after I add a few more lines.
This really seems to point to some sort of issue where I'm writing off the edge of an array or something (it seems that parts of my frame buffer are getting inadvertently overwritten? But then why just buttons? And why does it still happen when I essentially remove my application code?)
I've included my TOUCHGFXHAL code that has my driver code in it.
Details on my project:
MCU: STM32L496VGTN
Display: NHD-2.8-240320AF-CSXP-FT (ST7789 Controller) connected via FMC 16-bit parallel
External Flash: MT25QL128A using quad-spi with a custom loader
Firmware details: No RTOS (Bare Metal/super-loop)
Touch GFX Configuration:
Other possible helpful details:
Any tips, tricks, or advice towards debugging this issue would be greatly appreciated. You might say figuring this out has become my new year's resolution!
2025-01-31 06:02 AM
Yes, that is the setting I was referring to. You should ensure that DMA2D is enabled in your initialization code. The settings don't matter, TouchGFX will take care of this, as well as setting up transfers to the frame buffer.
You should not reconfigure DMA2D. If you want to use DMA to transfer data to the FMC, you should use plain DMA.
2025-01-31 06:22 AM - edited 2025-01-31 06:22 AM
Ah, yes that fixed the issue with the screen only partially drawing. I'll get normal DMA setup for my FMC transfers. No artifacts either, for the moment.
2025-01-31 09:19 AM - edited 2025-01-31 09:27 AM
Well, I should have tested the larger font size (Size 80, Verdana Bold) because that still has the problem I mentioned before:
When I disable ChromArt under the TouchGFX settings these weird character distortions go away.
2025-02-03 01:24 AM
Is that a static text or a number that increments? Are the artifacts consistent; in this case the 1 is correct, are all 1s correct? Are all 2s broken in exactly the same way?
Be aware that for normal DMA you may run into an issue with the number of blocks that can be transferred in one go. I so not recall if the DMA on L4 supports this, but some DMAs have a linked list mode to handle this, otherwise you can handle that manually in a callback.
Another option is to try to switch frame buffer strategy to partial frame buffer. This transfers a number of blocks of a predefined size to update the memory in the display. You can manually set the size and number of blocks, so you can limit the size to what you can handle in one transfer. At the same time, you will save some RAM since the normal framebuffer is replaced by the blocks.
The project for STM32U575 with RVA35 display module that ships with the TouchGFX designer is set up in this manner and uses DMA for FMC transfers, if you would like some inspiration.
2025-02-03 08:08 AM
The text is dynamic - I was updating it with every tick, mainly to force invalidations for those buttons and "stress test" the system. I just changed my code to increment the counter manually so that I could go through each digit manually.
void ArtifactTestScreenView::vfYesButton() { // Lower Left button
textPrompt.invalidate();
}
void ArtifactTestScreenView::vfNoButton() { // Lower right button
counter++;
Unicode::snprintf(textPromptBuffer, TEXTPROMPT_SIZE, "%d", counter);
textPrompt.invalidate();
}
The following digits are scrambled (and always in the same way as far as I can tell): 2, 3, 5, 6, 7, 9. The rest of the numbers seem to be ok (0, 1, 4, 8). I've included a video so that you can see it happen as I press the buttons (sorry the video is turned sideways)
What is interesting is that as it repaints the portion of the digit for the button depressed image, it seems to do it correctly, but as soon as I release the button, it goes back to garbage.
In regard to using normal DMA, it seems to be working ok - I'm doing the transfer in two equal blocks for the whole screen, as at the moment I'm just transferring the entire screen, which seems to be working, the previous complaint non-withstanding:
void TOUCHGFXHAL_signalVSync() { // Wrapper for triggering VSync
const uint32_t HALF_PIXEL_COUNT = (TFT_WIDTH * TFT_HEIGHT) / 2;
// VSync has occurred, increment TouchGFX engine vsync counter
HAL::getInstance()->vSync();
// VSync has occurred, signal TouchGFX engine
OSWrappers::signalVSync();
if (refreshRequested && !displayRefreshing) {
touchgfx::HAL::getInstance()->swapFrameBuffers();
displayRefreshing = true;
HAL_GPIO_WritePin(TP_PC0_GPIO_Port, TP_PC0_Pin, GPIO_PIN_HIGH); // Raise pin for timing display write
// Set LCD window
setLCDwindow(0, 0, TFT_WIDTH, TFT_HEIGHT);
LCD_IO_WriteReg(RAMWR);
// Configure 1st Half DMA transfer
if (HAL_DMA_Start(&hdma_memtomem_dma2_channel1,
(uint32_t)currFbBase, // Source: Framebuffer
(uint32_t)FMC_BANK1_MEM, // Destination: LCD GRAM
HALF_PIXEL_COUNT) != HAL_OK) // Number of 16-bit pixels
{
Error_Handler();
}
// Wait for DMA transfer to complete
if (HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_channel1, HAL_DMA_FULL_TRANSFER, 1000) != HAL_OK)
{
Error_Handler();
}
// Configure 2nd Half DMA transfer
if (HAL_DMA_Start(&hdma_memtomem_dma2_channel1,
(uint32_t)currFbBase+HALF_PIXEL_COUNT*2, // Start at halfway through the frame buffer which is the pixel count x the number of bytes per pixel (2 bytes for RGB565)
(uint32_t)FMC_BANK1_MEM, // Destination: LCD GRAM
HALF_PIXEL_COUNT) != HAL_OK) // Number of 16-bit pixels
{
Error_Handler();
}
// Wait for DMA transfer to complete
if (HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_channel1, HAL_DMA_FULL_TRANSFER, 1000) != HAL_OK)
{
Error_Handler();
}
HAL_GPIO_WritePin(TP_PC0_GPIO_Port, TP_PC0_Pin, GPIO_PIN_LOW); // Raise pin for timing display write
displayRefreshing = false;
}
}
I've been reluctant to move to a partial framebuffer strategy, mainly because transferring data to the display seemed to be working, but if you think this is a better strategy for my setup I'll see if I can get it working.
2025-02-04 01:14 AM
You could use a transfer complete interrupt on the DMA to increase performance by moving the update code to a dedicated screen transfer function that can take a variable to control which half of the pixels to transfer. That would free up some CPU cycles. But I guess that is not the problem here.
I have two things I would like to investigate.
Try to modify your code from above to this:
void ArtifactTestScreenView::vfYesButton() { // Lower Left button
counter--;
Unicode::snprintf(textPromptBuffer, TEXTPROMPT_SIZE, "%d", counter);
textPrompt.invalidate();
//invalidate();
}
void ArtifactTestScreenView::vfNoButton() { // Lower right button
counter++;
Unicode::snprintf(textPromptBuffer, TEXTPROMPT_SIZE, "%d", counter);
textPrompt.invalidate();
//invalidate();
}
First, it would be interesting to see if the scrambled letters are the same when counting up and counting down, I'm wondering whether it's the number being drawn incorrectly, or if there is somehow a part of the previous number remaining.
The lines with comments just invalidate the entire display, i wonder if that shows the entire number correctly.
2025-02-04 05:26 AM
Yes, thank you for that suggestion. I do plan to move to interrupt based processing of the screen transfers, but I wanted to keep things simple for debugging these issues for now, and even with blocking code, I still see at least a 10x performance improvement over the for-loop (CPU) transfer of data to the display.
Counting up or down to the errant numbers does not seem to make a difference:
Uncommenting the additional invalidate statement at the end does not appear to have any effect.