At wits' end with multi-byte SPI transfer

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

Fellow 'Freaks, I'm at my wits' end trying to get multi-byte SPI transmissions to work. I have an AVR SPI master sending a buffer (a union of some data and uint8_t[]) to the slave, periodically. The buffer is 16 bytes in size.

The slave's INT0 fires when the master wants to send. The slave raise a CTS (clear-to-send) signal when ready, then the master fires away. Here is the slave code:

/*==== THE MASTER CLOCK TICK ISR ==================================*/
ISR (INT0_vect)
{
	clk50Hz = true;
}
/*=================================================================*/


/*==== MAIN LOOP ==================================================*/
int main(void)
{
	initialize();
	while(true)
	{
		// bail if ISR has not set flag
		if (clk50Hz==false) goto MAIN_BOTTOM;

		// bail if master not ready to talk yet
		if (bit_is_set(PIN_SPI, IO_SS)) goto MAIN_BOTTOM;

		// reset byte counter
		spiByteCount = 0;

		// test pin on
		PORTA |= (1<<LED_TEST1);

		// set clear-to-send high to signal GO to master
		PORT_CTS |= (1<<CTS);

		while(!(SPSR & (1<<SPIF)))
		;
		xmitUnion.data[spiByteCount] = SPDR;
		spiByteCount++;
		SPDR = 0x55;

		// turn off clear-to-send until next time
		PORT_CTS &= ~(1<<CTS);

		// test pin off
		PORTA &= ~(1<<LED_TEST1);

		// Don't run again until flag is set again next tick.
		clk50Hz = false;

MAIN_BOTTOM: ;
	}
	return 0;
}
/*=================================================================*/

My scope shows that LED_TEST1 goes on and off almost instantly while something is passing on MOSI, but I can't figure out what.

I can't figure out how to get my debugger (JTAGICE II) to inspect the xmitUnion data, and it's worrisome that the LED_TEST1 goes on and off well before SCK even starts on byte zero.

Basically I'm wondering, do I have the right paradigm here? On INT0, set a flag in the main loop that shouldn't exit until the slave's SS line is brought high again, and all data stored in xmitUnion? What am I missing?

Getting grayer by the hour,

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

Before anyone posts a response to your other double post, you can delete it. Please do.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Thanks, Chuck, unintended, and deleted.

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

Why not use the existing hardware?

The Master asserts the /SS pin and starts sending its data.

The Slave either just replies that it is busy or a 0x55 if ok. If you use an SPI interrupt, your main Slave code is unaffected.

Of course you can use a separate CTS pin. The Master does not send until CTS is true.

Whichever way you do it, the Master should de-assert /SS when finished.

David.

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

David,

The main reason I'm using a CTS line instead of the method you outline is that the slave (when finished) will have some very time-sensitive interrupt-based code, and so far I've not figured out a way to tolerate an SPI interrupt that could add up to almost a millisecond at FCPU/8.

But having the difficulties I'm having is making me reconsider some things -- that's why I asked if my paradigm is wrong -- and will experiment on your idea today. Thanks!

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

Quote:

...to tolerate an SPI interrupt that could add up to almost a millisecond at FCPU/8.

You are losing me. The code above showed polled SPI slave. No SPI interrupt in sight.

At 8MHz a millisecond is 8000 CPU cycles. You don't have to sit and wait with interrupt-driven SPI; you get one interrupt when a byte transfer is complete. About 12 cycle overhead per ISR, plus register saving/restoring, plus your code. With careful coding let's say 40 to 50 cycles. So that is like 5-6us. Certainly there are apps that would need to eschew that--but rare.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

I'm going to investigate -- as I mentioned, I'm open to ideas -- but the code sample is simplified. There is a time slice during which a 4us delay will result in a tangible error. That's why, so far, I've clung to the idea of the slave having a state machine that only allows SPI during a certain time, then it does the next state, and the next... with total predictability.

When running for a long time, clock slew between the master and slave became a problem, so now the master issues a periodic strobe -- that's where the INT0 signal comes from.

I've been told that this is too much for a microcontroller, but those were ARMheads trying to get me to switch. ;)

I have one question in particular: Watching MISO vs. /SS, it looks like this is working:

      while(!(SPSR & (1<<SPIF)))
      ;
      xmitUnion.data[spiByteCount] = SPDR;
      spiByteCount++;
      SPDR = 0x55;

However, I can't figure out why activity created by that is not bounded by the CTS signal.

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

I am also at a loss. Using interrupts means that you spend NO time waiting in loops.

The actual service routine is a few microseconds even at 1MHz clock. And if you are really sensitive for a 4us period, disable the SPI irqs or even all irqs.

The point about using the hardware is that no time is wasted by your foreground code.

You can of course use a CTS pin. And your interrupt can immediately de-assert CTS as soon as it has received its data. You assert it later on when you are ready.

David.