Only able to read 0x0 or FF with spidev...

Hi,

I am trying to communicate with a device (ADS1299à by using the Beaglebone black but without success. First I have to enable spi dev entries in /dev. There are plenty of blog/tuto which are giving dtc file to generate our own dtbo. But dtc files seems to not be always the same.

In fact I can see that there are already some dtbo files in /lib/firmware :

root@beaglebone:~# ls /lib/firmware/ | grep SPI
ADAFRUIT-SPI0-00A0.dtbo
ADAFRUIT-SPI1-00A0.dtbo
BB-SPIDEV0-00A0.dtbo
BB-SPIDEV1-00A0.dtbo
BB-SPIDEV1A1-00A0.dtbo

So I want to use only SPI0 because I know that SPI1 is already used by HDMI. Can I used theses dtbo files ? Is it better to write my own ? It is quite strange, when I enable one of theses dtbo files, I get not only one but two entries in /dev ??

Currently I want to validate my wiring between the two boards. I found a piece of code to read the device id in register of the device though SPI. But when I try to read this register, I can only get 1 or only get 0 (depends of the dtbo files)

Any advice of the good way to process here ?

Thanks & Regards,
Arthur.

Hi,

Following the links: http://elinux.org/BeagleBone_Black_Enable_SPIDEV and http://www.nagavenkat.adurthi.com/2014/02/spi-communication-beaglebone-black-as-master-to-arduino-as-slave/ I was able to make it work, but I have a SS too long (1.4ms) for my need. For easy test connect MOSI and MISO with a wire (loopback) to eliminate connection problem with you SPI slave.
Hope this will help. When you done, can you measure the timing a let me know?
I can post you my code if you need it.

Jan

The two devices that show up are associated with the same clock and data lines, they just have different chip select lines to allow you to multiplex two devices – they cannot be used simultaneously.

…so if you enable both spidevs you have four devices show up.

Ok but how can I know the setup associated to the dtbo files ? Is there any reverse engineering to get the dtc file from the dtbo ? I want to comunicate by SPI0 so I guess that I have to choose ADAFRUIT-SPI0-00A0.dtbo or BB-SPIDEV0-00A0.dtbo. How do I choose ? How can I know if D0/D1 is an output/input in this dtbo configuration file ?

Jan I will run the test today. I will let you know the result

Arthur.

Hi Jan,

So I put a simple wire between pin 18 and 21 and try to run the spidev_test… Same result… FF only… I also check with an oscilloscope. I see nothing… I am not able to see my data.

root@beaglebone:~# ./spidev_test -D /dev/spidev1.0
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF

So first I am currently using my own dtc file from input on the web :

/dts-v1/;
/plugin/;
/ {
compatible = “ti,beaglebone”, “ti,beaglebone-black”;
/* identification /
part-number = “spi0pinmux”;
fragment@0 {
target = <&am33xx_pinmux>;
overlay {
spi0_pins_s0: spi0_pins_s0 {
pinctrl-single,pins = <
0x150 0x30 /
spi0_sclk, INPUT_PULLUP | MODE0 /
0x154 0x30 /
spi0_d0, INPUT_PULLUP | MODE0 /
0x158 0x10 /
spi0_d1, OUTPUT_PULLUP | MODE0 /
0x15c 0x10 /
spi0_cs0, OUTPUT_PULLUP | MODE0 */

;
};
};
};
fragment@1 {
target = <&spi0>;
overlay {
#address-cells = <1>;
#size-cells = <0>;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&spi0_pins_s0>;
spidev@0 {
spi-max-frequency = <24000000>;
reg = <0>;
compatible = “linux,spidev”;
};
};
};
};

So I build the dtbo file :

dtc -O dtb -o /lib/firmware/BB-ARTHUR-SPI0-00A0.dtbo -b 0 -@ BB-ARTHUR-SPI0-00A0.dts
echo BB-ARTHUR-SPI0 > /sys/devices/bone_capemgr.9/slots

Then I run uname -r to get the current linux version : 3.8.13-bone47
I download kernel source from kernel.org to get the spidev_test.c file. I compile it on the board and I run it.

Do I wrong somewhere ? I am currently running the original EMMC distribution of the beaglebone black ( The board was ordered few weeks ago, so I can guess that I used a very recent version of the beagleboard binary).

I also found in your link something about patching the kernel :

diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 911e9e0…622adf5 100644
— a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -646,6 +646,7 @@ static int spidev_remove(struct spi_device *spi)

static const struct of_device_id spidev_dt_ids = {
{ .compatible = “rohm,dh2228fv” },

  • { .compatible = “linux,spidev” },
    {},
    };

Do I have to download my own source to build a beagleboard image and to patch the kernel before with this patch to be able to use SPI ?

