Working on spidev.h and spidev.c translation

OK. I’ll give it a try. Thanks.

That did it!!! Thanks.

Here’s the change I made in Julia (per your suggestion).

function spi_open(spi::SPIDevice)
    #cout << "Opening the file: " << filename.c_str() << endl;
    O_RDWR = 0x0002             # open for reading and writing
    fd = ccall((:open, "libc"), Cint, (Cstring, Cint), spi.filename, O_RDWR)
    if (fd < 0)
       error("SPI: Can't open device.")
       return fd
    end

    spi.file = fd

    return 0
end

Here’s the results (step by step):

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> function combineValues(upper::UInt8, lower::UInt8)
           return (Int16(upper) << 8) | Int16(lower)
       end
combineValues (generic function with 1 method)

julia> println("Starting RPi SPI ADC Example")
Starting RPi SPI ADC Example

julia> spi = spidev.SPIDevice(0,0)
spidev.SPIDevice(0x00, 0x00, 0, "/dev/spidev0.0", 0x03, 0x08, 0x00077240, 0x0000)

julia> ret = spidev.spi_open(spi)
0

julia> spi.file
20

julia> ret = spidev.spi_setMode(spi, UInt8(SPI_MODE_0))
0

julia> ret = spidev.spi_setBitsPerWord(spi, UInt8(8))
0

julia> ret = spidev.spi_setSpeed(spi, UInt32(1000000))      # Have access to SPI Device object
0

julia> println("The SPI ADC is setup")
The SPI ADC is setup

julia> send = [0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> spi
spidev.SPIDevice(0x00, 0x00, 20, "/dev/spidev0.0", 0x00, 0x08, 0x000f4240, 0x0000)

julia> receive = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> len = 3
3

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x0000000067b8bb24, 0x0000000000015007, 0x00000000, 0x00000046, 0xbb20, 0xb8, 0x67, 0x00, 0x00, 0x0000)

julia> #see <linux/spi/spidev.h> for details!
           transfer.tx_buf = pointer(send)
Ptr{UInt8} @0x681f0fb0

julia> transfer.rx_buf = pointer(receive)
Ptr{UInt8} @0x6f482090

julia> transfer.len = len #number of bytes in vector
3

julia> transfer.speed_hz = spi.speed
0x000f4240

julia> transfer.delay_usecs = spi.delay
0x0000

julia> transfer.bits_per_word = spi.bits
0x08

julia> transfer.pad = 0
0

julia> transfer
spi_ioc_transfer(0x00000000681f0fb0, 0x000000006f482090, 0x00000003, 0x000f4240, 0x0000, 0x08, 0x67, 0x00, 0x00, 0x0000)

julia> spi.file
20

julia> ret = ccall((:ioctl, "libc"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}), spi.file, SPI_IOC_MESSAGE(1), transfer)
3

julia> receive
8-element Array{UInt8,1}:
 0x00
 0x01
 0x08
 0x00
 0x00
 0x00
 0x00
 0x00


julia> value = combineValues(receive[2] & 0b00000011, receive[3])
264

julia> println("This is the value " * string(value) * " out of 1024.")
This is the value 264 out of 1024.

julia> println("End of ERPi SPI ADC Example")
End of ERPi SPI ADC Example

1 Like

OK. So, in my spi_transfer() function - how do I make sure that the receive array retains its values? Di I use an unsafe_wrap routine?

function spi_transfer(spi::SPIDevice, send::Array{UInt8,1}, receive::Array{UInt8, 1}, len::Int32)
    transfer = spi_ioc_transfer()
 
    #see <linux/spi/spidev.h> for details!
    transfer.tx_buf = pointer(send)
    transfer.rx_buf = pointer(receive)
    transfer.len = len #number of bytes in vector
    transfer.speed_hz = spi.speed
    transfer.delay_usecs = spi.delay
    transfer.bits_per_word = spi.bits
    transfer.pad = 0

    ret = ccall((:ioctl, "libc"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), spi.file, SPI_IOC_MESSAGE(1), transfer) 
    
    if (ret < 0) 
        error("SPI: Transfer SPI_IOC_MESSAGE Failed")
        return -1
    end

    return ret
end

One thing more to add (and probably goes to how to retrieve data back from the buffers). How do I get the pointers back?

before the struct gets passed to ioctl:

julia> #see <linux/spi/spidev.h> for details!
       transfer.tx_buf = pointer_from_objref(send)
Ptr{Void} @0x6f54fd30

julia> transfer.rx_buf = pointer_from_objref(receive)
Ptr{Void} @0x66d882b0

And, after the ioctl call:

julia> ret = ccall((:ioctl, "libc"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), spi.file, SPI_IOC_MESSAGE(1), transfer)
3

julia> transfer.tx_buf
0x000000006f54fd30

julia> transfer.rx_buf
0x0000000066d882b0

The numbers are the same, but the tx_buf and rx_buf are just UInt64 numbers, now - not pointers.

