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

  • 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

Reply
  • 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

Children
  • 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

  • Hi Edvin,

    I too got things to work fine with the examples you mentioned in your post. I'm building off of a different example though (and I think the behaviors are actually different). I'm using the following 2 examples:

    SDK17.1.0/examples/peripherals/nrfx_spim

    SDK17.1.0/examples/peripherals/spis

    I've modified them slightly (to disable dcx and send larger packets) so you can use the base examples or my versions (attached below):

    0407.nrfx_spim.zip

    2262.spis.zip

    Thank you so so much - I can't tell you how much I appreciate it.

    Kindest regards,

    Ryan

  • Hello Ryan,

    I see. I actually wasn't aware there was an example called nrfx_spim. I guess I just used the spi example (which also uses the nrfx driver), and didn't notice that it was introduced (whenever that was).

    I haven't looked into the differences between nrfx_spim and spi, but both of them uses nrfx_spim_init(), so I guess they are using the same driver, but a bit differently.

    I tested the unmodified nrfx_spim with the spis example, and I saw, like you saw, that the first byte was corrupted. I tried adding a delay before pulling the CS/SS pin low in the nrfx_spim's main.c, and then I got the first byte to show correctly again.

    Can you try something like this:

    // main.c:
    
    int main(void)
    {
        ...
        	nrf_gpio_cfg_output(NRFX_SPIM_SS_PIN);
    		nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);
        ...
            spi_config.ss_pin         = NRFX_SPIM_PIN_NOT_USED;//NRFX_SPIM_SS_PIN;
        ...
        
        while (1)
        {
            nrf_gpio_pin_clear(NRFX_SPIM_SS_PIN);
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrfx_spim_xfer_dcx(&spi, &xfer_desc, 0, 15));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    				
    				nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
            nrf_delay_ms(1000);
        }
    }

    I attached the entire main.c file as well, for reference. Please note that I changed the pin numbers to match the spis example's pin numbers. You don't need to change these.

    /**
     * Copyright (c) 2017 - 2021, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "nrfx_spim.h"
    #include "app_util_platform.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.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 NRFX_SPIM_SCK_PIN  26//3
    #define NRFX_SPIM_MOSI_PIN 29//4
    #define NRFX_SPIM_MISO_PIN 30//28
    #define NRFX_SPIM_SS_PIN   31//29
    #define NRFX_SPIM_DCX_PIN  30
    
    #define SPI_INSTANCE  3                                           /**< SPI instance index. */
    static const nrfx_spim_t spi = NRFX_SPIM_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
    
    static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
    
    #define TEST_STRING "Nordic123456789012345678901234567890"
    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. */
    
    void spim_event_handler(nrfx_spim_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_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    		nrf_gpio_cfg_output(NRFX_SPIM_SS_PIN);
    		nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);
    
        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_PIN_NOT_USED;//NRFX_SPIM_SS_PIN;
        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_PIN_NOT_USED; //NRFX_SPIM_DCX_PIN;
        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.");
    
        while (1)
        {
    				nrf_gpio_pin_clear(NRFX_SPIM_SS_PIN);
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrfx_spim_xfer_dcx(&spi, &xfer_desc, 0, 15));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    				
    				nrf_gpio_pin_set(NRFX_SPIM_SS_PIN);
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
            nrf_delay_ms(1000);
        }
    }
    

    Best regards,

    Edvin

  • Hi Edvin,

    That did the trick! I must have messed up the init code when I tried toggling the ss earlier - removing my changes and adding yours worked like a charm!!

    Thank you so much for your help, I really really appreciate it.

    Kindest regards,

    Ryan

Related