cancel
Showing results for 
Search instead for 
Did you mean: 

SRAM/FSMC Framebuffer -> 8080 LCD FSMC DMA transfers example

Dolence
Associate III

Hi! After some struggle I got my SSD1963 LCD working. It's very basic right now, I still don't have resistive touch and SPI flash for storage working.

I'm using an STM32F407 168MHz chinese development board. SDRAM is only 1MB 16-bit and my display resolution is 800x480 16-bit, which doesn't allow for a double buffer setup. I don't have any FPS measurement system yet, but screen refreshes are very slow.

I'm coming from Atmel AVR 8-bit and still learning how to work with the way more complex STM32 ARM MCUs. I was able to make my driver for this display and initialize it, transfer data to it and make it work with TouchGFX, which is really nice. Now I understand the basics of GPIO, NVIC, FSMC, how to interface to LCD though it, how a RTOS work, etc.

I'm curious to test using DMA to transfer data from external SRAM framebuffer (accessible by the address 0x68000000) to my display internal framebuffer (0x6C000000) and see how much offloading the CPU would help on getting faster transfers and higher framerates. I guess it would be done using memory-to-memory DMA transfers (found it snooping at MX/.ioc file DMA options) but I don't know how it should be done. Are there any example of this?

I'm really lost in here and any help would be very appreciated. Thanks in advance!

8 REPLIES 8
MVoss.2
Associate II

Hello,

I'm using DMA to copy data frome external SDRAM (0xC0000000) to FMC (0x60000000) like this:

void STM32HAL::copyFrameBufferBlockToLCD(const touchgfx::Rect rect)

{

  uint16_t* ui16pfb = HAL::lockFrameBuffer();

  uint16_t* ptrui16 = getClientFrameBuffer();

  uint8_t bitDepth = HAL::lcd().bitDepth();

  uint32_t ui32SrcAdr = (uint32_t)ptrui16;

  

  /* Prepare to write to lcm RAM */

//set here LCM write data!!!

   

  /* Try to take a display semaphore - Always free at this point */ 

  OSWrappers::tryTakeFrameBufferSemaphore();

  for(int i =0; i<rect.height; i++)

  {   

     ptrui16 = getClientFrameBuffer() + rect.x + (i + rect.y) * "display width";

     ui32SrcAdr = (uint32_t)ptrui16;

HAL_DMA_Start_IT(ptDmaHalHandle,ui32SrcAdr,"destination adress FMC", "len is display width in dependency of memaligned of DMA Here in Words") ....

/* Wait for the DMA transfer to complete */

      OSWrappers::tryTakeFrameBufferSemaphore();

  }

  HAL::unlockFrameBuffer();

  OSWrappers::giveFrameBufferSemaphore();

}

void DMA_INSTANCE_IRQHANDLER(void)

{

  /* Check the interrupt and clear flag */

  HAL_DMA_IRQHandler(&DmaHandle);

  

  OSWrappers::giveFrameBufferSemaphoreFromISR(); 

}

/* config DMA */

/* always using DMA stream 2 for mem to mem copy!!! */

 DMA_InitTypeDef tInitDma;

tInitDma.Channel       = DMA_CHANNEL_0

tInitDma.Direction      = DMA_MEMORY_TO_MEMORY;

tInitDma.PeriphInc      = DMA_PINC_ENABLE;

tInitDma.MemInc       = DMA_MINC_ENABLE;

tInitDma.PeriphDataAlignment = DMA_PDATAALIGN_WORD;

tInitDma.MemDataAlignment  = DMA_MDATAALIGN_WORD; //word alignment, 4 Bytes max. 65535

tInitDma.Mode        = DMA_NORMAL;

tInitDma.Priority      = DMA_PRIORITY_HIGH;

tInitDma.FIFOMode      = DMA_FIFOMODE_ENABLE;

tInitDma.FIFOThreshold    = DMA_FIFO_THRESHOLD_1QUARTERFULL;

tInitDma.MemBurst      = DMA_MBURST_SINGLE;

tInitDma.PeriphBurst     = DMA_PBURST_SINGLE;

