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

Why is not my Timer triggered when configured through PPI and SPI?

Hello,

I am trying to use ArrayList for EasyDMA along with PPI but I am not sure whether or not I am configuring it properly to be used with the SPI interface.

So far I have set it up as below, in order to test the transfer I am connecting the MISO pin to ground(through a resistor) to fill the buffer with a null value. However,I never see an interruption from the configured TIMER happening, I mean, I don't see while running a call to timer_event_handler ( I have set a break point on that handler and it seems not to be called...)

Any suggestions of what might be cause not to see that interruption?

Thanks in advance,

#define SPI_INSTANCE  0 /**< SPI instance index. */
#define BUFF_LENGTH 4   /**< Transfer length. */
#define BUFF_RX_LENGTH 100

typedef struct ArrayList{
  uint8_t buffer[BUFF_LENGTH];
} ArrayList_type;


static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
static uint8_t       m_tx_buf[BUFF_LENGTH];  /**< TX buffer. */
//static uint8_t       m_rx_buf[BUFF_LENGTH];   /**< RX buffer. */
ArrayList_type m_rx_buf[BUFF_RX_LENGTH];  /**< RX buffer. */

static volatile bool burst_completed = false;
static volatile bool spi_xfer_done = false;  /**< Flag used to indicate that SPI instance completed the transfer. */

// PPI resrources
nrf_ppi_channel_t ppi_channel_spi;
nrf_ppi_channel_t ppi_channel_timer;

// Timer for burst read
const nrf_drv_timer_t timer = NRF_DRV_TIMER_INSTANCE(0);

void imu_select()
{
    nrf_gpio_pin_clear(SPIM0_SS_PIN);
}

void imu_deselect()
{
    nrf_gpio_pin_set(SPIM0_SS_PIN);
}

void burst_transfer_disable()
{
    ret_code_t err_code;

    // Configure short between spi end event and spi start task
    nrf_spim_shorts_disable(spi.p_registers, NRF_SPIM_SHORT_END_START_MASK);

    err_code = nrf_drv_ppi_channel_disable(ppi_channel_spi);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_disable(ppi_channel_timer);
    APP_ERROR_CHECK(err_code);
    
    nrf_drv_timer_disable(&timer);

    imu_deselect();

    burst_completed = true;
}

void timer_event_handler(nrf_timer_event_t event_type, void* p_context)
{
    switch (event_type)
    {
        case NRF_TIMER_EVENT_COMPARE0:
            burst_transfer_disable();
            break;

        default:
            //Do nothing.
            break;
    }
}

static void burst_setup()
{
    uint32_t spi_end_evt;
    uint32_t timer_count_task;
    uint32_t timer_cc_event;
    ret_code_t err_code;       

    //Configure timer
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
    err_code = nrf_drv_timer_init(&timer, &timer_cfg, timer_event_handler);
    APP_ERROR_CHECK(err_code);

    // Compare event after 4 transmissions
    nrf_drv_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL0, 1, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    timer_count_task = nrf_drv_timer_task_address_get(&timer, NRF_TIMER_TASK_COUNT);
    timer_cc_event = nrf_drv_timer_event_address_get(&timer,  NRF_TIMER_EVENT_COMPARE0);
    NRF_LOG_INFO("timer configured\n");

    // allocate PPI channels for hardware
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_spi);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_timer);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("ppi allocated\n");

    spi_end_evt = nrf_drv_spi_end_event_get(&spi);

    // Configure the PPI to count the trasnsactions on the TIMER
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_spi, spi_end_evt, timer_count_task);
    APP_ERROR_CHECK(err_code);


    // Configure another PPI to stop the SPI when 4 transactions have been completed
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_timer, timer_cc_event, NRF_SPIM_TASK_STOP);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("ppi configured\n");
}

static void burst_transfer_enable()
{
    ret_code_t err_code;

    burst_completed = false;

    err_code = nrf_drv_ppi_channel_enable(ppi_channel_spi);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel_timer);
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_enable(&timer);

    // Configure short between spi end event and spi start task
    nrf_spim_shorts_enable(spi.p_registers, NRF_SPIM_SHORT_END_START_MASK);

    imu_select();
}


