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

gpio_init_callback and gpio_pin_interrupt_configure issues

Hi all,

I am trying to write a driver for a TMP116 temp sensor based off the existing Zephyr TMP116 driver. I have added in functionality to enable the interrupt pin to enable a callback function to run when the interrupt pin is triggered. I have done this with success on a few other temperature sensors but I am running into an issue where my nRF5340 is not triggering/running the callback in this particular instance. I have the same code running in a similar temp sensor driver I wrote and have no issues there.

I will post my project files below:

nrf5340dk_nrf5340_cpuapp.overlay : 

&i2c1 {
    compatible = "nordic,nrf-twim";
	status = "okay";
	sda-pin = < 34 >;
	scl-pin = < 35 >;
    clock-frequency = <I2C_BITRATE_STANDARD>;

	ti_tmp116: tmp116@48 {
		compatible = "ti,tmp116";
		reg = <0x48>;
		label = "TMP116";
        drdy-gpios = <&gpio0 36 1>;
	};
	
};

prj.conf : 

# Basic Config Stuff
CONFIG_LOG=y
CONFIG_SERIAL=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_I2C_NRFX=y
CONFIG_SENSOR=y
CONFIG_SENSOR_LOG_LEVEL_WRN=y
CONFIG_GPIO=y

CONFIG_SOC_SERIES_NRF53X=y
CONFIG_ARM_MPU=y

CONFIG_ASSERT=n 	# validation


CONFIG_TMP116=y


# Zephyr Device Power Management
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_PM=y
CONFIG_PM_DEVICE=y
# Required to disable default behavior of deep sleep on timeout
#CONFIG_PM_STATE_LOCK=y 			# No longer needed as of NCS 1.6
#CONFIG_PM_DEEP_SLEEP_STATES=y		# No longer needed as of NCS 1.6


CONFIG_CBPRINTF_FP_SUPPORT=y

#CONFIG_BT=y

CMakeLists.txt : 

cmake_minimum_required(VERSION 3.13.1)

if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mcuboot.conf")
  set(mcuboot_CONF_FILE
    prj.conf
    ${CMAKE_CURRENT_LIST_DIR}/mcuboot.conf
  )
endif()

if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/spm.conf")
  set(spm_CONF_FILE
    prj.conf
    ${CMAKE_CURRENT_LIST_DIR}/spm.conf
  )
endif()

set(TEST_DTC_OVERLAY_FILE
  ${CMAKE_CURRENT_SOURCE_DIR}/nrf5340dk_nrf5340_cpuapp.overlay
)

set(PRJ_CONF_FILE
  prj.conf
  ${CMAKE_CURRENT_LIST_DIR}/prj.conf
)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hub_control)
zephyr_include_directories(include)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

tmp116.c : 

/*
 * Copyright (c) 2019 Centaur Analytics, Inc
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT ti_tmp116

#include <device.h>
#include <drivers/i2c.h>
#include <drivers/sensor.h>
#include <sys/util.h>
#include <sys/byteorder.h>
#include <sys/__assert.h>
#include <logging/log.h>
#include <kernel.h>

#include <drivers/gpio.h>

#include "tmp116.h"

#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
LOG_MODULE_REGISTER(TMP116);


static void ti_tmp_gpio_callback(const struct device *dev,
				  struct gpio_callback *cb, uint32_t pins)
{
	struct tmp116_data *data =
		CONTAINER_OF(cb, struct tmp116_data, gpio_cb);  

        printf("ti_tmp_gpio_callback!\n");

	ARG_UNUSED(pins);

    	gpio_pin_interrupt_configure(data->gpio, DT_INST_GPIO_PIN(0, drdy_gpios), GPIO_INT_DISABLE);
	k_sem_give(&data->data_sem);
}


static int tmp116_reg_read(const struct device *dev, uint8_t reg,
			   uint16_t *val)
{
	struct tmp116_data *drv_data = dev->data;
	const struct tmp116_dev_config *cfg = dev->config;

	if (i2c_burst_read(drv_data->i2c, cfg->i2c_addr, reg, (uint8_t *)val, 2)
	    < 0) {
		return -EIO;
	}

	*val = sys_be16_to_cpu(*val);

	return 0;
}

static int tmp116_reg_write(const struct device *dev, uint8_t reg,
			    uint16_t val)
{
	struct tmp116_data *drv_data = dev->data;
	const struct tmp116_dev_config *cfg = dev->config;
	uint8_t tx_buf[3] = {reg, val >> 8, val & 0xFF};

	return i2c_write(drv_data->i2c, tx_buf, sizeof(tx_buf),
			cfg->i2c_addr);
}

/**
 * @brief Check the Device ID
 *
 * @param[in]   dev     Pointer to the device structure
 * @param[in]	id	Pointer to the variable for storing the device id
 *
 * @retval 0 on success
 * @retval -EIO Otherwise
 */
