This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

SPIS data corrupted (it gets worse at faster speeds)

Hi,

I'm using two dev kits to test out some SPI firmware before I deploy it to our custom boards and I've been running into a weird issue where my SPIS sends out corrupted data. The data corruption exists at an SCK of 1MHz and it gets worse when I go up to 4MHz and even worse at 8MHz.

An example of the data corruption is as follows:

I fill up the SPIS's m_tx_buf with: 0x41410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000202

but when a spi transaction is triggered, the SPI peripheral shifts out the following over the MISO line:

0xff41410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002

 

I've probed the MISO line with an oscope and here's a screenshot of the first couple bytes. In it you can see that the spis actually 'adds' ff to the beginning. 

sometimes the data is corrupted differently and I will receive something like:

0xffa0a08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 which has 9 bits of 1 at the front (right?).

I've tried printing out these bytes in debug mode (using NRF_LOG_INFO()) and those report the correct bytes back regardless of when I query the buffer. My spis code is built off the example in SDK 17.1 and it's really really quite similar - is there some limitation for SPI packets >= 65 bytes? Here is a copy of my code as reference:

#include "sdk_config.h"
#include "nrf_drv_spis.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "app_error.h"
#include <string.h>

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#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 uint8_t       m_tx_buf[] = {65,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                                   0,0,0,2,2};


static uint8_t       m_rx_buf[sizeof(m_tx_buf) + 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_init(BSP_INIT_LEDS);

    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("SPIS example");

    nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
    spis_config.csn_pin               = APP_SPIS_CS_PIN;
    spis_config.miso_pin              = APP_SPIS_MISO_PIN;
    spis_config.mosi_pin              = APP_SPIS_MOSI_PIN;
    spis_config.sck_pin               = APP_SPIS_SCK_PIN;

    APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));

    while (1)
    {
        memset(m_rx_buf, 0, m_length);
        spis_xfer_done = false;

        APP_ERROR_CHECK(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);
    }
}



Parents
  • Hello,

    What is your SPI master? Can you please try to set the Chip Select pin low a little bit before you start the clock from the central and see whether the behavior changes?

    BR,

    Edvin

  • Hi Edvin,

    I'm using SPIM3 (I built my SPI master code off the nrfx_spim example from SDK 16 - I made it a while ago)! I tried adding a delay between the CS going low and the start of the clock by adding nrf_delay_us(10); right before spim_xfer() but that wouldn't change the timing between CS going low and the first SCLK. Do you have another way of changing the timing?

    nrfx_err_t nrfx_spim_xfer(nrfx_spim_t     const * const p_instance,
                              nrfx_spim_xfer_desc_t const * p_xfer_desc,
                              uint32_t                      flags)
    {
        spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
        NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
        NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
        NRFX_ASSERT(SPIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
                                         p_xfer_desc->rx_length,
                                         p_xfer_desc->tx_length));
    
        nrfx_err_t err_code = NRFX_SUCCESS;
    
        if (p_cb->transfer_in_progress)
        {
            err_code = NRFX_ERROR_BUSY;
            NRFX_LOG_WARNING("Function: %s, error code: %s.",
                             __func__,
                             NRFX_LOG_ERROR_STRING_GET(err_code));
            return err_code;
        }
        else
        {
            if (p_cb->handler && !(flags & (NRFX_SPIM_FLAG_REPEATED_XFER |
                                            NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER)))
            {
                p_cb->transfer_in_progress = true;
            }
        }
    
        p_cb->evt.xfer_desc = *p_xfer_desc;
    
        if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
        {
    #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
            if (!p_cb->use_hw_ss)
    #endif
            {
                if (p_cb->ss_active_high)
                {
                    nrf_gpio_pin_set(p_cb->ss_pin);
                }
                else
                {
                    nrf_gpio_pin_clear(p_cb->ss_pin);
                }
            }
        }
        
        nrf_delay_us(10);
        return spim_xfer(p_instance->p_reg, p_cb,  p_xfer_desc, flags);
    }

Reply
  • Hi Edvin,

    I'm using SPIM3 (I built my SPI master code off the nrfx_spim example from SDK 16 - I made it a while ago)! I tried adding a delay between the CS going low and the start of the clock by adding nrf_delay_us(10); right before spim_xfer() but that wouldn't change the timing between CS going low and the first SCLK. Do you have another way of changing the timing?

    nrfx_err_t nrfx_spim_xfer(nrfx_spim_t     const * const p_instance,
                              nrfx_spim_xfer_desc_t const * p_xfer_desc,
                              uint32_t                      flags)
    {
        spim_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
        NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0);
        NRFX_ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0);
        NRFX_ASSERT(SPIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
                                         p_xfer_desc->rx_length,
                                         p_xfer_desc->tx_length));
    
        nrfx_err_t err_code = NRFX_SUCCESS;
    
        if (p_cb->transfer_in_progress)
        {
            err_code = NRFX_ERROR_BUSY;
            NRFX_LOG_WARNING("Function: %s, error code: %s.",
                             __func__,
                             NRFX_LOG_ERROR_STRING_GET(err_code));
            return err_code;
        }
        else
        {
            if (p_cb->handler && !(flags & (NRFX_SPIM_FLAG_REPEATED_XFER |
                                            NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER)))
            {
                p_cb->transfer_in_progress = true;
            }
        }
    
        p_cb->evt.xfer_desc = *p_xfer_desc;
    
        if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED)
        {
    #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED)
            if (!p_cb->use_hw_ss)
    #endif
            {
                if (p_cb->ss_active_high)
                {
                    nrf_gpio_pin_set(p_cb->ss_pin);
                }
                else
                {
                    nrf_gpio_pin_clear(p_cb->ss_pin);
                }
            }
        }
        
        nrf_delay_us(10);
        return spim_xfer(p_instance->p_reg, p_cb,  p_xfer_desc, flags);
    }