I'm copying the frame buffer line by line with DMA. I found out when I am copying the entire frambuffer to FMC it doesn't work. I don't know why. Maybe anybody here in the forum knows more about?

Greetz

The procedure how to use DMA is described in the DMA chapter in RM. Basically, after clearing the status bits after the previous transfer, you set source and destination address and number of transfers into the respective registers of any chosen stream of DMA2, enable FIFO with basically any threshold, in control register set both increments, sizes to word, noncircular, mem2mem, and enable. That's all.

JW

> I found out when I am copying the entire frambuffer to FMC it doesn't work. 

Specify "doesn't work".

Couldn't the problem be in that you tried to set >65535 transfers?

JW

I mean "doesn't work" that not the entire buffer will be displayed on display. Some areas are keept blank. I tried like this:

uint32_t bufferSizeInBytes = lcd().framebufferStride() * FRAME_BUFFER_HEIGHT; // in my case 240*320px rgb 565 -> 240*320*2 = 153600 Bytes

uint32_t len = bufferSizeInBytes/4; // in my case Word alignment ->153600/4 = 38400 < 65535 -> should be ok

HAL_DMA_Start_IT("ptDmaHalHandle",ui32SrcAdr,ui32DstAdr,len)

But because of that i assume maybe i have any timing problems with FMC. Sometimes the data in FMC memory area for MCU is "dithering" (watching in memory window). I don't know how to solve it?

You mean, the readout of the SDRAM in debugger changes spontaneously?