static inline int tmp116_device_id_check(const struct device *dev, uint16_t *id)
{
	if (tmp116_reg_read(dev, TMP116_REG_DEVICE_ID, id) != 0) {
		LOG_ERR("%s: Failed to get Device ID register!",
			dev->name);
		return -EIO;
	}

	if ((*id != TMP116_DEVICE_ID) && (*id != TMP117_DEVICE_ID)) {
		LOG_ERR("%s: Failed to match the device IDs!",
			dev->name);
		return -EINVAL;
	}

	return 0;
}

static int tmp116_sample_fetch(const struct device *dev,
			       enum sensor_channel chan)
{
	struct tmp116_data *drv_data = dev->data;
	uint16_t value;
	int rc;

	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
			chan == SENSOR_CHAN_AMBIENT_TEMP);

	/* clear sensor values */
	drv_data->sample = 0U;


	gpio_pin_interrupt_configure(drv_data->gpio,
				     DT_INST_GPIO_PIN(0, drdy_gpios),
				     GPIO_INT_LEVEL_ACTIVE);

	k_sem_take(&drv_data->data_sem, K_FOREVER);

	// wait for the conversion to finish 
	k_msleep(TMP_CONVERSION_TIME);



	/* Get the most recent temperature measurement */
	rc = tmp116_reg_read(dev, TMP116_REG_TEMP, &value);
	if (rc < 0) {
		LOG_ERR("%s: Failed to read from TEMP register!",
			dev->name);
		return rc;
	}

	/* store measurements to the driver */
	drv_data->sample = (int16_t)value;

	return 0;
}

static int tmp116_channel_get(const struct device *dev,
			      enum sensor_channel chan,
			      struct sensor_value *val)
{
	struct tmp116_data *drv_data = dev->data;
	int32_t tmp;

	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
		return -ENOTSUP;
	}

	/*
	 * See datasheet "Temperature Results and Limits" section for more
	 * details on processing sample data.
	 */
	tmp = ((int16_t)drv_data->sample * (int32_t)TMP116_RESOLUTION) / 10;
	val->val1 = tmp / 1000000; /* uCelsius */
	val->val2 = tmp % 1000000;

	return 0;
}

static int tmp116_attr_set(const struct device *dev,
			   enum sensor_channel chan,
			   enum sensor_attribute attr,
			   const struct sensor_value *val)
{
	struct tmp116_data *drv_data = dev->data;
	int16_t value;

	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
		return -ENOTSUP;
	}

	switch (attr) {
	case SENSOR_ATTR_OFFSET:
		if (drv_data->id != TMP117_DEVICE_ID) {
			LOG_ERR("%s: Offset is only supported by TMP117",
			dev->name);
			return -EINVAL;
		}
		/*
		 * The offset is encoded into the temperature register format.
		 */
		value = (((val->val1) * 10000000) + ((val->val2) * 10))
						/ (int32_t)TMP116_RESOLUTION;

		return tmp116_reg_write(dev, TMP117_REG_TEMP_OFFSET, value);

	default:
		return -ENOTSUP;
	}
}

static const struct sensor_driver_api tmp116_driver_api = {
	.attr_set = tmp116_attr_set,
	.sample_fetch = tmp116_sample_fetch,
	.channel_get = tmp116_channel_get
};

static int tmp116_init(const struct device *dev)
{
	struct tmp116_data *drv_data = dev->data;
	const struct tmp116_dev_config *cfg = dev->config;
	uint16_t id;

        uint16_t  temp = 0;

	/* Bind to the I2C bus that the sensor is connected */
	drv_data->i2c = device_get_binding(cfg->i2c_bus_label);
	if (!drv_data->i2c) {
		LOG_ERR("Cannot bind to %s device!",
			cfg->i2c_bus_label);
		return -EINVAL;
	}

        //Wait for TMP116 POR
        k_sleep(K_MSEC(2));

	/* Check the Device ID */
	if (tmp116_device_id_check(dev, &id) != 0) {
		return -EIO;
	}
	LOG_DBG("Got device ID: %x", id);
	drv_data->id = id;


        // Configure Device Config Register (0x01) with therm mode 0x8290
	if (tmp116_reg_write(dev, TMP116_REG_CFGR, TMP116_CFGR_CFG)) {
		return -EIO;
	}

        //Configure Temperature Threshold HIGH (0x02) with TMP116_HIGH_LIM //High temp threshold for interrupt
	if (tmp116_reg_write(dev, TMP116_REG_HIGH_LIM, TMP116_HIGH_LIM)) {
		return -EIO;
	}

        /*
        //Configure Temperature Threshold LOW (0x03) with TMP116_LOW_LIM //Low temp threshold for interrupt (disabled)
	if (tmp116_reg_write(dev, TMP116_REG_LOW_LIM, TMP116_LOW_LIM)) {
		return -EIO;
	}
        */
        

	k_sem_init(&drv_data->data_sem, 0, UINT_MAX);

	/* setup data ready gpio interrupt */
	drv_data->gpio = device_get_binding(
				DT_INST_GPIO_LABEL(0, drdy_gpios));
	if (drv_data->gpio == NULL) {
		LOG_DBG("Failed to get pointer to %s device",
			 DT_INST_GPIO_LABEL(0, drdy_gpios));
		return -EINVAL;
	}

	gpio_pin_configure(drv_data->gpio, DT_INST_GPIO_PIN(0, drdy_gpios),
			   GPIO_INPUT | DT_INST_GPIO_FLAGS(0, drdy_gpios));

	gpio_init_callback(&drv_data->gpio_cb,
			   ti_tmp_gpio_callback,
			   BIT(DT_INST_GPIO_PIN(0, drdy_gpios)));

	if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) {
		LOG_DBG("Failed to set GPIO callback");
		return -EIO;
	}

	gpio_pin_interrupt_configure(drv_data->gpio,
					 DT_INST_GPIO_PIN(0, drdy_gpios),
					 GPIO_INT_LEVEL_ACTIVE);

        LOG_DBG("Init OK");
	return 0;
}

#define DEFINE_TMP116(_num) \
	static struct tmp116_data tmp116_data_##_num; \
	static const struct tmp116_dev_config tmp116_config_##_num = { \
		.i2c_addr = DT_INST_REG_ADDR(_num), \
		.i2c_bus_label = DT_INST_BUS_LABEL(_num) \
	}; \
	DEVICE_DT_INST_DEFINE(_num, tmp116_init, NULL,			\
		&tmp116_data_##_num, &tmp116_config_##_num, POST_KERNEL, \
		CONFIG_SENSOR_INIT_PRIORITY, &tmp116_driver_api)

DT_INST_FOREACH_STATUS_OKAY(DEFINE_TMP116);

tmp116.h : 