Thanks & Regards,
Arthur.

Hi Arthur,

BBB is a very time consuming hobby.
Take it ease, relax, go for a walk and when you return, try:

  1. try your project as root
    if no luck follow my (working) path

  2. Try to repeat my settings and see if it works for you.
    I wasn’t able to build the original Linux spidev_test, so…

What I have is:

root@beaglebone:~# uname -a
Linux beaglebone 3.8.13-bone50 #1 SMP Tue May 13 13:24:52 UTC 2014 armv7l GNU/Linux

Following the instruction from here http://www.nagavenkat.adurthi.com/2014/02/spi-communication-beaglebone-black-as-master-to-arduino-as-slave/
I created SPI-4SS-00A0.dts and compiled it into SPI-4SS-00A0.dts following exactly the instruction

By doing that you should have in /sys/devices/bone_capemgr.9/slots

0: 54:PF—
1: 55:PF—
2: 56:PF—
3: 57:PF—
4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
8: ff:P-O-L Override Board Name,00A0,Override Manuf,SPI-4SS

To test it I obtained and created files in /opt/test/test2 as attached:
build
SimpleGPIO.cpp - from Derek Molloy https://github.com/derekmolloy/beaglebone/blob/master/SimpleGPIO.cpp
SimpleGPIO.h - from Derek Molloy https://github.com/derekmolloy/beaglebone/blob/master/SimpleGPIO.h
test2.cpp - mine main file

unzip attached and compile

root@beaglebone:/opt/test/test2# ./build
Building test2 - J.Sz.
root@beaglebone:/opt/test/test2#

then run (have P9-18 and P9-21 connected as loopback)

root@beaglebone:/opt/test/test2# ./test2
Initialize SS pins for SPI
Initialize SPI
SPI transfer
rx = 30 31 32 33 34 35 36 37
rx = 31 32
rx = 33 34
rx = 35 36
rx = 37 38
rx = 30 31 32 33 34 35 36 37

press CTRL-C to terminate or let it run and observe with a scope all SPI0 signals

Hope this will help

Jan

Sorry, I don’t see how to insert a file so:
build is:

#!/bin/bash
echo “Building test2 - J.Sz.”
g++ test2.cpp SimpleGPIO.cpp -o test2

test2.cpp is

//#ifndef SPICOMM_H_
//#define SPICOMM_H_

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

using namespace std;

// example functions from http://www.nagavenkat.adurthi.com/2014/02/spi-communication-beaglebone-black-as-master-to-arduino-as-slave/

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

static void pabort(const char *s)
{
perror(s);
abort();
}

//4 SS pins for 4 SPI devices
void init_motor_spi_ss(int gpio1,int gpio2,int gpio3,int gpio4){
//export the pins. I have used then pins described in the table above
gpio_export(gpio1);
gpio_export(gpio2);
gpio_export(gpio3);
gpio_export(gpio4);

// set them as High. The SS pins should be high when idle(no commuication)

gpio_set_dir(gpio1, OUTPUT_PIN);
gpio_set_dir(gpio2, OUTPUT_PIN);
gpio_set_dir(gpio3, OUTPUT_PIN);
gpio_set_dir(gpio4, OUTPUT_PIN);

gpio_set_value(gpio1, HIGH);
gpio_set_value(gpio2, HIGH);
gpio_set_value(gpio3, HIGH);
gpio_set_value(gpio4, HIGH);

}

static const char *device = “/dev/spidev1.0”; // this is when SPI0 was enabled. change it when SPI1 is enabled
static uint8_t mode=0; //this mode works well for me
static uint8_t bits = 8; //arduino accepts 8 bits at once
//static uint32_t speed = 1000000; //1MHz speed
static uint32_t speed = 1000000; //2MHz speed
static uint16_t delay=0;

static void init_spi(void)
{
int fd;
int ret = 0;

fd = open(device, O_RDWR);
if (fd < 0)
pabort(“can’t open device”);

/*

  • spi mode
    */
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
    pabort(“can’t set spi mode”);
    ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1)
    pabort(“can’t get spi mode”);

/*

  • bits per word
    */
    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
    pabort(“can’t set bits per word”);
    ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
    pabort(“can’t get bits per word”);

/*

  • max speed hz
    */
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
    pabort(“can’t set max speed hz”);
    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
    pabort(“can’t get max speed hz”);

}

