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