drivers: sensors: add driver for amg88xx sensor

This patch adds the driver for Panasonic AMG88xx
infrared array sensor.

The driver was developed within the framework of a student
internship in the development department. The task of the
student was to implement the trigger part of the driver,
transfer and visualization of the measured data.

Signed-off-by: Johann Fischer <j.fischer@phytec.de>
This commit is contained in:
Johann Fischer 2017-09-25 17:38:33 +02:00 committed by Anas Nashif
commit 476f05d2f8
7 changed files with 561 additions and 0 deletions

View file

@ -1,5 +1,6 @@
add_subdirectory_ifdef(CONFIG_ADXL362 adxl362)
add_subdirectory_ifdef(CONFIG_AK8975 ak8975)
add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx)
add_subdirectory_ifdef(CONFIG_APDS9960 apds9960)
add_subdirectory_ifdef(CONFIG_BMA280 bma280)
add_subdirectory_ifdef(CONFIG_BMC150_MAGN bmc150_magn)

View file

@ -42,6 +42,8 @@ source "drivers/sensor/adxl362/Kconfig"
source "drivers/sensor/ak8975/Kconfig"
source "drivers/sensor/amg88xx/Kconfig"
source "drivers/sensor/apds9960/Kconfig"
source "drivers/sensor/bma280/Kconfig"

View file

@ -0,0 +1,2 @@
zephyr_sources_ifdef(CONFIG_AMG88XX amg88xx.c)
zephyr_sources_ifdef(CONFIG_AMG88XX_TRIGGER amg88xx_trigger.c)

View file