OK. I’ve had a chance to get back to this and even though I am able to make a legitimate ioctl call for the SPI transfer, I’m not getting the correct return array (and by the simulation - I’m not sure the right values are even going in.

So, here is the code for the simulated ioctl shared library. I’d ask that someone please be sure I am casting values correctly. You’ll notice that I print the contents for the tx and rx arrays before and after the ioctl call. The problem is - the data doesn’t look correct going in - so naturally it will be wrong coming out.

#include <iostream>
#include <cstdio>
#include <fcntl.h>
#include <errno.h> 
/* Not technically required, but needed on some UNIX distributions */
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

int fd_is_valid(int fd)
{
    return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}

extern "C" {
int simioctl(int fd, unsigned long cmd, unsigned long arg)
{
   struct spi_ioc_transfer *ioc;	

   ioc = (struct spi_ioc_transfer *) arg;

   int fdChk = fd_is_valid(fd);
   printf("fd_is_valid(fd): %d\n", fdChk);

   //if ((file = ::open("/dev/spidev0.0", O_RDWR))<0){
   //             perror("SPI: Can't open device.");
   //             return -1;
   //     }


   unsigned char *tx = (unsigned char *) ioc->tx_buf;
   unsigned char *rx = (unsigned char *) ioc->rx_buf;

   printf("fd: %d\n", fd);
   printf("cmd: %lX\n", cmd);
   printf("spi_ioc_transfer arg\n\n");
   printf("address of tx_buf: %llX\n", ioc->tx_buf);
   printf("address of rx_buf: %llX\n", ioc->rx_buf);
   printf("ioc->len: %d\n", ioc->len);
   printf("ioc->speed_hz: %d\n", ioc->speed_hz);
   printf("ioc->delay_usecs: %d\n", ioc->delay_usecs);
   printf("ioc->bits_per_word: %d\n", ioc->bits_per_word);
   printf("ioc->pad: %d\n", ioc->pad);

   int size_tx_buf = sizeof(ioc->tx_buf);


   printf("tx before ioctl\n");
   for (int i = 0; i < size_tx_buf; i++)
      printf("%X\n", tx[i]);
   printf("rx before ioctl\n");
   for (int i = 0; i < size_tx_buf; i++)
      printf("%X\n", rx[i]);

   int status = ioctl(fd, cmd, arg);
   if (status < 0) {
      perror("SPI: Transfer SPI_IOC_MESSAGE Failed");
      return status;
   }

   printf("tx after ioctl\n");
   for (int i = 0; i < size_tx_buf; i++)
      printf("%X\n", tx[i]);
   printf("rx after ioctl\n");
   for (int i = 0; i < size_tx_buf; i++)
      printf("%X\n", rx[i]);



   return 1;
}
}

Here’s the Julia run:

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> function combineValues(upper::UInt8, lower::UInt8)
           return (Int16(upper) << 8) | Int16(lower)
       end
combineValues (generic function with 1 method)

julia> println("Starting RPi SPI ADC Example")
Starting RPi SPI ADC Example

julia> spi = spidev.SPIDevice(0,0)
spidev.SPIDevice(0x00, 0x00, 0, "/dev/spidev0.0", 0x03, 0x08, 0x00077240, 0x0000)

julia> ret = spidev.spi_open(spi)
0

julia> ret = spidev.spi_setSpeed(spi, UInt32(1000000))      # Have access to SPI Device object
0

julia> ret = spidev.spi_setMode(spi, UInt8(SPI_MODE_0))
0

julia> ret = spidev.spi_setBitsPerWord(spi, UInt8(8))
0

julia> println("The SPI ADC is setup")
The SPI ADC is setup

julia> send = Array{UInt8, 1}(8)
8-element Array{UInt8,1}:
 0x30
 0xeb
 0xc9
 0x67
 0x00
 0x00
 0x00
 0x00

julia> send = zeros(send)
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> send[1] = 0b00000001 # The Start Bit followed
0x01

julia> # Set the SGL/Diff and D mode -- e.g., 1000 means single ended CH0 value
       send[2] = 0b10000000 # The MSB is the Single/Diff bit and it is followed by 000 for CH0
0x80

julia> send[3] = 0          # This byte doesn't need to be set, just for a clear display
0

julia> receive = Array{UInt8, 1}(8)
8-element Array{UInt8,1}:
 0x00
 0x63
 0x7b
 0x71
 0x50
 0x38
 0x6e
 0x71

julia> receive = zeros(receive)
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> len = 3
3

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x0000000067ba6e54, 0x0000000000015007, 0x00000000, 0x00000000, 0x6e50, 0xba, 0x67, 0x00, 0x00, 0x0000)

julia> transfer.tx_buf = pointer_from_objref(send)
Ptr{Void} @0x6f6b1db0

julia> transfer.rx_buf = pointer_from_objref(receive)
Ptr{Void} @0x6f7a0430

julia> tempRx_buf = pointer_from_objref(receive)
Ptr{Void} @0x6f7a0430

julia> transfer.len = len #number of bytes in vector
3

julia> transfer.speed_hz = spi.speed
0x000f4240

julia> transfer.delay_usecs = spi.delay
0x0000

julia> transfer.bits_per_word = spi.bits
0x08

julia> transfer.pad = 0
0

julia> ret = ccall((:simioctl, "/home/julia-user/julia-0.6.0/bin/libsimioctl.so"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}), spi.file, SPI_IOC_MESSAGE(1), transfer)
fd_is_valid(fd): 1
fd: 18
cmd: 40206B00
spi_ioc_transfer arg

address of tx_buf: 6F6B1DB0
address of rx_buf: 6F7A0430
ioc->len: 3
ioc->speed_hz: 1000000
ioc->delay_usecs: 0
ioc->bits_per_word: 8
ioc->pad: 0
tx before ioctl
F0
1D
6B
6F
8
0
0
0
rx before ioctl
70
4
7A
6F
8
0
0
0
tx after ioctl
F0
1D
6B
6F
8
0
0
0
rx after ioctl
0
1D
5C
6F
8
0
0
0
1

julia> send
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> receive
8-element Array{UInt8,1}:
 0x02
 0x00
 0x00
 0x00
 0x04
 0x00
 0x00
 0x00

pointer

1 Like

OK. Thanks. I’m going to award you a sainthood for patience with me!

Consistently, working! Yahoo!

Here’s a link to a demonstration of using SPI (pure Julia) to communicate between a Raspberry Pi and a MCP3008 chip (to get analog values from a potentiometer).

https://photos.app.goo.gl/DYCXtMNSYp1zIV5Y2

2 Likes