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

nRF52 with FreeRTOS: TWI error NRF_ERROR_INTERNAL

Hello,

I'm developing firmware for nRF52832 with FreeRTOS, I would like to use TWI to access to an external memory. Every time I try to use "nrf_drv_twi_tx" function i get NRF_ERROR_INTERNAL error.

I already read lot of answer about this issue, but any solution is working.

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include <nordic_common.h>
#include <nrf.h>

#include <nrf_drv_clock.h>
#include <nrf_log.h>
#include <nrf_log_ctrl.h>
#include <nrf_log_default_backends.h>

#include <nrf_sdh.h>
#include <nrf_sdh_soc.h>
#include <nrf_sdh_freertos.h>

#include <sdk_errors.h>
#include <nrf_drv_twi.h>
#include <nrf_gpio.h>

#include <app_timer.h>
#include <app_error.h>

#include <FreeRTOS.h>
#include <task.h>
#include <timers.h>
#include <SEGGER_RTT.h>


#define		VERB_HW_TWI																		// enable verbose output for debug

#define		TWI_SCL_PIN								9										// PIN		9 (P0.9)
#define		TWI_SDA_PIN								10										// PIN		10 (P0.10)
#define		TWI_INSTANCE							1										// value		TWI instance index.
#define		TWI_IRQ_PRIORITY						APP_IRQ_PRIORITY_LOW				// value		Interrupt priority.
#define		FM24CL16B_SLAVE_ADDR					0x50									// value		slave address (with A2=0, A1=0)

// tasks stack sizes and priorities: 0 = lowest priority; (configMAX_PRIORITIES–1) = highest priority
#define		vTask_Main_STACK_SIZE				300
#define		vTask_Main_PRIORITY					(1+tskIDLE_PRIORITY)
#define		vTask_LOGGER_STACK_SIZE				256
#define		vTask_LOGGER_PRIORITY				(2+tskIDLE_PRIORITY)


/**					Internal Variable Declaration									**/
static const nrf_drv_twi_t	twi_instance = NRF_DRV_TWI_INSTANCE( TWI_INSTANCE );				// Instance of TWI master driver that will be used for communication
static SemaphoreHandle_t	xProtection_smphr = NULL;


/* INIT for TWI hardware for peripheral */
void vInit_TWI_Hardware( void ) {
	NRF_LOG_INFO( "vInit_TWI_Hardware" );
	ret_code_t err_code;
	static const nrf_drv_twi_config_t twi_config = {
		.scl =						TWI_SCL_PIN,											// SCL pin number.
		.sda =						TWI_SDA_PIN,											// SDA pin number.
		.frequency =				NRF_DRV_TWI_FREQ_400K,								// TWI frequency.
		.interrupt_priority =	TWI_IRQ_PRIORITY,										// Interrupt priority.
		.clear_bus_init =			false,													// Clear bus during init.
		.hold_bus_uninit =		false														// Hold pull up state on gpio pins after uninit.
	};
	err_code = nrf_drv_twi_init( &twi_instance, &twi_config, NULL, NULL );
	APP_ERROR_CHECK( err_code );
	//
	nrf_gpio_cfg( TWI_SCL_PIN,											// pin_number
					  NRF_GPIO_PIN_DIR_INPUT,							// Input.
					  NRF_GPIO_PIN_INPUT_CONNECT,						// Connect input buffer.
					  NRF_GPIO_PIN_NOPULL,								// Pin pull-up resistor disabled.
					  NRF_GPIO_PIN_S0S1,									// Standard '0', standard '1'.
					  NRF_GPIO_PIN_NOSENSE								// Pin sense level disabled.
					);
	nrf_gpio_cfg( TWI_SDA_PIN,											// pin_number
					  NRF_GPIO_PIN_DIR_INPUT,							// Input.
					  NRF_GPIO_PIN_INPUT_CONNECT,						// Connect input buffer.
					  NRF_GPIO_PIN_NOPULL,								// Pin pull-up resistor disabled.
					  NRF_GPIO_PIN_S0S1,									// Standard '0', standard '1'.
					  NRF_GPIO_PIN_NOSENSE								// Pin sense level disabled.
					);
	// RTOS init
	xProtection_smphr = xSemaphoreCreateBinary();									// Ensure the semaphore is created before it gets used.
	ASSERT( xProtection_smphr );															// LOCK HERE: the semaphore could not be created
	xSemaphoreGive( xProtection_smphr );												// 'Give' the peripheral protection semaphore
	NRF_LOG_INFO( "vInit_TWI_Hardware: DONE" );
	return;
}

/*  */
ret_code_t xUtil_TWI_Read( uint8_t slave_addr, uint8_t start_addr, uint8_t *p_buff, uint16_t length ) {
	#ifdef VERB_HW_TWI
	NRF_LOG_INFO( "xUtil_TWI_Read:\tslave = 0x%02X\taddr = 0x%02X\tlenght = %u", slave_addr, start_addr, length );
	#endif
	ret_code_t err_code;
	static uint8_t reg_addr;
	reg_addr = start_addr;

	if( length == 0 ) {																							// sanity check for lenght
		err_code = NRF_ERROR_INVALID_LENGTH;

	} else if( xSemaphoreTake( xProtection_smphr, portMAX_DELAY ) == pdPASS ) {				// execute operation only if resuorce is free
		nrf_drv_twi_enable( &twi_instance );																// enable I2C
		err_code = nrf_drv_twi_tx( &twi_instance, slave_addr, &reg_addr, 1, true );
		APP_ERROR_CHECK( err_code );
		err_code = nrf_drv_twi_rx( &twi_instance, slave_addr, p_buff, length );
		APP_ERROR_CHECK( err_code );
		nrf_drv_twi_disable( &twi_instance );
		xSemaphoreGive( xProtection_smphr );																// 'Give' the semaphore to unblock the blocked task.

	} else { err_code = NRF_ERROR_RESOURCES; }

	return err_code;
}

/*  */
ret_code_t xUtil_TWI_Write( uint8_t slave_addr, uint8_t start_addr, uint8_t const *p_buff, uint16_t length ) {
	#ifdef VERB_HW_TWI
	NRF_LOG_INFO( "xUtil_TWI_Write:\tslave = 0x%02X\taddr = 0x%02X\tlenght = %u", slave_addr, start_addr, length );
	#endif
	ret_code_t err_code;
	static uint8_t reg_addr;
	reg_addr = start_addr;

	if( length == 0 ) {																							// sanity check for lenght
		err_code = NRF_ERROR_INVALID_LENGTH;

	} else if( xSemaphoreTake( xProtection_smphr, portMAX_DELAY ) == pdPASS ) {				// execute operation only if resuorce is free
		nrf_drv_twi_enable( &twi_instance );																// enable I2C
		err_code = nrf_drv_twi_tx( &twi_instance, slave_addr, &reg_addr, 1, true );
		APP_ERROR_CHECK( err_code );
		err_code = nrf_drv_twi_tx( &twi_instance, slave_addr, p_buff, length, false );
		APP_ERROR_CHECK( err_code );
		nrf_drv_twi_disable( &twi_instance );
		xSemaphoreGive( xProtection_smphr );																// 'Give' the semaphore to unblock the blocked task.

	} else { err_code = NRF_ERROR_RESOURCES; }

	return err_code;
}

/* MAIN Task */
static void vTask_Main( void *pvParameters ) {
	NRF_LOG_INFO( "Task started: vTask_Main" );
	UNUSED_PARAMETER( pvParameters );
	ret_code_t err_code;
	static uint8_t tx[2] = {100, 150};
	static uint8_t rx[2] = {200, 250};
	vInit_TWI_Hardware();

	while( 1 ) {																				// task Loop
		err_code = xUtil_TWI_Write( FM24CL16B_SLAVE_ADDR, 0, tx, 1 );
		NRF_LOG_INFO( "%10ums\tvTask_Main: write:\terr_code: %u\tbuffer = %u", xTaskGetTickCount(), err_code, tx[0] );
		err_code = xUtil_TWI_Read( FM24CL16B_SLAVE_ADDR, 0, rx, 1 );
		NRF_LOG_INFO( "%10ums\tvTask_Main: write:\terr_code: %u\tbuffer = %u", xTaskGetTickCount(), err_code, rx[0] );
		vTaskDelay( pdMS_TO_TICKS( 2000 ) );
	}
}

