Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

TWI reading and writing basics

Hello all, 

I have pinged the sensor on the "twi bus" successfully, now I am following the post on the read/write function that is needed to actually read the data off the sensor. This is what I am reading at the moment:  https://medium.com/vicara-hardware-university/setting-up-twi-on-nrf52xx-dcaf83a50baa

Indeed, I have the sensor's library , however, I am not really sure how to apply the following example and use the library.


/*
 * dev_addr -> Device address
 * reg_adddr -> Register address
 * data -> Buffer which holds data to be written
 * length -> Length of data to be written
 */
int8_t twi_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    uint32_t err_code;

    uint8_t buffer[255] = {0};
    buffer[0] = reg_addr;
    memcpy(&buffer[1], data, length);
    m_xfer_done = false;
    err_code = nrf_drv_twi_tx(&m_twi, dev_addr, buffer, sizeof(length), false);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

   return err_code;
}

/*
 * dev_addr -> Device address
 * reg_adddr -> Register address
 * data -> Buffer where data read from TWI will be stored
 * length -> Length of data to be read
 */
int8_t twi_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
    uint32_t err_code;
    m_xfer_done = false;
    err_code = nrf_drv_twi_tx(&m_twi, dev_addr, &reg_addr, sizeof(reg_addr), true);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

    m_xfer_done = false;
    err_code = nrf_drv_twi_rx(&m_twi, dev_addr, data, length);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

    return err_code;

}

and obtain the data from the sensors registers.

BTW the library uses the i2c for the STM32 like this:

//STM32F specific I2C API call
#ifdef STM32F
	//All of the I2C API functions (For Example: HAL_I2C_Master_Transmit()) are being called from stm32f4xx_hal.h

	//hi2c1 - The variable to the I2C handler which is needed later in the Write and Read I2C function.
	//hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef hi2c1;.
	extern I2C_HandleTypeDef hi2c1;

	//Master sends I2C write command via pointer *Data from the Sensor API.
	//The function returns HAL_OK (=0) when there is no error and -1 when there is an error.
	int WriteI2C_Bus(struct TransferData *Data)
	{
		//Initialization of intial Error = 0
		int Error = 0;

		//Define an array of 3 Bytes to be sent as I2C Write Command as shown in Fig. 10 in the Datasheet Page 7
		uint8_t WData[3]={Data->RegisterAddress, Data->WData[0], Data->WData[1]};

		//Send I2C Write Command as shown in Fig. 10 in the Datasheet Page 7 with Error checking.
		/*Format defined by STM:
		*HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout).
		*HAL_I2C_Master_Transmit() is used for transmitting data to the I2C device. It takes following arguments:
		*
		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler. hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef *hi2c1;.
		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
		*uint8_t *pData  - is the pointer to the data to be transmitted.
		*uint16_t Size  - is the size of the transmitted data in bytes.
		*uint32_t Timeout - timeout in millisecond in case of any error.
		*Return Error.
		*/
		Error = HAL_I2C_Master_Transmit(&hi2c1, (Data->Slave_Address)<<1, WData, 3, 100);

		//Return -1 when there is error and return HAL_OK (= 0) when no error
		if (Error != HAL_OK)
		{
			return -1;
		}
		else
		{
			return HAL_OK;
		}
	}

	//Master sends I2C Read command and save the read data via pointer *Data.
	//The function returns HAL_OK (=0) when no error and -1 when there is error.
	int ReadI2C_Bus (struct TransferData *Data)
	{
		//Initialization of intial Error = 0
		int Error = 0;

		//Send I2C Read Command as shown in Fig. 10 in the Datasheet Page 7 with Error checking.
		/*Format defined by STM:
		*HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c1, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout).
		*HAL_I2C_Mem_Read () is used for reading data from the I2C device. It has been used instead of HAL_I2C_Master_Receive() because it sends restart condition
		*while the latter which sends stop condition and thus not working. It takes following arguments:
		*
		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler.
		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
		*uint16_t MemAddress - is the Internal memory address in the slave which is the register address.
		*uint16_t MemAddSize - the size of memory address (in Byte) which is the size of register address (In the case of Vishay's Sensor always 1 Byte).
		*uint8_t *pData  - is the pointer to the data to be received.
		*uint16_t Size  - is the size of the received data in bytes.
		*uint32_t Timeout - timeout in millisecond in case of any error.
		*Return Error.
		*
		*/
		Error = HAL_I2C_Mem_Read(&hi2c1, (Data->Slave_Address)<<1, Data->RegisterAddress, 1,Data->RData,2,100);

		//Return -1 when there is error and return HAL_OK (= 0) when no error
		if (Error !=HAL_OK)
		{
			return -1;
		}
		else
		{
			return HAL_OK;
		}
	}

	//Master sends I2C Gesture Read command and save the read data via pointer *Data.
	//The function returns HAL_OK (=0) when no error and -1 when there is error.
    int ReadI2C_Bus_Gesture_Mode(struct GestureTransferData *Data)
	{
		//Initialization of intial Error = 0
		int Error = 0;

		//Send I2C Gesture Read Command as shown in the Application Note Page 19 with Error checking.
		/*Format defined by STM:
		*HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout).
		*HAL_I2C_Master_Receive () is used for reading data from the I2C device. It has been used instead of HAL_I2C_Mem_Read() because no restart condition is *needed.
		*It takes following arguments:
		*
		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler. hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef *hi2c1;.
		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
		*uint8_t *pData  - is the pointer to the data to be received.
		*uint16_t Size  - is the size of the received data in bytes (For Gesture Stream 6 Bytes are needed).
		*uint32_t Timeout - timeout in millisecond in case of any error.
		*Return Error.
		*
		*/
		Error = HAL_I2C_Master_Receive(&hi2c1,(Data->Slave_Address)<<1,Data->RData,6,100);

		//Return -1 when there is error and return HAL_OK (= 0) when no error
		if (Error != HAL_OK)
		{
			return -1;
		}
		else
		{
			return HAL_OK;
		}
	}
#endif

So, I need to port the nrf twi and its sdk, right ? to eventually use the library with the nrf52dk?

In addition, I would like to use the pooling rather than interrupt. I guess as well this is done in the main() but within the while(true) loop?

Any ideas, example how to do, approach this?

Best.

PS yeah , I am new to this, just realized that I should write the code to read off the sensor in the while (true) loop :) where I am now printing text , juhej! 

Parents
  • Hi and thank you for your input, it really helps. Indeed, this example should be a good start and I will have a go, however, can this example as well be use to use the library , how, or is this just to use for predefined registers? Such as these: 

    #define LM75B_REG_TEMP 0x00U
    #define LM75B_REG_CONF 0x01U
    #define LM75B_REG_THYST 0x02U
    #define LM75B_REG_TOS 0x03U

    For example, the library defines all the registers including the values and methods, etc. So, it is of importance to use the library... However, you mentioned I can use the "twi_write and twi_read function" as posted above , can you be more specific, maybe an example of how to actually do this Slight smile

    Thank you in advance.

    PS while cloning the "project" I got an error when compiling, namely "C:/components/libraries/log/src/nrf_log_backend_rtt.c does not exist." do I have to clone it together with the whole directory path?

  • Hi , 

    The macros defined in the code needs to be according to your sensor . This code is written for the LM75 (a digital temperature sensor with i2c/twi interface) . If you can share datasheet or part number of your sensor may be I can help.

    All the examples given in the SDK use a relative part to the sdk directories so one way is to clone the whole sdk (obviously removing all the other examples) and compile or just clone the certain example in the examples directory in the same directory so the relative path is not disturbed .

    Regards ,

    Ahmed

  • Hello All

    the library was compiled and the TWI is added, the sensor is connected to the nrf52dk and pullups are across the SCK, SDA ... However, I am getting strange behavior.

    In  general, the sensor readings freeze at some point, interestingly only when the barrier is introduced not at all channels, one is not affected, actually it looks like the first "goes" to the second, and eventually first and third freeze, while the second works normally :) all the way. The reset is pressed to start from the beginning...

    Please have a look at the picture (JScope) and please comment whether the nRF TWI driver fulfils the following write and read I2C protocols. For the Read I2C protocol, it is important that the restart condition is being sent before the second slave address and please ensure that no Stop condition being sent before the restart condition because some I2C library sends stop condition before restart condition ???

    Best.

      

  • Hi

    Our experts on this are out of office due to the Easter Holiday.

    We will have a closer look at this case during next week.

    Regards,
    Sigurd Hellesvik

  • Thank you and I am looking forward to the reply.

    Indeed, happy Easter holidays!

  • MuRa said:
    For the Read I2C protocol, it is important that the restart condition is being sent before the second slave address and please ensure that no Stop condition being sent before the restart condition because some I2C library sends stop condition before restart condition ???

    This is typically handled by the no_stop parameter, e.g.:

    /**
     * @brief Function for sending data to a TWI slave.
     *
     * The transmission will be stopped when an error occurs. If a transfer is ongoing,
     * the function returns the error code @ref NRF_ERROR_BUSY.
     *
     * @param[in] p_instance Pointer to the driver instance structure.
     * @param[in] address    Address of a specific slave device (only 7 LSB).
     * @param[in] p_data     Pointer to a transmit buffer.
     * @param[in] length     Number of bytes to send.
     * @param[in] no_stop    If set, the stop condition is not generated on the bus
     *                       after the transfer has completed successfully (allowing
     *                       for a repeated start in the next transfer).
     *
     * @retval NRF_SUCCESS                  If the procedure was successful.
     * @retval NRF_ERROR_BUSY               If the driver is not ready for a new transfer.
     * @retval NRF_ERROR_INTERNAL           If an error was detected by hardware.
     * @retval NRF_ERROR_INVALID_ADDR       If the EasyDMA is used and memory adress in not in RAM.
     * @retval NRF_ERROR_DRV_TWI_ERR_ANACK  If NACK received after sending the address in polling mode.
     * @retval NRF_ERROR_DRV_TWI_ERR_DNACK  If NACK received after sending a data byte in polling mode.
     */
    __STATIC_INLINE
    ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance,
                              uint8_t               address,
                              uint8_t const *       p_data,
                              uint8_t               length,
                              bool                  no_stop);

Reply
  • MuRa said:
    For the Read I2C protocol, it is important that the restart condition is being sent before the second slave address and please ensure that no Stop condition being sent before the restart condition because some I2C library sends stop condition before restart condition ???

    This is typically handled by the no_stop parameter, e.g.:

    /**
     * @brief Function for sending data to a TWI slave.
     *
     * The transmission will be stopped when an error occurs. If a transfer is ongoing,
     * the function returns the error code @ref NRF_ERROR_BUSY.
     *
     * @param[in] p_instance Pointer to the driver instance structure.
     * @param[in] address    Address of a specific slave device (only 7 LSB).
     * @param[in] p_data     Pointer to a transmit buffer.
     * @param[in] length     Number of bytes to send.
     * @param[in] no_stop    If set, the stop condition is not generated on the bus
     *                       after the transfer has completed successfully (allowing
     *                       for a repeated start in the next transfer).
     *
     * @retval NRF_SUCCESS                  If the procedure was successful.
     * @retval NRF_ERROR_BUSY               If the driver is not ready for a new transfer.
     * @retval NRF_ERROR_INTERNAL           If an error was detected by hardware.
     * @retval NRF_ERROR_INVALID_ADDR       If the EasyDMA is used and memory adress in not in RAM.
     * @retval NRF_ERROR_DRV_TWI_ERR_ANACK  If NACK received after sending the address in polling mode.
     * @retval NRF_ERROR_DRV_TWI_ERR_DNACK  If NACK received after sending a data byte in polling mode.
     */
    __STATIC_INLINE
    ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance,
                              uint8_t               address,
                              uint8_t const *       p_data,
                              uint8_t               length,
                              bool                  no_stop);