Children
  • Hello,

    Try using a separate GPIO as the CS, that is not configured as CS in the SPIM. 

    Set the p_cb->ss_pin to NRFX_SPIM_PIN_NOT_USED, and toggle the ss pin manually (like it is trying to do, depending on your settings, using nrf_gpio_pin_clear()).

    If you set ss_pin to NRFX_SPIM_PIN_NOT_USED, you need to configure the pin as an output pin manually from your application.

    Best regards,

    Edvin

  • Hi Edvin,

    Sorry for the basic question but could I get a little more guidance on how to do that? I added the following to my main.c but it didn't seem to actually toggle anything on the GPIO pin (I ended up using the same pin as was originally used).

    To toggle the pin manually I used:

    nrf_gpio_pin_clear(NRFX_SPIM_SS_PIN);

    and nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);

    #define NRFX_SPIM_SS_PIN   29
    
    ...
    ...
    
    void spim_event_handler(nrfx_spim_evt_t const * p_event,
                           void *                  p_context)
    {
        spi_xfer_done = true;
        
        nrf_gpio_pin_set(NRFX_SPIM_SS_PIN); // ADDED
    
        NRF_LOG_INFO(" Received:");
        NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
    }
    
    ...
    ...
    
    
    int main(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(m_tx_buf, m_length, m_rx_buf, m_length);
    
        nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
        spi_config.frequency      = NRF_SPIM_FREQ_1M;
        //spi_config.ss_pin         = NRFX_SPIM_SS_PIN;
        spi_config.ss_pin         = NRFX_SPIM_PIN_NOT_USED;
        spi_config.miso_pin       = NRFX_SPIM_MISO_PIN;
        spi_config.mosi_pin       = NRFX_SPIM_MOSI_PIN;
        spi_config.sck_pin        = NRFX_SPIM_SCK_PIN;
        //spi_config.dcx_pin        = NRFX_SPIM_DCX_PIN;
        spi_config.dcx_pin        = NRFX_SPIM_PIN_NOT_USED;
        spi_config.use_hw_ss      = true;
        spi_config.ss_active_high = false;
        APP_ERROR_CHECK(nrfx_spim_init(&spi, &spi_config, spim_event_handler, NULL));
    
        NRF_LOG_INFO("NRFX SPIM example started.");
        nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);
    
        while (1)
        {
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            nrf_gpio_pin_clear(NRFX_SPIM_SS_PIN); // ADDED
            APP_ERROR_CHECK(nrfx_spim_xfer(&spi, &xfer_desc, 0));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
            nrf_delay_ms(500);
        }
    }


    to make matters weirder I went back to using the base examples (with the only changes being the test string and the disabling of DCX) and the problem exists there as well! Rather than getting "nordic123456" the slave device sends out ".ordic123456" where the period is the NRF_LOG_HEXDUMP_INFO attempt at interpreting 'FF' or 'CE'.

    Am I not supposed to use the base spis as a slave device for the nrfx_spim? Surely someone else must have run into this issue...

    Here are zips of the my spis and nrfx_spim firmwares:

    spis_chars.zip

    3252.nrfx_spim.zip

    As always, thank you so much for your help. I'm so lost hahaha

    - Ryan

  • Hello,

    I have not had the chance to test this myself yet. And I am sorry for the short reply, but we are very short staffed here in Norway due to Easter Holidays this week (and Monday next week). I am sorry for the inconvenience regarding the delayed response. 

    As far as I can tell, you don't set the NRFX_SPIM_SS_PIN as an output pin. Try adding this to your code, before your main-loop.

        nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
    
        err_code = nrf_drv_gpiote_out_init(NRFX_SPIM_SS_PIN, &out_config);
        APP_ERROR_CHECK(err_code);
    

    And then you can use:

    nrf_drv_gpiote_out_clear(NRFX_SPIM_SS_PIN);

    to set the pin low (and nrf_drv_gpiote_out_set(NRFX_SPIM_SS_PIN); to set it high again).

    These functions are used in the pin_change_int example, for reference.

    Remember to sleep a short while between setting the CS low and sending the message:

    nrf_drv_gpiote_out_clear(NRFX_SPIM_SS_PIN);
    
    nrf_delay_ms(2)
    
    spim_send_function();

    Best regards,

    Edvin

  • No worries! I tried this out but kept hitting hard faults whenever the debugger would hit the nrf_drv_gpiote_init function. I was having a little trouble decoding what the error code meant - how would you recommend looking up the specific error meaning?

    Regardless, however, is this recommended for all SPIS use cases? Since I'm seeing this with the base example code maybe something else is wrong.

    Thank you so much for your help, Edvin!

    Ryan

  • Hello Ryan,

    ryerye120 said:
    Regardless, however, is this recommended for all SPIS use cases? Since I'm seeing this with the base example code maybe something else is wrong.

    You said that you are using the default nrfx_spim example. What example do you mean exactly?

    I tested these two, and connected the DKs using some PCB wires, and this is my outcome:

    SDK17.1.0\examples\peripheral\spi

    SDK17.1.0\examplws\peripheral\spis

    The master (spi example) is to the left, and the spis example is to the right. I don't see any issues using these unmodified examples, at least. Did you test them? I see that in your original question, your message is larger, and not from the examples. Is there a way for me to reproduce this using two nrf52840 DKs without any additional hardware? If so, can you zip the two project folders (please specify what SDK version you are using), and I can give it a go.

    Best regards,

    Edvin

Related