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

SPI connection between nrf52832 and Raspberry 3 Pi

Hello guys,

I'm having problems with communication between a dwm1001 module (from Decawave) which is a nrf52832 board, and my Raspberry 3 Pi model B.

I have connected the nrf52832 board to the Raspberry using the 26-pins header

My goal is to send/write a message from Raspberry to dwm1001 module. Inside the Raspberry I wrote a simple python script in order to write to the SPI port:

import spidev
import time
spi = spidev.SpiDev()
spi.open(0,0)
spi.mode = 0b00
while True:
    data = [ 0xcc ]
    resp = spi.xfer(data)
    print resp
    time.sleep(1)

So, now I don't know if this *is* the correct way, but I have found this in internet.

In the other side, that is the nrf52832 board, I currently implemented the C code firmware so read from SPI.

#define SPI_INSTANCE  0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */

#define TEST_STRING "Nordic"
static uint8_t       m_tx_buf[] = TEST_STRING;           /**< TX buffer. */
static uint8_t       m_rx_buf[sizeof(TEST_STRING) + 1];    /**< RX buffer. */
static const uint8_t m_length = sizeof(m_tx_buf);        /**< Transfer length. */

/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    spi_xfer_done = true;
    NRF_LOG_INFO("Transfer completed.");
    if (m_rx_buf[0] != 0)
    {
        NRF_LOG_INFO(" Received:");
        NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
    }
}

int main(void)
{
    bsp_board_leds_init();

    NRF_LOG_INIT(NULL);
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("SPI example.");

    /*
    FROM Decawave
    
    #define SPIS_MISO_PIN   28  // SPI MISO signal.
    #define SPIS_CSN_PIN    12  // SPI CSN signal.
    #define SPIS_MOSI_PIN   25  // SPI MOSI signal.
    #define SPIS_SCK_PIN    29  // SPI SCK signal.

    #define SPIM0_SCK_PIN   2   // SPI clock GPIO pin number.
    #define SPIM0_MOSI_PIN  3   // SPI Master Out Slave In GPIO pin number.
    #define SPIM0_MISO_PIN  4   // SPI Master In Slave Out GPIO pin number.
    #define SPIM0_SS_PIN    5   // SPI Slave Select GPIO pin number.

    #define SPIM1_SCK_PIN   16  // DWM1001 SPIM1 sck connected to DW1000
    #define SPIM1_MOSI_PIN  20  // DWM1001 SPIM1 mosi connected to DW1000
    #define SPIM1_MISO_PIN  18  // DWM1001 SPIM1 miso connected to DW1000
    #define SPIM1_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW // 
    #define SPIM1_SS_PIN    XX  //  Not used with DMW1001

    #define SPIM2_SCK_PIN   12  // SPI clock GPIO pin number.
    #define SPIM2_MOSI_PIN  13  // SPI Master Out Slave In GPIO pin number.
    #define SPIM2_MISO_PIN  14  // SPI Master In Slave Out GPIO pin number.
    #define SPIM2_SS_PIN    15  // SPI Slave Select GPIO pin number.
    */

    nrf_drv_spi_config_t spi_config; //= NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.ss_pin    = NRF_DRV_SPI_PIN_NOT_USED; //SPI_SS_PIN;
    spi_config.miso_pin	= 18; //SPI_MISO_PIN;
    spi_config.mosi_pin	= 20; //SPI_MOSI_PIN;
    spi_config.sck_pin  = 16; //SPI_SCK_PIN;
    spi_config.orc		= 0xFF;
    spi_config.frequency = NRF_DRV_SPI_FREQ_125K;
    spi_config.mode      = NRF_DRV_SPI_MODE_1;
    spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
    nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL);

    while (1)
    {
        // Reset rx buffer and transfer done flag
        memset(m_rx_buf, 0, m_length);
        spi_xfer_done = false;

        
        ret_code_t ret = nrf_drv_spi_transfer(&spi, m_tx_buf, m_length, m_rx_buf, m_length);
        NRF_LOG_INFO(" Code: %d:", ret);

        while (!spi_xfer_done)
        {
            __WFE();
        }

        NRF_LOG_FLUSH();

        bsp_board_led_invert(BSP_BOARD_LED_0);
        nrf_delay_ms(200);
    }
}

