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

nRF5340 unable to write with I2C_1 in Zephyr

I am attempting to write a basic I2C program using Zephyr, but I am currently unable to sent a single byte over I2C. Am I doing something wrong? I'd prefer not to use the nrfxlib. 

Here is a list of the presently known TWIM errata:
(https://infocenter.nordicsemi.com/topic/errata_nRF5340_EngA/ERR/nRF5340/EngineeringA/latest/err_340_new.html)

[21] TWIM: 1000 kbps baud rate is not functional
[37] TWIM: First clock pulse after clock stretching may be too long or too short
[47] TWIM: I2C timing spec is violated at 400 kHz
[82] TWIM: TWIM2 and TWIM3 are not functional

Platform: nRF5340 PDK
SDA: P0.30
SCL: P0.31

Project Configuration: 

CONFIG_NEWLIB_LIBC=y
CONFIG_PRINTK=y
CONFIG_HAS_SEGGER_RTT=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

# nRF5340 board config
CONFIG_TRUSTED_EXECUTION_SECURE=y

CONFIG_BT=n
CONFIG_TIMESLICING=n
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_DEEP_SLEEP_STATES=y
CONFIG_DEVICE_POWER_MANAGEMENT=y

CONFIG_I2C=y
CONFIG_I2C_1=y

main.c

#include <zephyr.h>
#include <device.h>
#include <sys/printk.h>
#include <drivers/i2c.h>

/* Application main Thread */
int main(void)
{
  struct device *p_i2c = device_get_binding("I2C_1");
  u32_t dev_config = I2C_SPEED_SET(I2C_SPEED_STANDARD) | I2C_MODE_MASTER;
  int res = i2c_configure(p_i2c, dev_config);
  if (res)
  {
    return res;
  }
  u8_t buf[2] = {0x0, 0xFF};
  i2c_burst_write(p_i2c, 0x77, 0x14, buf, 2);
  return 0;
}

Waveform:

Parents
  • First the HW related questions, do you have pull-ups turned on on the GPIOs? or do you have external pull up resistors? and what i2c device do you have connected to the bus? 

    Is i2c_burst_write() supported by the nrf5340? read https://docs.zephyrproject.org/apidoc/latest/group__i2c__interface_gae57ea5a1b4059e0d03d3bc068288de1f.html 

    I have been using this for simple transfers on the nrf52840 (previous generation nrf chip)

    int i;
    struct i2c_msg msgs[2];
    u8_t data[2][2];
    
    printf("pca9536_init().\n");
    i2c_dev = device_get_binding("I2C_1");
    if (!i2c_dev) {
    printf("pca9536_init(): I2C Device driver not found.\n");
    return;
    }
    // set the pins to outputs, and drive high
    data[0][0] = 3; // config register
    data[0][1] = 0xFF^((1<<PCA9536_RED_LED_PIN)|(1<<PCA9536_GREEN_LED_PIN)|(1<<PCA9536_BLUE_LED_PIN)); // 3 outputs
    msgs[0].buf = data[0];
    msgs[0].len = 2U;
    msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
    
    data[1][0] = 1; // output register
    data[1][1] = pca9536_state;
    msgs[1].buf = data[1];
    msgs[1].len = 2U;
    msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
    i = i2c_transfer(i2c_dev, &msgs[0], 2, (u8_t)PCA9536_I2C_ADDR);
    if (i!=0) {
    printf("pca9536_init(): transfer failed.\n");
    return;
    }

  • After more debugging, I found that the location in which the code is failing is when the i2c_nrfx_twim.c driver calls k_sem_take in the following code. In the snippet it is line 36. It seems to work everytime if I step across that line with the debugger. I think there is somehow a timing issue.

    i2c_nrfx_twim.c

    static int i2c_nrfx_twim_transfer(struct device *dev, struct i2c_msg *msgs,
    				  u8_t num_msgs, u16_t addr)
    {
    	int ret = 0;
    
    	k_sem_take(&(get_dev_data(dev)->transfer_sync), K_FOREVER);
    	nrfx_twim_enable(&get_dev_config(dev)->twim);
    
    	for (size_t i = 0; i < num_msgs; i++) {
    		if (I2C_MSG_ADDR_10_BITS & msgs[i].flags) {
    			ret = -ENOTSUP;
    			break;
    		}
    
    		nrfx_twim_xfer_desc_t cur_xfer = {
    			.p_primary_buf  = msgs[i].buf,
    			.primary_length = msgs[i].len,
    			.address	= addr,
    			.type		= (msgs[i].flags & I2C_MSG_READ) ?
    					  NRFX_TWIM_XFER_RX : NRFX_TWIM_XFER_TX
    		};
    		nrfx_err_t res = nrfx_twim_xfer(&get_dev_config(dev)->twim,
    					       &cur_xfer,
    					       (msgs[i].flags & I2C_MSG_STOP) ?
    					       0 : NRFX_TWIM_FLAG_TX_NO_STOP);
                    if (res != NRFX_SUCCESS) {
    			if (res == NRFX_ERROR_BUSY) {
    				ret = -EBUSY;
    				break;
    			} else {
    				ret = -EIO;
    				break;
    			}
    		}
                    
    		k_sem_take(&(get_dev_data(dev)->completion_sync), K_FOREVER);
    
    		res = get_dev_data(dev)->res;
    		if (res != NRFX_SUCCESS) {
    			LOG_ERR("Error %d occurred for message %d", res, i);
    			ret = -EIO;
    			break;
    		}
    	}
    
    	nrfx_twim_disable(&get_dev_config(dev)->twim);
    	k_sem_give(&(get_dev_data(dev)->transfer_sync));
    
    	return ret;
    }
     

Reply
  • After more debugging, I found that the location in which the code is failing is when the i2c_nrfx_twim.c driver calls k_sem_take in the following code. In the snippet it is line 36. It seems to work everytime if I step across that line with the debugger. I think there is somehow a timing issue.

    i2c_nrfx_twim.c

    static int i2c_nrfx_twim_transfer(struct device *dev, struct i2c_msg *msgs,
    				  u8_t num_msgs, u16_t addr)
    {
    	int ret = 0;
    
    	k_sem_take(&(get_dev_data(dev)->transfer_sync), K_FOREVER);
    	nrfx_twim_enable(&get_dev_config(dev)->twim);
    
    	for (size_t i = 0; i < num_msgs; i++) {
    		if (I2C_MSG_ADDR_10_BITS & msgs[i].flags) {
    			ret = -ENOTSUP;
    			break;
    		}
    
    		nrfx_twim_xfer_desc_t cur_xfer = {
    			.p_primary_buf  = msgs[i].buf,
    			.primary_length = msgs[i].len,
    			.address	= addr,
    			.type		= (msgs[i].flags & I2C_MSG_READ) ?
    					  NRFX_TWIM_XFER_RX : NRFX_TWIM_XFER_TX
    		};
    		nrfx_err_t res = nrfx_twim_xfer(&get_dev_config(dev)->twim,
    					       &cur_xfer,
    					       (msgs[i].flags & I2C_MSG_STOP) ?
    					       0 : NRFX_TWIM_FLAG_TX_NO_STOP);
                    if (res != NRFX_SUCCESS) {
    			if (res == NRFX_ERROR_BUSY) {
    				ret = -EBUSY;
    				break;
    			} else {
    				ret = -EIO;
    				break;
    			}
    		}
                    
    		k_sem_take(&(get_dev_data(dev)->completion_sync), K_FOREVER);
    
    		res = get_dev_data(dev)->res;
    		if (res != NRFX_SUCCESS) {
    			LOG_ERR("Error %d occurred for message %d", res, i);
    			ret = -EIO;
    			break;
    		}
    	}
    
    	nrfx_twim_disable(&get_dev_config(dev)->twim);
    	k_sem_give(&(get_dev_data(dev)->transfer_sync));
    
    	return ret;
    }
     

Children
Related