on my scope the only time the clock is active (which is generated by the master) is when the master is sending characters.
That's correct, I was referring to this
the master must do that by sending marker characters such as nulls.
and meant that there is no need for special marker or sync characters. Maybe I should have said that the clock pulses are independant of the "contents" of the data, not the fact that there
is data.
Here is a code snippet that should send 8 bytes back to the master
volatile byte spi_frame[] = {1,2,3,4,5,6,7,8};
volatile byte *spi_frame_ptr = NULL;
setup () {
SPDR = spi_buffer[0];
SPCR = (1 << SPE) | (1 << SPIE);
}
ISR (SPI_STC_vect) {
/////////////////////////////////////////////////////
// this interrupt is caused by the fact that the first byte
// has already gone.
//
// Now we're here though we don't use interrupts but poll the
// SPIF flag. This gives much faster response to the server and
// reduces the delays the server has to apply between bytes.
byte x;
unsigned int count = 0;
spi_frame_ptr = &spi_frame[1];
////////////////////////////////////////////////////
// set up a mask so we're not shifting bits during reception
// Why, because I'm not convinced the optimizer will turn
// (1 << SPIF) into a constant (should check this)
byte and_mask = (1 << SPIF);
////////////////////////////////////////////////////
// the first byte of the frame has gone, send the next 7
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // potential hangs here !!!!!!!!!!!!!
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 3
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 4
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 5
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 6
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 7
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {}; // byte 8
////////////////////////////////////////////////////
// clear the interrupt flag that would have been set
// by the above serial transfers
x = SPSR;
x = SPDR;
////////////////////////////////////////////////////////
// seed the SPI data register with the first character
// of the next frame ready for the next clock pulse
// stream from the master
SPDR = spi_frame[0]; // get a value from wherever so it's ready for the next transfer
}
NOTES:
- I have cut this down from something that had some error checking and double buffering of the data so there's a big chance that I stuffed something up in the process.
- For an ISR this is a very long function, however if the slave has nothing else to do this doesn't matter and anyway if you need speed I see no other way,
- If for any reason the master stops mid frame the slave will hang
- The master still has to insert delays, you need a longish delay after the first transfer to allow the ISR to be serviced, then short delays after each byte
- If the master and slave get out of sync you are in big trouble, most of the code I removed was to handle this situation but I didn't have an SS line to sync on and you do
This is the fastest way I know to get SPI data from an AVR processor without using assembler (damn AVR for not buffering the SPI). You could use the SPI_STC_vect interrupt for every byte but that would be a lot slower. To do this basically just remove all the while loops and load the next byte into SPDR, however the master will have to insert a longish delay for every byte in that case.
so I can preload before setting it
AFIAK that's the only way to do this.
______
Rob