nrf_gpio_pin_set causes random MCU resets during Manual-CS SPI transactions

I created a function, spi_read, that performs a SPI transaction to RAM, comprised of 400 chunks, each 96 bytes long.

The CS control is set to manual.

that function crashes every 5-30 runs, randomly, probably at the call to nrf_gpio_pin_set.

Please see its code below.

As a patch, I added a 1 microsecond wait after nrf_gpio_pin_set, which occurs after every chunk, which solves the issue,

If the PATCH segment is removed, the reset occurs randomly, every 5-30 runs.

I'm using the NRF52840, running SDK version 17.0.2.

astatus_t spi_read(const hal_spi_t * p_spi, uint16_t len, uint8_t reg_add, uint8_t * p_read_buf)
{
    astatus_t rc = AE_SUCCESS;
    spi_xfer_done = false;
    nrfx_spim_xfer_desc_t const tx_xfer_desc = NRFX_SPIM_XFER_TX(&reg_add, 1);
    nrf_gpio_pin_clear(p_spi->spi_manual_cs);
    if ( NRFX_SUCCESS != nrfx_spim_xfer(&m_spi_master[p_spi->spi_index], &tx_xfer_desc, 0) )
    {
        NRF_LOG_ERROR("atom_hal_spi_read_write nrfx_spim_xfer");
        rc = AE_FAILURE;
    }
    if (AE_SUCCESS == rc)
    {
        rc = wait_for_spi_completion(OP_TIMEOUT_MSEC);
    }
	if ( rc != AE_SUCCESS )
    {
        ATLOG_WARN("wait_for_spi_completion failed %d", rc);
        goto hal_spi_read_exit;
    }

    nrfx_spim_xfer_desc_t const rx_xfer_desc = NRFX_SPIM_XFER_RX(p_read_buf, len);
    spi_xfer_done = false;
    if ( NRFX_SUCCESS != nrfx_spim_xfer(&m_spi_master[p_spi->spi_index], &rx_xfer_desc, 0) )
    {
        NRF_LOG_ERROR("atom_hal_spi_read_write nrfx_spim_xfer");
        rc = AE_FAILURE;
    }
    if (AE_SUCCESS == rc)
    {
        rc = wait_for_spi_completion(OP_TIMEOUT_MSEC);
    }
hal_spi_read_exit:
    nrf_gpio_pin_set(p_spi->spi_manual_cs);
    
    /////////// PATCH
    atom_hal_time_delay_us(1);
    /////////// PATCH
    return rc;
}

How can I better solve this?

Parents
  • I do not see how adding a delay after pin set avoids a reset. We first need to know the reset reason before we can try to understand the context of the reset. As discussed in this thread, try to see if you can read the reset reason register after the reset happens, so that we can debug this further.

    If this turns out to be a soft-reset, this the application error handler could most likely be a reason. But at this point, we cannot debug this further without knowing the reason reason.

Reply
  • I do not see how adding a delay after pin set avoids a reset. We first need to know the reset reason before we can try to understand the context of the reset. As discussed in this thread, try to see if you can read the reset reason register after the reset happens, so that we can debug this further.

    If this turns out to be a soft-reset, this the application error handler could most likely be a reason. But at this point, we cannot debug this further without knowing the reason reason.

Children
  • it might not be known why, but it is what happens nonetheless. Adding a 1 microsecond delay between SPI transactions, after CS being set to inactive, right before the next transaction begins and CS is set to active again, avoids the reset.

    The reset reason is 0.

    (The reset read operations shows 1 after I press the reset button, so I believe it is working properly.)

    The SPI configuration is:

            .conf =
            {
                .sck_pin = NRFX_SPIM_SCK_PIN,
                .mosi_pin = NRFX_SPIM_MOSI_PIN,
                .miso_pin = NRFX_SPIM_MISO_PIN,
                .ss_pin = NRFX_SPIM_PIN_NOT_USED,
                .ss_active_high = false,
                .irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
                .orc = 0xFF,
                .frequency = NRF_SPIM_FREQ_8M,
                .mode = NRF_SPIM_MODE_0,
                .bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST,
                NRFX_SPIM_DEFAULT_EXTENDED_CONFIG
            },

  • I will check the trigger for the 96-byte SPI transaction. It should happen every 5 msec, trigger an ISR that starts the SPI transaction.

    The SPI transaction should complete in 96 micro-seconds (8MHz clock), which is a lot quicker than the external ISR rate, but there might be delays in the execution that cause the SPI transaction to not complete in time for the next interrupt.

  • Are you executing the SPI read from the ISR? If so which interrupt priority is this?

    kobyatom said:

    The reset reason is 0.

    (The reset read operations shows 1 after I press the reset button, so I believe it is working properly.)

    If you read the RESETREAS after the reset (do not read this register when you press the reset button, read after when the chip resets when you do not have delay added after nrf_gpio_pin_set) register.

    This register cannot read 0 after the MCU reset, as there should be some reason registered why the chip was reset. If you are reading this register as value 0, then something is not correct in how you are reading this register.

  • we carefully implemented this reset-reason feature a few months ago, and verified it to work with reset reasons SREQ and RESETPIN.

    Here is the implementation:

    We have a static member that gets the reset reason on init, from the RESETREAS reg

    const uint32_t reset_reason = nrf_power_resetreas_get();    // Do before any other action https://devzone.nordicsemi.com/f/nordic-q-a/48446/how-to-detect-cause-of-reset

    in main() we clear the resetreas register

    nrf_power_resetreas_clear(NRF_POWER_RESETREAS_OFF_MASK);    // Clear this register, instead of the bootloader.
    and later we print the previously saved reset reason

    ATLOG_INFO("reset_reason 0x%08x", reset_reason);

  • I have never seen before that the reset reasons reads 0 before clearing up the register. Can you help me reproduce your error. Maybe give me a simplistic project where I can see the same reset and same reset reason 0 as you see (when the delay is not added)

Related