Pages: [1]   Go Down
Author Topic: SPI byte echo  (Read 668 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello, I'm trying to get 2 arduinos talking over SPI. I want the master to send something (anything really, doesn't matter what it is) to the slave, and then have the slave echo that same byte straight back to the master. Can anybody give me a super simple, bare bones example of this? Heres what I have at the moment, which doesn't work (also from 2 seperate sketches I downloaded so probably messy...)

MASTER:
#include <SPI.h>

void setup (void)
{

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  Serial.begin(115200);
 
}  // end of setup


void loop (void)
{

int c = 0x0FF;
  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
    SPI.transfer (c);
   
    Serial.println("Sent");

  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (1000);  // 1 seconds delay
}  //




SLAVE:

#include <SPI.h>
 
char buf [100];
volatile byte pos;
volatile boolean process_it;
 
void setup (void)
{
  Serial.begin (115200);   // debugging
 
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
 
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
 
  // get ready for an interrupt
  pos = 0;   // buffer empty
  process_it = false;
 
  // now turn on interrupts
  SPI.attachInterrupt();
 
}  // end of setup
 
 
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
 
  // add to buffer if room
  if (pos < sizeof buf)
    {
    buf [pos++] = c;
   
    // example: newline means time to process buffer
 
     
    }  // end of room available
}  // end of interrupt routine SPI_STC_vect
 
// main loop - wait for flag set in interrupt routine
void loop (void)
{
 
    Serial.println (buf);
    pos = 0;
   
    }  // end of flag set
   
Logged

0
Offline Offline
Shannon Member
****
Karma: 200
Posts: 11694
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Please
Code:
// use the code (#) button to put code snippets in code tags like this

The slave interrupt handler has to set SPDR to the just-received value, then this will be
sent back on the _next_ SPI transfer.
Code:
// SPI interrupt routine
ISR (SPI_STC_vect)
{
  byte c = SPDR;  // grab byte from SPI Data Register
  SPDR = c ;
}

Writing SPDR puts a byte into the outgoing register, which is separate from the incoming register (which is actually double-buffered)

In the slave's setup() function set SPDR to the initial byte to send upon the first transfer...



I've got this working some time ago but without using the SPI library - presumably the same interrupt handling is compatible with
the SPI library.  You've fortunately used a clock rate that's 1/4 of system clock or slower (a requirement for slave to operate correctly).
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1) writing a spi slave is no simple matter;
2) you will need a way for the slave to understand when it is time to send data (aka a protocol).
Logged

0
Offline Offline
Shannon Member
****
Karma: 200
Posts: 11694
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1) writing a spi slave is no simple matter;
2) you will need a way for the slave to understand when it is time to send data (aka a protocol).


1) Well here's a (very simple) proof-of-concept SPI slave I wrote, and
2) the slave never has to understand _when_ it is time to send data, because the master always makes that decision for it. 
It does have to decide _what_ to send next time and stick it in SPDR.

A simple SPI slave looks at a command byte and queues up some response bytes or just does something...  The
same as for serial in essence.

Code:
const int pinSCLK = 13 ;
const int pinMISO = 12 ;
const int pinMOSI = 11 ;
const int pinSS = 10 ;

#include <avr/io.h>
#include <avr/interrupt.h>


byte message [8] ;
byte resp [8] ;
byte N = 0 ;

ISR (SPI_STC_vect)
{
  byte to = 0 ;
  while (to < 50 && !(SPSR & (1 << SPIF)))
  {
    to ++ ;
  }
  to = SPDR ;
  if (to == 0xE5 && N == 0)
  {
    N = 1 ;
    SPDR = 0x55 ;
  }
  else if (to == 0x6B && N == 1)
  {
    N = 2 ;
    SPDR = resp [0] ;
  }
  else if (N >= 2)
  {
    message [N-2] = to ;
    if (N < 9)
    {
      SPDR = resp [N-1] ;
      N++ ;
    }
    else
    {
      SPDR = 0xAA ;
      N = 0 ;
    }
  }
}

void setup ()
{
  pinMode (pinSS, INPUT) ;
 
  // SPCR = 11000011
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/16 rate
 
  SPCR = (1<<SPIE) | (1<<SPE) | (0<<MSTR) | 0x1 ;
  int junk = SPSR ;
  SPDR = 0xAA ; // first response
  pinMode (pinMISO, OUTPUT) ;
 
  Serial.begin (57600) ;
}

void barchart (byte size, byte percent)
{
  Serial.print ((char) 0xFE) ;
  Serial.print ('b') ;
  Serial.print ((char) size) ;
  Serial.print ((char) percent) ;
}
 
void loop ()
{
  delay (200) ;
  byte foo [8] ;
  foo[0] = message[0] ;
  foo[1] = message[1] ;
  foo[2] = message[2] ;
  foo[3] = message[3] ;
  foo[4] = message[4] ;
  foo[5] = message[5] ;
  foo[6] = message[6] ;
  foo[7] = message[7] ;
  for (byte i = 0 ; i < 6 ; i++)
  {
    barchart (i < 4 ? 3 : 2, foo[i] >> 1) ;
    resp [i] = analogRead (i) >> 2 ;
  }
  for (byte i = 0 ; i < 6 ; i++)
    barchart (i < 4 ? 3 : 2, resp[i] >> 1) ;
  Serial.println () ;
  delay (200) ;
}


Logged

[ I won't respond to messages, use the forum please ]

Pages: [1]   Go Up
Jump to: