Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

[SOLVED] How to manually toggle SPI CS?

Thu Apr 15, 2021 2:00 pm

How to manually toggle the chip select of SPI in the following code?
My objective is to call transfer() twice for 16-bit transaction.

Code: Select all

int
SPI::transfer(uint8_t *send, uint8_t *recv, unsigned len)
{
    if ((send == nullptr) && (recv == nullptr)) {
        return -EINVAL;
    }

    // set write mode of SPI
    int result = ::ioctl(_fd, SPI_IOC_WR_MODE, &_mode);

    if (result == -1) {
        PX4_ERR("can’t set spi mode");
        return PX4_ERROR;
    }

    spi_ioc_transfer spi_transfer{};

    spi_transfer.tx_buf = (uint64_t)send;
    spi_transfer.rx_buf = (uint64_t)recv;
    spi_transfer.len = len;
    spi_transfer.speed_hz = _frequency;
    spi_transfer.bits_per_word = 8;

    result = ::ioctl(_fd, SPI_IOC_MESSAGE(1), &spi_transfer);

    if (result != (int)len) {
        PX4_ERR("write failed. Reported %d bytes written (%s)", result, strerror(errno));
        return PX4_ERROR;
    }
    
Last edited by Skoog on Fri Apr 23, 2021 8:41 am, edited 1 time in total.

User avatar
joan
Posts: 15616
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: How to manually toggle SPI CS?

Thu Apr 15, 2021 3:39 pm

Why?

Please link to the specs of the datasheet of the device you want to use.

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Thu Apr 15, 2021 4:50 pm

joan wrote:
Thu Apr 15, 2021 3:39 pm
Why?

Please link to the specs of the datasheet of the device you want to use.
https://www.analog.com/media/en/technic ... S16470.pdf

User avatar
DougieLawson
Posts: 41300
Joined: Sun Jun 16, 2013 11:19 pm
Location: A small cave in deepest darkest Basingstoke, UK
Contact: Website Twitter

Re: How to manually toggle SPI CS?

Thu Apr 15, 2021 5:56 pm

Skoog wrote:
Thu Apr 15, 2021 2:00 pm

My objective is to call transfer() twice for 16-bit transaction.
You can send & receive up to 32 bytes with a single I/O operation. So if you fiddle your code to send uint16_t it will pull the CS line low for all 16 bits.

Code: Select all

int
SPI::transfer(uint16_t *send, uint16_t *recv, unsigned len)
{
    if ((send == nullptr) && (recv == nullptr)) {
        return -EINVAL;
    }

    // set write mode of SPI
    int result = ::ioctl(_fd, SPI_IOC_WR_MODE, &_mode);

    if (result == -1) {
        PX4_ERR("can’t set spi mode");
        return PX4_ERROR;
    }

    spi_ioc_transfer spi_transfer{};

    spi_transfer.tx_buf = (uint16_t)send;
    spi_transfer.rx_buf = (uint16_t)recv;
    spi_transfer.len = len;
    spi_transfer.speed_hz = _frequency;
    spi_transfer.bits_per_word = 8;

    result = ::ioctl(_fd, SPI_IOC_MESSAGE(1), &spi_transfer);

    if (result != (int)len) {
        PX4_ERR("write failed. Reported %d bytes written (%s)", result, strerror(errno));
        return PX4_ERROR;
    }
    
You may need to use htons() and ntohs() to get the right byte order.
Any language using left-hand whitespace for syntax is ridiculous

Any DMs sent on Twitter will be answered next month.
Fake doctors - are all on my foes list.

Any requirement to use a crystal ball or mind reading will result in me ignoring your question.

User avatar
joan
Posts: 15616
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: How to manually toggle SPI CS?

Thu Apr 15, 2021 7:44 pm

What does the following report?

sudo pigpiod # start the pigpio daemon

pigs spio 0 1000000 0 # use main spi ss GPIO 8 (if you are using ss GPIO 7 use pigs spio 1 1000000 0)
pigs spix 0 0x68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Fri Apr 16, 2021 8:11 am

DougieLawson wrote:
Thu Apr 15, 2021 5:56 pm
You can send & receive up to 32 bytes with a single I/O operation.
I will try what you have suggested, but shouldn't be maximum of 8 bits per word allowed in SPI BCM2835 driver will be a problem?
In fact I have tried the following code similar to what you have suggested but got the "can’t set 16 bit spi mode" error.

Code: Select all

int
SPI::transferhword(uint16_t *send, uint16_t *recv, unsigned len)
{
	if ((send == nullptr) && (recv == nullptr)) {
		return -EINVAL;
	}

	// set write mode of SPI
	int result = ::ioctl(_fd, SPI_IOC_WR_MODE, &_mode);

	if (result == -1) {
		PX4_ERR("can’t set spi mode");
		return PX4_ERROR;
	}

	int bits = 16;
	result = ::ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

	if (result == -1) {
		PX4_ERR("can’t set 16 bit spi mode");
		return PX4_ERROR;
	}

	spi_ioc_transfer spi_transfer[1] {};

	spi_transfer[0].tx_buf = (uint64_t)send;
	spi_transfer[0].rx_buf = (uint64_t)recv;
	spi_transfer[0].len = len * 2;
	spi_transfer[0].speed_hz = _frequency;
	//spi_transfer[0].bits_per_word = 8;
	//spi_transfer[0].delay_usecs = 10;
	spi_transfer[0].cs_change = true;

	result = ::ioctl(_fd, SPI_IOC_MESSAGE(1), &spi_transfer);

	if (result != (int)(len * 2)) {
		PX4_ERR("write failed. Reported %d bytes written (%s)", result, strerror(errno));
		return PX4_ERROR;
	}

	return PX4_OK;
}
What are the htons() and ntohs() and where can I get the detail of these?
joan wrote: What does the following report?

sudo pigpiod # start the pigpio daemon

pigs spio 0 1000000 0 # use main spi ss GPIO 8 (if you are using ss GPIO 7 use pigs spio 1 1000000 0)
pigs spix 0 0x68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I will try that.

User avatar
DougieLawson
Posts: 41300
Joined: Sun Jun 16, 2013 11:19 pm
Location: A small cave in deepest darkest Basingstoke, UK
Contact: Website Twitter

Re: How to manually toggle SPI CS?

Fri Apr 16, 2021 8:25 am

The SPI interface works by sending blocks of 8-bits up to 256 bits. However many bits are sent get returned. For a write only device you simply ignore the stuff that comes back.

This code sends 16-bits (and ignores anything returned).

Code: Select all

void writeByte(uint8_t reg, uint8_t data)
{
  uint8_t spiBufTx [3];
  uint8_t spiBufRx [3];
  struct spi_ioc_transfer spi;
  memset (&spi, 0, sizeof(spi));
  spiBufTx [0] = CMD_WRITE;
  spiBufTx [1] = reg;
  spiBufTx [2] = data;
  spi.tx_buf =(unsigned long)spiBufTx;
  spi.rx_buf =(unsigned long)spiBufRx;
  spi.len = 3;
  spi.delay_usecs = spiDelay;
  spi.speed_hz = spiSpeed;
  spi.bits_per_word = spiBPW;
  ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi);
}
This code sends 32-bits and prints the 32-bits that the SPI device sends back.

Code: Select all

static void
transfer (int fd, char *rd31855, int ret_tod)
{
  int ret;
  uint8_t wr_buf[] = { 0x00, 0x00, 0x00, 0x00 };
  uint8_t rd_buf[4];
  struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long) wr_buf,
    .rx_buf = (unsigned long) rd_buf,
    .len = ARRAY_SIZE (wr_buf),
    .delay_usecs = delay,
    .speed_hz = speed,
    .bits_per_word = bits,
  };

  time_t rawtime;
  struct tm *timeinfo;
  char time_of_day[16];

  if (fd <= 0)
    {
      sprintf (rd31855, "Device %s not found\n", device);
      return;
    }

  if ((ret = ioctl (fd, SPI_IOC_WR_MODE, &mode)) == -1)
    {
      sprintf (rd31855, "Can't set mode");
      return;
    }

  if ((ret = ioctl (fd, SPI_IOC_WR_BITS_PER_WORD, &bits)) == -1)
    {
      sprintf (rd31855, "Can't set number of bits");
      return;
    }

  if ((ret = ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed)) == -1)
    {
      sprintf (rd31855, "Can't set write speed");
      return;
    }

  if ((ret = ioctl (fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed)) == -1)
    {
      sprintf (rd31855, "Can't set read speed");
      return;
    }

  ret = ioctl (fd, SPI_IOC_MESSAGE (1), &tr);
  if (ret == 1)
    {
      sprintf (rd31855, "Can't send API message");
      return;
    }

  if (ret_tod > 0)
    {
      time (&rawtime);
      timeinfo = localtime (&rawtime);
      strftime (time_of_day, 16, "%T %b,%d", timeinfo);
      sprintf (rd31855, "%s\n%.4f %.4f", time_of_day,
	   (float) ((rd_buf[0] << 8) + rd_buf[1]) / 16,
	   (float) ((rd_buf[2] << 8) + rd_buf[3]) / 256);
    }
  else 
    {
      sprintf (rd31855, "%.4f %.4f", 
	   (float) ((rd_buf[0] << 8) + rd_buf[1]) / 16,
	   (float) ((rd_buf[2] << 8) + rd_buf[3]) / 256);
    }
}
Any language using left-hand whitespace for syntax is ridiculous

