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

How to send 1 byte via SPI from master to slave

Hello everyone, I'm trying to run my nrf51 with ADS1298 using SPI. nrf51 is master and ADS1298 is slave.

I configured SPI as

const nrf_drv_spi_config_t spi_config =
    {
    		.sck_pin = 25,
			.mosi_pin = 24,
			.miso_pin = 23,
			.ss_pin = 30,
			//.irq_pin = 22,
			.irq_priority = APP_IRQ_PRIORITY_HIGH,
			.orc = 0x00,
			.frequency = NRF_DRV_SPI_FREQ_4M,
			.mode = NRF_DRV_SPI_MODE_0,
			.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST
    };

    err_code = nrf_drv_spi_init(&spi, &spi_config, spi_event_handler);
    APP_ERROR_CHECK(err_code);

And I tried to send one byte via SPI

memset(m_rx_buf, 0, m_length);
spi_xfer_done = false;
uint8_t SDATAC = 0x11;
// it stops continuous mode
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &SDATAC, 1, m_rx_buf, m_length));

But my logic analyser shows that I sent 4 bytes

MOSI: 0x11 MISO 0x00
MOSI: 0xFF MISO 0x00
MOSI: 0xFF MISO 0x00
MOSI: 0xFF MISO 0x00

It wasn't what I expected

Moreover I need to send 3 separate commands in arduino it looks as:

//stop continuous mode
digitalWrite(IPIN_CS, LOW);
SPI.transfer(SDATAC);
delayMicroseconds(1);
digitalWrite(IPIN_CS, HIGH);

//read chip ID
int out = 0;
digitalWrite(IPIN_CS, LOW);
SPI.transfer(0x20 | 0x00);
delayMicroseconds(5);
SPI.transfer(0);	// number of registers to be read/written – 1
delayMicroseconds(5);
out = SPI.transfer(0);
delayMicroseconds(1);
digitalWrite(IPIN_CS, HIGH);

Like that I tried to send it on nrf51 but got 0x00

uint8_t m_tx_buf[] = {0x11, 0x20 | 0x00, 0x00};
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, sizeof(m_tx_buf), m_rx_buf, m_length));

How to send 1 byte and 3 bytes like below, also is it possible to insert delay between 2 transfers like in arduino code?

Also I have a problem with GPIO pin, I configured several pins as GPIO outputs input, they needed to control my ADS1298: START for starting conversion, PWDN to turn my chip on and DRDY to read the status when data from ADS1298 is ready to be read.

#define START 5
#define RESET 6
#define PWDN 7
#define DRDY 8


// init output pins
nrf_gpio_cfg_output(START);							//config pin START as output
nrf_gpio_cfg_output(RESET);							//config pin RESET as output
nrf_gpio_cfg_output(PWDN);		//config pin PWDN as output
nrf_gpio_cfg_input(DRDY, NRF_GPIO_PIN_NOPULL);		//config pin DRDY as input

and set values of pins

nrf_gpio_pin_set(START);
nrf_gpio_pin_set(PWDN);

my logic analyser shows toggling on these pins, but only master changes the state of them.

Also I put into main while this code and set breakpoint on if

while(1)
{
     if(nrf_gpio_pin_read(DRDY) == 0)
     {
          NRF_LOG_INFO("Data is ready\r\n");
     }
     while (!spi_xfer_done)
        {
            __WFE();
        }

        NRF_LOG_FLUSH();
}

Breakpoint works only once and the value is high, but I see on my logic analyser that sometimes it's tied to low, what is the best way to read the status of GPIO pin?

  • What is m_length set to? If you only want to send 1 byte then you have to set the rx_buffer_length 1 or p_rx_buffer to NULL if you do not need to receive any data.

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &SDATAC, 1, m_rx_buf, 1));
    

    Otherwise you will just clock out "garbage data". As for the pin read issue, I would recommend setting up the gpiote peripheral to generate an interrupt whenever there is a level change on the pin, like the pin_change_int example in the SDK.

    #include <stdbool.h>
    #include "nrf.h"
    #include "nrf_drv_gpiote.h"
    #include "app_error.h"
    #include "boards.h"
    
    #define PIN_OUT 8
    void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
        nrf_drv_gpiote_out_toggle(PIN_OUT);
    }
    /**
     * @brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output, 
     * and configures GPIOTE to give an interrupt on pin change.
     */
    static void gpio_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
        
        nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
    
        err_code = nrf_drv_gpiote_out_init(PIN_OUT, &out_config);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
        in_config.pull = NRF_GPIO_PIN_PULLUP;
    
        err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        gpio_init();
    
        while (true)
        {
            // Do nothing.
        }
    }
    
  • Thanks for your reply. I found the solution of pin changes in examples of SDK.

    So, about SPI, yes I need to send some data without receiving from MISO

    According to your answer it looks like

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &SDATAC, 1, NULL, 1));
    

    Do I need to change rx_buffer_length to 0?

    So and how to implement the arduino code when I send each 1 byte after delay?

    int out = 0;
    digitalWrite(IPIN_CS, LOW);
    SPI.transfer(0x20 | 0x00);
    delayMicroseconds(5);
    SPI.transfer(0);    // number of registers to be read/written – 1
    delayMicroseconds(5);
    out = SPI.transfer(0);
    delayMicroseconds(1);
    digitalWrite(IPIN_CS, HIGH);
    

    What is the best way send like this:

    uint8_t tx[3] = {0x11, 0x20 | 0x00, 0x00};
    

    or

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x11, 1, NULL, 1));
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x20 | 0x00, 1, NULL, 1));
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x00, 1, rx_buf, 1));
    
  • No, the buffer length wil be ignored if the rx buffer pointer is NULL. Does it have to be exactly 5 microseconds between the transfers?

  • Yes, about 5ms. From data sheet: "The ADS129x serial interface decodes commands in bytes, and requires 4 tCLK periods to decode and execute. Therefore, when sending multibyte commands, a 4 tCLK period must separate the end of one byte (or opcode) and the next."

  • Then you'll have to use the last approach,

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x11, 1, NULL, 1));
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x20 | 0x00, 1, NULL, 1));
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, 0x00, 1, rx_buf, 1));
    

    with nrf_delay_ms(5) in between the calls.

Related