From 2c53c45c4b60935eabaab2dc38d295a547925167 Mon Sep 17 00:00:00 2001 From: Patrick Moessler Date: Sat, 6 Jan 2024 22:03:13 +0100 Subject: [PATCH] add mlx90640 driver --- CMakeLists.txt | 2 + Kconfig | 2 +- drivers/CMakeLists.txt | 4 + drivers/Kconfig | 6 + drivers/sensor/CMakeLists.txt | 4 + drivers/sensor/Kconfig | 6 + drivers/sensor/mlx90640/CMakeLists.txt | 7 + drivers/sensor/mlx90640/Kconfig | 55 + drivers/sensor/mlx90640/mlx90640.c | 1739 ++++++++++++++++++++ drivers/sensor/mlx90640/mlx90640.h | 146 ++ drivers/sensor/mlx90640/mlx90640_trigger.c | 213 +++ dts/bindings/sensor/melexis,mlx90640.yaml | 48 + 12 files changed, 2231 insertions(+), 1 deletion(-) create mode 100644 drivers/CMakeLists.txt create mode 100644 drivers/Kconfig create mode 100644 drivers/sensor/CMakeLists.txt create mode 100644 drivers/sensor/Kconfig create mode 100644 drivers/sensor/mlx90640/CMakeLists.txt create mode 100644 drivers/sensor/mlx90640/Kconfig create mode 100644 drivers/sensor/mlx90640/mlx90640.c create mode 100644 drivers/sensor/mlx90640/mlx90640.h create mode 100644 drivers/sensor/mlx90640/mlx90640_trigger.c create mode 100644 dts/bindings/sensor/melexis,mlx90640.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index 722b1b1..46abc9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,2 +1,4 @@ # Copyright (c) 2024 PM # SPDX-License-Identifier: Apache-2.0 + +add_subdirectory(drivers) diff --git a/Kconfig b/Kconfig index ebd2da0..9c0a5f6 100644 --- a/Kconfig +++ b/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" \ No newline at end of file diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt new file mode 100644 index 0000000..40620f0 --- /dev/null +++ b/drivers/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2024 PM +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_SENSOR sensor) diff --git a/drivers/Kconfig b/drivers/Kconfig new file mode 100644 index 0000000..225e4c6 --- /dev/null +++ b/drivers/Kconfig @@ -0,0 +1,6 @@ +# Copyright (c) 2024 PM +# SPDX-License-Identifier: Apache-2.0 + +menu "Drivers" + rsource "sensor/Kconfig" +endmenu diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt new file mode 100644 index 0000000..da385e3 --- /dev/null +++ b/drivers/sensor/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2024 PM +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_MLX90640 mlx90640) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig new file mode 100644 index 0000000..5368eb1 --- /dev/null +++ b/drivers/sensor/Kconfig @@ -0,0 +1,6 @@ +# Copyright (c) 2024 PM +# SPDX-License-Identifier: Apache-2.0 + +if SENSOR + rsource "mlx90640/Kconfig" +endif # SENSOR diff --git a/drivers/sensor/mlx90640/CMakeLists.txt b/drivers/sensor/mlx90640/CMakeLists.txt new file mode 100644 index 0000000..d3e2729 --- /dev/null +++ b/drivers/sensor/mlx90640/CMakeLists.txt @@ -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) diff --git a/drivers/sensor/mlx90640/Kconfig b/drivers/sensor/mlx90640/Kconfig new file mode 100644 index 0000000..7d4c8e9 --- /dev/null +++ b/drivers/sensor/mlx90640/Kconfig @@ -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 diff --git a/drivers/sensor/mlx90640/mlx90640.c b/drivers/sensor/mlx90640/mlx90640.c new file mode 100644 index 0000000..c5e5711 --- /dev/null +++ b/drivers/sensor/mlx90640/mlx90640.c @@ -0,0 +1,1739 @@ +/* + * 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 + */ + +#define DT_DRV_COMPAT melexis_mlx90640 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mlx90640.h" + +LOG_MODULE_REGISTER(MLX90640, CONFIG_SENSOR_LOG_LEVEL); + +#define MLX90640_NO_ERROR 0 +#define MLX90640_I2C_NACK_ERROR 1 +#define MLX90640_I2C_WRITE_ERROR 2 +#define MLX90640_BROKEN_PIXELS_NUM_ERROR 3 +#define MLX90640_OUTLIER_PIXELS_NUM_ERROR 4 +#define MLX90640_BAD_PIXELS_NUM_ERROR 5 +#define MLX90640_ADJACENT_BAD_PIXELS_ERROR 6 +#define MLX90640_EEPROM_DATA_ERROR 7 +#define MLX90640_FRAME_DATA_ERROR 8 +#define MLX90640_MEAS_TRIGGER_ERROR 9 + +#define MLX90640_BIT_MASK(x) (1UL << (x)) +#define MLX90640_REG_MASK(sbit, nbits) ~((~(~0UL << (nbits))) << (sbit)) + +#define MLX90640_EEPROM_START_ADDRESS 0x2400 +#define MLX90640_EEPROM_DUMP_NUM 832 +#define MLX90640_PIXEL_DATA_START_ADDRESS 0x0400 +#define MLX90640_PIXEL_NUM 768 +#define MLX90640_LINE_NUM 24 +#define MLX90640_COLUMN_NUM 32 +#define MLX90640_LINE_SIZE 32 +#define MLX90640_COLUMN_SIZE 24 +#define MLX90640_AUX_DATA_START_ADDRESS 0x0700 +#define MLX90640_AUX_NUM 64 +#define MLX90640_STATUS_REG 0x8000 +#define MLX90640_INIT_STATUS_VALUE 0x0030 +#define MLX90640_STAT_FRAME_MASK MLX90640_BIT_MASK(0) +#define MLX90640_GET_FRAME(reg_value) (reg_value & MLX90640_STAT_FRAME_MASK) +#define MLX90640_STAT_DATA_READY_MASK MLX90640_BIT_MASK(3) +#define MLX90640_GET_DATA_READY(reg_value) (reg_value & MLX90640_STAT_DATA_READY_MASK) + +#define MLX90640_CTRL_REG 0x800D +#define MLX90640_CTRL_TRIG_READY_MASK MLX90640_BIT_MASK(15) +#define MLX90640_CTRL_REFRESH_SHIFT 7 +#define MLX90640_CTRL_REFRESH_MASK MLX90640_REG_MASK(MLX90640_CTRL_REFRESH_SHIFT, 3) +#define MLX90640_CTRL_RESOLUTION_SHIFT 10 +#define MLX90640_CTRL_RESOLUTION_MASK MLX90640_REG_MASK(MLX90640_CTRL_RESOLUTION_SHIFT, 2) +#define MLX90640_CTRL_MEAS_MODE_SHIFT 12 +#define MLX90640_CTRL_MEAS_MODE_MASK MLX90640_BIT_MASK(12) + +#define MLX90640_DEVICE_ID1_ADDRESS 0x2407 +#define MLX90640_DEVICE_ID2_ADDRESS 0x2408 +#define MLX90640_DEVICE_ID3_ADDRESS 0x2409 + +#define MLX90640_MS_BYTE_SHIFT 8 +#define MLX90640_MS_BYTE_MASK 0xFF00 +#define MLX90640_LS_BYTE_MASK 0x00FF +#define MLX90640_MS_BYTE(reg16) ((reg16 & MLX90640_MS_BYTE_MASK) >> MLX90640_MS_BYTE_SHIFT) +#define MLX90640_LS_BYTE(reg16) (reg16 & MLX90640_LS_BYTE_MASK) +#define MLX90640_MSBITS_6_MASK 0xFC00 +#define MLX90640_LSBITS_10_MASK 0x03FF +#define MLX90640_NIBBLE1_MASK 0x000F +#define MLX90640_NIBBLE2_MASK 0x00F0 +#define MLX90640_NIBBLE3_MASK 0x0F00 +#define MLX90640_NIBBLE4_MASK 0xF000 +#define MLX90640_NIBBLE1(reg16) ((reg16 & MLX90640_NIBBLE1_MASK)) +#define MLX90640_NIBBLE2(reg16) ((reg16 & MLX90640_NIBBLE2_MASK) >> 4) +#define MLX90640_NIBBLE3(reg16) ((reg16 & MLX90640_NIBBLE3_MASK) >> 8) +#define MLX90640_NIBBLE4(reg16) ((reg16 & MLX90640_NIBBLE4_MASK) >> 12) + +#define MLX90640_POW2(x) pow(2, (double)x) + +#define SCALEALPHA 0.000001 + +// static void mlx90640_int_extract_vdd_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_ptat_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_gain_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_tgc_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_resolution_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_ks_ta_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_ks_to_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_alpha_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_offset_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_kta_pixel_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_kv_pixel_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_cp_parameters(const struct device *dev, uint16_t *eeData); +// static void mlx90640_int_extract_cilc_parameters(const struct device *dev, uint16_t *eeData); +// static int mlx90640_int_extract_deviating_pixels(const struct device *dev, uint16_t *eeData); +// static int mlx90640_int_check_adjacent_pixels(uint16_t pix1, uint16_t pix2); +// static float mlx90640_int_get_median(float *values, int n); +// static int mlx90640_int_is_pixel_bad(const struct device *dev, uint16_t pixel); +// static int mlx90640_int_validate_frame_data(uint16_t *frameData); +// static int mlx90640_int_validate_aux_data(uint16_t *auxData); + +// static int mlx90640_int_i2c_reg_write(const struct device *dev, uint8_t reg_addr, uint16_t wdata); +// static int mlx90640_int_i2c_reg_read(const struct device *dev, uint8_t reg_addr, uint16_t num_words_read, +// uint16_t *rdata); + +//-------------------------------------- +// internal methods + +static int mlx90640_int_i2c_reg_write(const struct device *dev, uint16_t reg_addr, uint16_t wdata) +{ + const struct mlx90640_config *config = dev->config; + uint8_t data[4]; + data[0] = reg_addr >> 8; + data[1] = reg_addr & 0xFF; + data[2] = wdata >> 8; + data[3] = wdata & 0xFF; + return i2c_write_dt(&config->i2c, data, 4); +} +static int mlx90640_int_i2c_reg_read(const struct device *dev, uint16_t reg_addr, uint16_t num_words_read, + uint16_t *rdata) +{ + const struct mlx90640_config *config = dev->config; + uint8_t data[4]; + int error; + data[0] = reg_addr >> 8; + data[1] = reg_addr & 0xFF; + error = i2c_write_read_dt(&config->i2c, data, 2, rdata, num_words_read * 2); + for (uint16_t i = 0; i < num_words_read; i++) + { + uint16_t tmp = rdata[i]; + rdata[i] = (tmp >> 8) | (tmp << 8); + } + return error; +} + +int mlx90640_synch_frame(const struct device *dev) +{ + uint16_t dataReady = 0; + uint16_t statusRegister; + int error = 1; + + error = mlx90640_int_i2c_reg_write(dev, MLX90640_STATUS_REG, MLX90640_INIT_STATUS_VALUE); + if (error == -MLX90640_I2C_NACK_ERROR) + { + return error; + } + + while (dataReady == 0) + { + error = mlx90640_int_i2c_reg_read(dev, MLX90640_STATUS_REG, 1, &statusRegister); + if (error != MLX90640_NO_ERROR) + { + return error; + } + // dataReady = statusRegister & 0x0008; + dataReady = MLX90640_GET_DATA_READY(statusRegister); + } + + return MLX90640_NO_ERROR; +} + +int mlx90640_trigger_measurement(const struct device *dev) +{ + int error = 1; + uint16_t ctrlReg; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &ctrlReg); + + if (error != MLX90640_NO_ERROR) + { + return error; + } + + ctrlReg |= MLX90640_CTRL_TRIG_READY_MASK; + error = mlx90640_int_i2c_reg_write(dev, MLX90640_CTRL_REG, ctrlReg); + + if (error != MLX90640_NO_ERROR) + { + return error; + } + + // error = MLX90640_I2CGeneralReset(); + + // if (error != MLX90640_NO_ERROR) + // { + // return error; + // } + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &ctrlReg); + + if (error != MLX90640_NO_ERROR) + { + return error; + } + + if ((ctrlReg & MLX90640_CTRL_TRIG_READY_MASK) != 0) + { + return -MLX90640_MEAS_TRIGGER_ERROR; + } + + return MLX90640_NO_ERROR; +} + +static int mlx90640_int_validate_frame_data(uint16_t *frameData) +{ + uint8_t line = 0; + + for (int i = 0; i < MLX90640_PIXEL_NUM; i += MLX90640_LINE_SIZE) + { + if ((frameData[i] == 0x7FFF) && (line % 2 == frameData[833])) + return -MLX90640_FRAME_DATA_ERROR; + line = line + 1; + } + + return MLX90640_NO_ERROR; +} + +static int mlx90640_int_validate_aux_data(uint16_t *auxData) +{ + + if (auxData[0] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + + for (int i = 8; i < 19; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + for (int i = 20; i < 23; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + for (int i = 24; i < 33; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + for (int i = 40; i < 51; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + for (int i = 52; i < 55; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + for (int i = 56; i < 64; i++) + { + if (auxData[i] == 0x7FFF) + return -MLX90640_FRAME_DATA_ERROR; + } + + return MLX90640_NO_ERROR; +} + +int mlx90640_get_frame_data(const struct device *dev) +{ + uint16_t *frameData = ((struct mlx90640_data *)dev->data)->raw_data; + + uint16_t dataReady = 0; + uint16_t controlRegister1; + uint16_t statusRegister; + int error = 1; + uint16_t data[64]; + uint8_t cnt = 0; + + while (dataReady == 0) + { + error = mlx90640_int_i2c_reg_read(dev, MLX90640_STATUS_REG, 1, &statusRegister); + if (error != MLX90640_NO_ERROR) + { + return error; + } + // dataReady = statusRegister & 0x0008; + dataReady = MLX90640_GET_DATA_READY(statusRegister); + } + + error = mlx90640_int_i2c_reg_write(dev, MLX90640_STATUS_REG, MLX90640_INIT_STATUS_VALUE); + if (error == -MLX90640_I2C_NACK_ERROR) + { + return error; + } + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_PIXEL_DATA_START_ADDRESS, MLX90640_PIXEL_NUM, frameData); + if (error != MLX90640_NO_ERROR) + { + return error; + } + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_AUX_DATA_START_ADDRESS, MLX90640_AUX_NUM, data); + if (error != MLX90640_NO_ERROR) + { + return error; + } + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + frameData[832] = controlRegister1; + // frameData[833] = statusRegister & 0x0001; + frameData[833] = MLX90640_GET_FRAME(statusRegister); + + if (error != MLX90640_NO_ERROR) + { + return error; + } + + error = mlx90640_int_validate_aux_data(data); + if (error == MLX90640_NO_ERROR) + { + for (cnt = 0; cnt < MLX90640_AUX_NUM; cnt++) + { + frameData[cnt + MLX90640_PIXEL_NUM] = data[cnt]; + } + } + + error = mlx90640_int_validate_frame_data(frameData); + if (error != MLX90640_NO_ERROR) + { + return error; + } + + return frameData[833]; +} + +// //------------------------------------------------------------------------------ + +void mlx90640_calculate_to(const struct device *dev, float emissivity, float tr) +{ + mlx90640_params *params = &((struct mlx90640_data *)dev->data)->params; + uint16_t *frameData = ((struct mlx90640_data *)dev->data)->raw_data; + float *result = ((struct mlx90640_data *)dev->data)->temps; + + float vdd; + float ta; + float ta4; + float tr4; + float taTr; + float gain; + float irDataCP[2]; + float irData; + float alphaCompensated; + uint8_t mode; + int8_t ilPattern; + int8_t chessPattern; + int8_t pattern; + int8_t conversionPattern; + float Sx; + float To; + float alphaCorrR[4]; + int8_t range; + uint16_t subPage; + float ktaScale; + float kvScale; + float alphaScale; + float kta; + float kv; + + subPage = frameData[833]; + vdd = mlx90640_get_vdd(dev); + ta = mlx90640_get_ta(dev); + + ta4 = (ta + 273.15); + ta4 = ta4 * ta4; + ta4 = ta4 * ta4; + tr4 = (tr + 273.15); + tr4 = tr4 * tr4; + tr4 = tr4 * tr4; + taTr = tr4 - (tr4 - ta4) / emissivity; + + ktaScale = MLX90640_POW2(params->ktaScale); + kvScale = MLX90640_POW2(params->kvScale); + alphaScale = MLX90640_POW2(params->alphaScale); + + alphaCorrR[0] = 1 / (1 + params->ksTo[0] * 40); + alphaCorrR[1] = 1; + alphaCorrR[2] = (1 + params->ksTo[1] * params->ct[2]); + alphaCorrR[3] = alphaCorrR[2] * (1 + params->ksTo[2] * (params->ct[3] - params->ct[2])); + + //------------------------- Gain calculation ----------------------------------- + + gain = (float)params->gainEE / (int16_t)frameData[778]; + + //------------------------- To calculation ------------------------------------- + mode = (frameData[832] & MLX90640_CTRL_MEAS_MODE_MASK) >> 5; + + irDataCP[0] = (int16_t)frameData[776] * gain; + irDataCP[1] = (int16_t)frameData[808] * gain; + + irDataCP[0] = + irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3)); + if (mode == params->calibrationModeEE) + { + irDataCP[1] = + irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3)); + } + else + { + irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * + (1 + params->cpKv * (vdd - 3.3)); + } + + for (int pixelNumber = 0; pixelNumber < 768; pixelNumber++) + { + ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2; + chessPattern = ilPattern ^ (pixelNumber - (pixelNumber / 2) * 2); + conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * + (1 - 2 * ilPattern); + + if (mode == 0) + { + pattern = ilPattern; + } + else + { + pattern = chessPattern; + } + + if (pattern == frameData[833]) + { + irData = (int16_t)frameData[pixelNumber] * gain; + + kta = params->kta[pixelNumber] / ktaScale; + kv = params->kv[pixelNumber] / kvScale; + irData = irData - params->offset[pixelNumber] * (1 + kta * (ta - 25)) * (1 + kv * (vdd - 3.3)); + + if (mode != params->calibrationModeEE) + { + irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern; + } + + irData = irData - params->tgc * irDataCP[subPage]; + irData = irData / emissivity; + + alphaCompensated = SCALEALPHA * alphaScale / params->alpha[pixelNumber]; + alphaCompensated = alphaCompensated * (1 + params->KsTa * (ta - 25)); + + Sx = alphaCompensated * alphaCompensated * alphaCompensated * (irData + alphaCompensated * taTr); + Sx = sqrt(sqrt(Sx)) * params->ksTo[1]; + + To = sqrt(sqrt(irData / (alphaCompensated * (1 - params->ksTo[1] * 273.15) + Sx) + taTr)) - 273.15; + + if (To < params->ct[1]) + { + range = 0; + } + else if (To < params->ct[2]) + { + range = 1; + } + else if (To < params->ct[3]) + { + range = 2; + } + else + { + range = 3; + } + + To = sqrt(sqrt(irData / (alphaCompensated * alphaCorrR[range] * + (1 + params->ksTo[range] * (To - params->ct[range]))) + + taTr)) - + 273.15; + + result[pixelNumber] = To; + } + } +} + +//------------------------------------------------------------------------------ + +// void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result) +// { +// float vdd; +// float ta; +// float gain; +// float irDataCP[2]; +// float irData; +// float alphaCompensated; +// uint8_t mode; +// int8_t ilPattern; +// int8_t chessPattern; +// int8_t pattern; +// int8_t conversionPattern; +// float image; +// uint16_t subPage; +// float ktaScale; +// float kvScale; +// float kta; +// float kv; + +// subPage = frameData[833]; +// vdd = MLX90640_GetVdd(frameData, params); +// ta = MLX90640_GetTa(frameData, params); + +// ktaScale = MLX90640_POW2(params->ktaScale); +// kvScale = MLX90640_POW2(params->kvScale); + +// //------------------------- Gain calculation ----------------------------------- + +// gain = (float)params->gainEE / (int16_t)frameData[778]; + +// //------------------------- Image calculation ------------------------------------- + +// mode = (frameData[832] & MLX90640_CTRL_MEAS_MODE_MASK) >> 5; + +// irDataCP[0] = (int16_t)frameData[776] * gain; +// irDataCP[1] = (int16_t)frameData[808] * gain; + +// irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd +// - 3.3)); if( mode == params->calibrationModeEE) +// { +// irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd +// - 3.3)); +// } +// else +// { +// irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 +// + params->cpKv * (vdd - 3.3)); +// } + +// for( int pixelNumber = 0; pixelNumber < 768; pixelNumber++) +// { +// ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2; +// chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2); +// conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) +// * (1 - 2 * ilPattern); + +// if(mode == 0) +// { +// pattern = ilPattern; +// } +// else +// { +// pattern = chessPattern; +// } + +// if(pattern == frameData[833]) +// { +// irData = (int16_t)frameData[pixelNumber] * gain; + +// kta = params->kta[pixelNumber]/ktaScale; +// kv = params->kv[pixelNumber]/kvScale; +// irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3)); + +// if(mode != params->calibrationModeEE) +// { +// irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern; +// } + +// irData = irData - params->tgc * irDataCP[subPage]; + +// alphaCompensated = params->alpha[pixelNumber]; + +// image = irData*alphaCompensated; + +// result[pixelNumber] = image; +// } +// } +// } + +// //------------------------------------------------------------------------------ + +float mlx90640_get_vdd(const struct device *dev) +{ + mlx90640_params *params = &((struct mlx90640_data *)dev->data)->params; + uint16_t *frameData = ((struct mlx90640_data *)dev->data)->raw_data; + float vdd; + float resolutionCorrection; + + uint16_t resolutionRAM; + + resolutionRAM = (frameData[832] & ~MLX90640_CTRL_RESOLUTION_MASK) >> MLX90640_CTRL_RESOLUTION_SHIFT; + resolutionCorrection = MLX90640_POW2(params->resolutionEE) / MLX90640_POW2(resolutionRAM); + vdd = (resolutionCorrection * (int16_t)frameData[810] - params->vdd25) / params->kVdd + 3.3; + + ((struct mlx90640_data *)dev->data)->vdd = vdd; + + return vdd; +} + +// //------------------------------------------------------------------------------ + +float mlx90640_get_ta(const struct device *dev) +{ + mlx90640_params *params = &((struct mlx90640_data *)dev->data)->params; + uint16_t *frameData = ((struct mlx90640_data *)dev->data)->raw_data; + + int16_t ptat; + float ptatArt; + float vdd; + float ta; + + vdd = mlx90640_get_vdd(dev); + + ptat = (int16_t)frameData[800]; + + ptatArt = (ptat / (ptat * params->alphaPTAT + (int16_t)frameData[768])) * MLX90640_POW2(18); + + ta = (ptatArt / (1 + params->KvPTAT * (vdd - 3.3)) - params->vPTAT25); + ta = ta / params->KtPTAT + 25; + + ((struct mlx90640_data *)dev->data)->ta = ta; + + return ta; +} + +//------------------------------------------------------------------------------ + +static float mlx90640_int_get_median(float *values, int n) +{ + float temp; + + for (int i = 0; i < n - 1; i++) + { + for (int j = i + 1; j < n; j++) + { + if (values[j] < values[i]) + { + temp = values[i]; + values[i] = values[j]; + values[j] = temp; + } + } + } + + if (n % 2 == 0) + { + return ((values[n / 2] + values[n / 2 - 1]) / 2.0); + } + else + { + return values[n / 2]; + } +} + +//------------------------------------------------------------------------------ + +static int mlx90640_int_is_pixel_bad(const struct device *dev, uint16_t pixel) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + for (int i = 0; i < 5; i++) + { + if (pixel == mlx90640->outlierPixels[i] || pixel == mlx90640->brokenPixels[i]) + { + return 1; + } + } + + return 0; +} +//------------------------------------------------------------------------------ + +void mlx90640_bad_pixels_correction(const struct device *dev, int mode) +{ + mlx90640_params *params = &((struct mlx90640_data *)dev->data)->params; + uint16_t *pixels = ((struct mlx90640_data *)dev->data)->raw_data; + float *to = ((struct mlx90640_data *)dev->data)->temps; + + float ap[4]; + uint8_t pix; + uint8_t line; + uint8_t column; + + pix = 0; + while (pixels[pix] != 0xFFFF) + { + line = pixels[pix] >> 5; + column = pixels[pix] - (line << 5); + + if (mode == 1) + { + if (line == 0) + { + if (column == 0) + { + to[pixels[pix]] = to[33]; + } + else if (column == 31) + { + to[pixels[pix]] = to[62]; + } + else + { + to[pixels[pix]] = (to[pixels[pix] + 31] + to[pixels[pix] + 33]) / 2.0; + } + } + else if (line == 23) + { + if (column == 0) + { + to[pixels[pix]] = to[705]; + } + else if (column == 31) + { + to[pixels[pix]] = to[734]; + } + else + { + to[pixels[pix]] = (to[pixels[pix] - 33] + to[pixels[pix] - 31]) / 2.0; + } + } + else if (column == 0) + { + to[pixels[pix]] = (to[pixels[pix] - 31] + to[pixels[pix] + 33]) / 2.0; + } + else if (column == 31) + { + to[pixels[pix]] = (to[pixels[pix] - 33] + to[pixels[pix] + 31]) / 2.0; + } + else + { + ap[0] = to[pixels[pix] - 33]; + ap[1] = to[pixels[pix] - 31]; + ap[2] = to[pixels[pix] + 31]; + ap[3] = to[pixels[pix] + 33]; + to[pixels[pix]] = mlx90640_int_get_median(ap, 4); + } + } + else + { + if (column == 0) + { + to[pixels[pix]] = to[pixels[pix] + 1]; + } + else if (column == 1 || column == 30) + { + to[pixels[pix]] = (to[pixels[pix] - 1] + to[pixels[pix] + 1]) / 2.0; + } + else if (column == 31) + { + to[pixels[pix]] = to[pixels[pix] - 1]; + } + else + { + if (mlx90640_int_is_pixel_bad(dev, pixels[pix] - 2) == 0 && + mlx90640_int_is_pixel_bad(dev, pixels[pix] + 2) == 0) + { + ap[0] = to[pixels[pix] + 1] - to[pixels[pix] + 2]; + ap[1] = to[pixels[pix] - 1] - to[pixels[pix] - 2]; + if (fabs(ap[0]) > fabs(ap[1])) + { + to[pixels[pix]] = to[pixels[pix] - 1] + ap[1]; + } + else + { + to[pixels[pix]] = to[pixels[pix] + 1] + ap[0]; + } + } + else + { + to[pixels[pix]] = (to[pixels[pix] - 1] + to[pixels[pix] + 1]) / 2.0; + } + } + } + pix = pix + 1; + } +} + +//------------------------------------------------------------------------------ + +static int mlx90640_int_check_adjacent_pixels(uint16_t pix1, uint16_t pix2) +{ + int pixPosDif; + uint16_t lp1 = pix1 >> 5; + uint16_t lp2 = pix2 >> 5; + uint16_t cp1 = pix1 - (lp1 << 5); + uint16_t cp2 = pix2 - (lp2 << 5); + + pixPosDif = lp1 - lp2; + if (pixPosDif > -2 && pixPosDif < 2) + { + pixPosDif = cp1 - cp2; + if (pixPosDif > -2 && pixPosDif < 2) + { + return -6; + } + } + + return 0; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_vdd_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int8_t kVdd; + int16_t vdd25; + + kVdd = MLX90640_MS_BYTE(eeData[51]); + + vdd25 = MLX90640_LS_BYTE(eeData[51]); + vdd25 = ((vdd25 - 256) << 5) - 8192; + + mlx90640->kVdd = 32 * kVdd; + mlx90640->vdd25 = vdd25; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_ptat_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + float KvPTAT; + float KtPTAT; + int16_t vPTAT25; + float alphaPTAT; + + KvPTAT = (eeData[50] & MLX90640_MSBITS_6_MASK) >> 10; + if (KvPTAT > 31) + { + KvPTAT = KvPTAT - 64; + } + KvPTAT = KvPTAT / 4096; + + KtPTAT = eeData[50] & MLX90640_LSBITS_10_MASK; + if (KtPTAT > 511) + { + KtPTAT = KtPTAT - 1024; + } + KtPTAT = KtPTAT / 8; + + vPTAT25 = eeData[49]; + + alphaPTAT = (eeData[16] & MLX90640_NIBBLE4_MASK) / MLX90640_POW2(14) + 8.0f; + + mlx90640->KvPTAT = KvPTAT; + mlx90640->KtPTAT = KtPTAT; + mlx90640->vPTAT25 = vPTAT25; + mlx90640->alphaPTAT = alphaPTAT; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_gain_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + mlx90640->gainEE = (int16_t)eeData[48]; + ; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_tgc_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + mlx90640->tgc = (int8_t)MLX90640_LS_BYTE(eeData[60]) / 32.0f; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_resolution_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + uint8_t resolutionEE; + resolutionEE = (eeData[56] & 0x3000) >> 12; + + mlx90640->resolutionEE = resolutionEE; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_ks_ta_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + mlx90640->KsTa = (int8_t)MLX90640_MS_BYTE(eeData[60]) / 8192.0f; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_ks_to_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int32_t KsToScale; + int8_t step; + + step = ((eeData[63] & 0x3000) >> 12) * 10; + + mlx90640->ct[0] = -40; + mlx90640->ct[1] = 0; + mlx90640->ct[2] = MLX90640_NIBBLE2(eeData[63]); + mlx90640->ct[3] = MLX90640_NIBBLE3(eeData[63]); + + mlx90640->ct[2] = mlx90640->ct[2] * step; + mlx90640->ct[3] = mlx90640->ct[2] + mlx90640->ct[3] * step; + mlx90640->ct[4] = 400; + + KsToScale = MLX90640_NIBBLE1(eeData[63]) + 8; + KsToScale = 1UL << KsToScale; + + mlx90640->ksTo[0] = (int8_t)MLX90640_LS_BYTE(eeData[61]) / (float)KsToScale; + mlx90640->ksTo[1] = (int8_t)MLX90640_MS_BYTE(eeData[61]) / (float)KsToScale; + mlx90640->ksTo[2] = (int8_t)MLX90640_LS_BYTE(eeData[62]) / (float)KsToScale; + mlx90640->ksTo[3] = (int8_t)MLX90640_MS_BYTE(eeData[62]) / (float)KsToScale; + mlx90640->ksTo[4] = -0.0002; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_alpha_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int accRow[24]; + int accColumn[32]; + int p = 0; + int alphaRef; + uint8_t alphaScale; + uint8_t accRowScale; + uint8_t accColumnScale; + uint8_t accRemScale; + float alphaTemp[768]; + float temp; + + accRemScale = MLX90640_NIBBLE1(eeData[32]); + accColumnScale = MLX90640_NIBBLE2(eeData[32]); + accRowScale = MLX90640_NIBBLE3(eeData[32]); + alphaScale = MLX90640_NIBBLE4(eeData[32]) + 30; + alphaRef = eeData[33]; + + for (int i = 0; i < 6; i++) + { + p = i * 4; + accRow[p + 0] = MLX90640_NIBBLE1(eeData[34 + i]); + accRow[p + 1] = MLX90640_NIBBLE2(eeData[34 + i]); + accRow[p + 2] = MLX90640_NIBBLE3(eeData[34 + i]); + accRow[p + 3] = MLX90640_NIBBLE4(eeData[34 + i]); + } + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + if (accRow[i] > 7) + { + accRow[i] = accRow[i] - 16; + } + } + + for (int i = 0; i < 8; i++) + { + p = i * 4; + accColumn[p + 0] = MLX90640_NIBBLE1(eeData[40 + i]); + accColumn[p + 1] = MLX90640_NIBBLE2(eeData[40 + i]); + accColumn[p + 2] = MLX90640_NIBBLE3(eeData[40 + i]); + accColumn[p + 3] = MLX90640_NIBBLE4(eeData[40 + i]); + } + + for (int i = 0; i < MLX90640_COLUMN_NUM; i++) + { + if (accColumn[i] > 7) + { + accColumn[i] = accColumn[i] - 16; + } + } + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + for (int j = 0; j < MLX90640_COLUMN_NUM; j++) + { + p = 32 * i + j; + alphaTemp[p] = (eeData[64 + p] & 0x03F0) >> 4; + if (alphaTemp[p] > 31) + { + alphaTemp[p] = alphaTemp[p] - 64; + } + alphaTemp[p] = alphaTemp[p] * (1 << accRemScale); + alphaTemp[p] = (alphaRef + (accRow[i] << accRowScale) + (accColumn[j] << accColumnScale) + alphaTemp[p]); + alphaTemp[p] = alphaTemp[p] / MLX90640_POW2(alphaScale); + alphaTemp[p] = alphaTemp[p] - mlx90640->tgc * (mlx90640->cpAlpha[0] + mlx90640->cpAlpha[1]) / 2; + alphaTemp[p] = SCALEALPHA / alphaTemp[p]; + } + } + + temp = alphaTemp[0]; + for (int i = 1; i < MLX90640_PIXEL_NUM; i++) + { + if (alphaTemp[i] > temp) + { + temp = alphaTemp[i]; + } + } + + alphaScale = 0; + while (temp < 32767.4) + { + temp = temp * 2; + alphaScale = alphaScale + 1; + } + + for (int i = 0; i < MLX90640_PIXEL_NUM; i++) + { + temp = alphaTemp[i] * MLX90640_POW2(alphaScale); + mlx90640->alpha[i] = (temp + 0.5); + } + + mlx90640->alphaScale = alphaScale; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_offset_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int occRow[24]; + int occColumn[32]; + int p = 0; + int16_t offsetRef; + uint8_t occRowScale; + uint8_t occColumnScale; + uint8_t occRemScale; + + occRemScale = MLX90640_NIBBLE1(eeData[16]); + occColumnScale = MLX90640_NIBBLE2(eeData[16]); + occRowScale = MLX90640_NIBBLE3(eeData[16]); + offsetRef = (int16_t)eeData[17]; + + for (int i = 0; i < 6; i++) + { + p = i * 4; + occRow[p + 0] = MLX90640_NIBBLE1(eeData[18 + i]); + occRow[p + 1] = MLX90640_NIBBLE2(eeData[18 + i]); + occRow[p + 2] = MLX90640_NIBBLE3(eeData[18 + i]); + occRow[p + 3] = MLX90640_NIBBLE4(eeData[18 + i]); + } + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + if (occRow[i] > 7) + { + occRow[i] = occRow[i] - 16; + } + } + + for (int i = 0; i < 8; i++) + { + p = i * 4; + occColumn[p + 0] = MLX90640_NIBBLE1(eeData[24 + i]); + occColumn[p + 1] = MLX90640_NIBBLE2(eeData[24 + i]); + occColumn[p + 2] = MLX90640_NIBBLE3(eeData[24 + i]); + occColumn[p + 3] = MLX90640_NIBBLE4(eeData[24 + i]); + } + + for (int i = 0; i < MLX90640_COLUMN_NUM; i++) + { + if (occColumn[i] > 7) + { + occColumn[i] = occColumn[i] - 16; + } + } + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + for (int j = 0; j < MLX90640_COLUMN_NUM; j++) + { + p = 32 * i + j; + mlx90640->offset[p] = (eeData[64 + p] & MLX90640_MSBITS_6_MASK) >> 10; + if (mlx90640->offset[p] > 31) + { + mlx90640->offset[p] = mlx90640->offset[p] - 64; + } + mlx90640->offset[p] = mlx90640->offset[p] * (1 << occRemScale); + mlx90640->offset[p] = + (offsetRef + (occRow[i] << occRowScale) + (occColumn[j] << occColumnScale) + mlx90640->offset[p]); + } + } +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_kta_pixel_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int p = 0; + int8_t KtaRC[4]; + uint8_t ktaScale1; + uint8_t ktaScale2; + uint8_t split; + float ktaTemp[768]; + float temp; + + KtaRC[0] = (int8_t)MLX90640_MS_BYTE(eeData[54]); + ; + KtaRC[2] = (int8_t)MLX90640_LS_BYTE(eeData[54]); + ; + KtaRC[1] = (int8_t)MLX90640_MS_BYTE(eeData[55]); + ; + KtaRC[3] = (int8_t)MLX90640_LS_BYTE(eeData[55]); + ; + + ktaScale1 = MLX90640_NIBBLE2(eeData[56]) + 8; + ktaScale2 = MLX90640_NIBBLE1(eeData[56]); + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + for (int j = 0; j < MLX90640_COLUMN_NUM; j++) + { + p = 32 * i + j; + split = 2 * (p / 32 - (p / 64) * 2) + p % 2; + ktaTemp[p] = (eeData[64 + p] & 0x000E) >> 1; + if (ktaTemp[p] > 3) + { + ktaTemp[p] = ktaTemp[p] - 8; + } + ktaTemp[p] = ktaTemp[p] * (1 << ktaScale2); + ktaTemp[p] = KtaRC[split] + ktaTemp[p]; + ktaTemp[p] = ktaTemp[p] / MLX90640_POW2(ktaScale1); + } + } + + temp = fabs(ktaTemp[0]); + for (int i = 1; i < MLX90640_PIXEL_NUM; i++) + { + if (fabs(ktaTemp[i]) > temp) + { + temp = fabs(ktaTemp[i]); + } + } + + ktaScale1 = 0; + while (temp < 63.4) + { + temp = temp * 2; + ktaScale1 = ktaScale1 + 1; + } + + for (int i = 0; i < MLX90640_PIXEL_NUM; i++) + { + temp = ktaTemp[i] * MLX90640_POW2(ktaScale1); + if (temp < 0) + { + mlx90640->kta[i] = (temp - 0.5); + } + else + { + mlx90640->kta[i] = (temp + 0.5); + } + } + + mlx90640->ktaScale = ktaScale1; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_kv_pixel_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + int p = 0; + int8_t KvT[4]; + int8_t KvRoCo; + int8_t KvRoCe; + int8_t KvReCo; + int8_t KvReCe; + uint8_t kvScale; + uint8_t split; + float kvTemp[768]; + float temp; + + KvRoCo = MLX90640_NIBBLE4(eeData[52]); + if (KvRoCo > 7) + { + KvRoCo = KvRoCo - 16; + } + KvT[0] = KvRoCo; + + KvReCo = MLX90640_NIBBLE3(eeData[52]); + if (KvReCo > 7) + { + KvReCo = KvReCo - 16; + } + KvT[2] = KvReCo; + + KvRoCe = MLX90640_NIBBLE2(eeData[52]); + if (KvRoCe > 7) + { + KvRoCe = KvRoCe - 16; + } + KvT[1] = KvRoCe; + + KvReCe = MLX90640_NIBBLE1(eeData[52]); + if (KvReCe > 7) + { + KvReCe = KvReCe - 16; + } + KvT[3] = KvReCe; + + kvScale = MLX90640_NIBBLE3(eeData[56]); + + for (int i = 0; i < MLX90640_LINE_NUM; i++) + { + for (int j = 0; j < MLX90640_COLUMN_NUM; j++) + { + p = 32 * i + j; + split = 2 * (p / 32 - (p / 64) * 2) + p % 2; + kvTemp[p] = KvT[split]; + kvTemp[p] = kvTemp[p] / MLX90640_POW2(kvScale); + } + } + + temp = fabs(kvTemp[0]); + for (int i = 1; i < MLX90640_PIXEL_NUM; i++) + { + if (fabs(kvTemp[i]) > temp) + { + temp = fabs(kvTemp[i]); + } + } + + kvScale = 0; + while (temp < 63.4) + { + temp = temp * 2; + kvScale = kvScale + 1; + } + + for (int i = 0; i < MLX90640_PIXEL_NUM; i++) + { + temp = kvTemp[i] * MLX90640_POW2(kvScale); + if (temp < 0) + { + mlx90640->kv[i] = (temp - 0.5); + } + else + { + mlx90640->kv[i] = (temp + 0.5); + } + } + + mlx90640->kvScale = kvScale; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_cpp_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + float alphaSP[2]; + int16_t offsetSP[2]; + float cpKv; + float cpKta; + uint8_t alphaScale; + uint8_t ktaScale1; + uint8_t kvScale; + + alphaScale = MLX90640_NIBBLE4(eeData[32]) + 27; + + offsetSP[0] = (eeData[58] & MLX90640_LSBITS_10_MASK); + if (offsetSP[0] > 511) + { + offsetSP[0] = offsetSP[0] - 1024; + } + + offsetSP[1] = (eeData[58] & MLX90640_MSBITS_6_MASK) >> 10; + if (offsetSP[1] > 31) + { + offsetSP[1] = offsetSP[1] - 64; + } + offsetSP[1] = offsetSP[1] + offsetSP[0]; + + alphaSP[0] = (eeData[57] & MLX90640_LSBITS_10_MASK); + if (alphaSP[0] > 511) + { + alphaSP[0] = alphaSP[0] - 1024; + } + alphaSP[0] = alphaSP[0] / MLX90640_POW2(alphaScale); + + alphaSP[1] = (eeData[57] & MLX90640_MSBITS_6_MASK) >> 10; + if (alphaSP[1] > 31) + { + alphaSP[1] = alphaSP[1] - 64; + } + alphaSP[1] = (1 + alphaSP[1] / 128) * alphaSP[0]; + + cpKta = (int8_t)MLX90640_LS_BYTE(eeData[59]); + + ktaScale1 = MLX90640_NIBBLE2(eeData[56]) + 8; + mlx90640->cpKta = cpKta / MLX90640_POW2(ktaScale1); + + cpKv = (int8_t)MLX90640_MS_BYTE(eeData[59]); + + kvScale = MLX90640_NIBBLE3(eeData[56]); + mlx90640->cpKv = cpKv / MLX90640_POW2(kvScale); + + mlx90640->cpAlpha[0] = alphaSP[0]; + mlx90640->cpAlpha[1] = alphaSP[1]; + mlx90640->cpOffset[0] = offsetSP[0]; + mlx90640->cpOffset[1] = offsetSP[1]; +} + +//------------------------------------------------------------------------------ + +static void mlx90640_int_extract_cilc_parameters(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + float ilChessC[3]; + uint8_t calibrationModeEE; + + calibrationModeEE = (eeData[10] & 0x0800) >> 4; + calibrationModeEE = calibrationModeEE ^ 0x80; + + ilChessC[0] = (eeData[53] & 0x003F); + if (ilChessC[0] > 31) + { + ilChessC[0] = ilChessC[0] - 64; + } + ilChessC[0] = ilChessC[0] / 16.0f; + + ilChessC[1] = (eeData[53] & 0x07C0) >> 6; + if (ilChessC[1] > 15) + { + ilChessC[1] = ilChessC[1] - 32; + } + ilChessC[1] = ilChessC[1] / 2.0f; + + ilChessC[2] = (eeData[53] & 0xF800) >> 11; + if (ilChessC[2] > 15) + { + ilChessC[2] = ilChessC[2] - 32; + } + ilChessC[2] = ilChessC[2] / 8.0f; + + mlx90640->calibrationModeEE = calibrationModeEE; + mlx90640->ilChessC[0] = ilChessC[0]; + mlx90640->ilChessC[1] = ilChessC[1]; + mlx90640->ilChessC[2] = ilChessC[2]; +} + +//------------------------------------------------------------------------------ + +static int mlx90640_int_extract_deviating_pixels(const struct device *dev, uint16_t *eeData) +{ + mlx90640_params *mlx90640 = &((struct mlx90640_data *)dev->data)->params; + uint16_t pixCnt = 0; + uint16_t brokenPixCnt = 0; + uint16_t outlierPixCnt = 0; + int warn = 0; + int i; + + for (pixCnt = 0; pixCnt < 5; pixCnt++) + { + mlx90640->brokenPixels[pixCnt] = 0xFFFF; + mlx90640->outlierPixels[pixCnt] = 0xFFFF; + } + + pixCnt = 0; + while (pixCnt < MLX90640_PIXEL_NUM && brokenPixCnt < 5 && outlierPixCnt < 5) + { + if (eeData[pixCnt + 64] == 0) + { + mlx90640->brokenPixels[brokenPixCnt] = pixCnt; + brokenPixCnt = brokenPixCnt + 1; + } + else if ((eeData[pixCnt + 64] & 0x0001) != 0) + { + mlx90640->outlierPixels[outlierPixCnt] = pixCnt; + outlierPixCnt = outlierPixCnt + 1; + } + + pixCnt = pixCnt + 1; + } + + if (brokenPixCnt > 4) + { + warn = -MLX90640_BROKEN_PIXELS_NUM_ERROR; + } + else if (outlierPixCnt > 4) + { + warn = -MLX90640_OUTLIER_PIXELS_NUM_ERROR; + } + else if ((brokenPixCnt + outlierPixCnt) > 4) + { + warn = -MLX90640_BAD_PIXELS_NUM_ERROR; + } + else + { + for (pixCnt = 0; pixCnt < brokenPixCnt; pixCnt++) + { + for (i = pixCnt + 1; i < brokenPixCnt; i++) + { + warn = mlx90640_int_check_adjacent_pixels(mlx90640->brokenPixels[pixCnt], mlx90640->brokenPixels[i]); + if (warn != 0) + { + return warn; + } + } + } + + for (pixCnt = 0; pixCnt < outlierPixCnt; pixCnt++) + { + for (i = pixCnt + 1; i < outlierPixCnt; i++) + { + warn = mlx90640_int_check_adjacent_pixels(mlx90640->outlierPixels[pixCnt], mlx90640->outlierPixels[i]); + if (warn != 0) + { + return warn; + } + } + } + + for (pixCnt = 0; pixCnt < brokenPixCnt; pixCnt++) + { + for (i = 0; i < outlierPixCnt; i++) + { + warn = mlx90640_int_check_adjacent_pixels(mlx90640->brokenPixels[pixCnt], mlx90640->outlierPixels[i]); + if (warn != 0) + { + return warn; + } + } + } + } + + return warn; +} + +int mlx90640_int_extract_parameters(const struct device *dev) +{ + int error = 0; + + uint16_t *ee_data = k_calloc(MLX90640_EEPROM_DUMP_NUM, 2); + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_EEPROM_START_ADDRESS, MLX90640_EEPROM_DUMP_NUM, ee_data); + + mlx90640_int_extract_vdd_parameters(dev, ee_data); + mlx90640_int_extract_ptat_parameters(dev, ee_data); + mlx90640_int_extract_gain_parameters(dev, ee_data); + mlx90640_int_extract_tgc_parameters(dev, ee_data); + mlx90640_int_extract_resolution_parameters(dev, ee_data); + mlx90640_int_extract_ks_ta_parameters(dev, ee_data); + mlx90640_int_extract_ks_to_parameters(dev, ee_data); + mlx90640_int_extract_cpp_parameters(dev, ee_data); + mlx90640_int_extract_alpha_parameters(dev, ee_data); + mlx90640_int_extract_offset_parameters(dev, ee_data); + mlx90640_int_extract_kta_pixel_parameters(dev, ee_data); + mlx90640_int_extract_kv_pixel_parameters(dev, ee_data); + mlx90640_int_extract_cilc_parameters(dev, ee_data); + error = mlx90640_int_extract_deviating_pixels(dev, ee_data); + + k_free(ee_data); + + return error; +} + +//-------------------------------------- +// configuration methods + +int mlx90640_set_adc_resolution(const struct device *dev, enum mlx90640_adc_resolution res) +{ + const struct mlx90640_config *config = dev->config; + + uint16_t controlRegister1; + uint16_t value; + int error; + + value = ((uint16_t)res << MLX90640_CTRL_RESOLUTION_SHIFT); + value &= ~MLX90640_CTRL_RESOLUTION_MASK; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + + if (error == MLX90640_NO_ERROR) + { + value = (controlRegister1 & MLX90640_CTRL_RESOLUTION_MASK) | value; + error = mlx90640_int_i2c_reg_write(dev, MLX90640_CTRL_REG, value); + } + + return error; +} + +enum mlx90640_adc_resolution mlx90640_get_adc_resolution(const struct device *dev) +{ + uint16_t controlRegister1; + int resolutionRAM; + int error; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + if (error != MLX90640_NO_ERROR) + { + return error; + } + resolutionRAM = (controlRegister1 & ~MLX90640_CTRL_RESOLUTION_MASK) >> MLX90640_CTRL_RESOLUTION_SHIFT; + + return resolutionRAM; +} + +int mlx90640_set_refresh_rate(const struct device *dev, enum mlx90640_refresh_rate rate) +{ + uint16_t controlRegister1; + uint16_t value; + int error; + + value = ((uint16_t)rate << MLX90640_CTRL_REFRESH_SHIFT); + value &= ~MLX90640_CTRL_REFRESH_MASK; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + if (error == MLX90640_NO_ERROR) + { + value = (controlRegister1 & MLX90640_CTRL_REFRESH_MASK) | value; + error = mlx90640_int_i2c_reg_write(dev, MLX90640_CTRL_REG, value); + } + + return error; +} + +enum mlx90640_refresh_rate mlx90640_get_refresh_rate(const struct device *dev) +{ + uint16_t controlRegister1; + int refreshRate; + int error; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + if (error != MLX90640_NO_ERROR) + { + return error; + } + refreshRate = (controlRegister1 & ~MLX90640_CTRL_REFRESH_MASK) >> MLX90640_CTRL_REFRESH_SHIFT; + + return refreshRate; +} + +int mlx90640_set_reading_pattern(const struct device *dev, enum mlx90640_reading_pattern pattern) +{ + uint16_t controlRegister1; + uint16_t value; + int error; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + + if (error == 0) + { + value = (controlRegister1 & ~MLX90640_CTRL_MEAS_MODE_MASK); + if (pattern == MLX90640_PATTERN_CHESS) + { + value = (controlRegister1 | MLX90640_CTRL_MEAS_MODE_MASK); + } + error = mlx90640_int_i2c_reg_write(dev, MLX90640_CTRL_REG, value); + } + + return error; +} + +enum mlx90640_reading_pattern mlx90640_get_reading_pattern(const struct device *dev) +{ + uint16_t controlRegister1; + int modeRAM; + int error; + + error = mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &controlRegister1); + if (error != 0) + { + return error; + } + modeRAM = (controlRegister1 & MLX90640_CTRL_MEAS_MODE_MASK) >> MLX90640_CTRL_MEAS_MODE_SHIFT; + + return modeRAM; +} + +//-------------------------------------- +// API methods + +static int mlx90640_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + // this method gets the raw data + struct mlx90640_data *drv_data = dev->data; + const struct mlx90640_config *config = dev->config; + + __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP); + + return mlx90640_get_frame_data(dev); +} + +static int mlx90640_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) +{ + // mlx90640_calculate_to(dev, float emissivity, float tr); + + // struct mlx90640_data *drv_data = dev->data; + // size_t len = ARRAY_SIZE(drv_data->sample); + + // if (chan != SENSOR_CHAN_AMBIENT_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 = (((int32_t)drv_data->sample[idx]) * MLX90640_TREG_LSB_SCALING) / 1000000; + // val[idx].val2 = (((int32_t)drv_data->sample[idx]) * MLX90640_TREG_LSB_SCALING) % 1000000; + // } + + return 0; +} + +static int mlx90640_init_device(const struct device *dev) +{ + const struct mlx90640_config *config = dev->config; + uint16_t tmp; + uint16_t dev_id[3]; + + // initial wait, 80ms + 2s/refresh_rate below config + k_sleep(K_MSEC(80)); + + mlx90640_int_i2c_reg_read(dev, MLX90640_DEVICE_ID1_ADDRESS, 3, dev_id); + printf("MLX90640 Device ID1: %04x %04x %04x\n", dev_id[0], dev_id[1], dev_id[2]); + + mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 1, &tmp); + printf("MLX90640 CTRL: %04x\n", tmp); + + uint16_t regs[0x16]; + mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 4, regs); + + + // printf("writing eeprom\n"); + // k_sleep(K_SECONDS(10)); + // printf("w_err:%d",mlx90640_int_i2c_reg_write(dev,0x240c,0x0000)); + // k_sleep(K_MSEC(10)); + // mlx90640_int_i2c_reg_read(dev, 0x2400, 16, regs); + // printf("w_err:%d",mlx90640_int_i2c_reg_write(dev,0x240c,0x1901)); + // k_sleep(K_MSEC(10)); + // mlx90640_int_i2c_reg_read(dev, 0x2400, 16, regs); + // printf("w_err:%d",mlx90640_int_i2c_reg_write(dev,0x240f,0x0000)); + // k_sleep(K_MSEC(10)); + // mlx90640_int_i2c_reg_read(dev, 0x2400, 16, regs); + // printf("w_err:%d",mlx90640_int_i2c_reg_write(dev,0x240f,0xBE33)); + // k_sleep(K_MSEC(10)); + // mlx90640_int_i2c_reg_read(dev, 0x2400, 16, regs); + // printf("eeprom done\n"); + + // k_sleep(K_SECONDS(1)); + // mlx90640_int_i2c_reg_read(dev, 0x2400, 16, regs); + // mlx90640_int_i2c_reg_read(dev, MLX90640_CTRL_REG, 4, regs); + + // k_sleep(K_SECONDS(10)); + + + // mlx90640_set_adc_resolution(dev, config->adc_resolution); + // mlx90640_set_refresh_rate(dev, config->refresh_rate); + // mlx90640_set_reading_pattern(dev, config->reading_pattern); + + switch (config->refresh_rate) + { + case MLX90640_REFRESH_0_5: + k_sleep(K_MSEC(4000)); + break; + case MLX90640_REFRESH_1: + k_sleep(K_MSEC(2000)); + break; + case MLX90640_REFRESH_2: + k_sleep(K_MSEC(1000)); + break; + case MLX90640_REFRESH_4: + k_sleep(K_MSEC(500)); + break; + case MLX90640_REFRESH_8: + k_sleep(K_MSEC(250)); + break; + case MLX90640_REFRESH_16: + k_sleep(K_MSEC(125)); + break; + case MLX90640_REFRESH_32: + k_sleep(K_MSEC(63)); + break; + case MLX90640_REFRESH_64: + k_sleep(K_MSEC(32)); + break; + } + + mlx90640_int_extract_parameters(dev); + + return 0; +} + +int mlx90640_init(const struct device *dev) +{ + const struct mlx90640_config *config = dev->config; + + if (!device_is_ready(config->i2c.bus)) + { + LOG_ERR("Bus device is not ready"); + return -EINVAL; + } + + if (mlx90640_init_device(dev) < 0) + { + LOG_ERR("Failed to initialize device!"); + return -EIO; + } + + // #ifdef CONFIG_MLX90640_TRIGGER + // if (mlx90640_init_interrupt(dev) < 0) + // { + // LOG_ERR("Failed to initialize interrupt!"); + // return -EIO; + // } + // #endif + + return 0; +} + +static const struct sensor_driver_api mlx90640_driver_api = { + // #ifdef CONFIG_MLX90640_TRIGGER + // .attr_set = mlx90640_attr_set, + // .trigger_set = mlx90640_trigger_set, + // #endif + .sample_fetch = mlx90640_sample_fetch, + .channel_get = mlx90640_channel_get, +}; + +#define MLX90640_DEFINE(inst) \ + static struct mlx90640_data mlx90640_data_##inst; \ + \ + static const struct mlx90640_config mlx90640_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .refresh_rate = DT_INST_PROP(inst, refresh_rate), \ + .adc_resolution = DT_INST_PROP(inst, adc_resolution), \ + .reading_pattern = DT_INST_PROP(inst, reading_pattern), \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, mlx90640_init, NULL, &mlx90640_data_##inst, &mlx90640_config_##inst, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &mlx90640_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MLX90640_DEFINE) diff --git a/drivers/sensor/mlx90640/mlx90640.h b/drivers/sensor/mlx90640/mlx90640.h new file mode 100644 index 0000000..eb4f348 --- /dev/null +++ b/drivers/sensor/mlx90640/mlx90640.h @@ -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 +#include +#include +#include + +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 diff --git a/drivers/sensor/mlx90640/mlx90640_trigger.c b/drivers/sensor/mlx90640/mlx90640_trigger.c new file mode 100644 index 0000000..7b8d497 --- /dev/null +++ b/drivers/sensor/mlx90640/mlx90640_trigger.c @@ -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 +#include +#include +#include +#include +#include +#include "amg88xx.h" + +extern struct amg88xx_data amg88xx_driver; + +#include +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; +} diff --git a/dts/bindings/sensor/melexis,mlx90640.yaml b/dts/bindings/sensor/melexis,mlx90640.yaml new file mode 100644 index 0000000..784d26a --- /dev/null +++ b/dts/bindings/sensor/melexis,mlx90640.yaml @@ -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.