This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SPI communication on Thingy:91 board

Hello guys!

We want to configure ADXL362 accelerometer on Thingy:91 board to wake up nRF9160 SiP via INT1 external interrupt. For that purpose, we need to properly configure ADXL362. Consequently SPI driver is needed to access ADXL362 internal registers. ADXL362 supports both single-Byte and multi-Byte SPI transfers:

I tried to implement some SPI communication approaches I found here on the forum (e.g. here) but I have a hard time experiencing non-consistent behaviour (e.g. sometimes I properly read ADXL362 ID, sometimes every second ID read is successful, sometimes no success...).

Here are my functions for accessing ADXL362 registers:

int read_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                 u8_t addr, u8_t *data, u32_t num_bytes){
    
    int err = 0;

    /* read cmd */
    err = adxl362_access(spi, spi_cfg,
                           ADXL362_READ_CMD, addr, data, num_bytes);
    if (err) {
            printk("Error during SPI read\n");
            return -EIO;
    }

    return 0;       
}

int write_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                  u8_t addr, u8_t *data, u32_t num_bytes){

    int err = 0;
    /* write cmd */
    err = adxl362_access(spi, spi_cfg,
                           ADXL362_WRITE_CMD, addr, data, num_bytes);
    if (err) {
            printk("Error during SPI write\n");
            return -ENXIO;
    }

    return 0;
}

int adxl362_access(struct device *spi, const struct spi_config *spi_cfg,
			    u8_t cmd, u8_t addr, void *data, size_t len)
{
    int err_code = 0;
    u8_t access[2];
    access[0] = cmd;
    access[1] = addr;

    struct spi_buf bufs[] = {
        {
            .buf = access,
            .len = 2
        },
        {
            .buf = data,
            .len = len
        }
    };
    const struct spi_buf_set tx = {
            .buffers = bufs,
            .count = 2
    };

    const struct spi_buf_set rx = {
        .buffers = bufs,
        .count = 2
    };

    switch(cmd){
        case ADXL362_READ_CMD:
        case ADXL362_WRITE_CMD:
        case ADXL362_READ_FIFO_CMD:        
            err_code = spi_transceive(spi, spi_cfg, &tx, &rx);
            break;
        default:
            printk("unknown SPI command!\n");
            err_code = -EFAULT;
            break;        
    }

    return err_code;
}

Before applying those functions I initialize SPI driver with:

static const struct spi_config spi_cfg= {
    .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
    .frequency = 8000000,
    .slave = 0,
    .cs = &cs_control
};

void spi_init(void)
{
    const char* const spiName = "SPI_3";
    spi_dev = device_get_binding(spiName);

    if (spi_dev == NULL) {
            printk("Could not get %s device\n", spiName);
            return;
    }

    gpio_dev = device_get_binding("GPIO_0");
    if(!gpio_dev) {
        printk("could not find GPIO device\n");
        return;
    }

    cs_control.delay = 0;
    cs_control.gpio_dev = gpio_dev;
    cs_control.gpio_pin = ADXL362_CS;
}

Is there anything wrong with initialization process, is this a good way to specify CS pin of the SPI slave device?

What particularly confuses me is the fact that spi_transceive() function requires a set of transmit and receive buffers (tx_bufs, rx_bufs):

spi_transceive(struct device * dev, const struct spi_config * config, const struct spi_buf_set * tx_bufs, const struct spi_buf_set * rx_bufs)

Can we have just a single transmit buffer (tx_buf) that will contain command byte(s), address byte(s) as well as data to write (if there are some) as well as a single receive buffer (rx_buf) that will contain data read from SPI device? Something like this (for the case I want to read N-bytes from SPI device):

	static u8_t tx_buffer[2];
    tx_buffer[0] = cmd;
    tx_buffer[1] = addr;

	const struct spi_buf tx_buf = {
		.buf = tx_buffer,
		.len = sizeof(tx_buffer)
	};
	const struct spi_buf_set tx = {
		.buffers = &tx_buf,
		.count = 1
	};

	struct spi_buf rx_buf = {
		.buf = data,
		.len = N,
	};
	const struct spi_buf_set rx = {
		.buffers = &rx_buf,
		.count = 1
	};

	err = spi_transceive(spi_dev, spi_cfg, &tx, &rx);
	if (err) {
		printk("SPI error: %d\n", err);
	}

tx_buf contains only command and address bytes whereas rx_buf of size N is used to store received data after calling spi_transceive(spi_dev, spi_cfg, &tx, &rx) function. When I apply that approach I am unable to properly read ADXL362 registers.

Here are some questions that arise:

1) Is multi-Byte (burst) SPI transfer supported by SPI driver?

2) Where do I store data to write to SPI device before calling spi_transceive(spi_dev, spi_cfg, &tx, &rx) function?

3) How do I specify the length of the data I want to write to SPI device?

4) Where the data received from SPI device after calling spi_transceive(spi_dev, spi_cfg, &tx, &rx) function are stored?

5) How do I specify the length of the data I want to read from SPI device?