void spi_transfer(uint8_t *txbuffer, int size, int gpio)
{

int fd;
int ret = 0;
//uint8_t tx[8];
//tx[0]=‘0’;
//tx[1]=‘1’;
//tx[2]=‘2’;
//tx[3]=‘3’;
//tx[4]=‘4’;
//tx[5]=‘5’;
//tx[6]=‘6’;
//tx[7]=‘7’;

//uint8_t rx[ARRAY_SIZE(txbuffer)] = {0, };
uint8_t rx[size];

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

fd = open(device, O_RDWR);
if (fd < 0)
pabort(“can’t open device”);

gpio_set_value(gpio, LOW);
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort(“can’t send spi message”);

close(fd);
gpio_set_value(gpio, HIGH);

cout<<“rx = “;
for(int i=0;i<size;i++){
printf(”%.2X”,rx[i]);
cout<<" ";
}
cout<<endl;

}

int main(int argc, char *argv[]){

cout << “Initialize SS pins for SPI” << endl;
init_motor_spi_ss(48, 49, 60, 115);

cout << “Initialize SPI” << endl;
init_spi();

cout << “SPI transfer” << endl;
while(1)

{

spi_transfer((uint8_t *)“01234567”, 8, 48);
spi_transfer((uint8_t *)“12”, 2, 48);
spi_transfer((uint8_t *)“34”, 2, 49);
spi_transfer((uint8_t *)“56”, 2, 60);
spi_transfer((uint8_t *)“78”, 2, 115);
}

return 0;
}

THE END

Hi Jan,

Ok so your first advice was really good. I can see that I was not able to run a simple loopback between the D1/D0 pin on the board; So I conclude that the problem come from the wire. I was pretty sure about the configuration and software part.

So I finally change the jumper/wire used to rely the two pins and then miracle…

root@beaglebone:~# ./spidev_test -D /dev/spidev1.0
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D

So it was definitely a connexion issue. So Now just need to come back with my original setup to test my previous code to communicate between ads1299 and the Beaglebone black. So yes I move one ! Lets find the next issue !

Thanks again Jan,
Arthur.

Saisissez le code ici...

Hi Jan,

Ok so I have done some progress I am almost done. My last issue is purely a software issue.

So first I found a first issue which was due to bad voltage. I was supplying my slave thanks to the Beaglebone Black. I need a voltage of 5V delivered by the VDD_5V. Thanks to an oscilloscope I was able to see that the pin delivered 0V… In fact when you supply the BB by using the USB the VDD_5V cannot be use. You must use the 5V jack…

Thanks to that I can see to the oscilloscope that I sent the good message to my slave (CS, SCLK, MOSI) are ok !! So now when I want to read a register of my slave I can see the good answer on the oscilloscope on the MISO pin. So eveyrthing seems to be great. I am able to send a command to my slave and get the good answer. The problem is that I am not able to read properly this value on my MISO pin.

I found plenty of spidev example in c on the web… So I implemented three spidev code with three differents ways from the web. The three software get me wrong value and not the same (so funny…)

Code that I tried :

One spio_ioc_transfer structure per bytes (crazy ???!)
tx & rx in the same structure.

`
static void read_register_value (unsigned char *data, int length, int fd)
{
struct spi_ioc_transfer spi[length];
int i = 0;
int retVal = -1;

// one spi transfer for each byte

for (i = 0 ; i < length ; i++)
{
spi[i].tx_buf = (unsigned long)(data + i); // transmit from “data”
spi[i].rx_buf = (unsigned long)(data + i) ; // receive into “data”
spi[i].len = sizeof(*(data + i)) ;
spi[i].delay_usecs = 0 ;
spi[i].speed_hz = 1000000;
spi[i].bits_per_word = 8 ;
spi[i].cs_change = 0;
}

retVal = ioctl (fd, SPI_IOC_MESSAGE(length), &spi) ;

if(retVal < 0)
{
perror(“Problem transmitting spi data…ioctl”);
exit(1);
}
}
`

Only one spi_ioc_transfer structure for rx and tx
but use ioctl call for each byte

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

// send command of 2 bytes
// send the first byte
spiregister[0] = 0x20
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort(“Config : can’t send spi message”);
sleep(1);

// send the second byte
spiregister[1] = 0x0; // RREG of 1 register only
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort(“Config : can’t send spi message”);
sleep(1);

`

Use a structure for rx and a structure for tx, but send all in the same ioctl call.

`
unsigned char buf_rx[10];
unsigned char buf_tx[10];
struct spi_ioc_transfer mesg[2];

// write 2 bytes
buf_tx[0] = OPT_CODE_RREG + ID_REG;
buf_tx[1] = 0x0;
mesg[0].tx_buf = (unsigned long)buf_tx;
mesg[0].rx_buf = (unsigned long)NULL;
mesg[0].len = 2;
mesg[0].cs_change = 0;

// read 1 bytes
mesg[1].tx_buf = (unsigned long)NULL;
mesg[1].rx_buf = (unsigned long)buf_rx;
mesg[1].len = 1;
mesg[1].cs_change = 0;

