XMega32E5, UART in SPI mode, and EDMA

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

Hello all.

So I am setting up a few tests to read a block of SPI-attached flash mem (a W25Q16JV chip).

First test was just to be able to read from it. No prob.

Second test was to read from it using EDMA. Eventually, no prob. 

Third test was to prove that I was able to read in the data and store it in an array of bytes.

 

I have been confirming most of the TX and RX signals using a logic analyzer.

 

So the setup is: 

  • USART D in Master SPI mode
  • Two EDMA Peripheral channels, one for Rx, one for Tx. The Rx stores the DATA in an array, and the Tx just transmits a single "dummy" byte.

 

(For anyone not sure what this means, when you want to request data from a slave device over SPI, you usually send that slave a command and then X number of "dummy" bytes so that you can receive X number of bytes. Just the way SPI works.)

 

The problem: I can see the transmit and receive (MOSI and MISO) signals, along with CS and Clk, on the logic analyzer and they are exactly as expected.

So I decided to prove out that what was being stored in the byte array was correct. To do this, I just send out each byte of the array over the SPI and read it in the logic analyzer. Cheap and dirty debug. However, the data isn't correct. It seems that the data being stored in the array is just whatever the "dummy" byte is that I am using. It almost appears as if the EDMA channel that is "reading" the DATA register of the USART is not getting what was received but instead what was written to it. I'm hoping that I just messed up a setting in my code and that this is not a silicon bug.

Here's my code. It might not be pretty but it's just a PoC:

 

#define  F_CPU 32000000

#include <avr/io.h>
#include <util/delay.h>


#define MEM_READ_DATA 0x03

volatile uint8_t TX_DATA = 0x55;
volatile uint8_t buff[128];

void init_clock()
{
	
    OSC_CTRL |= OSC_RC32MEN_bm; //Setup 32Mhz internal
    
    while(!(OSC_STATUS & OSC_RC32MRDY_bm));
    
    CCP = CCP_IOREG_gc; //Trigger protection mechanism
    CLK_CTRL = CLK_SCLKSEL_RC32M_gc; //Enable internal  32Mhz internal
}

void init_usart()
{
	PORTD.DIRSET = PIN1_bm | PIN3_bm | PIN4_bm;
	PORTD.DIRCLR = PIN2_bm;
	
	USARTD0.CTRLA = USART_TXCINTLVL0_bm; //No interrupts
	USARTD0.CTRLB = USART_TXEN_bm | USART_RXEN_bm;// | USART_CLK2X_bm;
	USARTD0.CTRLC = USART_CMODE_MSPI_gc;
	USARTD0.CTRLD = 0; //No decoding or encoding
	USARTD0_BAUDCTRLA = 1;
	USARTD0.BAUDCTRLB = 0;
	
}

void init_mem_read_edma()
{
	//RX
	EDMA.CH0.CTRLA = EDMA_CH_SINGLE_bm;//no repeat, single shot, burst len = 1
	EDMA.CH0.CTRLB = 0; //ERR and TRN int level = 0
	EDMA.CH0.ADDRCTRL = EDMA_CH_RELOAD_TRANSACTION_gc | EDMA_CH_DIR_INC_gc; //
	EDMA.CH0.TRIGSRC = EDMA_CH_TRIGSRC_USARTD0_RXC_gc;

	//TX
	EDMA.CH1.CTRLA = EDMA_CH_SINGLE_bm;//no repeat, single shot, burst len = 1
	EDMA.CH1.CTRLB = 0; //ERR and TRN int level = 0
	EDMA.CH1.ADDRCTRL = EDMA_CH_RELOAD_TRANSACTION_gc | EDMA_CH_DIR_FIXED_gc; //
	EDMA.CH1.TRIGSRC = EDMA_CH_TRIGSRC_USARTD0_DRE_gc;
	
	EDMA.CTRL = EDMA_ENABLE_bm; //All ints off, perif mode, no double buff, round robin


}

