SPI connection between Beaglebone black and Arduino Uno

Hello All,

I just started to use BBB a couple days. My goal is trying to test the connection between the BBB and the Arduino over SPI.
I did some research on the internet and and borrow some of the code just to see if the result is the same as I expect.

  1. I make sure the SPI pins of the BBB is working by using the spidev_test.c and the result as below:
    sedf
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
 0xF0, 0x0D,


2) So, I tried to send to the Arduino some bytes and expected that the Arduino would show the result on the Serial Monitor. However, the Aruino seems not to get the correct the message.
I'm not sure the voltage difference between Arduino and BBB has anything to do with this. 

Please see the code for the BBB and The BBB below: 
Arduino: I looked at Nick Gammon blog and started to follow his example to make Arduino as slave

//this code is written by by Nick Gammon
#include <SPI.h>
char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
  Serial.begin (9600);   // 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 = 0; 

  // now turn on interrupts 
  SPI.attachInterrupt();

}  // end of setup

// SPI interrupt routine
ISR (SPI_STC_vect)
{
char c = SPDR;  // grab byte from SPI Data Register
  
  // add to buffer if room
  if (pos < sizeof buf)
    {
    buf [pos++] = c;
    Serial.println(buf[pos],HEX);
    // example: newline means time to process buffer
    if (c == 0x04)
      process_it = 1;
      
    }  // 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 == 1)
    {
    buf [pos] = 0;
    Serial.println("SPI");  
    Serial.println (buf);
    pos = 0;
    process_it = 0;
    }  // end of flag set
    
}  // end of loop

and BBB:

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

//define some constant variables
#define PATH "/dev/spidev2.0"

static	uint16_t delay =0;
static	uint32_t speed = 100000;
static uint8_t bits = 8;
static uint8_t mode = 0;

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

 void write_data(int fd){

	int ret;

	char tx[]= {0x08,0x01,0x04};

	char rx[ARRAY_SIZE(tx)] = {0, };

	struct spi_ioc_transfer tr ={
		.tx_buf = (unsigned long) tx,
		.rx_buf = (unsigned long) rx,
		.len = ARRAY_SIZE(tx),
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

	ret = ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
	if (ret<0){
		perror("SPI_IOC_MESSAGE");
	}
}

int main(void) {

	int ret,fd;

	fd = open(PATH,O_RDWR);
	if (fd<0){
		perror("Bad fd open - check PATH");}

	//setup SPI mode
	ret = ioctl(fd,SPI_IOC_WR_MODE,&mode);
	if(ret<0){
		perror("SPI_IOC_WR_MODE");}

	ret = ioctl(fd,SPI_IOC_RD_MODE,&mode);
	if(ret<0){
		perror("SPI_IOC_RD_MODE");}

	ret = ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&bits);
	if(ret<0){
		perror("SPI_IOC_WR_BITS_PER_WORD");}

	ret = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD,&bits);
	if(ret<0){
		perror("SPI_IOC_RD_BITS_PER_WORD");}

	ret = ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ,&speed);
	if(ret<0){
		perror("SPI_IOC_WR_MAX_SPEED_HZ");}

	ret = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ,&speed);
	if(ret<0){
		perror("SPI_IOC_RD_MAX_SPEED_HZ");}

	write_data(fd); // send data

	usleep(100*1000);
	close(fd);
	return 0;
}

Any comment/suggestion/answer would help.  If I write anything wrong, please feel free to point it out.
Also, this is first time I ask question online and join a forum.

Thank you.

Khoa 

Hi Khoa Le,

As I understand, you’re wishing to transmit from BBB (BBB as Master device), and receive at the Arduino (Slave).
If you’re using a 5V logic level Arduino (e.g. Arduino Uno) then make sure to keep the MISO pin on both BBB and Arduino disconnected.
The only pins that should be connected should be MOSI to MOSI, and similarly CLK to CLK, and CS to CS and GND to GND.
Is that how you’ve got it connected?
Also, this code needs changing I think:

buf [pos++] = c;
Serial.println(buf[pos],HEX);
The pos variable is incremented on the first line, but the second line is trying to print the contents of the array index after it has been incremented.
Best to put pos=pos++; on a separate line, so you can print before it is incremented.
Finally, (there may be other bugs too but I am not sure), although it may not be completely necessary here, generally a logic analyzer can help, and they are low-cost (search for ‘salae logic’ or a clone (there are open source and open hardware versions which are compatible), sometimes just $10 or so. With that, it is quick to narrow down which side the issue is on, i.e. if the BBB is transmitting correctly or not. But in this case it’s likely not needed if your BBB test was successful (I don’t know what that spidev_test.c is supposed to output).

Thanks,

Shabaz.

Sorry, typo, I meant
Best to put pos++; on a separate line.

Hi Shabaz,

Thank you for your response. Yes, I’m thinking that I might need a logic analyzer to see what I send and what I receive.

I manage to send the correct data from tx [] to Arduino with correct order. As I understand the SPI_IOC_MESSAGE is full-duplex so I also receive the response from the Arduino back to the BBB as well. However, one thing is weird is that BBB always receives the last byte first. For example, if tx [] = {0x08, 0x01, 0x02, 0x04}, what I receive is rx [] = {0x04, 0x08, 0x01,0x02}. Everything else after the last byte seems to be normal.
On the other hand, If I just use half duplex with read() only, I receive the correct order of what Arduino sends to BBB using SPI.transfer() (from Arduino).

Thank you.

Khoa Le.

I personally would not connect an Arduino such as an UNO, or really anything for that matter, directly to a Beaglebone board. I would add a level shifter between the two devices or other buffer device.
Ex:
https://www.sparkfun.com/products/12009

I’m assuming with regards to SPIDEV you have referenced this.
https://elinux.org/BeagleBone_Black_Enable_SPIDEV

Many of the links from the link are broken so I am not sure if is still being maintained.

Another spidev ref:
https://gist.github.com/pdp7/33a8ad95efcbcc0fadc3f96a70d4b159

You could use ‘strace’ listed at the bottom of the previous link to see what the command is doing when run.

You could use something like the Adafruit Python code to verify your config:
https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/spi

Cheers,

Jon

Which is somewhat out-of-date as most recent BBB images do not have a
"small FAT partition".

  AdaFruit has also been migrating from raw BBIO to using CircuitPython
libraries via adafruit_blinka (an emulation layer between CircuitPython and
board specific Linux)

Okay.

I didn’t know CircuitPython was supported with the Beaglebone boards.

Cheers,

Jon

It's not as easy to find, since the AdaFruit documentation shoves
"Raspberry-Pi" in one's face, and only mentions Beaglebone in a paragraph.
You use the regular Python(3) with the Blinka library to support
CircuitPython hardware API (and possibly device specific CircuitPython
libraries)

https://learn.adafruit.com/circuitpython-on-raspberrypi-linux

https://github.com/adafruit/Adafruit_Blinka

{Based on the code, the BB-AI is NOT [yet?] supported, but BBB IS}

I been using circuit python over a year. The docs are there if you look