Any DMs sent on Twitter will be answered next month.
Fake doctors - are all on my foes list.

Any requirement to use a crystal ball or mind reading will result in me ignoring your question.

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Fri Apr 16, 2021 6:57 pm

joan wrote:
Thu Apr 15, 2021 7:44 pm
What does the following report?

sudo pigpiod # start the pigpio daemon

pigs spio 0 1000000 0 # use main spi ss GPIO 8 (if you are using ss GPIO 7 use pigs spio 1 1000000 0)
pigs spix 0 0x68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
$ sudo pigpiod
$ pigs spio 0 1000000 0
0
$ pigs spix 0 0x68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
22 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255

User avatar
joan
Posts: 15616
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: How to manually toggle SPI CS?

Fri Apr 16, 2021 7:32 pm

Those results are possible but unlikely. I don't think the device is wired to the Pi correctly. Some clear photos showing the connections will help.

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Sat Apr 17, 2021 12:42 am

joan wrote:
Fri Apr 16, 2021 7:32 pm
Those results are possible but unlikely. I don't think the device is wired to the Pi correctly. Some clear photos showing the connections will help.
Some further investigation...

$ sudo pigpiod
$ pigs spio 0 200000 0
0
$ pigs spix 0 0x22 0x33
2 255 255
$ pigs spir 0 2
2 0 0
$ pigs spix 0 0x38 0x00
2 0 6
$ pigs spix 0 0x38 0x00 0x39 0x00
4 0 0 0 0
$ pigs spix 0 0x38 0x00
2 0 0

