TWIM (I2C) on NCS is not working properly.

First of all, the fast mode should be 400kHz, but it seems to be only 200kHz according to the waveform. Also, the duty cycle seems to be wrong.

Secondly, It seems that CONFIG_I2C_CALLBACK is not working at all. I have written the callback function as follows, but it is not called.

/* Callback */
K_SEM_DEFINE(k_sem, 0, 1);
static void twim_callback(const struct device *dev, int result, void *data)
{
    printk("Callback\n");
    k_sem_give(&k_sem);
}
That is obvious since there is no log display and no semaphore to go through.
I am working on a project using TWIM and I hope these bugs will be fixed soon.
  • Hello,

    Thank you for reporting this issue. I will do testing on it and will get back to you.

    Best regards,

    Michal

  • Thank you for reply and best regards.

    Yes, I just remembered. There are still a few things that seem to be glitches.

    1. Both SCL and SDA keep low level till the first TWI command executed if no I2C device is connected. Both SCL and SDA change high level after the first TWI command is executed. I guess the internal pull-up is enabled after the first command is executed.

    2. If 'nrf-twi' is specified in DeviceTree, the TWI read command(waveform) is not output. if 'nrf-twim' is specified, the TWI read command is output.

    I would appreciate it if you could look into this problem.

  • Sorry for the delays, I am still looking at it and will return to you as soon as possible. If there have been any changes in the meantime please let me know.

    Which NCS version are you using?

    Best regards,

    Michal

  • I have tried to use fast mode on NCS 2.1.2 using the I2C Fujitsu FRAM example and I got the correct 400 kHz.

    2.48µS period corresponds to 403 kHz.

    I haven't looked at the callback yet, but if you have any minimal code for me to test that would be helpful.

    1. Both SCL and SDA keep low level till the first TWI command executed if no I2C device is connected. Both SCL and SDA change high level after the first TWI command is executed. I guess the internal pull-up is enabled after the first command is executed.

    Good catch, I have managed to reproduce it. I will report this issue.

    2. If 'nrf-twi' is specified in DeviceTree, the TWI read command(waveform) is not output. if 'nrf-twim' is specified, the TWI read command is output.

    I am not exactly sure about it, but TWI and TWIM are two different peripherals on the device.

    They may require different configurations, you can read more at for example nordic,nrf-twi and nordic,nrf-twim.

    Best regards,

    Michal

  • Thanks for reply.

    I'm using NCS 2.2.0.

    > I got the correct 400 kHz.

    May I have the source code to implement it? My source code to implement it is the below. I try with 'nrf-twi'

     instead of 'nrf-twim', but the waveform still seems to be 200 kHz.

    Here is the TWI and TWIM projects, the difference of both projects include app.overlay or not.

    TWI (An error will occur with read command)

    TWIM 

    Source code is below. (Included in zip file)

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/i2c.h>
    #include "defines_ICM2064x.h"
    
    const uint8_t INIT_CMD[] = {
        UB0_PWR_MGMT_1,
        WAIT_MSEC,
        UB0_USER_CTRL,
        UB0_INT_PIN_CFG,
        UB0_PWR_MGMT_1,
        UB0_LP_CONFIG,
        UB0_REG_BANK_SEL,
        UB2_GYRO_SMPLRT_DIV,
        UB2_GYRO_CONFIG_1,
        UB2_GYRO_CONFIG_2,
        UB2_ACCEL_SMPLRT_DIV_1,
        UB2_ACCEL_SMPLRT_DIV_2,
        UB2_ACCEL_CONFIG,
        UB2_REG_BANK_SEL,
        UB0_PWR_MGMT_1,
    };
    const uint8_t INIT_PARAM[] = {
        PWR_MGMT_1_DEVICE_RESET,
        0x64,
        0x00,
        INT_PIN_CFG_BYPASS_EN,
        PWR_MGMT_1_CLKSEL_AUTO,
        LP_CONFIG_ACCEL_CYCLE | LP_CONFIG_GYRO_CYCLE,
        REG_BANK_SEL_USER_BANK_2,
        0x04,
        GYRO_CONFIG_1_GYRO_DLPFCFG_0 | GYRO_CONFIG_1_GYRO_FS_SEL_FULL | GYRO_CONFIG_1_GYRO_FS_SEL_GYRO_FCHOICE,
        0x00,
        0x00,
        0x04,
        ACCEL_CONFIG_ACCEL_FS_SEL_FULL,
        REG_BANK_SEL_USER_BANK_0,
        PWR_MGMT_1_LP_EN | PWR_MGMT_1_CLKSEL_AUTO,
    };
    
    static int twim_write(const struct device *i2c_dev, uint8_t addr, uint8_t *data, uint32_t num_bytes)
    {
    	struct i2c_msg msgs;
    
    	/* Data to be written, and STOP after this. */
    	msgs.buf = data;
    	msgs.len = num_bytes;
    	msgs.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
    
    	return i2c_transfer(i2c_dev, &msgs, 1, addr);
    }
    
    static int twim_read(const struct device *i2c_dev, uint8_t addr, uint8_t reg, uint8_t *data, uint32_t num_bytes)
    {
    	struct i2c_msg msgs[2] = {0};
    
    	/* Send the address to read from */
    	msgs[0].buf = ®
    	msgs[0].len = 1U;
    	msgs[0].flags = I2C_MSG_WRITE;
    
    	/* Read from device. STOP after this. */
    	msgs[1].buf = data;
    	msgs[1].len = num_bytes;
    	msgs[1].flags = I2C_MSG_READ | I2C_MSG_STOP;
    
    	return i2c_transfer(i2c_dev, &msgs[0], 2, addr);
    }
    
    void main(void)
    {
    	int err;
    	const struct device *i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c0));
    	uint8_t tx_buf[16] = {0};
    	uint8_t rx_buf[16] = {0};
    	int16_t accel[3], gyro[3];
    
    	if (!device_is_ready(i2c_dev)) {
    		printk("I2C: Device is not ready.\n");
    		return;
    	}
    
    	// Configure
    	// uint32_t i2c_cfg = (I2C_MODE_CONTROLLER | I2C_SPEED_SET(I2C_SPEED_FAST));
        // i2c_configure(i2c_dev, i2c_cfg);
    
    	for (int i = 0; i < sizeof(INIT_CMD); i++) {
    		if (INIT_CMD[i] == WAIT_MSEC) {
    			// Wait
    			k_sleep(K_MSEC(INIT_PARAM[i]));
    		} else {
    			// Transfer
    			tx_buf[0] = INIT_CMD[i];
    			tx_buf[1] = INIT_PARAM[i];
    			// write
    			err = twim_write(i2c_dev, ICM2064x_ADDR0, tx_buf, 2);
    			if (err) {
    				printk("Error writing to device! error code (%d)\n", err);
    				return;
    			}
    		}
    	}
    
    	for (;;) {
    		err = twim_read(i2c_dev, ICM2064x_ADDR0, UB0_ACCEL_XOUT_H, rx_buf, 12);
    		if (err) {
    			printk("Error reading from device! error code (%d)\n", err);
    			return;
    		}
    
    		accel[0] = (rx_buf[0] << 8) | rx_buf[1];
    		accel[1] = (rx_buf[2] << 8) | rx_buf[3];
    		accel[2] = (rx_buf[4] << 8) | rx_buf[5];
    		gyro[0] = (rx_buf[6] << 8) | rx_buf[7];
    		gyro[1] = (rx_buf[8] << 8) | rx_buf[9];
    		gyro[2] = (rx_buf[10] << 8) | rx_buf[11];
    		printk("ax = %d, ay = %d, az = %d, gx = %d, gy = %d, gz = %d\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
    	}
    }
    
    

    Callback test code is not included on both projects. If you need, I will attach project this coversation. 

    Thank you.

Related