@ -0,0 +1,94 @@
# Kconfig - AMG88XX infrared thermopile sensor configuration options
#
# Copyright (c) 2017 Phytec Messtechnik GmbH
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig AMG88XX
bool "AMG88XX Infrared Thermopile Sensor"
depends on I2C
default n
help
Enable driver for AMG88XX infrared thermopile sensor.
if AMG88XX
config AMG88XX_NAME
string "Driver name"
default "AMG88XX"
help
Device name identifying the AMG88XX sensor.
config AMG88XX_I2C_ADDR
hex "I2C address for AMG88XX Sensor"
default "0x68"
range 0x68 0x69
help
I2C address of the AMG88XX sensor.
0x68: AD-SELECT connected GND
0x69: AD-SELECT connected VDD
config AMG88XX_I2C_MASTER_DEV_NAME
string "I2C master where AMG88XX is connected"
default "I2C_0"
help
The I2C master device's name where the AMG88XX sensor is connected.
choice
prompt "Trigger mode"
default AMG88XX_TRIGGER_NONE
help
Specify the type of triggering used by the driver.
config AMG88XX_TRIGGER_NONE
bool "No trigger"
config AMG88XX_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select AMG88XX_TRIGGER
config AMG88XX_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select AMG88XX_TRIGGER
endchoice
config AMG88XX_TRIGGER
bool
config AMG88XX_GPIO_DEV_NAME
string "GPIO device"
default "GPIO_0"
depends on AMG88XX_TRIGGER
help
The GPIO device's name where the AMG88XX interrupt (alert) pin is
connected.
config AMG88XX_GPIO_PIN_NUM
int "Interrupt GPIO pin number"
default 0
depends on AMG88XX_TRIGGER
help
The GPIO pin number receiving the interrupt signal from the
AMG88XX sensor.
config AMG88XX_THREAD_PRIORITY
int "Thread priority"
depends on AMG88XX_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config AMG88XX_THREAD_STACK_SIZE
int "Thread stack size"
depends on AMG88XX_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif #if AMG88XX

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2017 Phytec Messtechnik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <device.h>
#include <i2c.h>
#include <gpio.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include <kernel.h>
#include <sensor.h>
#include <misc/__assert.h>
#include "amg88xx.h"
static int amg88xx_sample_fetch(struct device *dev, enum sensor_channel chan)
{
struct amg88xx_data *drv_data = dev->driver_data;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_TEMP);
if (i2c_burst_read(drv_data->i2c, CONFIG_AMG88XX_I2C_ADDR,
AMG88XX_OUTPUT_BASE,
(u8_t *)drv_data->sample,
sizeof(drv_data->sample))) {
return -EIO;
}
return 0;
}
static int amg88xx_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct amg88xx_data *drv_data = dev->driver_data;
size_t len = ARRAY_SIZE(drv_data->sample);
if (chan != SENSOR_CHAN_TEMP) {
return -ENOTSUP;
}
for (size_t idx = 0; idx < len; idx++) {
/* fix negative values */
if (drv_data->sample[idx] & (1 << 11)) {
drv_data->sample[idx] |= 0xF000;
}
val[idx].val1 = (((s32_t)drv_data->sample[idx]) *
AMG88XX_TREG_LSB_SCALING) / 1000000;
val[idx].val2 = (((s32_t)drv_data->sample[idx]) *
AMG88XX_TREG_LSB_SCALING) % 1000000;
}
return 0;
}
static int amg88xx_init_device(struct device *dev)
{
struct amg88xx_data *drv_data = dev->driver_data;
u8_t tmp;
if (amg88xx_reg_read(drv_data, AMG88XX_PCLT, &tmp)) {
SYS_LOG_ERR("Failed to read Power mode");
return -EIO;
}
SYS_LOG_DBG("Power mode 0x%02x", tmp);
if (tmp != AMG88XX_PCLT_NORMAL_MODE) {
if (amg88xx_reg_write(drv_data, AMG88XX_PCLT,
AMG88XX_PCLT_NORMAL_MODE)) {
return -EIO;
}
k_busy_wait(AMG88XX_WAIT_MODE_CHANGE_US);
}
if (amg88xx_reg_write(drv_data, AMG88XX_RST,
AMG88XX_RST_INITIAL_RST)) {
return -EIO;
}
k_busy_wait(AMG88XX_WAIT_INITIAL_RESET_US);
if (amg88xx_reg_write(drv_data, AMG88XX_FPSC, AMG88XX_FPSC_10FPS)) {
return -EIO;
}
SYS_LOG_DBG("");
return 0;
}
int amg88xx_init(struct device *dev)
{
struct amg88xx_data *drv_data = dev->driver_data;
drv_data->i2c = device_get_binding(CONFIG_AMG88XX_I2C_MASTER_DEV_NAME);
if (drv_data->i2c == NULL) {
SYS_LOG_ERR("Failed to get pointer to %s device!",
CONFIG_AMG88XX_I2C_MASTER_DEV_NAME);
return -EINVAL;
}
if (amg88xx_init_device(dev) < 0) {
SYS_LOG_ERR("Failed to initialize device!");
return -EIO;
}
#ifdef CONFIG_AMG88XX_TRIGGER
if (amg88xx_init_interrupt(dev) < 0) {
SYS_LOG_ERR("Failed to initialize interrupt!");
return -EIO;
}
#endif
return 0;
}
struct amg88xx_data amg88xx_driver;
static const struct sensor_driver_api amg88xx_driver_api = {
#ifdef CONFIG_AMG88XX_TRIGGER
.attr_set = amg88xx_attr_set,
.trigger_set = amg88xx_trigger_set,
#endif
.sample_fetch = amg88xx_sample_fetch,
.channel_get = amg88xx_channel_get,
};
DEVICE_AND_API_INIT(amg88xx, CONFIG_AMG88XX_NAME, amg88xx_init,
&amg88xx_driver, NULL,
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&amg88xx_driver_api);

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2017 Phytec Messtechnik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SENSOR_AMG88XX
#define _SENSOR_AMG88XX
#include <device.h>
#include <gpio.h>
#include <misc/util.h>
#define AMG88XX_I2C_ADDRESS CONFIG_AMG88XX_I2C_ADDR
#define AMG88XX_PCLT 0x00 /* Setting Power control register */
#define AMG88XX_RST 0x01 /* Reset register */
#define AMG88XX_FPSC 0x02 /* Setting frame rate register */
#define AMG88XX_INTC 0x03 /* Setting interrupt control register */
#define AMG88XX_STAT 0x04 /* Status register */
#define AMG88XX_SCLR 0x05 /* Status clear register */
#define AMG88XX_AVE 0x07 /* Setting verage register */
#define AMG88XX_INTHL 0x08 /* Interrupt level upper limit [7:0] */
#define AMG88XX_INTHH 0x09 /* Interrupt level upper limit [11:8] */
#define AMG88XX_INTLL 0x0a /* Interrupt level lower limit [7:0] */
#define AMG88XX_INTLH 0x0b /* Interrupt level lower limit [11:8] */
#define AMG88XX_INTSL 0x0c /* Interrupt hysteresis level [7:0] */
#define AMG88XX_INTSH 0x0d /* Interrupt hysteresis level [11:8] */
#define AMG88XX_TTHL 0x0e /* Thermistor temperature data [7:0] */
#define AMG88XX_TTHH 0x0f /* Thermistor temperature data [10:8] */
#define AMG88XX_INT0 0x10 /* Pixel 1..8 Interrupt Result */
#define AMG88XX_INT1 0x11 /* Pixel 9..16 Interrupt Result */
#define AMG88XX_INT2 0x12 /* Pixel 17..24 Interrupt Result */
#define AMG88XX_INT3 0x13 /* Pixel 25..32 Interrupt Result */
#define AMG88XX_INT4 0x14 /* Pixel 33..40 Interrupt Result */
#define AMG88XX_INT5 0x15 /* Pixel 41..48 Interrupt Result */
#define AMG88XX_INT6 0x16 /* Pixel 49..56 Interrupt Result */
#define AMG88XX_INT7 0x17 /* Pixel 57..64 Interrupt Result */
#define AMG88XX_OUTPUT_BASE 0x80 /* Base address for the output values */
#define AMG88XX_PCLT_NORMAL_MODE 0x00
#define AMG88XX_PCLT_SLEEEP_MODE 0x10
#define AMG88XX_PCLT_STAND_BY_60S_MODE 0x20
#define AMG88XX_PCLT_STAND_BY_10S_MODE 0x21
#define AMG88XX_RST_FLAG_RST 0x30
#define AMG88XX_RST_INITIAL_RST 0x3F
#define AMG88XX_FPSC_10FPS 0x00
#define AMG88XX_FPSC_1FPS 0x01
#define AMG88XX_INTC_DISABLED 0x00
#define AMG88XX_INTC_DIFF_MODE 0x01
#define AMG88XX_INTC_ABS_MODE 0x03
#define AMG88XX_STAT_INTF_MASK 0x02
#define AMG88XX_STAT_OVF_IRS_MASK 0x04
#define AMG88XX_SCLR_INTCLR_MASK 0x02
#define AMG88XX_SCLR_OVS_CLR_MASK 0x04
#define AMG88XX_AVE_MAMOD_MASK 0x20
/* 1 LSB is equivalent to 0.25 degree Celsius scaled to micro degrees */
#define AMG88XX_TREG_LSB_SCALING 250000
#define AMG88XX_WAIT_MODE_CHANGE_US 50000
#define AMG88XX_WAIT_INITIAL_RESET_US 2000
struct amg88xx_data {
struct device *i2c;
s16_t sample[64];
#ifdef CONFIG_AMG88XX_TRIGGER
struct device *gpio;
struct gpio_callback gpio_cb;
sensor_trigger_handler_t drdy_handler;
struct sensor_trigger drdy_trigger;
sensor_trigger_handler_t th_handler;
struct sensor_trigger th_trigger;
#if defined(CONFIG_AMG88XX_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_AMG88XX_THREAD_STACK_SIZE);
struct k_sem gpio_sem;
struct k_thread thread;
#elif defined(CONFIG_AMG88XX_TRIGGER_GLOBAL_THREAD)
struct k_work work;
struct device *dev;
#endif
#endif /* CONFIG_AMG88XX_TRIGGER */
};
static inline int amg88xx_reg_read(struct amg88xx_data *drv_data,
u8_t reg, u8_t *val)
{
return i2c_reg_read_byte(drv_data->i2c, CONFIG_AMG88XX_I2C_ADDR,
reg, val);
}
static inline int amg88xx_reg_write(struct amg88xx_data *drv_data,
u8_t reg, u8_t val)
{
return i2c_reg_write_byte(drv_data->i2c, CONFIG_AMG88XX_I2C_ADDR,
reg, val);
}
static inline int amg88xx_reg_update(struct amg88xx_data *drv_data, u8_t reg,
u8_t mask, u8_t val)
{
return i2c_reg_update_byte(drv_data->i2c, CONFIG_AMG88XX_I2C_ADDR,
reg, mask, val);
}
#ifdef CONFIG_AMG88XX_TRIGGER
int amg88xx_reg_read(struct amg88xx_data *drv_data, u8_t reg, u8_t *val);
int amg88xx_reg_write(struct amg88xx_data *drv_data, u8_t reg, u8_t val);
int amg88xx_reg_update(struct amg88xx_data *drv_data, u8_t reg,
u8_t mask, u8_t val);
int amg88xx_attr_set(struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
int amg88xx_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int amg88xx_init_interrupt(struct device *dev);
#endif /* CONFIG_AMG88XX_TRIGGER */
#define SYS_LOG_DOMAIN "AMG88XX"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL
#include <logging/sys_log.h>
#endif

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2017 Phytec Messtechnik GmbH
* Copyright (c) 2017 Benedict Ohl (Benedict-Ohl@web.de)
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <gpio.h>
#include <i2c.h>
#include <misc/util.h>
#include <kernel.h>
#include <sensor.h>
#include "amg88xx.h"
extern struct amg88xx_data amg88xx_driver;
int amg88xx_attr_set(struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct amg88xx_data *drv_data = dev->driver_data;
s16_t int_level = (val->val1 * 1000000 + val->val2) /
AMG88XX_TREG_LSB_SCALING;
u8_t intl_reg;
u8_t inth_reg;
if (chan != SENSOR_CHAN_TEMP) {
return -ENOTSUP;
}
SYS_LOG_DBG("set threshold to %d", int_level);
if (attr == SENSOR_ATTR_UPPER_THRESH) {
intl_reg = AMG88XX_INTHL;
inth_reg = AMG88XX_INTHH;
} else if (attr == SENSOR_ATTR_LOWER_THRESH) {
intl_reg = AMG88XX_INTLL;
inth_reg = AMG88XX_INTLH;
} else {
return -ENOTSUP;
}
if (amg88xx_reg_write(drv_data, intl_reg, (u8_t)int_level)) {
SYS_LOG_DBG("Failed to set INTxL attribute!");
return -EIO;
}
if (amg88xx_reg_write(drv_data, inth_reg, (u8_t)(int_level >> 8))) {
SYS_LOG_DBG("Failed to set INTxH attribute!");
return -EIO;
}
return 0;
}
static void amg88xx_gpio_callback(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct amg88xx_data *drv_data =
CONTAINER_OF(cb, struct amg88xx_data, gpio_cb);
gpio_pin_disable_callback(dev, CONFIG_AMG88XX_GPIO_PIN_NUM);
#if defined(CONFIG_AMG88XX_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_AMG88XX_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void amg88xx_thread_cb(void *arg)
{
struct device *dev = arg;
struct amg88xx_data *drv_data = dev->driver_data;
u8_t status;
if (amg88xx_reg_read(drv_data, AMG88XX_STAT, &status) < 0) {
return;
}
if (drv_data->drdy_handler != NULL) {
drv_data->drdy_handler(dev, &drv_data->drdy_trigger);
}
if (drv_data->th_handler != NULL) {
drv_data->th_handler(dev, &drv_data->th_trigger);
}
gpio_pin_enable_callback(drv_data->gpio, CONFIG_AMG88XX_GPIO_PIN_NUM);
}
#ifdef CONFIG_AMG88XX_TRIGGER_OWN_THREAD
static void amg88xx_thread(int dev_ptr, int unused)
{
struct device *dev = INT_TO_POINTER(dev_ptr);
struct amg88xx_data *drv_data = dev->driver_data;
ARG_UNUSED(unused);
while (42) {
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
amg88xx_thread_cb(dev);
}
}
#endif
#ifdef CONFIG_AMG88XX_TRIGGER_GLOBAL_THREAD
static void amg88xx_work_cb(struct k_work *work)
{
struct amg88xx_data *drv_data =
CONTAINER_OF(work, struct amg88xx_data, work);
amg88xx_thread_cb(drv_data->dev);
}
#endif
int amg88xx_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct amg88xx_data *drv_data = dev->driver_data;
amg88xx_reg_write(drv_data, AMG88XX_INTC,
AMG88XX_INTC_DISABLED);
gpio_pin_disable_callback(drv_data->gpio, CONFIG_AMG88XX_GPIO_PIN_NUM);
if (trig->type == SENSOR_TRIG_THRESHOLD) {
drv_data->th_handler = handler;
drv_data->th_trigger = *trig;
} else {
SYS_LOG_ERR("Unsupported sensor trigger");
return -ENOTSUP;
}
gpio_pin_enable_callback(drv_data->gpio, CONFIG_AMG88XX_GPIO_PIN_NUM);
amg88xx_reg_write(drv_data, AMG88XX_INTC,
AMG88XX_INTC_ABS_MODE);
return 0;
}
int amg88xx_init_interrupt(struct device *dev)
{
struct amg88xx_data *drv_data = dev->driver_data;
/* setup gpio interrupt */
drv_data->gpio = device_get_binding(CONFIG_AMG88XX_GPIO_DEV_NAME);
if (drv_data->gpio == NULL) {
SYS_LOG_DBG("Failed to get pointer to %s device!",
CONFIG_AMG88XX_GPIO_DEV_NAME);
return -EINVAL;
}
gpio_pin_configure(drv_data->gpio, CONFIG_AMG88XX_GPIO_PIN_NUM,
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
gpio_init_callback(&drv_data->gpio_cb,
amg88xx_gpio_callback,
BIT(CONFIG_AMG88XX_GPIO_PIN_NUM));
if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) {
SYS_LOG_DBG("Failed to set gpio callback!");
return -EIO;
}
#if defined(CONFIG_AMG88XX_TRIGGER_OWN_THREAD)
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
k_thread_create(&drv_data->thread, drv_data->thread_stack,
CONFIG_AMG88XX_THREAD_STACK_SIZE,
(k_thread_entry_t)amg88xx_thread, dev,
0, NULL, K_PRIO_COOP(CONFIG_AMG88XX_THREAD_PRIORITY),
0, 0);
#elif defined(CONFIG_AMG88XX_TRIGGER_GLOBAL_THREAD)
drv_data->work.handler = amg88xx_work_cb;
drv_data->dev = dev;
#endif
return 0;
}