7 years, 7 months ago.

Problem with Ticker on mbedos and Nordic nRF51 and nRF52

Hello,

When running the code below (interrupt writes through SPI and blinks LED1) on the nRF52_DK, the program hangs (I don't see LED1 blink). If I comment out the SPI write in the PeriodicFunction, program runs fine (LED1 blinks). If I move the SPI write to the main loop, program runs fine as well.

I observed similar behavior on the nRF51. Also, similar behavior observed if using serial writes instead of spi writes.

Also, testing the code on LPC1768 (with spi (p5,p6,p7)) runs with no problem.

To me it looks like a problem with the ticker library implementation for the Nordic family chips.

For reference, I'm using mbed-os commit #da14bce7a28b.

Any help on this is greatly appreciated.

Thanks!

include the mbed os library with this snippet

#include "mbed.h"

DigitalOut led1(LED1);

SPI spi_object(p23,p24,p25);

void PeriodicFunction(){

    spi_object.write(0x00);
    led1=!led1;

}

int main(void)
{
    spi_object.format(8,0);

    Ticker controller; 
    controller.attach(&PeriodicFunction, 1);

    while(true){

    }
}

1 Answer

6 years, 3 months ago.

I encountered the same problem in one of our programs. I actually think it's the nrf SPI driver code. mBed uses the non-easyDMA version of the driver. I think that the problem is in the code below. The SPI interrupt gets disabled, then the TXD is set, and only after that the interrupt gets enabled. The ticker interrupt happens in between the disable/enable phase, when the interrupt is disabled and right after TXD was set. That causes the SPI module to not raise the interrupt.

static void spi_xfer(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb, nrf_drv_spi_xfer_desc_t const * p_xfer_desc) { nrf_spi_int_disable(p_spi, NRF_SPI_INT_READY_MASK); p_cb->bytes_transferred = 0;

nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY);

Start the transfer by writing some byte to the TXD register; if TX buffer is not empty, take the first byte from this buffer, otherwise - use over-run character. nrf_spi_txd_set(p_spi, (p_xfer_desc->tx_length > 0 ? p_xfer_desc->p_tx_buffer[0] : p_cb->orc));

TXD register is double buffered, so next byte to be transmitted can be written immediately, if needed, i.e. if TX or RX transfer is to be more that 1 byte long. Again - if there is something more in TX buffer send it, otherwise use over-run character. if (p_xfer_desc->tx_length > 1) { nrf_spi_txd_set(p_spi, p_xfer_desc->p_tx_buffer[1]); } else if (p_xfer_desc->rx_length > 1) { nrf_spi_txd_set(p_spi, p_cb->orc); }

For blocking mode (user handler not provided) wait here for READY events (indicating that the byte from TXD register was transmitted and a new incoming byte was moved to the RXD register) and continue transaction until all requested bytes are transferred. In non-blocking mode - IRQ service routine will do this stuff. if (p_cb->handler) { nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK); }

Tested and verified thanks to Jumper.io emulator.