Skip to main content
Harvey White
Senior III
September 4, 2026
Question

ChromART and repeated calls

  • September 4, 2026
  • 2 replies
  • 40 views

I’ve got a working graphics system.  It does high level operations on a 32 bit virtual image of the display’s resolution.   The image is then translated to a 16 bit buffer image (RGB565 for an ILI9341 display).  That image is then transferred over SPI to the display.  The SPI drivers can be polled, interrupt driven, or DMA driven.  Software drivers are used for all of this, and work.

Moving to a different processor (U575 from L562), ChromART is available.  It could be used for filling the virtual image, translating to the RGB565 image.  2D DMA can be used to transfer modified blocks to the display.

Now the problem.

The transfer works erratically in some cases.  Some data (and I’m using mostly saturated colors) doesn’t transfer well into RGB565 (say 100% blue), it may just end up with black.  Other times, the complete color palette is transferred properly.

I’m investigating whether or not the repeated restart of the ChromART peripheral is a problem.  Mostly what happens is that the mode should remain unchanged, but the transfer source and destination change (allows partial blocks).  However, since the same hardware would be used to fill areas in the virtual image, and that will require changing more mode.

I’m working only on the ARGB8888 to RGB565 translation, Graphics plane image to RGB565 image, straight translate.  Areas are trimmed to the limits of the display.  Code is included, but trimmed.

I seem to be missing something.


