ATSAME70 | How to reduce the time between two transfers in DMA-SPI?

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

 

I am working on simple project in atsame70q21 xplained board (using atmel start v4), which is to drive RA8875 LCD controller via SPI protocol. I used LVGL library to make the GUI drawing on the screen. Till now I have managed to get it working smoothly. I did it with both sync SPI and dma SPI in two separate projects. The sync SPI version works perfectly with just 26us between each transfer. However, the dma SPI version on the other hand takes 4ms between each transfer.

 

I cannot use the sync version because while the data (more than 16000 bytes) is transferred, the processor is needed to process or render the next chunk of data.  Now, the problem is that when I send multiple transfers of two bytes each via DMA, it takes about 4ms between two transfers. The reason that each transfer has only two bytes because I want the chip select pin to be high at end of each second byte (to tell the LCD controller the transfer has ended) otherwise the LCD won't work. The only place I can keep it low in more than two bytes is when transferring the bitmap bytes. ( about 16000 bytes )

 

 

The code/logic I use in the dma version:

// Send a buffer with two bytes on each transfer
void RA8875_sendBuffer(int numOfBytes, uint8_t* currArray){
	currentCounter = 0;
	numberOfBytes = numOfBytes;
	currentArray = currArray;
	SPITransferDone = false;
	SPINoTransaction = false;

	spi_m_dma_transfer(&SPI_0, currArray, NULL, 2);
	while(!SPITransferDone){
		delay_us(1);
	}

	return RA8875_SUCCESS;
}

// Send large chunk of data in one transfer
void RA8875_flushBuffer(int numOfBytes, uint8_t* currArray){
    currentCounter = 0;
    numberOfBytes = -1;
    currentArray = currArray;
    SPINoTransaction = false;
    SPITransferDone = false;
    isFlushing = true;
    
    spi_m_dma_transfer(&SPI_0, currArray, NULL, numOfBytes);
    
    //return RA8875_SUCCESS;
}

 

 

// Callback function when DMA transfer completed.
static void tx_complete_cb_SPI_0(struct _dma_resource *resource)
{

	currentCounter++;

	// IsFlushing is true when large data is sent in one tranfer
	if(isFlushing){
		//printf("Flush is ended.\r\n");
		isFlushing = false;
		SPITransferDone = true;
		lv_disp_flush_ready(disp_drv_p);

	}else if(currentCounter < numberOfBytes/2){
		delay_us(100);
		spi_m_dma_transfer(&SPI_0, currentArray + (2*currentCounter) , NULL, 2);

	}else if(currentCounter == numberOfBytes/2){
			SPITransferDone = true;
	}

	SPINoTransaction = true;
}

 

SPI Config File:

/* Auto-generated config file hpl_spi_config.h */
#ifndef HPL_SPI_CONFIG_H
#define HPL_SPI_CONFIG_H

// <<< Use Configuration Wizard in Context Menu >>>

#include <peripheral_clk_config.h>

// Enable configuration of module
#ifndef CONF_SPI_0_ENABLE
#define CONF_SPI_0_ENABLE 1
#endif

// Set module in SPI Master mode
#ifndef CONF_SPI_0_MODE
#define CONF_SPI_0_MODE 0x01
#endif

// Set FIFO disable
#ifndef CONF_SPI_0_FIFO_DISABLE
#define CONF_SPI_0_FIFO_DISABLE 0x01
#endif

// Set peripheral select as fixed
#ifndef CONF_SPI_0_PS
#define CONF_SPI_0_PS 0x0
#endif

// Set chip select decode as directly
#ifndef CONF_SPI_0_PCSDEC
#define CONF_SPI_0_PCSDEC 0x0
#endif

//<o> SPI DMA TX Channel <0-23>
//<i> This defines DMA channel to be used
//<id> spi_master_dma_tx_channel
#ifndef CONF_SPI_0_M_DMA_TX_CHANNEL
#define CONF_SPI_0_M_DMA_TX_CHANNEL 0
#endif

