cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F1 transmit images from external FLASH to the LCD via SPI and DMA

Grzegorz Wójcik
Associate II

Hello,

my STM32F103C8T6 is being used for driving LCD display (ST7789, SPI2). Images are stored in the external FLASH memory (GD25Q16C, SPI1).

I am looking for an efficient way to transfer images from external FLASH to the LCD. In order to read data from the FLASH, I have to send start address, and then keep sending dummy bytes (so the chip increments address before sending new byte).

So far, I have managed to do the following procedure:

-tell LCD, that incoming bytes will be pixel colors,

-send start address to the External Flash chip

-send dummy byte, receive corresponding pixel color, send it to the LCD. Repeat 115200 times (1PAGE = 240x240x2bytes)

**LCD displays image**

I would like to improve this process, for example by using DMA in following way:

-tell LCD, that incoming bytes will be pixel colors,

-send start address to the External Flash chip

-enable SPI1 (flash) DMA transmit channel: send dummy byte 115200 times,

-enable SPI1 (flash) receive channel: receive data and put it into SPI2 (lcd) DMA Transmit register immediately

I am however unsure if this reasonable way, or if it is even possible to get data from SPI1 RX and put it into SPI2 TX by DMA

1 ACCEPTED SOLUTION

Accepted Solutions

On a 'F1 this IMO may be possible.

> -enable SPI1 (flash) DMA transmit channel: send dummy byte 115200 times,

> -enable SPI1 (flash) receive channel: receive data and put it into SPI2 (lcd) DMA Transmit register immediately

No, this happens simultaneously. So you want

  • enable DMA triggered by SPI1 Rx, set the DMA input pointer to SPI1 data register and output pointer to SPI2 data register (as there is no source of clocks yet, nothing will happen on the external buses at this point)
  • transmit into SPI1 Tx the dummy data, you can do it "manually" first, observing using logic analyzer or oscilloscope the data flow on both buses
  • when you get this working, you can replace the second step by setting up DMA triggered by SPI1 Tx, to transer the required number of transfers into data register

JW

View solution in original post

2 REPLIES 2

On a 'F1 this IMO may be possible.

> -enable SPI1 (flash) DMA transmit channel: send dummy byte 115200 times,

> -enable SPI1 (flash) receive channel: receive data and put it into SPI2 (lcd) DMA Transmit register immediately

No, this happens simultaneously. So you want

  • enable DMA triggered by SPI1 Rx, set the DMA input pointer to SPI1 data register and output pointer to SPI2 data register (as there is no source of clocks yet, nothing will happen on the external buses at this point)
  • transmit into SPI1 Tx the dummy data, you can do it "manually" first, observing using logic analyzer or oscilloscope the data flow on both buses
  • when you get this working, you can replace the second step by setting up DMA triggered by SPI1 Tx, to transer the required number of transfers into data register

JW

Grzegorz Wójcik
Associate II

@Community member​ thanks for your reply!

I did what you said, althought I had some doubts regarding DMA peripheral-to-peripheral transfer for STM32, mainly because during DMA configuration I havent seen option for periph-to-periph direction. But I have just put SPI register into MemoryBaseAddr, and this worked beautifully (which had shocked me by the way):

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPI2->DR;

Later on, I have added second DMA channel, that was used for sending dummy bytes to the external flash chip. All works very nice now 😉

If anyone has a similar problem, then this is my DMA configuration:

uint8_t volatile SPI_FLASH_dummyByte[1] = {0x00};
 
//==Configure DMA1 - Channel4== (SPI1.RX -> SPI2.TX)
/* External Flash sends data to our MCU -> this triggers DMA request
 * DMA handler passes this data straight to the LCD_SPI transmit register */
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&FLASH_SPIx->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&LCD_SPIx->DR;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 57600;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
 
//==Configure DMA1 - Channel5== (Memory -> SPI1.TX)
/* MCU sends dummy bytes to the External Flash, so the
 * External Flash sends us data, increments address
 * and repeats this DMA_BufferSize times */
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&FLASH_SPIx->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_FLASH_dummyByte;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 57600;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
 
 
/* Enable SPI_MASTER DMA Rx request for the EXTERNAL FLASH SPI */
SPI_I2S_DMACmd(FLASH_SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
 
/* Enable SPI_MASTER DMA Tx request for the EXTERNAL FLASH SPI */
SPI_I2S_DMACmd(FLASH_SPIx, SPI_I2S_DMAReq_Tx, ENABLE);

and this code triggers communication between external flash and my LCD screen (written in C++)

display->startDrawing(); 
display->setAddress(0, 0, LCD_W - 1, LCD_H - 1); 	//LCD will start filling screen within specified area
 
Ext_flash::select();			//Begin communication with flash chip
Ext_flash::spi_sendrecv(0x03);	//READ_DATA command
Ext_flash::spi_sendrecv( ((0x00 >> 16) & 0xFF ) );	//Send 24-bit address
Ext_flash::spi_sendrecv( ((0x00 >> 8) 	& 0xFF ) );
Ext_flash::spi_sendrecv( ((0x00) 		& 0xFF ) );
 
/*** Configure DMA1_Channel4 so it sends 57600 bytes from MCU to FLASH */
/* Disable DMA1_ChannelX in order to set new DataCounter */
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_SetCurrDataCounter(DMA1_Channel4, 57600);
 
/*** Configure DMA1_Channel5 so it receives 57600 bytes from FLASH (SPI2.RX) and immediately sends it to the LCD (SPI1.TX) */
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_ClearFlag(DMA1_FLAG_TC5);
DMA_SetCurrDataCounter(DMA1_Channel5, 57600);
 
 
/* Enable DMA channels */
DMA_Cmd(DMA1_Channel5, ENABLE);
/* Enable DMA channels */
DMA_Cmd(DMA1_Channel4, ENABLE);
/* Transfer complete */
while(!DMA_GetFlagStatus(DMA1_FLAG_TC5));
/* Transfer complete */
while(!DMA_GetFlagStatus(DMA1_FLAG_TC4));
 
Ext_flash::deselect();
display->endDrawing();