add mlx90640 driver
This commit is contained in:
parent
6250786c66
commit
2c53c45c4b
12 changed files with 2231 additions and 1 deletions
|
@ -1,2 +1,4 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory(drivers)
|
||||
|
|
2
Kconfig
2
Kconfig
|
@ -5,5 +5,5 @@
|
|||
# as the module Kconfig entry point (see zephyr/module.yml). You can browse
|
||||
# module options by going to Zephyr -> Modules in Kconfig.
|
||||
|
||||
# rsource "drivers/Kconfig"
|
||||
rsource "drivers/Kconfig"
|
||||
# rsource "lib/Kconfig"
|
4
drivers/CMakeLists.txt
Normal file
4
drivers/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_SENSOR sensor)
|
6
drivers/Kconfig
Normal file
6
drivers/Kconfig
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Drivers"
|
||||
rsource "sensor/Kconfig"
|
||||
endmenu
|
4
drivers/sensor/CMakeLists.txt
Normal file
4
drivers/sensor/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_MLX90640 mlx90640)
|
6
drivers/sensor/Kconfig
Normal file
6
drivers/sensor/Kconfig
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
if SENSOR
|
||||
rsource "mlx90640/Kconfig"
|
||||
endif # SENSOR
|
7
drivers/sensor/mlx90640/CMakeLists.txt
Normal file
7
drivers/sensor/mlx90640/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(mlx90640.c)
|
||||
# zephyr_library_sources_ifdef(CONFIG_MLX90640_TRIGGER mlx90640_trigger.c)
|
||||
# zephyr_library_sources(vendor/functions/MLX90640.c)
|
55
drivers/sensor/mlx90640/Kconfig
Normal file
55
drivers/sensor/mlx90640/Kconfig
Normal file
|
@ -0,0 +1,55 @@
|
|||
# MLX90640 infrared thermopile sensor configuration options
|
||||
|
||||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# menuconfig MLX90640
|
||||
config MLX90640
|
||||
bool "MLX90640 Infrared Thermopile Sensor"
|
||||
default y
|
||||
depends on DT_HAS_MELEXIS_MLX90640_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable driver for MLX90640 infrared thermopile sensor.
|
||||
|
||||
# if MLX90640
|
||||
|
||||
# choice
|
||||
# prompt "Trigger mode"
|
||||
# default MLX90640_TRIGGER_NONE
|
||||
# help
|
||||
# Specify the type of triggering used by the driver.
|
||||
|
||||
# config MLX90640_TRIGGER_NONE
|
||||
# bool "No trigger"
|
||||
|
||||
# config MLX90640_TRIGGER_GLOBAL_THREAD
|
||||
# bool "Use global thread"
|
||||
# depends on GPIO
|
||||
# select MLX90640_TRIGGER
|
||||
|
||||
# config MLX90640_TRIGGER_OWN_THREAD
|
||||
# bool "Use own thread"
|
||||
# depends on GPIO
|
||||
# select MLX90640_TRIGGER
|
||||
|
||||
# endchoice
|
||||
|
||||
# config MLX90640_TRIGGER
|
||||
# bool
|
||||
|
||||
# config MLX90640_THREAD_PRIORITY
|
||||
# int "Thread priority"
|
||||
# depends on MLX90640_TRIGGER_OWN_THREAD
|
||||
# default 10
|
||||
# help
|
||||
# Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
# config MLX90640_THREAD_STACK_SIZE
|
||||
# int "Thread stack size"
|
||||
# depends on MLX90640_TRIGGER_OWN_THREAD
|
||||
# default 1024
|
||||
# help
|
||||
# Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
# endif # MLX90640
|
1739
drivers/sensor/mlx90640/mlx90640.c
Normal file
1739
drivers/sensor/mlx90640/mlx90640.c
Normal file
File diff suppressed because it is too large
Load diff
146
drivers/sensor/mlx90640/mlx90640.h
Normal file
146
drivers/sensor/mlx90640/mlx90640.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2024 PM
|
||||
*
|
||||
* using code from Melexis https://github.com/melexis/mlx90640-library,
|
||||
* commit f6be7ca1d4a55146b705f3d347f84b773b29cc86 under Apache-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_MLX90640_MLX90640_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_MLX90640_MLX90640_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int16_t kVdd;
|
||||
int16_t vdd25;
|
||||
float KvPTAT;
|
||||
float KtPTAT;
|
||||
uint16_t vPTAT25;
|
||||
float alphaPTAT;
|
||||
int16_t gainEE;
|
||||
float tgc;
|
||||
float cpKv;
|
||||
float cpKta;
|
||||
uint8_t resolutionEE;
|
||||
uint8_t calibrationModeEE;
|
||||
float KsTa;
|
||||
float ksTo[5];
|
||||
int16_t ct[5];
|
||||
uint16_t alpha[768];
|
||||
uint8_t alphaScale;
|
||||
int16_t offset[768];
|
||||
int8_t kta[768];
|
||||
uint8_t ktaScale;
|
||||
int8_t kv[768];
|
||||
uint8_t kvScale;
|
||||
float cpAlpha[2];
|
||||
int16_t cpOffset[2];
|
||||
float ilChessC[3];
|
||||
uint16_t brokenPixels[5];
|
||||
uint16_t outlierPixels[5];
|
||||
} mlx90640_params;
|
||||
|
||||
enum mlx90640_refresh_rate
|
||||
{
|
||||
MLX90640_REFRESH_0_5 = 0,
|
||||
MLX90640_REFRESH_1 = 1,
|
||||
MLX90640_REFRESH_2 = 2,
|
||||
MLX90640_REFRESH_4 = 3,
|
||||
MLX90640_REFRESH_8 = 4,
|
||||
MLX90640_REFRESH_16 = 5,
|
||||
MLX90640_REFRESH_32 = 6,
|
||||
MLX90640_REFRESH_64 = 7,
|
||||
};
|
||||
|
||||
enum mlx90640_adc_resolution
|
||||
{
|
||||
MLX90640_ADC_RES_16 = 0,
|
||||
MLX90640_ADC_RES_17 = 1,
|
||||
MLX90640_ADC_RES_18 = 2,
|
||||
MLX90640_ADC_RES_19 = 3,
|
||||
};
|
||||
|
||||
enum mlx90640_reading_pattern
|
||||
{
|
||||
MLX90640_PATTERN_INTERLEAVED = 0,
|
||||
MLX90640_PATTERN_CHESS = 1,
|
||||
};
|
||||
|
||||
struct mlx90640_config
|
||||
{
|
||||
const struct i2c_dt_spec i2c;
|
||||
const enum mlx90640_refresh_rate refresh_rate;
|
||||
const enum mlx90640_adc_resolution adc_resolution;
|
||||
const enum mlx90640_reading_pattern reading_pattern;
|
||||
};
|
||||
|
||||
struct mlx90640_data
|
||||
{
|
||||
float temps[768];
|
||||
float vdd;
|
||||
float ta;
|
||||
uint16_t raw_data[833]; // 768 px + 64 metadata + 1 status
|
||||
mlx90640_params params;
|
||||
|
||||
#ifdef CONFIG_MLX90640_TRIGGER
|
||||
const struct device *dev;
|
||||
struct gpio_callback gpio_cb;
|
||||
|
||||
sensor_trigger_handler_t drdy_handler;
|
||||
const struct sensor_trigger *drdy_trigger;
|
||||
|
||||
sensor_trigger_handler_t th_handler;
|
||||
const struct sensor_trigger *th_trigger;
|
||||
|
||||
#if defined(CONFIG_MLX90640_TRIGGER_OWN_THREAD)
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_MLX90640_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
#elif defined(CONFIG_MLX90640_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_MLX90640_TRIGGER */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MLX90640_TRIGGER
|
||||
int mlx90640_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr,
|
||||
const struct sensor_value *val);
|
||||
|
||||
int mlx90640_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler);
|
||||
|
||||
int mlx90640_init_interrupt(const struct device *dev);
|
||||
#endif /* CONFIG_MLX90640_TRIGGER */
|
||||
|
||||
// data
|
||||
int mlx90640_synch_frame(const struct device *dev);
|
||||
int mlx90640_trigger_measurement(const struct device *dev);
|
||||
int mlx90640_get_frame_data(const struct device *dev);
|
||||
|
||||
// process
|
||||
float mlx90640_get_vdd(const struct device *dev);
|
||||
float mlx90640_get_ta(const struct device *dev);
|
||||
|
||||
// void mlx90640_get_image(const struct device *dev);
|
||||
// or!
|
||||
void mlx90640_calculate_to(const struct device *dev, float emissivity, float tr);
|
||||
|
||||
// optional /
|
||||
void mlx90640_bad_pixels_correction(const struct device *dev, int mode);
|
||||
|
||||
int mlx90640_set_adc_resolution(const struct device *dev, enum mlx90640_adc_resolution res);
|
||||
enum mlx90640_adc_resolution mlx90640_get_adc_resolution(const struct device *dev);
|
||||
|
||||
int mlx90640_set_refresh_rate(const struct device *dev, enum mlx90640_refresh_rate res);
|
||||
enum mlx90640_refresh_rate mlx90640_get_refresh_rate(const struct device *dev);
|
||||
|
||||
int mlx90640_set_reading_pattern(const struct device *dev, enum mlx90640_reading_pattern res);
|
||||
enum mlx90640_reading_pattern mlx90640_get_reading_pattern(const struct device *dev);
|
||||
|
||||
#endif
|
213
drivers/sensor/mlx90640/mlx90640_trigger.c
Normal file
213
drivers/sensor/mlx90640/mlx90640_trigger.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019 Phytec Messtechnik GmbH
|
||||
* Copyright (c) 2017 Benedict Ohl (Benedict-Ohl@web.de)
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include "amg88xx.h"
|
||||
|
||||
extern struct amg88xx_data amg88xx_driver;
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(AMG88XX, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static inline void amg88xx_setup_int(const struct amg88xx_config *cfg,
|
||||
bool enable)
|
||||
{
|
||||
unsigned int flags = enable
|
||||
? GPIO_INT_EDGE_TO_ACTIVE
|
||||
: GPIO_INT_DISABLE;
|
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->int_gpio, flags);
|
||||
}
|
||||
|
||||
int amg88xx_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
const struct amg88xx_config *config = dev->config;
|
||||
int16_t int_level = (val->val1 * 1000000 + val->val2) /
|
||||
AMG88XX_TREG_LSB_SCALING;
|
||||
uint8_t intl_reg;
|
||||
uint8_t inth_reg;
|
||||
|
||||
if (!config->int_gpio.port) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
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 (i2c_reg_write_byte_dt(&config->i2c,
|
||||
intl_reg, (uint8_t)int_level)) {
|
||||
LOG_DBG("Failed to set INTxL attribute!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (i2c_reg_write_byte_dt(&config->i2c,
|
||||
inth_reg, (uint8_t)(int_level >> 8))) {
|
||||
LOG_DBG("Failed to set INTxH attribute!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amg88xx_gpio_callback(const struct device *dev,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
struct amg88xx_data *drv_data =
|
||||
CONTAINER_OF(cb, struct amg88xx_data, gpio_cb);
|
||||
const struct amg88xx_config *config = drv_data->dev->config;
|
||||
|
||||
amg88xx_setup_int(config, false);
|
||||
|
||||
#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(const struct device *dev)
|
||||
{
|
||||
struct amg88xx_data *drv_data = dev->data;
|
||||
const struct amg88xx_config *config = dev->config;
|
||||
uint8_t status;
|
||||
|
||||
if (i2c_reg_read_byte_dt(&config->i2c,
|
||||
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);
|
||||
}
|
||||
|
||||
amg88xx_setup_int(config, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMG88XX_TRIGGER_OWN_THREAD
|
||||
static void amg88xx_thread(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p2);
|
||||
ARG_UNUSED(p3);
|
||||
|
||||
struct amg88xx_data *drv_data = p1;
|
||||
|
||||
while (42) {
|
||||
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
|
||||
amg88xx_thread_cb(drv_data->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(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct amg88xx_data *drv_data = dev->data;
|
||||
const struct amg88xx_config *config = dev->config;
|
||||
|
||||
if (!config->int_gpio.port) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (i2c_reg_write_byte_dt(&config->i2c,
|
||||
AMG88XX_INTC, AMG88XX_INTC_DISABLED)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
amg88xx_setup_int(config, false);
|
||||
|
||||
if (trig->type == SENSOR_TRIG_THRESHOLD) {
|
||||
drv_data->th_handler = handler;
|
||||
drv_data->th_trigger = trig;
|
||||
} else {
|
||||
LOG_ERR("Unsupported sensor trigger");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
amg88xx_setup_int(config, true);
|
||||
|
||||
if (i2c_reg_write_byte_dt(&config->i2c,
|
||||
AMG88XX_INTC, AMG88XX_INTC_ABS_MODE)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amg88xx_init_interrupt(const struct device *dev)
|
||||
{
|
||||
struct amg88xx_data *drv_data = dev->data;
|
||||
const struct amg88xx_config *config = dev->config;
|
||||
|
||||
if (!gpio_is_ready_dt(&config->int_gpio)) {
|
||||
LOG_ERR("%s: device %s is not ready", dev->name,
|
||||
config->int_gpio.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT | config->int_gpio.dt_flags);
|
||||
|
||||
gpio_init_callback(&drv_data->gpio_cb,
|
||||
amg88xx_gpio_callback,
|
||||
BIT(config->int_gpio.pin));
|
||||
|
||||
if (gpio_add_callback(config->int_gpio.port, &drv_data->gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set gpio callback!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
drv_data->dev = dev;
|
||||
|
||||
#if defined(CONFIG_AMG88XX_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
|
||||
|
||||
k_thread_create(&drv_data->thread, drv_data->thread_stack,
|
||||
CONFIG_AMG88XX_THREAD_STACK_SIZE,
|
||||
amg88xx_thread, drv_data,
|
||||
NULL, NULL, K_PRIO_COOP(CONFIG_AMG88XX_THREAD_PRIORITY),
|
||||
0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_AMG88XX_TRIGGER_GLOBAL_THREAD)
|
||||
drv_data->work.handler = amg88xx_work_cb;
|
||||
#endif
|
||||
amg88xx_setup_int(config, true);
|
||||
|
||||
return 0;
|
||||
}
|
48
dts/bindings/sensor/melexis,mlx90640.yaml
Normal file
48
dts/bindings/sensor/melexis,mlx90640.yaml
Normal file
|
@ -0,0 +1,48 @@
|
|||
# Copyright (c) 2024 PM
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Melexis MLX90640 32x24 (768) pixel infrared array sensor
|
||||
|
||||
compatible: "melexis,mlx90640"
|
||||
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
refresh-rate:
|
||||
type: int
|
||||
default: 2
|
||||
enum:
|
||||
- 0 # 0.5HZ
|
||||
- 1 # 1HZ
|
||||
- 2 # 2HZ
|
||||
- 3 # 4HZ
|
||||
- 4 # 8HZ
|
||||
- 5 # 16HZ
|
||||
- 6 # 32HZ
|
||||
- 7 # 64HZ
|
||||
description:
|
||||
Refresh rate of the sensor itself.
|
||||
The I2C must be configured to keep up with the data flow.
|
||||
This configures the rate for subfields, i.e. the full frame rate is half of this value.
|
||||
|
||||
adc-resolution:
|
||||
type: int
|
||||
default: 2
|
||||
enum:
|
||||
- 0 # 16BIT
|
||||
- 1 # 17BIT
|
||||
- 2 # 18BIT
|
||||
- 3 # 19BIT
|
||||
description:
|
||||
ADC resolution.
|
||||
|
||||
reading-pattern:
|
||||
type: int
|
||||
default: 1
|
||||
enum:
|
||||
- 0 # CHESS
|
||||
- 1 # INTERLEAVE
|
||||
description:
|
||||
Reading pattern.
|
||||
The sensor can be read in either line-interleaved subfields (similar to a TV video)
|
||||
or a pixel alternating "chess board" pattern.
|
Loading…
Reference in a new issue