switch (ILI9341_ROTATE)
{
// ************************************************************* portrait mode ********************************************************************
case 90:
case 270:
{
break;
}

// ************************************************************* landscape mode *******************************************************************
default:
{
// working
// HAL LEVEL DRIVERS
// default orientation, 0, 180
// with RB regular and bytes-regular
// blue and green are reversed

// with RB regular and bytes-swap
// blue--> red; and green--> green; red--> blue are reversed

// USE CHROMART SYSTEM TO FILL VIRTUAL MEMORY WITH A BLOCK OF DATA

int w = 2;

// WIDEN TRANSFER WINDOW
x0 = minmax(x0-w, 0, 319);
x1 = minmax(x1+w, 0, 319);
y0 = minmax(y0-w, 0, 239);
y1 = minmax(y1+w, 0, 239);

if (x0 > x1) swap32(x0,x1);
if (y0 > y1) swap32(y0,y1);

// ************************************************************* source 1 select ******************************************************************
switch (source1)
{
case TEXT_PLANE:
{
if(_QVGA_PAGES > 1)
{
srcPTR = &QVGA.Lpixel[TEXT_PLANE][y0][x0]; // text plane upper left of block to be written
}
else
{
srcPTR = &QVGA.Lpixel[GRAPHICS_PLANE][y0][x0]; // text plane upper left of block to be written
}
break;
}
case FOREGROUND_COLOR:
{
RGB1 = RGB(foreground_color); // foreground color
srcPTR = &RGB1;
break;
}
case BACKGROUND_COLOR:
{
RGB1 = RGB(background_color); // foreground color
srcPTR = &RGB1;
break;
}
default:
{
srcPTR = &QVGA.Lpixel[GRAPHICS_PLANE][y0][x0]; // graphics plane upper left of block to be written
// graphics plane is default
}
};

// ************************************************************* source 2 select ******************************************************************
switch (source2)
{
case GRAPHICS_PLANE:
{
src2PTR = &QVGA.Lpixel[TEXT_PLANE][y0][x0]; // graphics plane upper left of block to be written
break;
}
case FOREGROUND_COLOR:
{
RGB1 = RGB(foreground_color); // foreground color
src2PTR = &RGB1;
break;
}
case BACKGROUND_COLOR:
{
RGB1 = RGB(background_color); // foreground color
src2PTR = &RGB1;
break;
}
default:
{
if(_QVGA_PAGES > 1)
{
srcPTR = &QVGA.Lpixel[TEXT_PLANE][y0][x0]; // text plane upper left of block to be written
}
else
{
srcPTR = &QVGA.Lpixel[GRAPHICS_PLANE][y0][x0]; // graphics plane upper left of block to be written
}
// text plane is default
}
}


// ************************************************************* ChromART setup *******************************************************************
hdma2d.Instance = DMA2D;
dstPTR = &QVGA_image.Ppixel[y0][x0]; // upper left of block to be read
// DMA init mode
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; // MUST BE SET FOR 16 BIT COLOR (REQIRED AS SET) // write output VM image mode (32 bit)
hdma2d.Init.OutputOffset = 0; // = 0 next line. =320 skips a line

// swapping bytes switches blue and green, text is green
hdma2d.Init.BytesSwap = DMA2D_BYTES_SWAP; // reverse of bytes (needed to swap blue and red)
// hdma2d.Init.BytesSwap = DMA2D_BYTES_REGULAR; // reverse of bytes (needed to swap blue and red)
hdma2d.Init.LineOffsetMode = 0; // 0 is required

// destination
// dstPTR = &QVGA_image.Lpixel[y0][x0]; // upper left of block to be read

// ************************************************************* layer 1 configuration ************************************************************
if (_QVGA_PAGES > 1)
{
switch (PLANE_MODE)
{
case PLANE_OR:
{
break;
}
default:
{
// result = page1 (GRAPHICS PLANE)

switch (source2)
{
case FOREGROUND_COLOR:
case BACKGROUND_COLOR:
{
// layer 1 config
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; // must be set to 16 bit color (RGB565)
hdma2d.LayerCfg[1].InputOffset = 0; // does not matter
hdma2d.LayerCfg[1].InputAlpha = 0xFF; // preserve alpha
hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;

hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_SWAP; // reverses blue and red
// hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR; // reverses blue and red
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
{
Error_Handler();
}
hdma2d.Init.Mode = DMA2D_R2M; // NEEDED FOR TRANSLATE
// translate block to be updated
result = HAL_DMA2D_Start(&hdma2d,(unsigned int)srcPTR, (unsigned int) dstPTR, (x1+1-x0), (y1+1-y0) ); // X pixels * Y pixels
result = HAL_DMA2D_PollForTransfer(&hdma2d, 100);// rectangle_build_XY(&rect, x0, y0, x1, y1); // for display refresh for block
break;
}
default:
{
// layer 1 config
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888; // must be set to 32 bit color (ARGB8888)
hdma2d.LayerCfg[1].InputOffset = 0; // does not matter
hdma2d.LayerCfg[1].InputAlpha = 0xFF; // preserve alpha
hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;

hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_SWAP; // reverses blue and red
// hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR; // reverses blue and red
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
{
Error_Handler();
}
hdma2d.Init.Mode = DMA2D_M2M_PFC; // NEEDED FOR TRANSLATE
// translate block to be updated
result = HAL_DMA2D_Start(&hdma2d,(unsigned int)srcPTR, (unsigned int) dstPTR, (x1+1-x0), (y1+1-y0) ); // X pixels * Y pixels
result = HAL_DMA2D_PollForTransfer(&hdma2d, 100);// rectangle_build_XY(&rect, x0, y0, x1, y1); // for display refresh for block
}
}

 

 

 

 

2 replies

KDJEM.1
ST Technical Moderator
September 21, 2026

Hello ​@Harvey White ;

 

I recommend you to check if the DMA2D is started by verifying the control register. 

What do you mean by “The transfer works erratically in some cases.” Could you please share more details about cases.

 

Thank you.

Kaouthar

To give better visibility on the answered topics, please click on "Best answer" on the reply which solved your issue or answered your question.
Harvey White
Senior III
September 26, 2026

Sorry for the delay, changing the main computer from Windows to Linux and it’s taking a bit.

With that programming, most transfers start (in fact, all do) which I determine by poll for transfer.  However, the large areas seem to be transferred over, but some of the lines do not (1 pixel wide lines, for instance).

Examining the destination and source, the source is correct, the destination is often unchanged.

I wonder (since I’m changing setups as you can see) if I’m doing the setup right, or should I de-init the chip first?  

I intend to do the area fills on the virtual image (32 bit), and use the DMA2D to do the translation from 32 bit color to RGB565.  I could probably just convert the entire display at one time, but it seems very inefficient.  Widening the area seems not to help a lot.

I do assume that the poll for transfer always returns true when the transfer is done, or should I start looking for error codes?