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

Transfer more than 255 bytes with SPI master on nrf52

Hi everybody,

I am trying to read a 1024 bytes FIFO buffer from an IMU (Bosh BMI160) using the SPI master driver on nrf52832 and sdk 12.2.

I know others had the same problem and I've read the other posts but since I just started with the nrf52 I couldn't figure out exactly how to implement this so I decided to open a new question.

For what I understood from other posts I should split the transfer into single transactions (4 in my case) and then using PPI count them with a TIMER and when the TIMER reaches 4, stop the SPI transfer, again with PPI. This should use the DMA and transfer the bytes directly into the ram.

Here is the code I am using:

// handler to disable the timer and set CS when the timer reaches 4
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, 4, 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);

    // allocate PPI channels
    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);

    spi_start_task = nrf_drv_spi_start_task_get(&spi);
    spi_end_evt = nrf_drv_spi_end_event_get(&spi);

    // Configure the PPI to toggle the CS (high to low) and to start the spi when there is an interrupt
    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 toggle again the CS (low to high) when the spi transfer is finished
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_timer, timer_cc_event, NRF_SPIM_TASK_STOP);
    APP_ERROR_CHECK(err_code);
}

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

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

s8 bmi160_bus_burst_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u32 cnt)
{
	m_tx_buf[0] = reg_addr | READ_MASK;
	m_tx_buf[1] = 0;

	nrf_drv_spi_xfer_desc_t xfer = NRF_DRV_SPI_XFER_TRX(m_tx_buf, 1, reg_data, 255);
    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();

	spi_start_task = 0x1UL;

	while(!burst_completed){
		__WFE();
	}
	spi_xfer_done = false;
	burst_completed = false;

	return SUCCESS;
}

The device crashes after the SPI is started and I get a APP_ERROR:ERROR:Fatal.

Is this the correct way to do this? Do you have an idea of where the problem might be?

Thanks!

Parents
  • The SPIM peripheral can only transfer 255 bytes (0xFF) in one transaction, but if you use the LIST feature (NRF_DRV_SPI_FLAG_TX_POSTINC flag in the driver) the RAM address will be incremented with the length of the buffer after each transfer and you can automatically start the next transfer (using short between END and START). You should make sure that the buffer is large enough for the total number of bytes you will receive (bytes per transaction times the number of transactions), or else you may hardfault or get RAM corruption.

    If you disable the shorts on the forth END event, the SPIM will try to send out a fifth frame. You should stop it on the third END event to get four transfers.

    When you get APP_ERROR:ERROR:Fatal on the logging, you should enable DEBUG in your preprocessor symbols, go into debug mode and check which function returned an error code. See here for more info.

    1024 divided by 255 is larger than four so you have to do five transfers.

Reply
  • The SPIM peripheral can only transfer 255 bytes (0xFF) in one transaction, but if you use the LIST feature (NRF_DRV_SPI_FLAG_TX_POSTINC flag in the driver) the RAM address will be incremented with the length of the buffer after each transfer and you can automatically start the next transfer (using short between END and START). You should make sure that the buffer is large enough for the total number of bytes you will receive (bytes per transaction times the number of transactions), or else you may hardfault or get RAM corruption.

    If you disable the shorts on the forth END event, the SPIM will try to send out a fifth frame. You should stop it on the third END event to get four transfers.

    When you get APP_ERROR:ERROR:Fatal on the logging, you should enable DEBUG in your preprocessor symbols, go into debug mode and check which function returned an error code. See here for more info.

    1024 divided by 255 is larger than four so you have to do five transfers.

Children
No Data
Related