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

Issues Writing Registers on ADXL375 with SPI

I'm attempting to write a library to allow a NRF52 DK (PCA10040) to read data from a ADXL375 accelerometer through SPI. I'm using SDK 15.0.0. I am able to read from the registers just fine, but cannot write to them. Depending on the circumstances, it will either write a persistent value to the register and then never change regardless of input; otherwise it will just not write at all.

It would be great if someone could shed some light on this. Here is the code for the read and write functions:

static const              nrf_drv_spi_t * p_spi_master;
static volatile bool    spi_xfer_done = true;  /**< Flag used to indicate that SPI instance completed the transfer. */
static const              nrf_drv_spi_config_t adxl375_spi_config =
{
    .irq_priority   = APP_IRQ_PRIORITY_LOW,
    .orc               = 0xCC,
    .frequency    = NRF_DRV_SPI_FREQ_4M,
    .mode           = NRF_DRV_SPI_MODE_3,        // CPOL = 0, CPHA = 0
    .bit_order      = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,
    .mosi_pin      = SPIM1_MOSI_PIN,
    .ss_pin          = SPIM1_SS_PIN,
    .miso_pin      = SPIM1_MISO_PIN,
    .sck_pin         = SPIM1_SCK_PIN,
};

void adxl375_spi_srv_init(const nrf_drv_spi_t * m_spi_master)
{
    p_spi_master = m_spi_master;
    // Initialize the SPI instance to use with ADXL362 accelerometer
    APP_ERROR_CHECK(nrf_drv_spi_init(m_spi_master, &adxl375_spi_config, NULL, NULL)); // No event handler, SPI will be in blocking mode
}

uint8_t SPItransfer(uint8_t txData)
{
    uint8_t p_tx_data;
    uint8_t p_rx_data;
    p_tx_data = txData;
    uint32_t err_code = nrf_drv_spi_transfer(p_spi_master, &p_tx_data, 1, &p_rx_data, 2);
    APP_ERROR_CHECK(err_code);
    return p_rx_data;
}

uint8_t oneByteRead(uint8_t address) {

    uint8_t tx = (ADXL375_SPI_READ | (address & 0x3F));
    uint8_t rx = 0;

    nrf_gpio_pin_clear(adxl375_spi_config.ss_pin);
    //Send address to read from.
    SPItransfer(tx);
    //Read back contents of address.
    rx = SPItransfer(0x00);
    while (!spi_xfer_done)
        __WFE();
    nrf_gpio_pin_set(adxl375_spi_config.ss_pin);

    return rx;

}

void multiByteRead(int startAddress, char* buffer, int size) {

    int tx = (ADXL375_SPI_READ | ADXL375_MULTI_BYTE | (startAddress & 0x3F));

    nrf_gpio_pin_clear(adxl375_spi_config.ss_pin);
    //Send address to start reading from.
    SPItransfer(tx);

    for (int i = 0; i < size; i++) {
        buffer[i] = SPItransfer(0x00);
    }

    nrf_gpio_pin_set(adxl375_spi_config.ss_pin);

}

void oneByteWrite(uint8_t address, uint8_t data) {

    int i;
    int tx = (ADXL375_SPI_WRITE | (address & 0x3F));
    nrf_gpio_pin_clear(adxl375_spi_config.ss_pin);
    //nrf_delay_ms(10);
    //Send address to write to.
    SPItransfer(tx);
    //Send data to write.
    SPItransfer(data);
    while (!spi_xfer_done)
        __WFE();
    nrf_gpio_pin_set(adxl375_spi_config.ss_pin);

}

