xmega32C4 strange USARTC0 in SPI mode behavior (SOLVED)

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

Folks,

 

I've written a short test program to verify that USARTC0 in SPI mode on a ATXMEGA32C4 is working correctly.  I'm new to the xmega's and wanted to build up in steps.

 

I've initialized the chipselect, SCK and MOSI pins as outputs and set the CS pin high and verified that it is doing as it should in the simulator I/O view.

 

The main routine simply sets the sysclock to the 32MHz internal clock (also verified working) then configures the USART like so:

USARTC0.CTRLB = (USART_TXEN_bm | USART_RXEN_bm);
USARTC0.CTRLC = (USART_CMODE_MSPI_gc);

Basically, I am just enabling both the receiver and transmitter and then putting it in SPI.  SBEL is 0, so it is running as fast as possible (FPER/2 = 16 MHz).

 

After that, I go into the while(1) loop and send out blocks of data:

	uint8_t count = 0;
	while (1)
	{
		count = 0;
		TFT_CS_PORT.OUTCLR = TFT_CS_PIN;                     // Set chip select output low
		while (count++ < 6)                                  // Just to have some different data to send each time
		{
			USARTC0.DATA = count*4;                      // Multiplying by four to make the different bytes very obvious on the logic analyser
			while(!(USARTC0.STATUS & USART_TXCIF_bm));   // Wait for it to complete sending this byte
		}

		TFT_CS_PORT.OUTSET = TFT_CS_PIN;                     // Set the chip select pin high once more 

		_delay_us(20);                                       // Delay a short bit to put a bit of space between each transactions
	}

It works.  The USART is putting out SPI data (and correct data at that) and it is repeating exactly as ordered.

 

This is the problem though:

XMEGA32C4 SPI Output

 

As you can see from the code, the chip select line is dropped at the beginning of the transaction (above the while loop) and CS is clearly asserted several microseconds before the SCK starts up.  But the chip select line is deasserted WELL before the transaction finishes.  My understanding of the datasheet says the TXCIF flag in the STATUS register is set once ALL the bits have been sent:

 Bit 6 – TXCIF: Transmit Complete Interrupt Flag
This flag is set when the entire frame in the transmit shift register has been shifted out and there are no new data in the
transmit buffer (DATA). TXCIF is automatically cleared when the transmit complete interrupt vector is executed.

Given that the chip select line is raised below the loop and before the short wait, I would think that I couldn't get to that line UNTIL everything has been sent.  I also did check the definition of TXCIF in the header and it does match the datasheet bit position:

#define USART_TXCIF_bm  0x40  /* Transmit Interrupt Flag bit mask. */

So that this point, I have to assume I am missing something, but I have no idea what it could be.  My first thought was "VOLATILE!", but the only variable defined by me is the count and it is only changed under program control, so it isn't volatile.  Any suggestions?

 

Regards,

Clint

 

Clint

Last Edited: Wed. Jan 11, 2017 - 05:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well that is interesting.  I turned off optimizations completely and it gave me this:

XMEGA32C4 SPI Output no compiler optimizations

 

This looks much more like what I expect:

  1. Chip select lowered
  2. Bytes sent
  3. Wait for finish
  4. Chip select raised.

 

For this one, I turned OFF all compiler optimizations.  It was at -O1. 

 

I liked the fact that the compiler had the bytes right on top of each other with no detectable delay between them.  But that whole raising the chip select in the middle of a transaction will not turn out well.  Any ides on how to address this optimization issue (assuming that truly is the issue)?

 

Clint

 

Clint

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

From the C Manual:

 

 

 

You aren't clearing the TXCIF flag.  Try this:

	while (count++ < 6)
	{
		USARTC0.DATA = count*4;
		while(!(USARTC0.STATUS & USART_TXCIF_bm));
		USARTC0.STATUS |= USART_TXCIF_bm.
	}

And turn optimization back on.

 

 

EDIT: or this:

	while (count++ < 6)
	{
		USARTC0.STATUS |= USART_TXCIF_bm.
		USARTC0.DATA = count*4;
		while(!(USARTC0.STATUS & USART_TXCIF_bm));
	}

 

Greg Muth

Portland, OR, US

Last Edited: Tue. Jan 10, 2017 - 09:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greg,

 

I was working on some related code just now and needed to look at the transmit buffer empty flag.  I opened up the datasheet to look at the status register to get the name of this register and read the other register descriptions.

 

And noticed that that (TXCIF) was the INTERRUPT flag...  So I come here to verify which flag I used in my example code (I didn't have the test program project open) and here was your reply!

 

Thank you.

 

I really hate these little "that isn't working QUITE right..." issues.  It is ALWAYS something I overlooked or misread in the datasheet!  Ok.  Or a stupid typo!

 

Clint

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

So, I fired it up real quick and made a few changes:

  • Reenabled optimization (-O1)
  • Changed the flag I THOUGH was "transmit register empty flag" (TXCIF) to "transmit BUFFER empty flag" (DREIF)
  • Added a while loop to test for the TXCIF flag once all the data has been sent and the send while exits (before CS high)
  • Reset the TXCIF flag
  • Removed the short delay

 

Now it looks like this:

XMEGA32C4 SPI Fixed

 

That looks MUCH better!  I'm actually a bit surprised just how tight the double buffer keeps the data bytes at this speed (F_CPU/2).

 

Clint

 

Clint

Last Edited: Wed. Jan 11, 2017 - 05:06 AM