// <e> SPI RX Channel Enable
// <id> spi_master_rx_channel
#ifndef CONF_SPI_0_RX_CHANNEL
#define CONF_SPI_0_RX_CHANNEL 1
#endif

//<o> DMA Channel <0-23>
//<i> This defines DMA channel to be used
//<id> spi_master_dma_rx_channel
#ifndef CONF_SPI_0_M_DMA_RX_CHANNEL
#define CONF_SPI_0_M_DMA_RX_CHANNEL 1
#endif

// </e>

// <h> Basic Configuration

// <o> Character Size
// <i> Bit size for all characters sent over the SPI bus (CHSIZE)
// <0x0=>8 bits
// <0x1=>9 bits
// <0x2=>10 bits
// <0x3=>11 bits
// <0x4=>12 bits
// <0x5=>13 bits
// <0x6=>14 bits
// <0x7=>15 bits
// <0x8=>16 bits
// <id> spi_master_character_size
#ifndef CONF_SPI_0_CHSIZE
#define CONF_SPI_0_CHSIZE 0x0
#endif

// <o> Baud rate <1-12000000>
// <i> The SPI data transfer rate. Note: (fspi_clock / baudrate) < 255
// <id> spi_master_baud_rate
#ifndef CONF_SPI_0_BAUD
#define CONF_SPI_0_BAUD 1000000
#endif

// </h>

// <e> Advanced Configuration
// <id> spi_master_advanced
#ifndef CONF_SPI_0_ADVANCED
#define CONF_SPI_0_ADVANCED 0
#endif

// <o> Dummy byte <0x00-0xFFFF>
// <id> spi_master_dummybyte
// <i> Dummy byte used when reading data from the slave without sending any data
#ifndef CONF_SPI_0_DUMMYBYTE
#define CONF_SPI_0_DUMMYBYTE 0xffff
#endif

// <o> Clock Polarity
// <0=>The inactive state value of SPCK is logic level zero.
// <1=>The inactive state value of SPCK is logic level one.
// <i> Determines the inactive state value of the serial clock (SPCK).
// <id> spi_master_arch_cpol
#ifndef CONF_SPI_0_CPOL
#define CONF_SPI_0_CPOL 0x0
#endif

// <o> Clock Phase
// <0x0=>Data is changed on the leading edge of SPCK and captured on the following edge of SPCK.
// <0x1=>Data is captured on the leading edge of SPCK and changed on the following edge of SPCK.
// <i> Determines which edge of SPCK causes data to change and which edge causes data to be captured.
// <id> spi_master_arch_cpha
#ifndef CONF_SPI_0_NCPHA
#define CONF_SPI_0_NCPHA 0x1
#endif

// <o> Delay Before SPCK (ns) <0-255000>
// <i> This field defines the delay from NPCS falling edge (activation) to the first valid SPCK transition (in ns).
// <id> spi_master_dlybs
#ifndef CONF_SPI_0_DLY_SPCK
#define CONF_SPI_0_DLY_SPCK 800
#endif

// <o> Delay Between Consecutive Transfers (ns) <0-8160000>
// <i> This field defines the delay between two consecutive transfers with the same peripheral without removing the chip select (in ns).
// <id> spi_master_dlybct
#ifndef CONF_SPI_0_DLY_BCT
#define CONF_SPI_0_DLY_BCT 800
#endif

// </e>

/* Calculate baud register value from requested baudrate value */
#ifndef CONF_SPI_0_BAUD_RATE
#define CONF_SPI_0_BAUD_RATE (CONF_SPI0_FREQUENCY / CONF_SPI_0_BAUD)
#endif

/* Calculates the value of the CSR DLYBS field given the desired delay (in ns) */
#ifndef CONF_SPI_0_DLYBS
#define CONF_SPI_0_DLYBS (((CONF_SPI0_FREQUENCY / 1000000) * CONF_SPI_0_DLY_SPCK) / 1000)
#endif