ret = ioctl(fd, SPI_IOC_MESSAGE(2), mesg);
if (ret < 1)
pabort(“Config : can’t send spi message”);
usleep(100);
`

So I have a very dummy question. What is the good way to write/read from the master with spidev in C.

Requisite here :

  • Send a command to say which register I want to read.
  • Read the answer from my spi slave

My command must be 2 bytes longs.
My answer is 1 byte longs.

So I would appreciate to find a working example which using spidev properly.

Thanks !
Arthur.

Hi Artur,

Sorry to hear, you have problems, but usually there is something very simple, that you might be doing wrong:

  1. Check you SPI setting. For your device you need CPOL=0 and CPHA=1 - see page 8 of http://www.ti.com/lit/ds/symlink/ads1299.pdf and the max freq of master clock 444ns → 2.252… MHz (please verify that)

  2. You need to understand a bigger picture, how the SPI works. In a nutshell it is a Master/Slave arrangement, where the master always generates the clock. In your case BBB is a master and ADS1299 is a slave. The data on DIN, DOUT or MISO, MOSI lines flows both ways simultaneously.
    2a) To perform the write transaction, master sends a byte (bytes) generating the signals CS, CLK and MOSI. At the same time MISO is coming from slave device. You need to read the incoming data to flush the buffer, even if it is (the data) meaningless.
    2b)
    To perform the read transaction, master sends a dummy data and redas the response.

  3. In you case it depends on the transaction, but as you mention, you need to sens 2 bytes and read one response, you do the followin:
    write 3 byes, specifically 2 command byte and one dummy byte (usually 0xff) and read back 3 bytes. Ignore the first 2 bytes read and the third one will be your valid data. All within the same transaction (one CS)

REMEMBER: to read you need to perform a dummy write.
As for your code, all version should work. I would use a simultaneous read write of 3 bytes.

Hope this will help.

Jan

Hi Jan,

Thanks for your answer.

SPI_MODE :
I am currently using the SPI_MODE_1 so it seems ok (CPOL = 0x02 | CPHA = 0x01 ==> 0x01)

I have also a MODE0 in my device tree file :

spi0_pins_s0: spi0_pins_s0 { pinctrl-single,pins = < 0x150 0x10 /* spi0_sclk, OUTPUT_PULLUP | MODE0 */ 0x154 0x30 /* spi0_d0, INPUT_PULLUP | MODE0 */ 0x158 0x10 /* spi0_d1, OUTPUT_PULLUP | MODE0 */ 0x15c 0x10 /* spi0_cs0, OUTPUT_PULLUP | MODE0 */

But it is not the same thing right ? In the datasheet of the Beaglebone Black (link), Page 72, in the P9 header Pinout, I can see that the pin that I need is a spi with particular mode. That’s why I put MODE 0 here.

Speed Clock
My speed is currently 1 000 000 (in my dtc file and in my code).
I have no idea how to setup the speed, I see often 1 000 000 in example so I took this value.

So if I have a look on the datasheet and compute the min/max, its seems that the value must be between 1.501 MHz and 2.252 MHz…

So I will try to update the speed to see if I can see any differences in my result

But in fact in my case I have not to handle the CS or the fact to write dummy byte right? Spidev handles the CS for me. Moreover If I use only one struct spi_ioc_transfer to handle my message I always write the same number of bytes that I read (because I have only 1 field len in the structure)

If I follow your advice with a len of 3 bytes it means that at each iocl call I will write/read 3 bytes. I guess that I must handle this dummy byte if I used oldchool way like read/write syscall (But in my understanding I cannot do it because I need to have the CS low between the write and the read and I cannot set the CS from userland)

I will follow your advise and use this method with only one structure with a len set to 3.
The dummy byte of 0xff it is just a convention right ? I can put 0x0 or 0x42 it will be the same result right ?

Thanks again for your advice.
Arthur.

Hi,

  1. I said your clock should not be much higher than the maximum clock, that a device can handle. It can be anything within the range. You are OK with 1MHz, no need to change it.
    SPI mode and pin mode on BBB are 2 different things, you are OK here as well.

  2. Your software calls to IOCtl will handle CS, just use a 3 bytes transfer. And yes the dummy value can be anything.

Change only one thing at a time. My guess is that by doing #2 above you will be fine, unless there are things you didn’t tell me like hardware issues (wrong pinouts, wrong power supply, wrong voltage levels)

Jan

Hi,

I have the save problem. I’m writing 2 bytes to an external device over SPI and i’m waiting for one byte response.

I saw on the scope that the device send the response on the MISO, but i can’t read it with spidev.

i’m using one transfert struct for full duplex commnication and i’m waiting for 2 dummy byte and 1 real response byte, but unfortunately it’s not the case.

when i short the MISO and MOSI, i can read what i write on the spi bus.

do you have some suggestions?

Thanks,