Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Getting Data from I2C via Interrupt With MPU-6050

Hi all, I am going through the beginner course and am using the MPU-6050 over I2C. I am able to communicate with the board where I get the WHO_AM_I. I can also read from the sensor's accel registers. However, when trying to incorporate what I have learned about interrupts, I can't seem to get it right.

I hooked up pin 25 on my nRF52DK evaluation board to the INT pin on the MPU-6050's breakout board. I am using this datasheet to configure the interrupt and FIFO registers. I think I am doing everything I need to in the enable_fifo and enable_interrupt functions, but I must be missing something. Basically, I am getting no interrupts triggered from the sensor so my callback never gets called. I did try testing it by connecting pin 25 to VDD and it did in fact trigger.

Here is my code:

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
// #include <zephyr/drivers/uart.h>
#include <zephyr/drivers/gpio.h>
/* STEP 3 - Include the header file of the I2C API */
#include <zephyr/drivers/i2c.h>
/* STEP 4.1 - Include the header file of printk() */
#include <zephyr/sys/printk.h>
#include "i2c_regs.h"

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 1000

static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C0_NODE);
static const struct gpio_dt_spec int_pin = GPIO_DT_SPEC_GET(INT_PIN, gpios);
static struct gpio_callback cb_data;
static sensor_regs sr = {
	.fifo_count_h = I2C_FIFO_COUNT_H,
	.fifo_count_l = I2C_FIFO_COUNT_L,
	.fifo_rw = I2C_FIFO_RW_REG,
	.int_clear = I2C_INT_STATUS};

void read_from_fifo(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	/* STEP 10 - Read from sensor */
	printk("INT!\n\r");
	uint8_t buf[3] = {0};
	// 1. get fifo count
	int res = i2c_write_read_dt(&dev_i2c, &sr.fifo_count_h, 1, &buf, 1);
	if (res == 0)
	{
		res = i2c_write_read_dt(&dev_i2c, &sr.fifo_count_l, 1, &buf[1], 1);
		if (res == 0)
		{
			uint16_t bytes_to_read = (uint16_t)buf[0] << 8 | buf[1];
			printk("bytes to read: %d\n\r", bytes_to_read);
			// 2. burst read that amount
			uint8_t sensor_data[bytes_to_read];
			res = i2c_write_read_dt(&dev_i2c, &sr.fifo_rw, 1, &sensor_data, bytes_to_read);
			if (res == 0)
			{
				printk("no idea what i am printing here: %x\n\r", sensor_data[0]);
			}
			else
			{
				printk("something dumb happened in when reading from fifo\n\r");
			}
		}
		else
		{
			printk("something dumb happened in int low byte\n\r");
		}
	}
	else
	{
		printk("something dumb happened in int high byte\n\r");
	}
	// clear the int:
	res = i2c_write_read_dt(&dev_i2c, &sr.int_clear, 1, &buf[2], 1);
	if (res == 0)
	{
		printk("int cleared %x\n\r", buf[2]);
	}
	else
	{
		printk("failed to clear int code: %d\n\r", res);
	}
	/////////////////////////////////////////////////////////// approach 2: read directly from sensor regs without fifo
	// uint8_t reg[2] = {I2C_READ_X_HIGH, I2C_READ_X_LOW};
	// int res = i2c_write_read_dt(&dev_i2c, &reg[0], 1, &buf, 1);
	// if (res == 0)
	// {
	// 	int res = i2c_write_read_dt(&dev_i2c, &reg[1], 1, &buf[1], 1);
	// 	if (res == 0)
	// 	{
	// 		printk("High x: %x and low: %x\n\r", buf[0], buf[1]);
	// 	}
	// 	else
	// 	{
	// 		printk("LOW byte x error %d\n\r", res);
	// 	}
	// }
	// else
	// {
	// 	printk("high byte x error %d\n\r", res);
	// }
	////////////////////////////////////approach 3: just read int status reg to see what kind of int?
	
	// uint8_t reg[1] = {I2C_INT_STATUS};
	// int res = i2c_write_read_dt(&dev_i2c, &reg, 1, &buf, 1);
	// if (res == 0){
	// 	printk("int status: %x\n\r", buf[0]);
	// }else{
	// 	printk("dead %d \n\r", res);
	// }
	printk("leaving now\n\r");
}

int enable_interrupts()
{
	uint8_t sensor_config_regs[2] = {0};
	int res;
	sensor_config_regs[0] = I2C_INT_EN;
	sensor_config_regs[1] = 0x1; // enable data ready interrupt
	printk("Enabling INT \n\r");
	res = i2c_write_dt(&dev_i2c, sensor_config_regs, 2);
	if (res != 0)
	{
		printk("could not enable INT!\n\r"); // maybe replace w log?
		return -1;
	}
	sensor_config_regs[0] = I2C_INT_REG;
	// sensor_config_regs[1] = 0x20; // 00110000 - enable int latch, clear on any read OR 0x20 for int latch with clear on status read only
	sensor_config_regs[1] = 0x40; // 0100 0000 - enable int latch, clear on any read, open drain instead of push pull
	printk("writing INT config\n\r");
	res = i2c_write_dt(&dev_i2c, sensor_config_regs, 2);
	if (res != 0)
	{
		printk("could not enable interrupts!\n\r"); // maybe replace w log?
		return -1;
	}
	return 0;
}