/**					MAIN Program														**/
int main( void ) {
	ret_code_t err_code;
	// Initialize clock
	err_code = nrf_drv_clock_init();
	APP_ERROR_CHECK( err_code );
	// Initialize logging
	err_code = NRF_LOG_INIT( NULL );
	APP_ERROR_CHECK( err_code );
	NRF_LOG_DEFAULT_BACKENDS_INIT();
	NRF_LOG_INFO( RTT_CTRL_CLEAR );									// clear the terminal window
	NRF_LOG_INFO( RTT_CTRL_BG_BLACK );
	NRF_LOG_INFO( RTT_CTRL_TEXT_BRIGHT_WHITE );
	// Do not start any interrupt that uses system functions before system initialisation.
	// The best solution is to start the OS before any other initalisation.
	#if NRF_LOG_ENABLED

	// LOG task creation
	if( pdPASS != xTaskCreate( vTask_Logger, "Logger", vTask_LOGGER_STACK_SIZE, NULL, vTask_LOGGER_PRIORITY, &m_task_handle_Logger ) ) {
		APP_ERROR_HANDLER( NRF_ERROR_NO_MEM );
	}

	#endif
	SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;								// Activate deep sleep mode.
	// Initialize the SoftDevice and the stack event interrupt.
	err_code = nrf_sdh_enable_request();
	APP_ERROR_CHECK( err_code );
	ASSERT( nrf_sdh_is_enabled() );									// sanity check for SoftDevice Initialized
	err_code = app_timer_init();										// Initialize timer module (used by BLE)
	APP_ERROR_CHECK( err_code );
	//

	if( xTaskCreate( vTask_Main, "main", vTask_Main_STACK_SIZE, NULL, vTask_Main_PRIORITY, NULL ) != pdPASS ) {
		APP_ERROR_HANDLER( NRF_ERROR_NO_MEM );						// LOCK HERE: the task could not be created
	}

	NRF_LOG_INFO( "FreeRTOS: Starting Scheduler..." );
	vTaskStartScheduler();												// Start FreeRTOS scheduler

	while( 1 ) { APP_ERROR_HANDLER( NRF_ERROR_FORBIDDEN ); }	// LOCK HERE: there is not enough FreeRTOS heap memory available to create the Idle and Timer tasks
}

when I run the code, every time it call the "nrf_drv_twi_tx" function i get NRF_ERROR_INTERNAL error.

Please can you help me?