/* Calculates the value of the CSR DLYBCT field given the desired delay (in ns) */
#ifndef CONF_SPI_0_DLYBCT
#define CONF_SPI_0_DLYBCT (((CONF_SPI0_FREQUENCY / 1000000) * CONF_SPI_0_DLY_BCT) / 32000)
#endif

// <<< end of configuration section >>>

#endif // HPL_SPI_CONFIG_H

 

Is there any settings I can change to reduce this 4ms? or can I combine the sync version and the dma version in the same Atmel Start project in which I send large data through DMA and send the configuration data through sync spi? 

 

 

Thank you in advance.

 

 

This topic has a solution.
Last Edited: Tue. Jan 25, 2022 - 03:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This picture is from my logic analyzer to show the delay between two transfers.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you know where in the code this unexpected time is spent? Is it while waiting here?
 

    while(!SPITransferDone){
        delay_us(1);
    }

Can you attach a example project or at least the start configuration .atzip file?
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I removed the delay_us function and it doesn't change at all. Honestly, I don't know where the time is spent, but apparently the program wait in the while loop for the DMA complete callback function to be executed.

 

I stripped out any unrelated code to my problem from my project and uploaded it to my google drive, any suggestions are very very appreciated. 

 

GOOGLE DRIVE LINK

 

 

Thank you, 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I haven't really a clear advice but what about trying to change the parameters: 

CONF_SPI_0_DLYBCT, 
CONF_SPI_0_DLY_BCT
   and 
CONF_SPI_0_DLY_SPCK

 

 

Best Regards

Markus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried to set both CONF_SPI_0_DLY_BCT and CONF_SPI_0_DLY_BCT to zero and no noticeable changes appeared.

Last Edited: Mon. Jan 24, 2022 - 07:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a question. Is it a good idea to wait for the DMA interrupt to send the next two bytes or should I enable the SPI interrupts instead?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Finally, I found the thing that causes the problem.  I used somewhere in my program

spi_m_dma_set_baudrate(&SPI_0, 10);

and after that the delay appears which is very wired. smiley

 

Now the questions are how can I change the baudrate in runtime without using this method? Also, what is the maximum spi baudrate this processor can reach?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

You need "volatile" for the variables shared with the callback:

// ==========================
// Global Variables
// ==========================
uint8_t*	currentArray;
int		numberOfBytes;
int		currentCounter;
bool		SPITransferDone;
bool		isFlushing;
bool		SPINoTransaction;

Is this why you have turned optimization off (usually not good to turn that off completely)?
 

Why do you need to change the baudrate? Anyway, I expect the LCD would be limiting this, the SAME70 limit is in "59.13.1.6.1 Maximum SPI Frequency", there is no absolute number there because the limit is not from the clock generation but from the IO. Likely > 20MHz but that depends on how the LCD is connected.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ooh nice catch, I just realized that I changed the optimization to none just for debugging. When the debugger stops at a breakpoint most of the variables are optimized out. I will set it to default and see what changes. 

 

​​​​​​

The lcd controller doesnt work with high frequency., so I need to initialize the lcd controller's PLL first at low frequency. After that it should be fine to increase the frequency and continue drawing with no problems. So that why I need to change the baudrate, any idea how to do it? 

 

 

Thank you Lajon and Markus for your support I really really appreciate your help yes

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I turned the optimization on again to (-O3) in addition to making the variables volatile, and that solved the problem ( becomes about 105us ) << If there are more suggestions to make it lesser I wouldn't say no winklaugh.

 

Thank you soo much lajon. heart

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
		delay_us(100);

Is this still in the callback?

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lajon wrote:

		delay_us(100);

Is this still in the callback?

/Lars

 

 

Yes, it still there. When I remove it the LCD goes to the weird mode and doesn't work properly. I make it 80us and the delay between transfer becomes about 85us. So I guess this delay cannot be minimized since it is needed by the LCD.  

That is amazing, I am very happy now. 

 

Thanks a lot. 🌹