I have an UNO R3 as SPI master and an ATmega 1284P as SPI slave. I'm trying to get some communication going between them. I have started with simple memory read/write. I have run into a strange timing issue that I can not explain. When reading memory from the slave, the first byte of memory returned is the last address byte sent and not the desired memory. However if I insert a small 10 microsecond delay between writing the last address byte and reading the first byte of memory, it works. I am hoping you can take a look and tell me what is going on.
Here is the master code...
#include <SPI.h>
#define READ_CMD 1
#define WRITE_CMD 2
#define SSpin 6
void setup() {
Serial.begin(19200);
pinMode(SSpin,OUTPUT);
digitalWrite(SSpin,HIGH);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV32);
}
void loop() {
char c;
while (Serial.available() > 0) {
c = Serial.read();
if (c=='w') {
Serial.println("write");
writeData();
}
else if (c=='r') {
Serial.println("read");
readData();
}
}
}
void writeData() {
byte d;
unsigned int address=1;
digitalWrite(SSpin,LOW);
SPI.transfer(WRITE_CMD);
SPI.transfer((address >> 8) & 0xff);
SPI.transfer(address & 0xff);
for (int i=10;i<20;i++) {
SPI.transfer(i);
}
digitalWrite(SSpin,HIGH);
}
void readData() {
byte d;
unsigned int address=1;
digitalWrite(SSpin,LOW);
SPI.transfer(READ_CMD);
SPI.transfer((address >> 8) & 0xff);
d = SPI.transfer(address & 0xff);
//delayMicroseconds(10);
for (int i=0;i<10;i++) {
d = SPI.transfer(0);
Serial.println(d);
}
digitalWrite(SSpin,HIGH);
}
Here is the slave code...
#define READ_CMD 1
#define WRITE_CMD 2
// states
#define COMMAND 1
#define ADDR1 2
#define ADDR2 3
#define DATA 4
#define ERROR 5
#define DATA_SIZE 4096
volatile byte command = 0;
volatile byte state=COMMAND;
volatile unsigned int address;
volatile byte eeprom[DATA_SIZE];
void setup (void) {
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
}
ISR (SPI_STC_vect) {
byte c = SPDR;
switch (state) {
case COMMAND:
command = c;
SPDR = 0;
if (command == READ_CMD || command == WRITE_CMD)
state = ADDR1;
else
state = ERROR;
break;
case ADDR1:
address = c;
SPDR = 0;
state = ADDR2;
break;
case ADDR2:
address = address << 8 | c;
address %= DATA_SIZE;
SPDR = eeprom[address];
state = DATA;
if (command == WRITE_CMD)
break;
case DATA:
if (command == READ_CMD) {
SPDR = eeprom[address];
}
else if (command == WRITE_CMD) {
eeprom[address] = c;
SPDR = 0;
}
++address %= DATA_SIZE;
break;
default:
SPDR = 0;
break;
}
}
void loop (void)
{
if (digitalRead (SS) == HIGH)
state = COMMAND;
}
And here is the serial output...
read
1
11
12
13
14
15
16
17
18
19