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

TWI Slave and APP_TIMER issues

Hi,

I'm working on a custom bootloader which does DFU over I2C. As a fail safe mechanism I want to have a timer running that gets reset between each Image data block, so incase we dont get a new image data block in 5-10 seconds (unless we have received the entire image) time the nRF52832 resets itself and goes back to the bootloader and waiting for a complete transfer.

The problems I'm seeing is that the I2C transfers stops working when I try to reset the timer in the I2C interrupt. If I start the timer at boot it runs and reaches its handler for as long as I dont transfer anything over I2C. But as soon as I transfer something it does not hit the handler and the I2C transfers stops.

I'm not quite sure what's happening here as I dont seem to hit any error handlers. Since the timer is not supposed to hit hits handler (if the DFU proccess is successful) they dont run in the same context either. But I have tried changing so that the TWI uses a higher interrupt priority than the APP_TIMER_MODULE, but that does not seem to help.

Here's my TWIS evt handler:

static void twis_event_handler(nrfx_twis_evt_t const * const p_event)
{
    ret_code_t err_code;
    switch(p_event->type)
    {
    case NRFX_TWIS_EVT_READ_REQ:
        if(p_event->data.buf_req)
        {
            err_code = nrfx_twis_tx_prepare(&instance, tx_buff, tx_len);
            NRF_LOG_DEBUG("TX prepare returned err_code: %d", err_code);
        }
        break;
    case NRFX_TWIS_EVT_READ_DONE:
        break;
    case NRFX_TWIS_EVT_READ_ERROR:
        NRF_LOG_DEBUG("I2C read error!")
        break;
    case NRFX_TWIS_EVT_WRITE_REQ:
        if(p_event->data.buf_req)
        {
            err_code = nrfx_twis_rx_prepare(&instance, rx_buff, sizeof(uint8_t) * 128);
            NRF_LOG_DEBUG("RX prepare returned err_code: %d", err_code);
        }
        break;
    case NRFX_TWIS_EVT_WRITE_DONE:
        if (p_event->data.rx_amount > 2)
        {
            NRF_LOG_DEBUG("Received data, DECODING");
            comm_timer_restart();
            handleData(rx_buff, (uint16_t) p_event->data.rx_amount);
        }
        break;
    case NRFX_TWIS_EVT_WRITE_ERROR:
        NRF_LOG_DEBUG("I2C Write error!");
        break;
    case NRFX_TWIS_EVT_GENERAL_ERROR:
        NRF_LOG_DEBUG("I2C Error!");
        break;
    default:
        NRF_LOG_DEBUG("I2C handler default case??");
        break;
    }
}

  • Hi,

    Sorry for the late reply,

    I have a couple questions I would like you to answer :) : 

    • Which SDK and Softdevice (if you are) version are you using?
    • Could you provide a bit more code? I'm specifically interested in the initialization of the Timer and the I2C modules. Could you also share the timer handler, and how you reset the timer? I can make the case private if you prefer it. 
    • Where exactly is the code stuck? Have you tried using the debugger and single step through the code?

    best regards

    Jared

  • Hi Jared.

    SDK V15.3, Softdevice V6.1.1.

    Timer module:

    #include "comm_timer.h"
    #include "app_timer.h"
    #include "nrf_log.h"
    
    app_timer_def(comm_timer);
    
    static uint32_t communication_timeout_ms;
    
    void comm_timer_init(on_communication_timeout comm_timeout, uint32_t timeout_ms)
    {
        ret_code_t err_code;
    
        communication_timeout_ms = app_timer_ticks(timeout_ms);
    
        err_code = app_timer_create
        (
            &comm_timer,
            app_timer_mode_single_shot,
            comm_timeout
        );
    
        nrf_log_debug("initializing comm timer returned: %d", err_code);
    
        if(err_code != nrf_success)
        {
            nrf_log_info("error initializing boot image timer");
        }
    
    }
    
    void comm_timer_start(void)
    {
        ret_code_t err_code;
    
        err_code = app_timer_start(
            comm_timer,
            communication_timeout_ms,
            null
        );
        nrf_log_debug("starting comm timer returned err_code: %d", err_code);
    
        if (err_code != nrf_success)
        {
            nrf_log_info("failed to start comm_timer");
        }
    }
    
    void comm_timer_stop(void)
    {
        app_timer_stop(comm_timer);
    }
    
    void comm_timer_restart(void)
    {
        comm_timer_stop();
        comm_timer_start();
    }

    Callback (timeout handler) passed to comm_timer

    static void communication_timeout(void * p_context)
    {
        NRF_LOG_DEBUG("Communication timeout!");
    }
    
    

    TWI Slave module:

    #include "twi_slave.h"
    #include "nrfx_twis.h"
    #include "nrf_gpio.h"
    #include "nrf_log.h"
    
    #define ADDRESS (0x56U >> 1)
    #define SDA_PIN 8
    #define SCL_PIN 7
    
    static const nrfx_twis_t instance = NRFX_TWIS_INSTANCE(0);
    static uint8_t tx_buff[128];
    static uint8_t rx_buff[128];
    static size_t  tx_len;
    
    static void twis_event_handler(nrfx_twis_evt_t const * const p_event);
    
    void twi_slave_init(void)
    {
        ret_code_t err_code;
        
        nrfx_twis_config_t config = 
        {
            .addr               = { ADDRESS },
            .scl                = SCL_PIN,
            .scl_pull           = (nrf_gpio_pin_pull_t) NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL,
            .sda                = SDA_PIN,
            .sda_pull           = (nrf_gpio_pin_pull_t) NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL,
            .interrupt_priority = NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY
        };
    
        memset(rx_buff, 0, (sizeof(rx_buff) / sizeof(rx_buff[0])));
        memset(tx_buff, 0, (sizeof(tx_buff) / sizeof(tx_buff[0])));
    
        err_code = nrfx_twis_init(&instance, &config, twis_event_handler);
    
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Error initializing TWI slave!");
        }
    
        nrfx_twis_enable(&instance);
    }
    
    void twi_slave_uninit(void)
    {
        nrfx_twis_uninit(&instance);
        nrfx_twis_disable(&instance);
    }
    
    void twi_slave_update_tx_buffer(const uint8_t * p_data, uint16_t length)
    {
        NRF_LOG_DEBUG("Sending response data");
        uint8_t i;
        for (i = 0; i < length; i++)
        {
            tx_buff[i] = p_data[i];
        }
        tx_len = sizeof(uint8_t) * length;
    }
    

  • Also,

    Since I'm not using Segger Embedded Studio I'm working on getting the debugging working. I'm having some issues with Ozone. Will update you as soon as I get it to work..

  • But as soon as I transfer something it does not hit the handler and the I2C transfers stops.

     Could you elaborate on this? My understanding of your project is that you don't want the timer to hit its handler when you transfer as this would cause a reset of the nRF. Instead, you would want to reset the timer after the transfer is complete in the NRFX_TWIS_EVT_WRITE_DONE case.  Is the problem that the I2C transfer doesn't start again after the timer is reset? again, it would be good to know where in the code it stops.

  • Hi Jared,

    You are correct that I dont want it to hit the timeout handler.

    What's happening is that my timer reset is too quick, I stop it quickly and then start it immediately. This does not seem to reset the timer count, causing it to hit the timer timeout handler. And that apparently breaks the I2C transfers. Probably because I will keep trying to restart the timer each time a new I2C message is received.

    What I need to solve here is to reset the timer in a manner that ensures that the timer count is reset before I start it again.

    Any ideas how I can achieve that?

Related