I have written a simple SPI slave on an ATMEGA328PB. It mostly works, however, I can't seem to get the first byte to be what I want. The difference between the first, second, and all other attempts is also confusing me.
The master pulls the SS line low, waits 500us, then clocks out 15 '0xFF' followed by a '0x00', waits another 500us, then releases the SS. Simultaneously the AVR responds with its 'payload'.
#define F_CPU 8000000L
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#define togbit(port,bit) (port) ^= (1 << (bit)) // XOR
// SPI variables
volatile uint8_t received = 0;
volatile uint8_t cnt = 0;
volatile uint8_t incoming[16] = {0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA};
volatile uint8_t payload[16] = "ABCDEFGHIJKLMNO";
uint8_t spiCom(uint8_t data)
{
SPDR0 = data; // Put current 'payload' into outgoing buffer
return SPDR0; // return incoming byte
}
ISR(SPI0_STC_vect)
{
incoming[received] = spiCom(payload[cnt]); // Transfer a byte
if (incoming[received] == 0xFF) // start clocking out data when receiving 0xFF padding
{
cnt++;
received++;
}
if (received >= 16 || incoming[received] == 0x00) // reset array position counters if buffer overflows or received NULL
{
received = 0;
cnt = 0;
}
}
void initSPI(void)
{
volatile char IOReg; // Ignore compiler warnings about me...I'm needed to clear a flag
DDRB |= (1<<PINB4); // Set PB4(MISO) as output
SPCR0 |= (1<<SPIE)|(1<<SPE); // Enable SPI Interrupt and Slave Mode with SCK = CK/4
// Clear SPIF bit in SPSR by accessing the SPI registers
IOReg = SPSR0;
IOReg = SPDR0;
sei();
}
int main(void)
{
DDRB = 0x00; // all of PORTB is input
initSPI();
DDRB |= (1<<PINB0); // LED I/O is output
SPDR0 = 0xC8; // pre-load something identifiable into SPI buffer
while (1)
{
togbit(PORTB, PINB0); // Blink LED while waiting for SPI interrupt
_delay_ms(1000);
}
}
The first attempt at communication, the master receives:
0xC8,A,B,C,D…etc.
The second attempt at communication, the master receives:
0x00,A,B,C,D…etc.
The third and all subsequent communications, the master receives:
A,A,B,C,D…etc.
These signals were verified by my logic analyzer.
I can almost accept how the first attempt goes, since 0xC8 is what I pre-load the SPDR register with (as a test). That said, I still found this behavior unexpected, so I must be misunderstanding something in the SPI section of the AVR's datasheet.
I cannot wrap my head around what is happening after that. If my misunderstanding has to do with the order with which the SPDR register (buffer?) is handled during communication, I still can't figure out the difference between the second and third (and all subsequent) communications.
I've read just about every SPI on AVR tutorial I could find, but can't figure out where I am going wrong. Any advice would be much appreciated.
Best Answer
The answer is in your question already. Simultaneously being the key word.
Interrupt is called on RX complete, which is when the a byte from master has already been received. This must then also mean a byte from slave has already been sent, and your code sets up the next byte to be sent.
You have to set up the first byte to be sent before the communication starts.