Parents
  • I can't spend more than a few minutes, but it's clear the use of SPI in not quite as you hope. The code above shows this:

    uint8_t SPItransfer(uint8_t txData)
    {
        uint8_t p_tx_data;
        uint8_t p_rx_data;
        p_tx_data = txData;
        uint32_t err_code = nrf_drv_spi_transfer(p_spi_master, &p_tx_data, 1, &p_rx_data, 2);
        APP_ERROR_CHECK(err_code);
        return p_rx_data;
    }

    That code will not work correctly, since the rx buffer will corrupt the stack when 2 bytes are stuffed into an area for 1 byte. This is a corrected version:

    uint8_t SPItransfer(uint8_t txData)
    {
        uint8_t p_tx_data;
        uint8_t p_rx_data[2];  // Make this an array to receive more bytes, 2 in this case
        p_tx_data = txData;
        uint32_t err_code = nrf_drv_spi_transfer(p_spi_master, &p_tx_data, 1, &p_rx_data, 2);
        APP_ERROR_CHECK(err_code);
        return p_rx_data[1];  // Return the second byte, not the first
    }

    This corrected version only works for a single command byte expecting a single response byte which will be the second of two bytes received over SPI. To read more than a single byte after a single command byte do not use a loop, ask for all the (sequential) bytes in a single transfer.

    uint8_t tx_data;
    uint8_t rx_data[20];  // Make this an array to receive lots ofbytes, say 20
    
    uint32_t SPItransfer(uint8_t txData)
    {
        tx_data = txData;
        uint32_t err_code = nrf_drv_spi_transfer(p_spi_master, &tx_data, 1, &rx_data, sizeof(rx_data));
        APP_ERROR_CHECK(err_code);
        return err_code;  // Return the error code, look in p_rx_data for response string
    }
    
    void multiByteRead(int startAddress, char* buffer, int size) {
        uint8_t tx = (ADXL375_SPI_READ | ADXL375_MULTI_BYTE | (startAddress & 0x3F));
        //Send address to start reading from.
        SPItransfer(tx);
        for (int i = 0; i < size; i++) {
            // this is just a simple example, but try not to be silly
            if (i >= sizeof(rx_data)) break;
            buffer[i] = rx_data[i];
        }
    }
    

    There are other potential issues; you might have to increase port pin drive strength on the SCK pin; 4MHz might be too high a clock speed so maybe try something lower until it works then speed up until it doesn't; the accelerometer may require a delay between the command byte and the subsequent response bytes (some do, some don't).

  • the use of SPI in not quite as you hope

    Indeed.

        uint8_t p_tx_data;
        uint8_t p_rx_data;

    - The use of "p_" as a prefix like that usually indicates that the variable is a pointer.

    Is that what you intended?

    If so, you also need to define somewhere to store the actual data!

  • Thank you all for the help. I rewrote the SPITransfer function using an array as suggested by , and made sure that I had somewhere to store my data. The biggest difference was made by sending the write data as an array instead of 2 separate transmissions. The code that worked correctly was this, case this is of use to anyone:

    uint32_t oneByteWrite(uint8_t address, uint8_t data) 
    {
        uint32_t err_code;
        uint8_t packet[2] = {address, data};
        address = address | ADXL375_SPI_WRITE;    
        err_code = nrf_drv_spi_transfer(p_spi_master, packet, 2, NULL, 0);
        if(err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        return err_code;
    }

  • This doesn't look right:

        uint8_t packet[2] = {address, data};
        address = address | ADXL375_SPI_WRITE;    
        err_code = nrf_drv_spi_transfer(p_spi_master, packet, 2, NULL, 0);

    You copy the address into packet[0], and you use the packet[] array for your transfer.

    Therefore OR-ing the address with ADXL375_SPI_WRITE after you've copied it into the array won't have any effect on the transmission!

Reply
  • This doesn't look right:

        uint8_t packet[2] = {address, data};
        address = address | ADXL375_SPI_WRITE;    
        err_code = nrf_drv_spi_transfer(p_spi_master, packet, 2, NULL, 0);

    You copy the address into packet[0], and you use the packet[] array for your transfer.

    Therefore OR-ing the address with ADXL375_SPI_WRITE after you've copied it into the array won't have any effect on the transmission!

Children
Related