Linux - Accessing SPI Bus from User Space - Part II

In this post we will see how to send and receive SPI messages to the device from userspace.

You can open the SPI device by using the below function call.


int fd = 0;
fd = open("/dev/spidev2.0", O_RDWR);
if (fd < 0)
{
printf("SPI : can't open device");
return -1;
}
You can now set the configuration for the SPI using ioctl calls.

To set the SPI mode
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't set spi mode");
return 1;
}
To get the SPI mode
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't get spi mode");
return 1;
}
Other SPI Bus properties can be manipulated in the same way.

Now the send a message over SPI Bus, You will need to fill up the message structure and pass it down.
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
To receive SPI message
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].rx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
There may be requirements where a write has to be followed by a read without toggling the Chip Select signal. You can form 2 messages as below to handle such situations.
int spidev_if_write_read(unsigned int num_out_bytes,
unsigned char *out_buffer,
unsigned int num_in_bytes,
unsigned char *in_buffer)
{
struct spi_ioc_transfer mesg[2] = { 0, };
uint8_t num_tr = 0;
int ret;

if((out_buffer != NULL) && (num_out_bytes != 0))
{
mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].rx_buf = (unsigned long)NULL;
mesg[0].len = num_out_bytes;
mesg[0].cs_change = 0;
num_tr++;
}

if((in_buffer != NULL) && (num_in_bytes != 0))
{
mesg[1].tx_buf = (unsigned long)NULL;
mesg[1].rx_buf = (unsigned long)in_buffer;
mesg[1].len = num_in_bytes;
num_tr++;
}

if(num_tr > 0)
{
ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), mesg);
if(ret == 1)
{
return 1;
}
}

return 0;
}
The variable cs_change=0 disables the change in CS signal state between the two transactions.

6 Responses to "Linux - Accessing SPI Bus from User Space - Part II"

DataDiary.com responded on August 4, 2011 at 9:41 PM #

this was really good script but actual i am fresh and don't know how to use it

Sountharya responded on October 11, 2011 at 12:25 AM #

Thats really helpful. how to transfer 16 bit data. I can able to transfer 8 bit data without any problem but transmitting 16 bit data fails.I changed the .bits per word in user space program to 16

Halosys responded on October 29, 2011 at 4:50 AM #

web application development in usa

bowerymarc responded on April 5, 2012 at 10:57 PM #

if you only have one message you don't have to make an array... e.g.:

struct spi_ioc_transfer mesg;
memset(&mesg, 0, sizeof mesg);

mesh.tx_buf = (unsigned long)out_buffer;
mesg.len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &mesg);

Anonymous responded on March 12, 2013 at 12:47 PM #

In your final example (the full duplex transfer), is the response stored in the buffer pointed to by in_buffer? I have created a very similar function, but it accepts only one "buffer" argument that is initially stuffed with data to write and _should_ contain response data when execution completes. For some reason, my response buffer contains nothing but NULL characters. Using a logic analyzer I can verify that a response is sent out over the bus, but my buffer does not have this "read" data.

Let's say that for debugging purposes, you wanted to print the data you read in spidev_if_write_read(). How would you do that? Something like this?

unsigned char* buf_ptr;
printf( "\nSPI Response\n" );
for( buf_ptr = in_buffer; num_in_bytes; num_in_bytes-- )
{
printf( "%02x\n", *buf_ptr++ );
}

Anonymous responded on September 24, 2015 at 6:48 AM #

Is it possible to increase the array beyond 2? For example, I setup an array with 4 transfers: (TX, RX, TX, RX) and used SPI_IOC_MESSAGE(4).

ret = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);

However, I wasn't having any success. Any ideas?