void mem_read_edma(uint32_t starting_address, uint8_t *buff, uint8_t len)
{
	PORTD.OUTCLR = PIN4_bm; //Set Pin 4 (CS) to low
	
	//Send read command	
	USARTD0.DATA = MEM_READ_DATA; //Transmit data
	while(!(USARTD0.STATUS & USART_DREIF_bm));
		
	//Send address
	USARTD0.DATA = starting_address >> 16; //Send upper byte of 24 bit address
	while(!(USARTD0.STATUS & USART_DREIF_bm));

	USARTD0.DATA = starting_address >> 8; //Send middle byte
	while(!(USARTD0.STATUS & USART_DREIF_bm));

	USARTD0.DATA = starting_address >> 0; //Send lower byte
	while(!(USARTD0.STATUS & USART_DREIF_bm));
		
	
	EDMA.CH0.TRFCNT = len;
	EDMA.CH0.ADDR = (uint16_t)buff;
	EDMA.CH0.CTRLA |= EDMA_CH_ENABLE_bm;

	EDMA.CH1.TRFCNT = len;
	EDMA.CH1.ADDR = (uint16_t)&TX_DATA;
	EDMA.CH1.CTRLA |= EDMA_CH_ENABLE_bm | EDMA_CH_TRFREQ_bm;
	
	//End the read
	while( (EDMA.CH0.CTRLB & EDMA_CH_TRNIF_bm) == 0);
	while((USARTD0.STATUS & USART_TXCIF_bm) == 0 );	
		
	//Clear interrupt flags	
	USARTD0.STATUS |= USART_TXCIF_bm;
	EDMA.CH0.CTRLB |= EDMA_CH_TRNIF_bm;
	
	PORTD.OUTSET = PIN4_bm;
		
}

int main(void)
{
	init_clock();
	init_usart();
	init_mem_read_edma();
	PORTD.OUTSET = PIN4_bm;
	uint8_t i = 0;
	
    /* Replace with your application code */
    while (1) 
    {
		mem_read_edma(0, buff, 32);
		_delay_ms(1);
		
		//Test received	
		//Send read command first so we know we are not going to corrupt the data with a random command
		PORTD.OUTCLR = PIN4_bm; //Set Pin 4 (CS) to low
		USARTD0.DATA = MEM_READ_DATA; //Transmit data
		while(!(USARTD0.STATUS & USART_DREIF_bm));
	
		while (i < 32)
		{	
			USARTD0.DATA = buff[i]; 
			while(!(USARTD0.STATUS & USART_DREIF_bm));
			i++;
		}
	
		while((USARTD0.STATUS & USART_TXCIF_bm) == 0 );
	
	
		USARTD0.STATUS |= USART_TXCIF_bm;
	
		PORTD.OUTSET = PIN4_bm;	
		 i = 0;
		_delay_ms(99);

    }
}

I attached an export of the data as a csv. The first 4 lines are the "read" and 24-bit address command sent to the flash mem, followed by 32 lines with a 0x55 dummy Tx and that Rx from the flash. Then the next 33 lines should be another read command (0x03) and the 32 bytes stored in the byte array "buff".

 

Can anyone tell what I did wrong to cause this or if I am missing something here in my testing or code?

Thanks in advance!

Attachment(s): 

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

So with this question being a week old and with over 100 views and no answers, is there some other information I should include or something this question is making difficult to understand?

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

You are enabling the EMDA controller in your init routine. That prevents the address from being set/changed in the read routine.

From the XMEGA-E Manual:

 

 

Nevermind, You have the enables in the correct place.

 

I've only used standard mode DMA, not the peripheral mode of EDMA (fyi). I wonder if there's an issue with TRIGSRC.

 

Does it change at lower baudrate? I wonder if there is contention for the memory bus moving a byte each direction in just 4 cycles.

 

Does it change if you clear RXC after the address write? That might explain the first byte being wrong.

 

Last Edited: Tue. Oct 20, 2020 - 12:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks balisong42. Think both of those suggestions are worth exploring. I know I've been bitten by high rates for other things in the past.

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

Some errata make this problem difficult.

When using receive DMA, it is necessary to clear the unintended RXCIF.

The previous DMA transfer does not clear this.

Also, writing USART_RXCIF to USARTD0.STATUS does not clear it. (Contrary to the description in the data sheet)

The only way to clear this is to do an empty read.

    USARTD0.DATA;

 

And there is bad code that doesn't help solve this problem.

    USARTD0.STATUS | = USART_TXCIF_bm;

You probably want to clear only TXCIF.

But this clears all flags.

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

Thanks a ton, kabasan!

I'll make some updates and respond as soon as I can.