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

MCU reboots when the radio is in RX state and power management is used.

Dear support,

I am developing a TDMA protocol on a custom board with the NRF52840 and SEGGER EMbedded Studio and the SDK 15.0.0. Everything went well until I wished to reduce the power consumption using nrf_pwr_mgmt_run().

Now, on the logical analyser, I observe  that the MCU reboots about 2.6 ms after the radio is listening (READY event and START by shortcut).

Trying to solve the problem, I reduced my code and removed my protocol and I could reproduce the problem with the code below:

Radio initialisation (mode = proprietary 1 Mbps, same issue observed with BLE_1Mbps):

void radio_Init()
{
    wiseradio.hfClockAlwaysOn = FALSE;
    wiseradio.txStartCallback = NULL;
    // Proprietary mode 1 Mbps
    NRF_RADIO->MODE      = (RADIO_MODE_MODE_Nrf_1Mbit << RADIO_MODE_MODE_Pos);

    // Radio config
    WiseRadio.radio_SetTxPower(RADIO_TXPOWER_TXPOWER_Pos4dBm);
    WiseRadio.radio_SetChannel(mib.beaconChannel);

    // Radio address config
    NRF_RADIO->PREFIX0 =
        ((uint32_t)swap_bits(0xC3) << 24) // Prefix byte of address 3 converted to nRF24L series format
      | ((uint32_t)swap_bits(0xC2) << 16) // Prefix byte of address 2 converted to nRF24L series format
      | ((uint32_t)swap_bits(0xC1) << 8)  // Prefix byte of address 1 converted to nRF24L series format
      | ((uint32_t)swap_bits(0xC0) << 0); // Prefix byte of address 0 converted to nRF24L series format

    NRF_RADIO->PREFIX1 =
        ((uint32_t)swap_bits(0xC7) << 24) // Prefix byte of address 7 converted to nRF24L series format
      | ((uint32_t)swap_bits(0xC6) << 16) // Prefix byte of address 6 converted to nRF24L series format
      | ((uint32_t)swap_bits(0xC4) << 0); // Prefix byte of address 4 converted to nRF24L series format

    NRF_RADIO->BASE0 = bytewise_bitswap(0x01234567UL);  // Base address for prefix 0 converted to nRF24L series format
    NRF_RADIO->BASE1 = bytewise_bitswap(0x89ABCDEFUL);  // Base address for prefix 1-7 converted to nRF24L series format

    NRF_RADIO->TXADDRESS   = 0x00UL;  // Set device address 0 to use when transmitting
    NRF_RADIO->RXADDRESSES = 0x01UL;  // Enable device address 0 to use to select which addresses to receive

    // Packet configuration
    NRF_RADIO->PCNF0 = (PACKET_S1_FIELD_SIZE     << RADIO_PCNF0_S1LEN_Pos) |
                       (PACKET_S0_FIELD_SIZE     << RADIO_PCNF0_S0LEN_Pos) |
                       (PACKET_LENGTH_FIELD_SIZE << RADIO_PCNF0_LFLEN_Pos); //lint !e845 "The right argument to operator '|' is certain to be 0"

    // Packet configuration
    NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
                       (RADIO_PCNF1_ENDIAN_Big       << RADIO_PCNF1_ENDIAN_Pos)  |
                       (PACKET_BASE_ADDRESS_LENGTH   << RADIO_PCNF1_BALEN_Pos)   |
                       (PACKET_STATIC_LENGTH         << RADIO_PCNF1_STATLEN_Pos) |
                       (PACKET_PAYLOAD_MAXSIZE       << RADIO_PCNF1_MAXLEN_Pos); //lint !e845 "The right argument to operator '|' is certain to be 0"

    // CRC Config
    NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits
    if ((NRF_RADIO->CRCCNF & RADIO_CRCCNF_LEN_Msk) == (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos))
    {
        NRF_RADIO->CRCINIT = 0xFFFFUL;   // Initial value
        NRF_RADIO->CRCPOLY = 0x11021UL;  // CRC poly: x^16 + x^12^x^5 + 1
    }
    else if ((NRF_RADIO->CRCCNF & RADIO_CRCCNF_LEN_Msk) == (RADIO_CRCCNF_LEN_One << RADIO_CRCCNF_LEN_Pos))
    {
        NRF_RADIO->CRCINIT = 0xFFUL;   // Initial value
        NRF_RADIO->CRCPOLY = 0x107UL;  // CRC poly: x^8 + x^2^x^1 + 1
    }
    WiseRadio.radio_Off();
}