Some fresh reference on nRF9160 SPI example would also be useful for me.

Thank you very much for your time and efforts!

Sincerely,

Bojan.

Parents
  • OK, guys, I think I figured out how to send/receive data over SPIM. It is indeed necessary to have a single transmit buffer (tx_buf) that will contain command byte(s), address byte(s) as well as data to write (if there are some) as well as a single receive buffer (rx_buf) that will contain data read from SPI device. Here is how I accessed single register of ADXL362 device:

    int read_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                     u8_t addr, u8_t *data, u32_t num_bytes){
        
        int err = 0;
    
        /* read cmd */
        err = adxl362_access(spi, spi_cfg,
                               ADXL362_READ_CMD, addr, data, num_bytes);
        if (err) {
                printk("Error during SPI read\n");
                return -EIO;
        }
    
        return 0;       
    }
    
    int write_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                      u8_t addr, u8_t *data, u32_t num_bytes){
    
        int err = 0;
        /* write cmd */
        err = adxl362_access(spi, spi_cfg,
                               ADXL362_WRITE_CMD, addr, data, num_bytes);
        if (err) {
                printk("Error during SPI write\n");
                return -ENXIO;
        }
    
        return 0;
    }
    
    int adxl362_access(struct device *spi, const struct spi_config *spi_cfg,
    			    u8_t cmd, u8_t addr, void *data, size_t len)
    {
    
    	int err;
    	static u8_t tx_buffer[2];
    	//static u8_t rx_buffer[1];
            tx_buffer[0] = cmd;
            tx_buffer[1] = addr;
    
    	const struct spi_buf tx_buf = {
    		.buf = tx_buffer,
    		.len = sizeof(tx_buffer)
    	};
    	const struct spi_buf_set tx = {
    		.buffers = &tx_buf,
    		.count = 1
    	};
    
    	struct spi_buf rx_buf = {
    		.buf = data,
    		.len = len + 2,
    	};
    	const struct spi_buf_set rx = {
    		.buffers = &rx_buf,
    		.count = 1
    	};
    
    	err = spi_transceive(spi_dev, spi_cfg, &tx, &rx);
    	if (err) {
    		printk("SPI error: %d\n", err);
    	}
    
            return err;
    }

    According to Nordic's documentation, SPI master is a synchronous interface and for every byte that is sent, a different byte will be received at the same time. That is why we should look the content of the register read into rx_buf[2]. rx_buf[0] and rx_buf[1] store some data received while sending command and address bytes (tx_buf[0] and tx_buf[1], respectively).

    Cheers!

    Bojan.

Reply
  • OK, guys, I think I figured out how to send/receive data over SPIM. It is indeed necessary to have a single transmit buffer (tx_buf) that will contain command byte(s), address byte(s) as well as data to write (if there are some) as well as a single receive buffer (rx_buf) that will contain data read from SPI device. Here is how I accessed single register of ADXL362 device:

    int read_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                     u8_t addr, u8_t *data, u32_t num_bytes){
        
        int err = 0;
    
        /* read cmd */
        err = adxl362_access(spi, spi_cfg,
                               ADXL362_READ_CMD, addr, data, num_bytes);
        if (err) {
                printk("Error during SPI read\n");
                return -EIO;
        }
    
        return 0;       
    }
    
    int write_adxl362(struct device *spi, const struct spi_config *spi_cfg,
                      u8_t addr, u8_t *data, u32_t num_bytes){
    
        int err = 0;
        /* write cmd */
        err = adxl362_access(spi, spi_cfg,
                               ADXL362_WRITE_CMD, addr, data, num_bytes);
        if (err) {
                printk("Error during SPI write\n");
                return -ENXIO;
        }
    
        return 0;
    }
    
    int adxl362_access(struct device *spi, const struct spi_config *spi_cfg,
    			    u8_t cmd, u8_t addr, void *data, size_t len)
    {
    
    	int err;
    	static u8_t tx_buffer[2];
    	//static u8_t rx_buffer[1];
            tx_buffer[0] = cmd;
            tx_buffer[1] = addr;
    
    	const struct spi_buf tx_buf = {
    		.buf = tx_buffer,
    		.len = sizeof(tx_buffer)
    	};
    	const struct spi_buf_set tx = {
    		.buffers = &tx_buf,
    		.count = 1
    	};
    
    	struct spi_buf rx_buf = {
    		.buf = data,
    		.len = len + 2,
    	};
    	const struct spi_buf_set rx = {
    		.buffers = &rx_buf,
    		.count = 1
    	};
    
    	err = spi_transceive(spi_dev, spi_cfg, &tx, &rx);
    	if (err) {
    		printk("SPI error: %d\n", err);
    	}
    
            return err;
    }

    According to Nordic's documentation, SPI master is a synchronous interface and for every byte that is sent, a different byte will be received at the same time. That is why we should look the content of the register read into rx_buf[2]. rx_buf[0] and rx_buf[1] store some data received while sending command and address bytes (tx_buf[0] and tx_buf[1], respectively).

    Cheers!

    Bojan.

Children
No Data
Related