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?

  • Hi Stefano,

    Have you tried to step inside nrf_drv_twi_tx() to see what exactly throwing NRF_ERROR_INTERNAL  error ? 
    Usually it's about hardware issue:

    * @retval NRF_ERROR_INTERNAL             If an error was detected by hardware.

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

  • 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.

  • 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.

  • 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 don't think so. I checked with oscilloscope and there is no signal running on TWI pins. whe the circuit powers up the SDA and SCL goes high (by an externel pullup 4k7 resistor) and remains high.

    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 

    I based my code exactly on this example, so I can't figure out what is wrong.

    what other test I should do?

    Thanks.

  • Hi Stefano, 

    Please try testing with unmodified twi_master_with_twis_slave example. Please check if you get any error and if you have the signals go out of the pins.

    Do you have a nRF5 DK to test ? We may need to verify that the hardwares on your custom board are OK. 

Related