Turn off radio = turn off HFCLK clock as the radio is disabled by END -> DISABLE shortcut: In this case hfClockAlwaysOn is FALSE.

void radio_Off(void)
{
    DBG_DISPLAY_VAL8(0xA8);
    NRF_RADIO->EVENTS_DISABLED = 0;
    NRF_RADIO->TASKS_DISABLE = 1;
    //while (NRF_RADIO->EVENTS_DISABLED == 0){}   //Wait while radio is ramping down.   
    if (wiseradio.hfClockAlwaysOn == FALSE) {
        stop_NrfHFClock();
    }
    wiseradio.state = WISERADIO_IDLE;
}

The test program initialises the board and starts a timer which turns the radio on, set it in listening mode and when something is received, the radio is turned off and a new timer starts the same process again.

Turn on radio:

void radio_On (void)  
{
    // Because we are using the shortcuts between READY and START, here radio_On basically means
    // starting the HF clock, which takes about 350 us. The delay on waiting for this operation
    // should be set to 400 us to be on the safe side.   
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART    = 1;
}

Start listening:

void  radio_ReceiveFrame(uint8_t *buf, uint8_t maxlen, uint8_t * lenPtr, uint8_t *rssiPtr, t_timTime *timePtr,
                              void (*funStart)(void *), void (*funEnd)(void *))
{
    DBG_DISPLAY_VAL8(0xC6);
    wiseradio.rxBufLen = maxlen;
    wiseradio.rxCallback = funEnd;
    wiseradio.startCallback = funStart;
    wiseradio.rxLenPtr = lenPtr;
    *wiseradio.rxLenPtr = 0;
    wiseradio.rssiPtr = rssiPtr;
    *wiseradio.rssiPtr= 0;
    wiseradio.timePtr = timePtr;
    wiseradio.rxBufPtr = buf;
    wiseradio.rxBufferAvailable = 1;

    NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
                       (RADIO_PCNF1_ENDIAN_Big       << RADIO_PCNF1_ENDIAN_Pos)  |
                       (PACKET_BASE_ADDRESS_LENGTH   << RADIO_PCNF1_BALEN_Pos)   |
                       (PACKET_STATIC_LENGTH         << RADIO_PCNF1_STATLEN_Pos) |
                       (((uint32_t) maxlen)          << RADIO_PCNF1_MAXLEN_Pos);
    NRF_RADIO->PACKETPTR = (uint32_t)buf;
    NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos) |
                        (RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos);

    wiseradio.state = WISERADIO_RX_WAIT_SYNC;
    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->EVENTS_ADDRESS = 0;
    NRF_RADIO->EVENTS_PAYLOAD = 0;
    NRF_RADIO->TASKS_RXEN = 1;
}

Finally, the main program:

int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    appl_Init();  // init system such as timer

    wisenode_SetUpGPIOs();  // simply configure some GPIOs
    //wisenode_SetupI2C();
   

    DBG_DISPLAY_VAL8(0x89);
    DBG_TRIGGER(6);

    // This function starts a time with startRxTest as call back. It uses app_timer_create() and app_timer_start()
    tim_TimerStartFast(TIM_TIMER_MAC1, TIM_ONE_SHOT, 100000, startRxTest, NULL, FALSE); // timeout in microseconds, thus here 100ms

    LED1_OFF();
    LED2_OFF();
    LED3_OFF();

    nrf_pwr_mgmt_init();

    while (TRUE) {
        nrf_pwr_mgmt_run();
    }
}

void startRxTest(void* arg)
{
    WiseRadio.radio_On();

    // RADIO_OFF_TO_HFCLK_ON_DELAY_US = 400 us. Increasing this value doesn't help.
    tim_TimerStartFast(TIM_TIMER_MAC1, TIM_ONE_SHOT, RADIO_OFF_TO_HFCLK_ON_DELAY_US, rxTest, NULL, FALSE);   
}

