lardconcepts
Posts: 36
Joined: Mon Feb 04, 2013 11:32 am

SPI to Arduino slave with wiringPI2 on Raspberry PI master?

Sun Sep 01, 2013 7:05 am

After a bit of a fight, I managed to get wiringpi2 and the the wiringpi2 python wrapper installed on the NOOBS Raspbian PI distro. The Adafruit 4 channel logic level converter kept the PI safe and sending data to the Arduino was as simple as this on the PI side:

Code: Select all

import wiringpi2
wiringpi2.wiringPiSPISetup(1,5000)
wiringpi2.wiringPiSPIDataRW(1,'HELLO WORLD\n')
plus the Arduino code here from Nick Gammon.

So, I know the wiring works. But that's not the way round I actually want - I want to read a pin from the Arduino to the PI.
Well, ultimately, I've got to read the number of clicks from a rotary encoder since it was last read, and the direction turned. But right now I'd be happy to read ANYTHING!

I read the Arduino SPI reference which states:
This library allows you to communicate with SPI devices, with the Arduino as the master device.
The PI must be the master device. I thought I was doomed until I read Nick Gammon's excellent page about Arduino SPI which demonstrates 2 Arduinii talking to each other.

Also, the Arduino SPI transfer() command would suggest you CAN write from the Arduino.

I'm now at the stage where all the links of the the first 4 result pages of Google show as "followed" - so it's not for lack of Googling!

In theory, shouldn't this work if I use the READ method on the PI end? (Note: this is just one of many, many attempts, not the only one!)

Code: Select all

#include <SPI.h>
void setup (void)
{
  SPI.begin();
  pinMode(MISO, OUTPUT);
  
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
}

void loop  (void) {
  SPI.transfer('TEST'); 
}
And on the PI end of things:

Code: Select all

import wiringpi2
wiringpi2.wiringPiSPISetup(1,5000)
stuff = wiringpi2.wiringPiSPIDataRW(1,'\n')
print stuff
WiringPI says the incoming data will overwrite my data, and the SPIDataRW takes exactly 2 inputs, so shouldn't I be getting "test" back?

What am I missing here? Any pointers greatly appreciated. And to pre-empt all the obvious questions:
  • Yes, I know I could easily plug a rotary directly to the PI's GPIO lines and use any number of other libraries like Py-Gaugette.
    Yes, I know there are probably better ways than SPI for this.
But I don't have a choice :)

Thanks!

lardconcepts
Posts: 36
Joined: Mon Feb 04, 2013 11:32 am

Re: SPI to Arduino slave with wiringPI2 on Raspberry PI mast

Thu Sep 12, 2013 2:39 pm

I bought a £10 logic analyser which helped a lot, so I'm now at the stage where I've got something working. I've documented and written up the whole thing along with code and Fritzing diagrams, installation guide etc.
https://gist.github.com/lardconcepts/6528855

It works. Just. But the code is horribly inefficient especially in a couple of places and makes the PI run at 50% CPU when polling the Arduino 100 times a second.

I'd appreciated advice on two bits in particular. When you send a bit on SPI, you get a bit back. That first bit is junk really, as the slave end doesn't know what you want.
If I just use "wiringpi2.wiringPiSPIDataRW(1,myData)" to get a response from the Arduino, I find the data "ping pongs" back and forth up to 7 (but never more) times.
To make matters more confusing, myData becomes the data being received back at the same time as you send it.
The only way I found round it was to call wiringPiSPIDataRW TWICE - this stops the "ping pong" but doubles the load.

Code: Select all

    # Because SPI recevies a byte when it sends, for some reason I found you 
    # need to send TWO calls oherwise you get into a "ping pong" scenario 
    wiringpi2.wiringPiSPIDataRW(1,myData)
    wiringpi2.wiringPiSPIDataRW(1,myData)
    print myData
The other problem I had was testing the byte for junk before processing it.
If it's 0x03, it's nothing that I want - it means no buttons are pressed. Anything else and I want to do a switch/case on it.