Children
  • Hello Kenneth thank you for your reply, sorry for being such a beginner, but I had a look and so far I am seeing this code within the "nrf_drw_twi.h"  when debugged :

    __STATIC_INLINE
    ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance,
                              uint8_t               address,
                              uint8_t const *       p_data,
                              uint8_t               length,
                              bool                  no_stop)
    {
        ret_code_t result = 0;
        if (NRF_DRV_TWI_USE_TWIM)
        {
            result = nrfx_twim_tx(&p_instance->u.twim,
                                    address, p_data, length, no_stop);
        }
        else if (NRF_DRV_TWI_USE_TWI)
        {
            result = nrfx_twi_tx(&p_instance->u.twi,
                                   address, p_data, length, no_stop);
        }
        return result;
    }
    

    Indeed, it seems that " no_stop " is used , so, do I have to change this, and into what? Not really sure how to implement what is expected from the above instruction of the Read I2C protocol ...  

    Any help is much appreciated!

    Best. 

    PS this is my twi_init()

    void twi_init(void) {
      ret_code_t err_code;
    
      const nrf_drv_twi_config_t twi_config = {
          .scl = 26, // Add you SCL Pin Here
          .sda = 27, // Add you SDA Pin Here
          .frequency = NRF_DRV_TWI_FREQ_100K,
          .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
          .clear_bus_init = false};
    
      err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL);
      APP_ERROR_CHECK(err_code);
    
      nrf_drv_twi_enable(&m_twi);
    }

  • Are you calling nrf_drv_twi_tx() in your application?

    Kenneth

  • Yes, and the  nrf_drv_twi_rx(), is this ok?

    Attaching the whole API for the sensor...

    Best.

    extern const nrf_drv_twi_t m_twi;
    extern volatile bool m_xfer_done;
    
    //nRF52DK specific I2C API call
    #ifdef nRF52DK
    	//All of the I2C API functions (For Example: HAL_I2C_Master_Transmit()) are being called from stm32f4xx_hal.h
    
    	//hi2c1 - The variable to the I2C handler which is needed later in the Write and Read I2C function.
    	//hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef hi2c1;.
    	//extern I2C_HandleTypeDef hi2c1;
    
    	//Master sends I2C write command via pointer *Data from the Sensor API.
    	//The function returns HAL_OK (=0) when there is no error and -1 when there is an error.
    	int WriteI2C_Bus(struct TransferData *Data)
    	{
    		//Initialization of intial Error = 0
    		ret_code_t Error = 0;
    
    		//Define an array of 3 Bytes to be sent as I2C Write Command as shown in Fig. 10 in the Datasheet Page 7
    		uint8_t WData[3]={Data->RegisterAddress, Data->WData[0], Data->WData[1]};
    
    		//Send I2C Write Command as shown in Fig. 10 in the Datasheet Page 7 with Error checking.
    		/*Format defined by STM:
    		*HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout).
    		*HAL_I2C_Master_Transmit() is used for transmitting data to the I2C device. It takes following arguments:
    		*
    		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler. hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef *hi2c1;.
    		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
    		*uint8_t *pData  - is the pointer to the data to be transmitted.
    		*uint16_t Size  - is the size of the transmitted data in bytes.
    		*uint32_t Timeout - timeout in millisecond in case of any error.
    		*Return Error.
    		*/
    		m_xfer_done = false;
    
    		Error = nrf_drv_twi_tx(&m_twi, Data->Slave_Address, WData, 3, false);  
                                   
                    APP_ERROR_CHECK(Error);
    		while (m_xfer_done == false );
    		//Error = HAL_I2C_Master_Transmit(&hi2c1, (Data->Slave_Address)<<1, WData, 3, 100);
    
    		//Return -1 when there is error and return NRF_SUCCESS (= 0) when no error
    		if (Error != NRF_SUCCESS)
    		{
    			return -1;
    		}
    		else
    		{
    			return NRF_SUCCESS;
    		}
    	}
    
    	//Master sends I2C Read command and save the read data via pointer *Data.
    	//The function returns NRF_SUCCESS (=0) when no error and -1 when there is error.
    	int ReadI2C_Bus (struct TransferData *Data)
    	{
    		//Initialization of intial Error = 0
    		ret_code_t Error = 0;
    
    		//Send I2C Read Command as shown in Fig. 10 in the Datasheet Page 7 with Error checking.
    		/*Format defined by STM:
    		*HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c1, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout).
    		*HAL_I2C_Mem_Read () is used for reading data from the I2C device. It has been used instead of HAL_I2C_Master_Receive() because it sends restart condition
    		*while the latter which sends stop condition and thus not working. It takes following arguments:
    		*
    		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler.
    		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
    		*uint16_t MemAddress - is the Internal memory address in the slave which is the register address.
    		*uint16_t MemAddSize - the size of memory address (in Byte) which is the size of register address (In the case of Vishay's Sensor always 1 Byte).
    		*uint8_t *pData  - is the pointer to the data to be received.
    		*uint16_t Size  - is the size of the received data in bytes.
    		*uint32_t Timeout - timeout in millisecond in case of any error.
    		*Return Error.
    		*
    		*/
    		m_xfer_done = false;
    		Error = nrf_drv_twi_tx(&m_twi, Data->Slave_Address, &Data->RegisterAddress, 1, false);
    		APP_ERROR_CHECK(Error);
    	
    		while (m_xfer_done == false );
    	
    		m_xfer_done = false;
    		
    		/* Read from register */
    		Error = nrf_drv_twi_rx(&m_twi, Data->Slave_Address, Data->RData, 2);
    		APP_ERROR_CHECK(Error);
    	
    		while (m_xfer_done == false );
    		
    		//Error = HAL_I2C_Mem_Read(&hi2c1, (Data->Slave_Address)<<1, Data->RegisterAddress, 1,Data->RData,2,100);
    
    		//Return -1 when there is error and return NRF_SUCCESS (= 0) when no error
    		if (Error !=NRF_SUCCESS)
    		{
    			return -1;
    		}
    		else
    		{
    			return NRF_SUCCESS;
    		}
    	}
    
    	//Master sends I2C Gesture Read command and save the read data via pointer *Data.
    	//The function returns NRF_SUCCESS (=0) when no error and -1 when there is error.
        int ReadI2C_Bus_Gesture_Mode(struct GestureTransferData *Data)
    	{
    		//Initialization of intial Error = 0
    		ret_code_t Error = 0;
    
    		//Send I2C Gesture Read Command as shown in the Application Note Page 19 with Error checking.
    		/*Format defined by STM:
    		*HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout).
    		*HAL_I2C_Master_Receive () is used for reading data from the I2C device. It has been used instead of HAL_I2C_Mem_Read() because no restart condition is *needed.
    		*It takes following arguments:
    		*
    		*I2C_HandleTypeDef *hi2c - is the pointer to the i2c handler. hi2c1 is defined in the stm32f4xx_hal.h and being called here via extern I2C_HandleTypeDef *hi2c1;.
    		*uint16_t DevAddress - is the Address of the I2C device (Need to be shifted by 1 to left since the input of function expects 8 bits).
    		*uint8_t *pData  - is the pointer to the data to be received.
    		*uint16_t Size  - is the size of the received data in bytes (For Gesture Stream 6 Bytes are needed).
    		*uint32_t Timeout - timeout in millisecond in case of any error.
    		*Return Error.
    		*
    		*/
    		/*m_xfer_done = false;
    		err_code = nrf_drv_twi_tx(&m_twi, Data->Slave_Address, &Data->RegisterAddress, 1, false);
    		APP_ERROR_CHECK(err_code);
    	
    		while (m_xfer_done == false );*/
    	
    		m_xfer_done = false;
    		
    		/* Read from register */
    		Error = nrf_drv_twi_rx(&m_twi, Data->Slave_Address, Data->RData, 6);
    		APP_ERROR_CHECK(Error);
    	
    		while (m_xfer_done == false );
    		
    		//Error = HAL_I2C_Master_Receive(&hi2c1,(Data->Slave_Address)<<1,Data->RData,6,100);
    
    		//Return -1 when there is error and return NRF_SUCCESS (= 0) when no error
    		if (Error != NRF_SUCCESS)
    		{
    			return -1;
    		}
    		else
    		{
    			return NRF_SUCCESS;
    		}
    	}
    #endif

  • What a simple "solution"! Just to let you know, that if I have used the "no_stop" parameter as true , but only when calling the "reading" of the twi ! Indeed, on the write it does not applicable, so it is only used on "reading" ...

Related