How to use spi_write_dt

Hello,

I am attempting to use the function spi_write_dt to write data (configuration files and lots of data) to a specific register. is there a better function for SPI that would do what I need it to?

This is my read function and I think it is working...

BMA4_INTF_RET_TYPE bma4_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
    tx.buf = &reg_addr;    
    tx.len = sizeof(reg_addr);
    printf("tx.buf read = %x\n", tx.buf);
    rx.buf = (uint8_t *)reg_data;
    rx.len = len;
    rslt= spi_transceive_dt(&dev_spi, &tx_bufs, &rx_bufs);
    reg_data = rx.buf; //<-Does this do what I think?   
    return rslt;
}

I want the read to something similar to the I2C. the reg_data needs to hold the read info from the register for the program to work properly.

BMA4_INTF_RET_TYPE bma4_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{   
    uint8_t buffer[] = {reg_addr, *reg_data};         
    rslt = i2c_write_read_dt(&dev_i2c, &buffer[0], 1, reg_data, len);
    return rslt;   
}

Here is my attempted write function but I am stuck on how to put the register address in front of the data being sent.

The device tree:

&spi1 {
    // compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi1_default>;
	pinctrl-1 = <&spi1_sleep>;
	// pinctrl-names = "default", "sleep";  
    cs-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>; 
    a: bma456-a@0 {        
        compatible = "revel,spi-device";
        status = "okay";       
        reg = <0>;  
        spi-max-frequency = <7000000>;
        //spi-max-frequency = <900000>;                   
    };
};

Here are some of the definitions:

#define SPI1_NODE DT_NODELABEL(a)
#define SPI_OP  SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB
static const struct spi_dt_spec dev_spi = SPI_DT_SPEC_GET(SPI1_NODE, SPI_OP, 0);

static struct spi_buf rx;
const static struct spi_buf_set rx_bufs = {
	.buffers = &rx,
	.count = 1,
};

static struct spi_buf tx;
const static struct spi_buf_set tx_bufs = {
	.buffers = &tx,
	.count = 1,
}; 

BMA4_INTF_RET_TYPE bma4_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{    
    tx.buf = &reg_addr;    
    printf("tx.buf = %x\n", tx.buf);
    tx.len = sizeof(reg_addr);
    rslt= spi_write_dt(&dev_spi, &tx_bufs); 
    tx.buf = (uint8_t *)reg_data;
    printf("tx.buf 2= %x\n", tx.buf);
    tx.len = len;
    rslt= spi_write_dt(&dev_spi, &tx_bufs);    
    return rslt;
}

The biggest problem is trying to figure out how to write data to a specific register address. I want it to do what the I2c version does.

BMA4_INTF_RET_TYPE bma4_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{   
    uint8_t buffer[] = {reg_addr, *reg_data};
    rslt= i2c_burst_write_dt(&dev_i2c, buffer[0], (uint8_t *)reg_data, len);
    return rslt;
}
and another write version...
BMA4_INTF_RET_TYPE bma4_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{    
    uint8_t txbuffer[2] = {reg_addr, *reg_data};
    struct spi_buf tx = {
        .buf = txbuffer,  .len = sizeof(txbuffer),};
    struct spi_buf_set tx_bufs = {
        .buffers = &tx, .count = 1,};
    rslt= spi_write_dt(&dev_spi, &tx_bufs);       
    return rslt;
}

Any help would be much appreciated!!

Best regards,

Jared

  • Hi Jared,

    SPI just transfers data, and if you are writing to a specific register on the slave device it just still just data seen from the nRF. From what I can see from the BMA400 datasheet under "SPI Interface and protocol", the address should just be the first byte when you write (then you can add another register and data byte if you like, and so on).

    Einar

  • Hello Einar,

    Thank you for the quick response! 

    I was hoping you could take a look at SPI code in the post and give me some pointers on best practices of how to make sure my data is in the proper format and length. When I create the buffer, it seems to cut off the rest of the data and only shows eight bits.

    Best regards,

    Jared

  • Hi Jared,

    jbeh444 said:
    I was hoping you could take a look at SPI code in the post and give me some pointers on best practices of how to make sure my data is in the proper format and length. When I create the buffer, it seems to cut off the rest of the data and only shows eight bits.

    I can take a look. I guess the relevant part is this function;

    BMA4_INTF_RET_TYPE bma4_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
    {    
        uint8_t txbuffer[2] = {reg_addr, *reg_data};
        struct spi_buf tx = {
            .buf = txbuffer,  .len = sizeof(txbuffer),};
        struct spi_buf_set tx_bufs = {
            .buffers = &tx, .count = 1,};
        rslt= spi_write_dt(&dev_spi, &tx_bufs);       
        return rslt;
    }

    Here I see you ignore the len parameter of your function, and always set len in the spi_buf instante to the size of the array, which is 2. And that matches what I saw in the datasheet when I briefely looked yesterday. I did not read it carefully so I might be wrong, but it seemd to me like you always write a 1 byte address and 1 byte data. Then you could add on with another 1 byte address and 1 byte data, and so on. The latter is not supported by your function here.

    If you wanted to do that you would have to buidl a larger array of every second byte addres an data. There should not be anything preventing you from doing that though. Essentially make your txbuffer larger, populate it properly, and that should be it. (I have no knowledge of the sensor though and did not carefully read the datasheet, so there may be details I am missing).

  • Thank you for the great insight! The I2c version worked on this sensor on the dev kit. We moved to a custom board which the pins were switch to an SPI. I am now attempting to correct this by getting the SPI working. The BMA456 uses a burst write to write a config file. To change settings, it does a burst read on the same register. I am trying to understand how to write a file of 5016 bytes and what the limits are on the SPI TX. 

    #1  I need the read function to be a modifiable buffer size because the number of bytes it reads can vary. The read must be saved to reg_data in the function call as this is what the other functions read to see if the chip_id and register data is correct.

    #2 The write function needs to be a modifiable buffer of various sizes as well. 

    I apologize for my lack of understanding about the SPI buffers and pointers to data.

    Here is my latest attempt to modify the code to attempt to initialize the sensor.

    BMA4_INTF_RET_TYPE bma4_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
    {  
        static struct spi_buf rx;
        static struct spi_buf_set rx_bufs = {
        .buffers = &rx,
        .count = 1,
        };
    
        static struct spi_buf tx;
        static struct spi_buf_set tx_bufs = {
        .buffers = &tx,
        .count = 1,
        }; 
        
        tx.buf = &reg_addr;       
        tx.len = 1;
        rx.buf = reg_data;       
        rx.len = len;   
        rslt= spi_transceive_dt(&dev_spi, &tx_bufs, &rx_bufs);
    
        return rslt;
    }
    
    BMA4_INTF_RET_TYPE bma4_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
    {    
        struct spi_buf tx[len+1];
                
        tx[0].buf = &reg_addr;
        tx[0].len = 1; 
        for(int i = 0; i < len; i++)
        {
            tx[i+1].buf = (uint8_t*)reg_data+i; 
            tx[i+1].len = 1;       
        }      
        struct spi_buf_set tx_bufs = {
            .buffers = tx,
            .count = 1,
        };     
        rslt= spi_write_dt(&dev_spi, &tx_bufs);       
        return rslt;
    }

  • Hi Jared,

    I have not had a chance to look at this unfortuantely, and I will be OoO next week. DevZone will also have reduced staffing. That said, I se Bosch has a driver that you may look at.

Related