I have only anecdotal experience with SDRAMs through STM32 FMC (basically just playing with a 'F429 Disco) - the errata didn't plant much confidence do we tried to avoid it in our designs so far ...

JW

Yes I mean that but not in SDRAM, rather in memory area in FMC. For example one color over the entire area is in frame buffer (located on SDRAM) ok. But after copying the data to FMC memory area there are some "corrupt data" which means the display area contains other data instead of the color.

See screenshoots

greetz0693W000003Ojc5QAC.png0693W000003OjbqQAC.png

OK so it's the LCD controller side which is problematic, and if colour is incorrect, it will be at least the write (still may be read, too, and SDRAM read, too).

Hard to tell exactly what may be the reason with having the system physically at my bench... I have a working design with SSD1963 but was quite a challenge to get the it right, IIRC it did not exactly follow the timing in its datasheet and timing had to be relaxed. I don't remember the details (worked with several LCD controllers and the individual details tend to blur...)

OTOH, the screenshot indicates flipped bits, which might be also due to inadequate ground/return and/or crosstalk, so maybe some careful oscilloscope work might be useful, too.

And I don't use SDRAM which may be a factor, too.

JW

MVoss.2
Associate II

Crosstalk was my guess as well.

So thats why i putted the frame buffer from external SDRAM to internal RAM for testing. Lo and behold, it works. Running framebuffer from internal RAM is ok. No currupt data in memory and screen will be displayed witout any failure. Thats's why i would exclude timing problems from FMC and LCM controller (Sitronix ST7789V) od lcd display.

I assume that copying data via DMA from SDRAM to SRAM(LCD) which should be done simultaneously doesnt't work anyhow. By the way copying data manually via cpu clock doesn't work as well, because it takes way too long.

So now is the qeustion, how copying data works in this constilation?

The function call for DMA transfer looks like:

"HAL_DMA_Start_IT(ptDmaHalHandle,"adress framebuffer on SDRAM",ADRESS of NOR/SRAM for LCD,ui32Len)."

Does anybody know how exactly the DMA transfer this?

Enabling the FIFO of FMC (SRAM) also has no change..

My hardware configuration:

FMC SRAM (LCD):

SRAM_HandleTypeDef hsram1;

hsram1.Instance = FMC_NORSRAM_DEVICE;

 hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;

 hsram1.Init.NSBank = FMC_NORSRAM_BANK1;

 hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;

 hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;

 hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;

 hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;

 hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;

 hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;

 hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;

 hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;

 hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;

 hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;

 hsram1.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;

 hsram1.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;

 hsram1.Init.WriteFifo = FMC_WRITE_FIFO_DISABLE;

 hsram1.Init.PageSize = FMC_PAGE_SIZE_NONE;

 PD14  ------> FMC_D0

 PD15  ------> FMC_D1

 PD0  ------> FMC_D2

 PD1  ------> FMC_D3

 PE7  ------> FMC_D4

 PE8  ------> FMC_D5

 PE9  ------> FMC_D6

 PE10  ------> FMC_D7

 PE11  ------> FMC_D8

 PE12  ------> FMC_D9

 PE13  ------> FMC_D10

 PE14  ------> FMC_D11

 PE15  ------> FMC_D12

 PD8  ------> FMC_D13

 PD9  ------> FMC_D14

 PD10  ------> FMC_D15

 PD4  ------> FMC_NOE

 PD5  ------> FMC_NWE

 PD7  ------> FMC_NE1

 PG3  ------> FMC_A13

FMC SDRAM:

SDRAM_HandleTypeDef hsdram1;

 hsdram1.Instance = FMC_SDRAM_DEVICE;

 /* hsdram1.Init */

 hsdram1.Init.SDBank = FMC_SDRAM_BANK1;

 hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;

 hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;

 hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;

 hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;

 hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;

 hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;

 hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;

 hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;

 hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;

PF0  ------> FMC_A0

 PF1  ------> FMC_A1

 PF2  ------> FMC_A2

 PF3  ------> FMC_A3

 PF4  ------> FMC_A4

 PF5  ------> FMC_A5

 PF12  ------> FMC_A6

 PF13  ------> FMC_A7

 PF14  ------> FMC_A8

 PF15  ------> FMC_A9

 PG0  ------> FMC_A10

 PG1  ------> FMC_A11

 PC3  ------> FMC_SDCKE0

 PA7  ------> FMC_SDNWE

 PC4  ------> FMC_SDNE0

 PF11  ------> FMC_SDNRAS

 PD14  ------> FMC_D0

 PD15  ------> FMC_D1

 PD0  ------> FMC_D2

 PD1  ------> FMC_D3

 PE7  ------> FMC_D4

 PE8  ------> FMC_D5

 PE9  ------> FMC_D6

 PE10  ------> FMC_D7

 PE11  ------> FMC_D8

 PE12  ------> FMC_D9

 PE13  ------> FMC_D10

 PE14  ------> FMC_D11

 PE15  ------> FMC_D12

 PD8  ------> FMC_D13

 PD9  ------> FMC_D14

 PD10  ------> FMC_D15

 PG4  ------> FMC_BA0

 PG5  ------> FMC_BA1

 PG8  ------> FMC_SDCLK

 PG15  ------> FMC_SDNCAS

 PE0  ------> FMC_NBL0

 PE1  ------> FMC_NBL1

DMA (DMA2)

 DMA_InitTypeDef tInitDma;

tInitDma.Channel = DMA_CHANNEL_0;

tInitDma.Direction      = DMA_MEMORY_TO_MEMORY;

 tInitDma.PeriphInc      = DMA_PINC_ENABLE;

tInitDma.MemInc       = DMA_MINC_ENABLE;

tInitDma.PeriphDataAlignment = DMA_PDATAALIGN_WORD;

 tInitDma.MemDataAlignment  = DMA_MDATAALIGN_WORD;

 tInitDma.Mode        = DMA_NORMAL;

tInitDma.Priority      = DMA_PRIORITY_HIGH;

tInitDma.FIFOMode      = DMA_FIFOMODE_ENABLE;

tInitDma.FIFOThreshold    = DMA_FIFO_THRESHOLD_1QUARTERFULL;

 tInitDma.MemBurst      = DMA_MBURST_SINGLE;

 tInitDma.PeriphBurst     = DMA_PBURST_SINGLE;

Adress line A0-A11 is connected to SDRAM, A13 is DC (data or command) pin for LCD. The data lines D0-D15 are shared form SDRAm and SRAM(LCD).

I'm working on STM32F750Z8. Reffered to errata, there is no description that static and dynmaic memories doesn't work simultaneously.

In contrast to STM32F429, there could be a problem. (reffered errata) (https://community.st.com/s/question/0D50X0000A1lTvV/repost-touchgfx-port-for-stm32f4xx-using-8080-if-ii-mode-communication).