But I cannot read anything. In practice I checked that the flow passes through the spi_event_handler function, but my goal is not reached.

Finally, my very final goal is the following:

Thanks in advance.

  • SPI has a concept of a master and slave. Master is the side that initiates and manages the communication (providing clock).

    I understand RPi is the master in your case, so the nRF would be the slave.

    nRF SPI peripheral does have master and slave modes. In fact SPI master and SPI slave are documented as two different peripherals (sharing resources) in the docs, look for SPIM vs. SPIS (SPIS is the SPI slave instance.

    I see your code resembles the spi example from the SDK ($SDK/examples/peripheral/spi/main.c). There is a very similar example for SPIS, look in $SDK/examples/peripheral/spis/main.c. The code is almost the same, its mostly SPI vs. SPIS prefix in the calls and a little different setup.

  • Thanks for your reply!

    Ok, I will try to change the "side" from master to slave on my nRF board (also changing the code).

    Moreover, I will let you know in any case.

    Thanks a lot :-)

  • Now I have swapped the side, using this code:

    #define SPIS_INSTANCE 1 /**< SPIS instance index. */
    static const nrf_drv_spis_t spis = NRF_DRV_SPIS_INSTANCE(SPIS_INSTANCE);/**< SPIS instance. */
    
    #define TEST_STRING "Nordic"
    static uint8_t       m_tx_buf[] = TEST_STRING;           /**< TX buffer. */
    static uint8_t       m_rx_buf[sizeof(TEST_STRING) + 1];    /**< RX buffer. */
    static const uint8_t m_length = sizeof(m_tx_buf);        /**< Transfer length. */
    
    static volatile bool spis_xfer_done; /**< Flag used to indicate that SPIS instance completed the transfer. */
    
    /**
     * @brief SPIS user event handler.
     *
     * @param event
     */
    void spis_event_handler(nrf_drv_spis_event_t event)
    {
        if (event.evt_type == NRF_DRV_SPIS_XFER_DONE)
        {
            spis_xfer_done = true;
            NRF_LOG_INFO(" Transfer completed. Received: %s",(uint32_t)m_rx_buf);
        }
    }
    
    int main(void)
    {
        // Enable the constant latency sub power mode to minimize the time it takes
        // for the SPIS peripheral to become active after the CSN line is asserted
        // (when the CPU is in sleep mode).
        NRF_POWER->TASKS_CONSTLAT = 1;
    
        bsp_board_leds_init();
    
        NRF_LOG_INIT(NULL);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("SPIS example");
    
        /*
        #define SPIS_MISO_PIN   28  // SPI MISO signal.
        #define SPIS_CSN_PIN    12  // SPI CSN signal.
        #define SPIS_MOSI_PIN   25  // SPI MOSI signal.
        #define SPIS_SCK_PIN    29  // SPI SCK signal.
    
        #define SPIM0_SCK_PIN   2   // SPI clock GPIO pin number.
        #define SPIM0_MOSI_PIN  3   // SPI Master Out Slave In GPIO pin number.
        #define SPIM0_MISO_PIN  4   // SPI Master In Slave Out GPIO pin number.
        #define SPIM0_SS_PIN    5   // SPI Slave Select GPIO pin number.
    
        #define SPIM1_SCK_PIN   16  // DWM1001 SPIM1 sck connected to DW1000
        #define SPIM1_MOSI_PIN  20  // DWM1001 SPIM1 mosi connected to DW1000
        #define SPIM1_MISO_PIN  18  // DWM1001 SPIM1 miso connected to DW1000
        #define SPIM1_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW // 
        #define SPIM1_SS_PIN    XX  //  Not used with DMW1001
    
        #define SPIM2_SCK_PIN   12  // SPI clock GPIO pin number.
        #define SPIM2_MOSI_PIN  13  // SPI Master Out Slave In GPIO pin number.
        #define SPIM2_MISO_PIN  14  // SPI Master In Slave Out GPIO pin number.
        #define SPIM2_SS_PIN    15  // SPI Slave Select GPIO pin number.
        */
    
        nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
        spis_config.csn_pin               = 12; //APP_SPIS_CS_PIN;
        spis_config.miso_pin              = 28; //APP_SPIS_MISO_PIN;
        spis_config.mosi_pin              = 25; //APP_SPIS_MOSI_PIN;
        spis_config.sck_pin               = 29; //APP_SPIS_SCK_PIN;
        spis_config.mode                  = 1;
    
        nrf_drv_spis_init(&spis, &spis_config, spis_event_handler);
    
        while (1)
        {
            memset(m_rx_buf, 0, m_length);
            spis_xfer_done = false;
    
            nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length);
    
            while (!spis_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
        }
    }
    

    I have found that the flow stops at the "if" on spis_event_handler function, but the "event" is only *NRF_DRV_SPIS_BUFFERS_SET_DONE*

    Does it mean that from nRF side I'm done and I have to check the master on RPi?

  • Yes, I think so.

    SPI Master, besides providing the clock, also needs to "select" the slave (since SPI is a multipoint bus, there might be more slaves connected). SPIS uses the chip select (spis_config.csn_pin) for this purpose. The master needs to drive slave's CS pin low before starting the transaction. I see you have the csn_pin set to 12 on the nRF side, so your nRF expects the master to pull the p0.12 low. Where do you have that pin connected on the rPI?

    You'd need to add the code to your rPI side to set the corresponding rPI GPIO as output and pull it low before sending the data.

  • I tried this code, but doesn't work Disappointed

    // spin.c
    //
    // Example program for bcm2835 library
    // Shows how to interface with SPI to transfer a number of bytes to and from an SPI device
    //
    // After installing bcm2835, you can build this
    // with something like:
    // gcc -o spin spin.c -l bcm2835
    // sudo ./spin
    //
    // Or you can test it before installing with:
    // gcc -o spin -I ../../src ../../src/bcm2835.c spin.c
    // sudo ./spin
    //
    // Author: Mike McCauley
    // Copyright (C) 2012 Mike McCauley
    // $Id: RF22.h,v 1.21 2012/05/30 01:51:25 mikem Exp $
    #include <bcm2835.h>
    #include <stdio.h>
    
    #define PIN RPI_GPIO_P1_12
    
    int main(int argc, char **argv)
    {
        if (!bcm2835_init())
        {
          printf("bcm2835_init failed. Are you running as root??\n");
          return 1;
        }
        if (!bcm2835_spi_begin())
        {
          printf("bcm2835_spi_begin failed. Are you running as root??\n");
          return 1;
        }
        bcm2835_spi_begin();
    
        bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
        bcm2835_gpio_write(PIN, LOW);
        //bcm2835_gpio_set_pud(PIN, BCM2835_GPIO_PUD_DOWN);
    
        bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);      // The default
        bcm2835_spi_setDataMode(BCM2835_SPI_MODE1);                   // The default
        bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default
        bcm2835_spi_chipSelect(BCM2835_SPI_CS0);                      // The default
        bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);      // the default
    
        // Send a byte to the slave and simultaneously read a byte back from the slave
        // If you tie MISO to MOSI, you should read back what was sent
        uint8_t send_data = 0x23;
        uint8_t read_data = bcm2835_spi_transfer(send_data);
        printf("Sent to SPI: 0x%02X. Read back from SPI: 0x%02X.\n", send_data, read_data);
        if (send_data != read_data)
          printf("Do you have the loopback from MOSI to MISO connected?\n");
    
        bcm2835_gpio_write(PIN, HIGH);
        bcm2835_spi_end();
        bcm2835_close();
        return 0;
    }
    

Related