void spi_event_handler(nrf_drv_spi_evt_t const * p_event)
{
    if(p_event->type == NRF_DRV_SPI_EVENT_DONE)
    {
    	spi_xfer_done = true;
    }
    else
    {
        NRF_LOG_ERROR("Wrong Event\n");
        // Something is wrong
    }
}

int8_t bmi160_bus_burst_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint32_t cnt)
{
//    m_tx_buf[0] = reg_addr | READ_MASK;

    nrf_drv_spi_xfer_desc_t xfer = NRF_DRV_SPI_XFER_RX(m_rx_buf, 1);
    uint32_t flags = NRF_DRV_SPI_FLAG_HOLD_XFER |
                     NRF_DRV_SPI_FLAG_REPEATED_XFER |
                     NRF_DRV_SPI_FLAG_RX_POSTINC  |
                     NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER;
    nrf_drv_spi_xfer(&spi, &xfer, flags);  

    burst_transfer_enable();

    nrf_spim_task_trigger(spi.p_registers, NRF_SPIM_TASK_START);

    while(!burst_completed){
        NRF_LOG_INFO("wating for interruption\n");
//        __WFE();
    }
    spi_xfer_done = false;
    burst_completed = false;

    return NRF_SUCCESS;
}

int8_t spi_init(void)
{
    ret_code_t err_code;
    
    nrf_gpio_cfg_output(SPIM0_SS_PIN);
    imu_deselect();
    
    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("ppi initialised\n");
    
    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.ss_pin   = NRF_DRV_SPI_PIN_NOT_USED;		//I control the CS pin 
    spi_config.miso_pin = SPIM0_MISO_PIN;
    spi_config.mosi_pin = SPIM0_MOSI_PIN;
    spi_config.sck_pin  = SPIM0_SCK_PIN;                               
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));
    NRF_LOG_INFO("SPI1 Master initialised.\r\n");


    NRF_SPIM0->RXD.PTR  =  m_rx_buf;
    NRF_SPIM0->RXD.MAXCNT = BUFF_LENGTH;

    // Reset rx buffer and transfer done flag
    memset(m_rx_buf, 0x05, sizeof(ArrayList_type)*BUFF_RX_LENGTH);
    spi_xfer_done = false; 

//    // To read the Device ID (0xD1)
//    uint8_t data = 0;
//    bmi160_bus_read(0, 0x0, &data, 1);
//    NRF_LOG_INFO("DEVICE ID (0xD1): %#01x\r\n", data);

    return NRF_SUCCESS;
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{
    burst_setup();
    spi_init();

    while (true)
    {
          bmi160_bus_burst_read(NULL,NULL,NULL,NULL);
    }
//    bmi160_bus_burst_read(NULL,NULL,NULL,NULL);
}
  • Thanks @MartinBL. To make your example run for SDK 14.0, do you recommend any changes besides adding the context parameter to nrf_drv_spi_init() and spi_event_handler()? This will be TX only and using a SoftDevice so I've used NRF_DRV_SPI_XFER_TX() with flag NRF_DRV_SPI_FLAG_TX_POSTINC and have avoided using TIMER0.

  • Also @MartinBL, (sorry to keep bothering you!) was there a specific requirement for using 16000 for the CC value for "timer_timer"? How short of a delay can I have for starting a transfer?

    EDIT: Looks like the longer my transfer, the more important it is this value is 16000. With lower values occasionally the data appears corrupted. Also, after the transfer is completed, erroneous data can still be transmitted. Any ideas?

  • Regarding SDK14 I can't see any reason to change anything other than what is necessary to port the API. In SDK 14 you also have the option to use the new SPI transaction manager. I haven't tried it yet, but it looks kind of neat.

    Regarding the CC value; Have you made sure that your transfers are completed before the timer reaches 16000? It depends on how large your transfers are of course, but if your timer use prescaler 0 and counts fast, your SPI use a slow transfer rate, and you transfer long strings of data I guess it is plausible.

Related