Do I need pull-up resistors when using I2C or does the driver set them up in software?

I'm pretty new to embedded development/electronics and I'm trying to interface with a I2C pressure sensor, but I can't seem to communicate with it. I'm using nrf52840dk board and nrf Connect sdk / toolchain v2.7.0.

At first, I just connected the sensor directly to the i2c0 pins. Clock (SCL) on P0.27 and data (SDA) on P0.26. I also connected the sensor's GND/VDD wires to the boards GND/VDD inputs. Finally, the sensor has two modes (sleep and awake). there is a "PD" wire that is supposed to "wake up" the sensor when it's LOW, so I connected it to P1.10.

So it looks like this:

nrf52840     sensor
--------     ------
P0.27        SCL
P0.26        SDA
P1.10        PD
GND          GND
VDD          VDD

I tried to communicate with the sensor using i2c device but it always errors out when using the `i2c_write_read_dt` function.

I then proceeded to learn a bit more about i2c and realized that the SDA/SCL lines need to be pulled up with an external resistor to VDD. Is that correct? Is it possible to do it in software? If not, what resistor values should I use? 

Also, does the GPIO pin p1.10 need to use an external resistor as well? (see code below) 

Thanks

For reference, here is my code so far:

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   1000

// read-only
// main data channel registers, signed number
#define PRESSURE_SENSOR_PDATA_23_16_REG 0x06
#define PRESSURE_SENSOR_PDATA_15_8_REG  0x07
#define PRESSURE_SENSOR_PDATA_7_0_REG   0x08

/* Get the node identifier of the sensor */
#define I2C_NODE DT_NODELABEL(pressure_sensor)

#if !DT_NODE_EXISTS(DT_NODELABEL(pressure_sensor_wake))
#error "Overlay for power output node not properly defined."
#endif
static const struct gpio_dt_spec pressure_sensor_wake =
	GPIO_DT_SPEC_GET_OR(DT_NODELABEL(pressure_sensor_wake), gpios, {0});

/* Retrieve the API-specific device structure and make sure that the device is ready to use  */
static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C_NODE);

int main(void)
{
	int ret;

	if (!gpio_is_ready_dt(&pressure_sensor_wake)) {
		printk("The pressure sensor wake pin GPIO port is not ready.\n");
		return -1;
	}

	printk("Initializing pin with active level.\n");

	ret = gpio_pin_configure_dt(&pressure_sensor_wake, GPIO_OUTPUT_ACTIVE);
	if (ret != 0) {
		printk("error");
		printf("Configuring GPIO pin failed: %d\n", ret);
		return -1;
	}
    
	ret = device_is_ready(dev_i2c.bus);
	if (!ret) {
		printk("error");
		printf("I2C bus %s is not ready!\n",dev_i2c.bus->name);
		return -1;
	}

	ret = i2c_configure(dev_i2c.bus, I2C_SPEED_SET(I2C_SPEED_FAST));
	if (ret != 0) {
		printf("I2C configuration failed!\n",dev_i2c.bus->name);
		return -1;
	}

	while (1) {
        /* Read the data from the sensor */
		uint8_t pdata_regs_data[3]= {0};
		uint8_t pdata_regs[3] ={PRESSURE_SENSOR_PDATA_23_16_REG, PRESSURE_SENSOR_PDATA_15_8_REG, PRESSURE_SENSOR_PDATA_7_0_REG};
		ret = i2c_write_read_dt(&dev_i2c,&pdata_regs[0],1,&pdata_regs_data[0],1);
		if(ret != 0){
			printf("Failed to write/read I2C device address %x at Reg. %x \n", dev_i2c.addr, pdata_regs[0]);
		}
		ret = i2c_write_read_dt(&dev_i2c,&pdata_regs[1],1,&pdata_regs_data[1],1);
		if(ret != 0){
			printf("Failed to write/read I2C device address %x at Reg. %x \n", dev_i2c.addr, pdata_regs[1]);
		}
		ret = i2c_write_read_dt(&dev_i2c,&pdata_regs[2],1,&pdata_regs_data[2],1);
		if(ret != 0){
			printf("Failed to write/read I2C device address %x at Reg. %x \n", dev_i2c.addr, pdata_regs[2]);
		}
		// Print reading to console  
		printf("PDATA_23_16: %x\n", pdata_regs_data[0]);
		printf("PDATA_15_8: %x\n", pdata_regs_data[1]);
		printf("PDATA_7_0: %x\n", pdata_regs_data[2]);

		k_msleep(SLEEP_TIME_MS);
	}
}

Parents Reply Children
  • Hi,

     

    Glad to hear that you got it working.

    To enable internal pull resistors, you need to set this in the device tree node by adding "bias-pull-up;":

    &pinctrl {
    	new_pinctrl_default: new_pinctrl_default {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 26)>,
    				<NRF_PSEL(TWIM_SCL, 0, 27)>;
    			bias-pull-up;
    		};
    	};
    
    	new_pinctrl_sleep: new_pinctrl_sleep {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 26)>,
    				<NRF_PSEL(TWIM_SCL, 0, 27)>;
    			low-power-enable;
    		};
    	};    
    };
    
    &i2c0 {
        ...
    	pinctrl-0 = <&new_pinctrl_default>;
    	pinctrl-1 = <&new_pinctrl_sleep>;
    	pinctrl-names = "default", "sleep";
    };

     

    Please note that the internal pull resistors are too weak for 400 kHz operation, but will work for 100 kHz i2c transfers.

     

    Kind regards,

    Håkon

Related