$ pigs spix 0 0x68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

User avatar
joan
Posts: 15616
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: How to manually toggle SPI CS?

Sat Apr 17, 2021 8:00 am

Need those connection photos.

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Mon Apr 19, 2021 5:29 am

joan wrote:
Sat Apr 17, 2021 8:00 am
Need those connection photos.
Now I am using ADIS16477 instead of ADIS16354 with the same issue. The SPI specs and read/write sequence is same for both parts.
Connection photo: https://drive.google.com/file/d/1xUvelK ... sp=sharing
Sensor breakout board used: https://wiki.analog.com/resources/eval/ ... s1647x-pcb
DougieLawson wrote: You can send & receive up to 32 bytes with a single I/O operation. So if you fiddle your code to send uint16_t it will pull the CS line low for all 16 bits.

Code: Select all

int
SPI::transfer(uint16_t *send, uint16_t *recv, unsigned len)
{
    if ((send == nullptr) && (recv == nullptr)) {
        return -EINVAL;
    }

    // set write mode of SPI
    int result = ::ioctl(_fd, SPI_IOC_WR_MODE, &_mode);

    if (result == -1) {
        PX4_ERR("can’t set spi mode");
        return PX4_ERROR;
    }

    spi_ioc_transfer spi_transfer{};

    spi_transfer.tx_buf = (uint16_t)send;
    spi_transfer.rx_buf = (uint16_t)recv;
    spi_transfer.len = len;
    spi_transfer.speed_hz = _frequency;
    spi_transfer.bits_per_word = 8;

    result = ::ioctl(_fd, SPI_IOC_MESSAGE(1), &spi_transfer);

    if (result != (int)len) {
        PX4_ERR("write failed. Reported %d bytes written (%s)", result, strerror(errno));
        return PX4_ERROR;
    }
    
You may need to use htons() and ntohs() to get the right byte order.
This code didn't work... unable to correctly read/write registers.

Skoog
Posts: 25
Joined: Sat Aug 24, 2019 6:45 pm

Re: How to manually toggle SPI CS?

Fri Apr 23, 2021 8:34 am

It worked... the correct sequence is to send 2 consecutive bytes and after that receive 2 bytes.
Thanks.

Return to “Interfacing (DSI, CSI, I2C, etc.)”