If I run the incoming byte through

Code: Select all

hexdata = ''.join('%02x' % ord(byte) for byte in myData)
then I get something that both python and myself can "see".

But if I try

Code: Select all

if myData != 0x03 
(or 03 or '03' or '0x03' or '3' or 3 and so on)

then it never tests true (or false). So it always has to be run through the hexdata routine, meaning more unnecessary CPU work.

Code: Select all

    # Turn the data into something we can use
    # http://stackoverflow.com/questions/1916928/
    hexdata = ''.join('%02x' % ord(byte) for byte in myData)
    doActions(hexdata)

[...]

def doActions(hexdata):    
# See http://www.asciitable.com/
# DEFAULT 03 LEFT: 13 MID: 0b RIGHT: 07 L+M 1b R+M: 0f
    for case in switch(hexdata):
        if case('03'): # default, could also just omit condition or 'if True'
            #print hexdata
            middle.config(state='disabled')
            right.config(state='disabled')
            left.config(state='disabled')
            break
         # No need to break here, it'll stop anyway        
        if case('13'):
            #print ("left")
            left.config(state='normal')
            break
        if case('0b'):
            #print "middle"
            middle.config(state='normal')
            break
[...]
And finally, a tkinter question!

I've setup 3 buttons to represent switches pressed. Is there a more efficient way of getting the buttons to default to their disabled state than resetting their config to "disabled" on each and every pass (ie: 100 times per second) which I'm doing here:

Code: Select all

if case('03'): # default, could also just omit condition or 'if True'
            #print hexdata
            middle.config(state='disabled')
            right.config(state='disabled')
            left.config(state='disabled')
            break
[...] 
All advice appreciated by this learning newbie who's googled but not found all the answers....

lardconcepts
Posts: 36
Joined: Mon Feb 04, 2013 11:32 am

Re: SPI to Arduino slave with wiringPI2 on Raspberry PI mast

Thu Oct 03, 2013 8:14 pm

I'm trying to do some communicating between the Raspberry PI and the Arduino.

I'm using Nick Gammon's excellent page on Arduino SPIand Gordon Henderson's also-excellent WiringPI, which implements Wiring on a PI, along with his SPI library.

Examples for Gordon's PI SPI library are sparse, but following one of the examples I've now got this on the PI:

Code: Select all

#include <stdio.h>
#include <inttypes.h> // provides uint8_t etc
#include <wiringPi.h>
#include <wiringPiSPI.h>

void sendSPI (void);


int main (void)
{   
    wiringPiSetup();
	wiringPiSPISetup(1, 500000);
    sendSPI();
    return(0); 
}  


void sendSPI (void)
{
uint8_t a[] = {'H'};
uint8_t b[] = {'\n'};
wiringPiSPIDataRW(1,a,1);
wiringPiSPIDataRW(1,b,1);
    
uint8_t p[] = {'H','E','L','L','O','\n'};
    while (1){
       wiringPiSPIDataRW(1,p,6);
        delayMicroseconds(1);  // 1 seconds delay 
    }
}
And this on the Arduino:

Code: Select all

#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
    if (c == '\n')
      process_it = true;
      
    }  // end of room available
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;  
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // end of flag set
    
}  // end of loop
Nothing happens on the Arduino serial monitor, so I plugged in my logic analyzer and saw this:

Image

Eh?! What's happening here?!

You'll notice I first of all run
wiringPiSPIDataRW(1,a,1);
wiringPiSPIDataRW(1,b,1);

before sending it into a loop. This is because I know that the Arduino receiving code is waiting for separate "triggers", rather than the "burst" I'm sending it. Either way, what I can't figure out after several long, long nights is how/why it seems to be doing this "cyclical" ping pong. I've tried slower speeds, faster speeds, I've tried wiringPiSPIDataRW(1,p,7); and wiringPiSPIDataRW(1,p,5);. Why is the Arduino even responding with what I sent it just before?!