Parents
  • Have you tried to step inside nrf_drv_twi_tx() to see what exactly throwing NRF_ERROR_INTERNAL  error ? 

    Yes, the steps are:

    nrf_drv_twi_tx -> nrfx_twi_tx -> nrfx_twi_xfer -> twi_xfer -> twi_tx_start_transfer

    twi_tx_start_transfer calls "twi_transfer" into while loop (line 470 of nrfx_twi.c)

    then the check "if (p_cb->error){"  (line 475 of nrfx_twi.c) pass b ecause p_cb->error==0

    so the code checks "if (hw_timeout <= 0)" (line 489 of nrfx_twi.c) that fails and here ret_code = NRFX_ERROR_INTERNAL;

    This is the nrfx_twi.c function

    static nrfx_err_t twi_tx_start_transfer(NRF_TWI_Type        * p_twi,
                                            twi_control_block_t * p_cb)
    {
        nrfx_err_t ret_code = NRFX_SUCCESS;
        volatile int32_t hw_timeout;
    
        hw_timeout = HW_TIMEOUT;
    
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
        nrf_twi_shorts_set(p_twi, 0);
    
        p_cb->bytes_transferred = 0;
        p_cb->error             = false;
    
        // In case TWI is suspended resume its operation.
        nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
    
        if (p_cb->prev_suspend != TWI_SUSPEND_TX)
        {
            nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX);
        }
    
        (void)twi_send_byte(p_twi, p_cb);
    
        if (p_cb->handler)
        {
            p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK   |
                             NRF_TWI_INT_ERROR_MASK     |
                             NRF_TWI_INT_TXDSENT_MASK   |
                             NRF_TWI_INT_RXDREADY_MASK;
    
            nrf_twi_int_enable(p_twi, p_cb->int_mask);
        }
        else
        {
            while ((hw_timeout > 0) &&
                   twi_transfer(p_twi, p_cb))
            {
                hw_timeout--;
            }
    
            if (p_cb->error)
            {
                uint32_t errorsrc =  nrf_twi_errorsrc_get_and_clear(p_twi);
    
                if (errorsrc)
                {
                    ret_code = twi_process_error(errorsrc);
                }
                else
                {
                    ret_code = NRFX_ERROR_INTERNAL;
                }
            }
    
            if (hw_timeout <= 0)
            {
                nrf_twi_disable(p_twi);
                nrf_twi_enable(p_twi);
                ret_code = NRFX_ERROR_INTERNAL;
            }
    
        }
        return ret_code;
    }

    It seems to be something related to hw_timeout, unfortunatly there is no comments in nordic library :(

    Its not easy to figure out why it is not working.

    Have you tried to test with an TWI example without FreeRTOS ? 

    Yes, I tested the function before starting the FreeRTOS sheduler and they do not work.

    Could you help me to fix this issue?

    thanks for your help.

Reply
  • Have you tried to step inside nrf_drv_twi_tx() to see what exactly throwing NRF_ERROR_INTERNAL  error ? 

    Yes, the steps are:

    nrf_drv_twi_tx -> nrfx_twi_tx -> nrfx_twi_xfer -> twi_xfer -> twi_tx_start_transfer

    twi_tx_start_transfer calls "twi_transfer" into while loop (line 470 of nrfx_twi.c)

    then the check "if (p_cb->error){"  (line 475 of nrfx_twi.c) pass b ecause p_cb->error==0

    so the code checks "if (hw_timeout <= 0)" (line 489 of nrfx_twi.c) that fails and here ret_code = NRFX_ERROR_INTERNAL;

    This is the nrfx_twi.c function

    static nrfx_err_t twi_tx_start_transfer(NRF_TWI_Type        * p_twi,
                                            twi_control_block_t * p_cb)
    {
        nrfx_err_t ret_code = NRFX_SUCCESS;
        volatile int32_t hw_timeout;
    
        hw_timeout = HW_TIMEOUT;
    
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT);
        nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY);
        nrf_twi_shorts_set(p_twi, 0);
    
        p_cb->bytes_transferred = 0;
        p_cb->error             = false;
    
        // In case TWI is suspended resume its operation.
        nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
    
        if (p_cb->prev_suspend != TWI_SUSPEND_TX)
        {
            nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX);
        }
    
        (void)twi_send_byte(p_twi, p_cb);
    
        if (p_cb->handler)
        {
            p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK   |
                             NRF_TWI_INT_ERROR_MASK     |
                             NRF_TWI_INT_TXDSENT_MASK   |
                             NRF_TWI_INT_RXDREADY_MASK;
    
            nrf_twi_int_enable(p_twi, p_cb->int_mask);
        }
        else
        {
            while ((hw_timeout > 0) &&
                   twi_transfer(p_twi, p_cb))
            {
                hw_timeout--;
            }
    
            if (p_cb->error)
            {
                uint32_t errorsrc =  nrf_twi_errorsrc_get_and_clear(p_twi);
    
                if (errorsrc)
                {
                    ret_code = twi_process_error(errorsrc);
                }
                else
                {
                    ret_code = NRFX_ERROR_INTERNAL;
                }
            }
    
            if (hw_timeout <= 0)
            {
                nrf_twi_disable(p_twi);
                nrf_twi_enable(p_twi);
                ret_code = NRFX_ERROR_INTERNAL;
            }
    
        }
        return ret_code;
    }

    It seems to be something related to hw_timeout, unfortunatly there is no comments in nordic library :(

    Its not easy to figure out why it is not working.

    Have you tried to test with an TWI example without FreeRTOS ? 

    Yes, I tested the function before starting the FreeRTOS sheduler and they do not work.

    Could you help me to fix this issue?

    thanks for your help.

Children
  • It's most likely the timeout occurred when there is no response (ACK) from the slave. 

    Could you show the schematics and a close up photo of your setup ? 

    I would suggest to test TWI with emulated EEPROM memory before you test with the actual memory. We have an example in nRF5 SDK that does that, please check the twi_master_with_twis_slave (you need to short the I2C pins of the master to the emulated slave). If that works you can use a logic analyzer and check the signal on the I2C pins. Then instead of using the emulated EEPROM, you can connect to your own external memory.

Related