int enablefifo()
{
	uint8_t sensor_config_regs[2] = {0};
	int res;
	sensor_config_regs[0] = I2C_FIFO_EN_REG;
	sensor_config_regs[1] = 0x80; // 10000000 - enable fifo, nothing else.
	printk("enabling fifo\n\r");
	res = i2c_write_dt(&dev_i2c, sensor_config_regs, 2);
	if (res != 0)
	{
		printk("could not enable fifo!\n\r"); // maybe replace w log?
		return -1;
	}
	sensor_config_regs[0] = I2C_FIFO_DATA_EN_REG;
	sensor_config_regs[1] = 0x8; // 00001000: ACCEL_XOUT_H, ACCEL_XOUT_L,ACCEL_YOUT_H, ACCEL_YOUT_L, ACCEL_ZOUT_H, and ACCEL_ZOUT_L (Registers 59 to 64) to be written into the FIFO buffer.
	printk("writing FIFO config\n\r");
	res = i2c_write_dt(&dev_i2c, sensor_config_regs, 2);
	if (res != 0)
	{
		printk("could not enable accel data in fifo!\n\r"); // maybe replace w log?
		return -1;
	}
	return 0;
}
int main(void)
{
	printk("peeps\n\r");
	/* STEP 7 - Retrieve the API-specific device structure and make sure that the device is
	 * ready to use  */
	if (!device_is_ready(dev_i2c.bus))
	{
		printk("I2C bus %s not ready\n\r", dev_i2c.bus->name);
		return -1;
	}
	if (!device_is_ready(int_pin.port))
	{
		printk("gpio pin %d not ready\n\r", int_pin.pin);
		return -1;
	}
	int res = gpio_pin_configure_dt(&int_pin, GPIO_INPUT);
	if (res < 0)
	{
		return -1;
	}

	uint8_t value;
	uint8_t setup_buf[3] = {0};
	// uint8_t sensor_config_regs[2] = {I2C_PWR_MGMT_REG, 0x0}; // 0x80 if 10000000 ( reset on, sleep and everything else off, last 0 is clock select - internal)
	printk("Waking processor. (writing to powermanagement register.)\n\r");
	value = 0x0;
	res = i2c_reg_write_byte_dt(&dev_i2c, I2C_PWR_MGMT_REG, value);
	if (res != 0)
	{
		printk("could not power on device\n\r"); // maybe replace w log?
		return -1;
	}

	printk("reading who_am_i: ");
	uint8_t sensor_config_regs[2] = {I2C_WHO_AM_I, 0};
	i2c_write_read_dt(&dev_i2c, &sensor_config_regs, 1, &setup_buf[0], 1);
	if (setup_buf[0] != I2C_ADDR)
	{
		printk("expecting 0x68 and got: %x\n\r", setup_buf[0]);
		return -1;
	}
	else
	{
		printk("0x%02hhX == 0x%02hhX\n\r", I2C_ADDR, setup_buf[0]);
	}

	if (enable_interrupts() != 0){
		return -1;
	}
	if (enablefifo() !=0){
		return -1;
	}

	printk("devices are ready. Registering interrupt callback\n\r");
	res = gpio_pin_interrupt_configure_dt(&int_pin, GPIO_INT_EDGE_TO_ACTIVE);
	gpio_init_callback(&cb_data, read_from_fifo, BIT(int_pin.pin));
	gpio_add_callback(int_pin.port, &cb_data);
	printk("configured, lets loop!\n\r");
	while (1)
	{
		k_msleep(SLEEP_TIME_MS);
	}
}

My header file:

#include <zephyr/kernel.h>

#define I2C_ADDR 0x68 //7 bit addr: 1101000, where last bit is tied to ground via AD0
#define I2C_PWR_MGMT_REG 0x6B
#define I2C_INT_REG 0x37
#define I2C_INT_EN 0x38
#define I2C_INT_STATUS 0x3A //used to clear int by reading
#define I2C_FIFO_EN_REG 0x6A
#define I2C_FIFO_DATA_EN_REG 0x23
#define I2C_FIFO_COUNT_H 0x72
#define I2C_FIFO_COUNT_L 0x73
#define I2C_FIFO_RW_REG 0x74
#define I2C_WHO_AM_I 0x75
#define I2C_READ_X_HIGH 0x3B
#define I2C_READ_X_LOW 0x3C
#define I2C_READ_Y_HIGH 0x3D
#define I2C_READ_Y_LOW 0x3E
#define I2C_READ_Z_HIGH 0x3F
#define I2C_READ_Z_LOW 0x40
/* STEP 6 - Get the node identifier of the sensor */
#define I2C0_NODE DT_NODELABEL(mysensor)
#define INT_PIN DT_NODELABEL(intpin)

int enablefifo(void);
int enable_interrupts(void);

typedef struct {
		uint8_t fifo_count_h;
		uint8_t fifo_count_l;
		uint8_t fifo_rw;
		uint8_t int_clear;
    }sensor_regs;

Here is my overlay file:

&i2c0 {  
    mysensor: mysensor@68{
        compatible = "i2c-device";
        status = "okay";
        reg = < 0x68 >;
    };
};

/ {
    
    userpin {
        compatible = "gpio-keys";
        intpin: intpini2c {
            gpios = <&gpio0 25 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
            label = "Pin tied to INT pin output from I2C sensor.";
        };
    };
};

Any help would be appreciated! 

Related