Here's more data. No idea how to get this table to line up. Sorry!

Code: Select all

Time [s]	Packet ID	MOSI	MISO
0		H	\n
0.000097		\n	H
0.000196		H	\n
0.000215		E	H
0.000233		L	E
0.000252		L	L
0.00027		O	L
0.000289		\n	O
0.0004		\n	\n
0.000419		H	\n
0.000437		E	H
0.000456		L	E
0.000474		L	L
0.000492		O	L
0.000615		\n	O
0.000634		\n	\n
0.000652		H	\n
0.000671		E	H
0.000689		L	E
0.000708		L	L
0.000799		O	L
0.000818		\n	O
0.000836		\n	\n
0.000855		H	\n
0.000873		E	H
0.000892		L	E
0.000996		L	L
0.001014		O	L
0.001032		\n	O
0.001051		\n	\n
0.001069		H	\n
0.001088		E	H
0.001176		L	E
0.001194		L	L
0.001212		O	L
0.001231		\n	O

User avatar
gordon@drogon.net
Posts: 1955
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: SPI to Arduino slave with wiringPI2 on Raspberry PI mast

Thu Oct 03, 2013 9:03 pm

I'm not sure anything is wrong here, unless I'm missing the obvious.
You're sending data to the Arduino.
The Arduino s taking the data and printing it out via the serial line.
What the Arduino sends back on the MISO line isn't relevant as you've not told the ATmega to send anything. (From waht I recall, It will clock out the last value in the data register unless you change it)

And remember it will clock out as it clocks in. Think of the SPI interface as an 8-bit FIFO.

MOSI is really the only signal you're interested in here. (You're not reading anything back from the ATmega) Putting SCLK, MOSI and CE1 on the scope would be more informative.

Is the Arduino not printing anything, or not printing the correct values?

Incidentally, you can simply do this:

Code: Select all

  char data [32] ;

  sprintf (data, "%s", "Hello\n") ;

  wiringPiSPIDataRW (channel, data, strlen (data)) ;
to save all that fiddling with char arrays. You can even do:

Code: Select all

  write (wiringPiSPIGetFd (channel), data, strlen (data)) ;
And in your code, you have

delayMicroseconds (1) ;

with a comment about it being one second - it's not one second, it's one microsecond - if you want 1 second, then use

delay (1000) ;

or

sleep (1) ;

but I'm not sure that's an issue here.

-Gordon
--
Gordons projects: https://projects.drogon.net/

lardconcepts
Posts: 36
Joined: Mon Feb 04, 2013 11:32 am

Re: SPI to Arduino slave with wiringPI2 on Raspberry PI mast

Sun Oct 06, 2013 9:31 pm

Hi Drogon,

Thank you SO much for your very quick reply - I am sorry I didn't thank you earlier, but for some reason I appear to have defaulted to "don't notify when reply posted" so I only just saw it - changed it back now.

And thank you also for the top tip about sprintf formatting the data to send - that'll save a fair bit of typing and mess in the future.

Yes, I see what you mean about the SPI cycling the buffer. And, of course, when I was looping through the array, the next element became what had just been sent. I mean received. Er... sent and received.

In fact, the Arduino doesn't actually NEED to display anything - that was just me sending something so I could see what was actually happening on the logic analyser to try and deduce what was going wrong.

In fact, what I have to do is read a buffer of switch and rotary encoder states from an Arduino (well, an Attiny 84 apparently, but the Arduino will do to test things).

It goes

