0

i have build a Smartmeter based on an SPI ADC from Analog Devices. The measured values are beeing transfered to an Raspberry Pi with lib bcm2835 for visualization. The lib works with single bytes in its transfer functions and the ADC with 32bit registers. So i have to work with a lot of bit shifting. My Code for this can be found here: Github SPI Routine.

In the next step i want to make use of the Waveform Buffers the ADE9000 offers. The Datasheet says there are 2048 32bit Registers that can be read with a single Command Header continously.

My question is how to adress this amount of data with the bcm2835 library in a useful way. Do i have to declare a receive buffer of 2048 * 4 bytes and puzzle the whole chunck of data together after i have received it? Or is there a better way / other lib that could make my life easier?

Thanks!

  • I doubt you will find (or someone will be able to suggest you) a library if your specification for it is to "puzzle the whole chunk of data together". – Dmitry Grigoryev Jun 25 '18 at 8:32
0

I am not quite sure what you need help with.

You could use the Linux SPI driver and standard C read/write calls via the device, e.g. /dev/spidev0.0

Alternatively wiringPi has a wrapper around the standard driver.

The bcm2835 and (my) pigpio libraries have their own SPI drivers, partly because the Linux SPI driver did not exist when those libraries were prepared and partly because they are much faster.

They all should be capable of transferring an arbitrary number of bytes.

E.g. pigpio in Python spi_read, in C spiRead, from a shell SPIR.

  • Question is if there is a quicker way (processing time) than declare a big 2048 * 4 byte buffer and bit shift every 4 bytes into a 32bit variable after i am finished reading. – dEx Jun 17 '18 at 12:35
  • The processing time will be a tiny fraction of the transfer time. I think you are worrying without need. – joan Jun 17 '18 at 13:02
0

I'm less familiar with C or C++, but depending on endianness and alignment you may be able to cast one array type into another, otherwise you'll have to resort to iterating with endian swapping functions or bit shifting. Overall that math is something that C is very good at, so as Joan mentioned, it's not likely to be significant. See this question for more information on the subject.

I'm most familiar with using the spidev module interface (although in python, not C), which is portable between different linux platforms (RasPi, BeagleBone, etc), and handles a lot of the particulars for you like chip selects and can take advantage of platform specific features like DMA. Reading the full 8kB is definitely the simplest method, and I can't think any reason to avoid it if you're using a fully-featured linux system. If it's a rolling buffer you may be able to read and process smaller chunks, but that would depend on other parts of your project that I'm not familiar enough with to decide on.

For your use case it looks like you'll need to write two bytes for the "CMD_HDR" that includes start address, read/write bit, and some padding bits; followed by reading the data you want. You could try something like the following (inspired by spidev_fdx.c (spidev full-duplex) example code (adjust to actual C/C++ and to your needs).

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

int fd;
unsigned char buf[8192]; // Nice if this is page aligned
unsigned char cmd[2];
struct spi_ioc_txr xfer[3]; //struct is 32 bytes x3
int status;

fd = open('/dev/spidev<bus>.<device>', O_RDWR)
// handle error if fd < 0

memset - buf, cmd, xfer
cmd[0] = address >> 4;
cmd[1] = (address << 4) & 0xff;
if(read_command) {
    cmd[1] |= 1 << 3;
} // else write command
xfer[0].tx_buf = (unsigned long)cmd;
xfer[0].len = 2
xfer[0].speed_hz = 20000000; // 20,000,000 20MHz

xfer[1].rx_buf = (unsigned long)buf;
xfer[1].len = 4096;
xfer[1].speed_hz = 20000000;
xfer[2].rx_buf = (unsigned long)(buf + 4096);
xfer[2].len = 4096;
xfer[2].speed_hz = 20000000;

status = ioctl(fd, SPI_IOC_MESSAGE(3), xfer);
// error if < 0
// data now in xfer as bytes

One caveat is that you need to (re)load the spidev module with a larger that default buffer to handle transfers with a total size above 4096 bytes (each direction counted separately).

$ sudo rmmod spidev
$ sudo modprobe spidev bufsiz=8192

or to add it permanently (following reboot):

$ echo "options spidev bufsiz=8192" | sudo tee /etc/modprobe.d/spidev.conf

Configuration of the interface (max speed, SPI mode, lsb/msb first, etc) are done with ioctl calls on the file descriptor, more ioctl usage examples are shown in spidev_test.c

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.