DMA2D skips pixels when sending directly to display over FMC
Hello,
We are working with a custom board using the STM32H753II that connects to a 480x320 ILI9486 LCD via the FMC (8080 interface). The display and its custom-written driver work fine when writing pixel data using the CPU. To improve performance, we want to use the DMA2D available in the MCU for most rendering tasks.
The first thing I tried was using the DMA2D in Mem-to-Mem with Blending and fixed color BG mode (MODE 101) with the FG color format set to A4 for anti-aliased font rendering (size 12x16). OMAR was set to the same address used when writing manually. I expected this to work just fine. However, the displayed characters are extremely distorted and not recognizable unless you know what they should be.
To further debug this issue, I created some test characters. One consists of a gradient from 0x0 to 0xB from left to right. The other consists of a 2px wide vertical line in the center.
All characters were drawn in the order they are shown and right after one another using interrupts.
Here is a close-up of the distorted characters (on cyan background to visualize where pixels were written:
This image shows the gradient character. The black line next to it shows how tall the character is supposed to be. This is the only character from the transfer that was started outside of the DMA2D interrupt.
This image shows three of the of the vertical line characters and one more gradient character. The reddish hue on the last character is just some dirt, sorry about that.
The vertical line gets chopped up into 2x2 squares distributed all over the visible character. The gradient also shifts about every two lines. Interestingly, the characters are not fully drawn. Depending on the character (and whether or not the character was drawn already), some of the bottom lines may be fully or partially missing. Additionally, the shifting looks different depending on whether the MCU is cold-booting or reset using the NRST pin.
A closer look at the gradient characters shows that 4 pixels seem to go missing at a time, though I only checked the first few lines since it is sometimes not clear which pixels are missing.
To ensure that this issue isn't caused by the A4 mode, I also tested the Register-to-Memory mode. There, the same issues could be observed. For a 64x128 rectangle, only about half was actually drawn. When drawing a 64x256 rectangle, only a few extra lines were drawn. The number of pixels drawn seemed to largely be independent of the orientation of the rectangles.
Here is the initialization code:
// Densely packed output
DMA2D->OOR = 0;
DMA2D->AMTCR = (0xA8<<DMA2D_AMTCR_DT_Pos) | DMA2D_AMTCR_EN; // Does not change anything
DMA2D->CR = 0;
__HAL_RCC_DMA2D_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA2D_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2D_IRQn);Here is the code preparing for text rendering:
// Mem-to-mem with blend and fixed color bg
DMA2D->CR = ((0b101)<<DMA2D_CR_MODE_Pos | GLI_IE_FLAGS);
DMA2D->FGOR = 0; // Pixel data is densely packed
// FGMAR, OMAR, NLR are set per character
// BGMAR, BGOR are not used, set to dummy values
DMA2D->BGMAR = 0;
DMA2D->BGOR = 0;
// CLUT not used
// OCOLR not used, set to dummy values
DMA2D->OCOLR = MAGENTA;
DMA2D->OOR = 0;
// A4 FG format
DMA2D->FGPFCCR = ((0b00)<<DMA2D_FGPFCCR_AM_Pos | (0b1010)<<DMA2D_FGPFCCR_CM_Pos);
DMA2D->FGCOLR = GL_RGB565_TO_RGB888(BLACK);
// RGB888 BG format
DMA2D->BGPFCCR = ((0b00)<<DMA2D_BGPFCCR_AM_Pos | (0b0001)<<DMA2D_BGPFCCR_CM_Pos);
DMA2D->BGCOLR = GL_RGB565_TO_RGB888(WHITE);
// RGB565 Output format
DMA2D->OPFCCR = ((0b010)<<DMA2D_OPFCCR_CM_Pos);Here is the per-character code:
// Prepare draw area (function is the same as direct write code)
// Using FONT_WIDTH+4 to visually separate characters for testing
gli_set_draw_area(dqelem->x+(FONT_WIDTH+4)*gli_curtextidx, dqelem->y, FONT_WIDTH, FONT_HEIGHT);
// Set font size
DMA2D->NLR = (uint32_t)((FONT_WIDTH)<<DMA2D_NLR_PL_Pos | (FONT_HEIGHT)<<DMA2D_NLR_NL_Pos);
// Fetch the character to be displayed and put its address in FGMAR
uint8_t c = dqelem->text[gli_curtextidx];
DMA2D->FGMAR = (uint32_t)(&(DEFAULT_FONT[c]));
// Increase character to draw for next iteration
gli_curtextidx++;
// (Re-)set output address
DMA2D->OMAR = LCD_DATA_ADDR;
// Send Memory Write command to LCD
GLI_START_DRAW();
// D/I Barriers, don't change anything visually
__DSB();
__ISB();
// Start the transfer
DMA2D->CR |= (uint32_t)DMA2D_CR_START;Here is the rectangle code:
// Prepare draw area (function is the same as direct write code)
gli_set_draw_area(dqelem_r->x, dqelem_r->y, dqelem_r->w, dqelem_r->h);
// Reg-to-mem
DMA2D->CR = ((0b011)<<DMA2D_CR_MODE_Pos | GLI_IE_FLAGS);
// FGMAR, BGMAR, FGOR, BGOR, FGPFCCR, BGPFCCR are not used, leave as is
// Set OCOLR to output color
DMA2D->OCOLR = dqelem_r->color;
DMA2D->OPFCCR = ((0b010)<<DMA2D_OPFCCR_CM_Pos);
// Set NLR to right size
DMA2D->NLR = (uint32_t)((dqelem_r->w)<<DMA2D_NLR_PL_Pos | (dqelem_r->h)<<DMA2D_NLR_NL_Pos);
// Send MemWrite Command
GLI_START_DRAW();
// D/I Barriers
__DSB();
__ISB();
// Start the transfer
DMA2D->CR |= (uint32_t)DMA2D_CR_START;The FMC init code is relatively standard. If needed, I can also post it here.
I have already read the DMA2D section of the Reference Manual several times and also checked for errata, but could find nothing there.
Thanks in advance for suggestions!
EDIT: After putting the __HAL_RCC_DMA2D_CLK_ENABLE(); line at the start of the init function, at least the text renders fine. Considering that OOR and CR should already be reset to zero and AMTCR should only cause performance issues, this is still weird and should probably be mentioned somewhere.
The rectangles still don't work, just as before.