/*
 * Copyright (c) 2019 Centaur Analytics, Inc
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#ifndef ZEPHYR_DRIVERS_SENSOR_TMP116_TMP116_H_
#define ZEPHYR_DRIVERS_SENSOR_TMP116_TMP116_H_

#define TMP116_REG_TEMP		0x0
#define TMP116_REG_CFGR		0x1
#define TMP116_REG_HIGH_LIM		0x2
#define TMP116_REG_LOW_LIM		0x3
#define TMP116_REG_EEPROM_UL		0x4
#define TMP116_REG_EEPROM1		0x5
#define TMP116_REG_EEPROM2		0x6
#define TMP116_REG_EEPROM3		0x7
#define TMP117_REG_TEMP_OFFSET		0x7
#define TMP116_REG_EEPROM4		0x8
#define TMP116_REG_DEVICE_ID		0xF

#define TMP116_RESOLUTION		78125       /* in tens of uCelsius*/
#define TMP116_RESOLUTION_DIV		10000000

#define TMP116_DEVICE_ID		0x1116
#define TMP117_DEVICE_ID		0x0117

#define TMP116_CFGR_CFG     0x028C
#define TMP116_HIGH_LIM     0x8000
#define TMP116_LOW_LIM      0x0
#define TMP116_EEPROM_UL    0x8000

/* For conversion Temp */
#define TMP_CONVERSION_TIME     24

struct tmp116_data {
	const struct device *i2c;
	uint16_t sample;
	uint16_t id;

#if DT_INST_NODE_HAS_PROP(0, drdy_gpios)
	const struct device *gpio;
	struct gpio_callback gpio_cb;
	struct k_sem data_sem;
#endif  /* DT_INST_NODE_HAS_PROP(0, drdy_gpios) */
};

struct tmp116_dev_config {
	uint16_t i2c_addr;
	char *i2c_bus_label;
};

#endif /*  ZEPHYR_DRIVERS_SENSOR_TMP116_TMP116_H_ */

App code : 

void temp_entry_point(void) {
    printf("temp_entry_point!\n");

    int th_status = 0;

    //Allocate data for this event/node
    const int node_data_size = sizeof(TH_Event_t);
    char __aligned(4) data_buffer[MAX_INST_TH * node_data_size]; //create MAX_INST blocks, each "node_data_size" big, to store x struct TH_Event instances
    k_mem_slab_init(&data_slab_th, data_buffer, node_data_size, MAX_INST_TH);

    //Initialize HDC 2022 Temp and Humidity Sensor	
    printf("Running on %s!\n", CONFIG_ARCH);
    const struct device *dev2 = device_get_binding(DT_LABEL(DT_INST(0, ti_tmp116)));
    if (dev2 == NULL) {
		printf("Could not get %s device\n", DT_LABEL(DT_INST(0, ti_tmp116)));
		return;
	}
    printf("Temp Sensor %p name %s is ready!\n", dev2, dev2->name);

    //Program loop for temp/humidity thread
    while (1) {
      printf("START OF temp_entry_point LOOP\n");

      printf("Waiting for a TH threshold event\n");
      sensor_sample_fetch(dev2);

      //Gets latest data from sensor channel
      sensor_channel_get(dev2, SENSOR_CHAN_AMBIENT_TEMP, &temp);

      //Print the result 
      //printf("Time = %d ", dynamic_data->time);
      printf("Temp = %d.%06d C\n", temp.val1, temp.val2);

      //Record the event as a node in the SLL
      th_status = record_th_event(&data_slab_th);

      /*
      //Check to see if device is heating up (config temp sensor to interrupt on low temp and turn system off)
      if(th_status == 1) {
        
        //Set sensor to listen and interrupt on low temp
        sensor_attr_set(dev2,0,0,0);
        printf("Turning off device to listen for low temp interrupt\n");

        //Power off device and listen for interrupt

        // Configure to generate PORT event (wakeup) on temp sensor INT1 
        
        printk("Entering system off; generate interrupt from temp sensor to wake\n");
	nrf_gpio_cfg_input(12,
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(12,
			       NRF_GPIO_PIN_SENSE_LOW);
        
        //Force deep sleep
        pm_power_state_force((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
      } else if (th_status == 2){
        //Do something here to handle error/failure
        //TODO
      }
      */

      k_sleep(K_MSEC(1000));    
      printf("END OF temp_entry_point LOOP\n");
    }

}

Any advice would be greatly appreciated

Related