void rxTest(void* arg)
{
    DBG_TRIGGER(4);
    MyBuffer = buff_RxGetNew();

    WiseRadio.radio_ReceiveFrame((uint8_t*) dataBuf[MyBuffer].buffer, BUFF_MAX_SIZE-1, (uint8_t*) &(dataBuf[MyBuffer].len),
                                 (uint8_t*) &(dataBuf[MyBuffer].rxRssi), (t_timTime*) &(dataBuf[MyBuffer].receivedTime),
                                 rxTestStart, rxTestEnd);
}

// This routine below is called when the radio triggers the END event.

void rxTestEnd(void* arg)
{
    uint8_t i;
    uint8_t len;
    uint8_t status;

    DBG_TRIGGER(3);
    DBG_DISPLAY_VAL8(0xBF);   
    status = WiseRadio.radio_ReturnState();
    DBG_DISPLAY_VAL8(status);
    len = dataBuf[MyBuffer].len;
    /*DBG_DISPLAY_VAL8(len);
    DBG_DISPLAY_VAL8(dataBuf[MyBuffer].buffer[0]);
    if (status == WISERADIO_RX_COMPLETE) {
        for (i = 1; i <= len; i++) {
            DBG_DISPLAY_VAL8(dataBuf[MyBuffer].buffer[i]);
        }
    }*/
    WiseRadio.radio_Off();
    tim_TimerStartFast(TIM_TIMER_MAC1, TIM_ONE_SHOT, 10000, startRxTest, NULL, FALSE);
    buff_Release(MyBuffer, 0xE1);
}

When the program is executed, the MCU crashes (= reboots) about 2.6 ms after the radio event READY is thrown shortly after rxTest() is executed. If nrf_pwr_mgmt_run(); is commented out in the main loop, the same problem appears. However, if some instruction that turns on and off a GPIO is added instead of nrf_pwr_mgmt_run();, then there is no reboot.

I have spent lots of time trying to understand this issue without succes, thus any help would be greatly appreciated. Thanks and best regards,

Damien

Parents
  • Hello Vidar,

    Thank you for your response,

    The code does not fall into app_error_fault_handler().

    On the Logical analyser, RESETREAS is always zero at start-up. In the debugger, I saw value 4 (CPU lock-up) but I am not sure this is a right  catch. Just in case, what could cause a CPU lock-up ?

    I have also tried my code on the PCA10056 development kit, and it works in that case. One notable difference with my own custom board is the use of a RV-3028-C7 RTC for the clock signal. Otherwise there is no other peripheral when I test. The problem happens when the radio is receiving, and in certain situations, when transmitting.

    In summary there is something wrong when the radio is active and the CPU is not. Does that ring a bell with respect to the other situations you have encountered ?

    Thanks and best regards,

    Damien

Reply
  • Hello Vidar,

    Thank you for your response,

    The code does not fall into app_error_fault_handler().

    On the Logical analyser, RESETREAS is always zero at start-up. In the debugger, I saw value 4 (CPU lock-up) but I am not sure this is a right  catch. Just in case, what could cause a CPU lock-up ?

    I have also tried my code on the PCA10056 development kit, and it works in that case. One notable difference with my own custom board is the use of a RV-3028-C7 RTC for the clock signal. Otherwise there is no other peripheral when I test. The problem happens when the radio is receiving, and in certain situations, when transmitting.

    In summary there is something wrong when the radio is active and the CPU is not. Does that ring a bell with respect to the other situations you have encountered ?

    Thanks and best regards,

    Damien

Children
  • Thanks for confirming. Value 4 is actually corresponding to the SREQ bit (soft reset), and is a much more common reset source. This bit will be set after the device is reset by the debugger (e.g. after programming), or if the application has run the NVIC_SystemReset() function (typically from the app_error_fault_handler()).

    Lockup reset can happen if you are really unlucky and trigger a hardfault within a hardfault. Fortunalely, that doesn't happen too often.

    Damien Piguet said:
    I have also tried my code on the PCA10056 development kit, and it works in that case. One notable difference with my own custom board is the use of a RV-3028-C7 RTC for the clock signal

    Intereseting that it doesn't happen with the DK. On the custom board, does the issue also occur in debug mode, or do you have to disconnect the debugger to reproduce the problem?

Related