1: Poll(ignore junk)
2: Poll(switch data (4 switches so low nybble - if high nybble is F then next packet is last)
3: Poll(rotary in two's complement)
Repeat and buffer while stage 2 high nybble isn't 0xF, otherwise do something.

Now, I'm not asking you to write my code for me here, but getting some sense back on the SPI would be a fine start!

Here's what's on the PI and Arduino. At the moment, I realise the 3 "reads" returning random numbers makes no sense as I've yet to figure out how to increment a counter on the Arduino each time an interrupt is triggered. Setting a global volatile int and incrementing it each time just doesn't seem to work within an interrupt routing. But what I WOULD expect here is random(15) to return something less zero-y than (eg) 0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5,0,0,0,0,0.
Even though I am clearly setting the SPI buffer with something on the Arduino, it doesn't seem to actually send back what it should.

And yet, if I do a very simple test of sending, say, 0x42 to the PI in response to every single "ping", it'll do it just fine.

I've tried at slow SPI speeds, I've made the delay between "pings" up to half a second. It's no good me posting the logic analyser screenshot as the gap between readings is too wide to see, but the Arduino doesn't appear to respond to every "ping" when sending a random number. Off the responses I DO get, this is a sample:

Code: Select all

Time [s]	Packet ID	MOSI	MISO
0		'0'	'0'
0.010177		'0'	'0'
0.035713		'0'	'0'
0.0458855		'0'	'0'
0.067803		'0'	'0'
0.077997		'0'	'0'
0.0986785		'0'	'0'
0.108851		'0'	'0'
0.129466		'0'	'0'
0.139644		'0'	'0'
0.1603105		'0'	'4'
0.1704995		'0'	'0'
0.191119		'0'	'3'
So, here's the PI code, then the Arduino code. And apologies, but I've only been doing C for a couple of weeks, so don't expect quality code. Still doesn't explain the SPI oddness. Or does it?
#include <stdio.h>
#include <inttypes.h> // provides uint8_t etc
#include <wiringPi.h>
#include <wiringPiSPI.h>

void pollSPI (void);
uint8_t twoComp(uint8_t number);

//Nice trick to display dec as bin http://stackoverflow.com/a/3208376/2104428
#define BBPAT "%d%d%d%d%d%d%d%d"
#define BB(byte) \
(byte & 0x80 ? 1 : 0), \
(byte & 0x40 ? 1 : 0), \
(byte & 0x20 ? 1 : 0), \
(byte & 0x10 ? 1 : 0), \
(byte & 0x08 ? 1 : 0), \
(byte & 0x04 ? 1 : 0), \
(byte & 0x02 ? 1 : 0), \
(byte & 0x01 ? 1 : 0)

//Lookup for switch values: (E)ncoder (L)eft (M)iddle
static char lookup[][28] = {
  "None", // 00000000
  "R", // 00000001
  "M", // 00000010
  "R M", // 00000011
  "L", // 00000100
  "L R", // 00000101
  "L M", // 00000110
  "L M R", // 00000111
  "E", // 00001000
  "E R", // 00001001
  "E M", // 00001010
  "E M R", // 00001011
  "E L", // 00001100
  "E L R", // 00001101
  "E L M R" // 00001111
};

int main (void)
{   
  wiringPiSetup();
  wiringPiSPISetup(1, 1000000);
  pollSPI();
  return(0);
}  

void pollSPI (void)
{  
  for ( ;; ){
    uint8_t switches [] = {  0x00     }; 
    uint8_t encoder [] = {   0x00     }; 
    wiringPiSPIDataRW ( 1, 0x00, 1 ); // throw away this starter packet
    delayMicroseconds ( 100 );
    wiringPiSPIDataRW ( 1, switches, 1 ); // exchange for switches byte
    delayMicroseconds ( 100 );
    wiringPiSPIDataRW ( 1, encoder, 1 ); // exchange for encoder byte
    delayMicroseconds ( 100 );

    // http://stackoverflow.com/a/3124978/315699
    // http://en.wikipedia.org/wiki/Nibble#Ext ... rom_a_byte
    // No need to test for encoder high nibble as encoder is last byte anyway
    uint8_t switchesHighNibble = (uint8_t)((switches[0] & 0xF0) >> 4); 
    uint8_t switchesLowNibble = (uint8_t)(switches[0] & 0xF0); 

    uint8_t two = twoComp(encoder[0]); // calculate 2's comp from encoder

    printf("Switches: %x("BBPAT") Encoder: %o("BBPAT") \
         \t 2's comp: %d(%x) \t = %s \n", switches[0], BB(switches[0]), \
(int)encoder[0], BB(encoder[0]), two,two, lookup[switches[0]]);


    if (switchesHighNibble == 0xF)
    {
      printf("*** GOT AN 0xF - NEXT BYTE IS LAST\n\n");
    }   
  }   
}


uint8_t twoComp(uint8_t number){   
  uint8_t two;
  two = ~number; //One's compliment
  two = two + 1; //Two's compliment
  return(two);
}

And on the Arduino
// Based on tutorial from http://gammon.com.au/spi

#include <SPI.h>

volatile boolean process_it;
uint8_t clr;
uint8_t randNumber;

void setup (void)
{
  // Serial.begin (115200); // Turned off as it apparently affects interrupts
  pinMode(MISO, OUTPUT); // master in, *slave out*
  SPCR |= _BV(SPE); // turn on SPI in slave mode
  process_it = false;
  clr=SPDR; // clear out any junk from past runs
  SPI.attachInterrupt();
}  // end of setup

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  process_it = true;


void loop (void) // wait for flag set in interrupt routine
{
  if (process_it)
  {
    randNumber = random(15);
    SPI.transfer(randNumber);
    process_it = false;
  }  
}

ricardosp
Posts: 1
Joined: Mon Oct 14, 2013 5:31 pm

Re: SPI to Arduino slave with wiringPI2 on Raspberry PI mast

Mon Oct 14, 2013 5:42 pm

Hello,

I did not read your post in detail, but it seems to me that you have more or less the same problem I had in the last days with SPI.
I was developing a program for a Texas Instruments microcontroller and SPI drove me crazy until I found an explanation in a TI forum:
http://e2e.ti.com/support/microcontroll ... 59710.aspx I paste here the explanation.

SPI communication is driven by CLK signal (it is generated by Master for Slave) synchronizing both serial data transmission and reception that is essentially occuring simultanesously on both Master and Slave devices. In other words, both Master and Slave devices will always transmit and receive the same amount of data at the same (understand logically) time. Slave can transmit/recieve data only when Master is transmitting/receiving the data, because it generates CLK signal for the Slave.

Assuming simple example that Master transmitts 0x01 data. After these are received by a Slave, the Slave MCU reads this "command" and responds with 0x02 data that should be recieved back by Master. In other words: Master sends 0x01 and receives 0x02.

It is app. specific but the scenario for your case might be e.g. following:

1) Write 0x01 to Master TX FIFO (Slave TX FIFO is empty). This initiates the first transmission. As the Slave TX FIFO is empty, the Slave device will transmit a "dummy" (any) data.

2) After the first transmission, Master RX FIFO contains "dummy" data transmitted by the Slave, and Slave RX FIFO contains 0x01 transmitted by the Master.

3) Master MCU can read out and discard the "dummy" data to empty its RX FIFO. Slave MCU process data from its RX FIFO and writes 0x02 to its TX FIFO as a response on received 0x01 command.

4) As only Master can initiate the transmission, a "dummy" (any) data must be written to Master TX FIFO in order to get back a data from the Slave. This initiates next transmission.

5) After this transmission, Master RX FIFO contains 0x02 transmitted by the Slave, and Slave RX FIFO contains "dummy" data transmitted by the Master.

You may want to have a look for instance here: http://en.wikipedia.org/wiki/Serial_Per ... erface_Bus. It should be more clear then.

---- Paste ends here

I hope this will be helpfull for you.

I am planning to use PI and arduino to communicate with the TI microcontroller, so I will keep an eye in your post.

Best regards

Return to “Interfacing (DSI, CSI, I2C, etc.)”

Who is online

Users browsing this forum: No registered users and 16 guests