From 7689622982481fd6704c41a1b24072d9e65a714f Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Tue, 12 Jan 2021 16:35:59 -0800 Subject: [PATCH] Add support for the MLX90641 (#1117) --- .../04-Thermal/thermal_camera.py | 3 + .../12-Thermopile-Shield/thermal_camera.py | 3 + src/Makefile | 1 + src/drivers/mlx90640/src/MLX90640_API.c | 38 +- src/drivers/mlx90641/LICENSE | 201 +++ src/drivers/mlx90641/Makefile | 25 + src/drivers/mlx90641/include/MLX90641_API.h | 69 + .../mlx90641/include/MLX90641_I2C_Driver.h | 25 + src/drivers/mlx90641/src/MLX90641_API.c | 1334 +++++++++++++++++ .../mlx90641/src/MLX90641_I2C_Driver.c | 69 + src/omv/boards/NANO33/omv_boardconfig.h | 1 + src/omv/boards/OPENMV2/omv_boardconfig.h | 1 + src/omv/boards/OPENMV3/omv_boardconfig.h | 1 + src/omv/boards/OPENMV4/omv_boardconfig.h | 1 + src/omv/boards/OPENMV4P/imlib_config.h | 2 +- src/omv/boards/OPENMV4P/omv_boardconfig.h | 1 + src/omv/boards/OPENMVPT/omv_boardconfig.h | 1 + src/omv/boards/PORTENTA/omv_boardconfig.h | 1 + src/omv/modules/py_fir.c | 139 +- src/omv/modules/qstrdefsomv.h | 1 + src/omv/ports/nrf/omv_portconfig.mk | 3 + src/omv/ports/stm32/omv_portconfig.mk | 4 + 22 files changed, 1899 insertions(+), 25 deletions(-) create mode 100644 src/drivers/mlx90641/LICENSE create mode 100644 src/drivers/mlx90641/Makefile create mode 100644 src/drivers/mlx90641/include/MLX90641_API.h create mode 100644 src/drivers/mlx90641/include/MLX90641_I2C_Driver.h create mode 100644 src/drivers/mlx90641/src/MLX90641_API.c create mode 100644 src/drivers/mlx90641/src/MLX90641_I2C_Driver.c diff --git a/scripts/examples/Arduino/Nano 33 BLE Sense/04-Thermal/thermal_camera.py b/scripts/examples/Arduino/Nano 33 BLE Sense/04-Thermal/thermal_camera.py index 008a96d44..eaff8bf8b 100644 --- a/scripts/examples/Arduino/Nano 33 BLE Sense/04-Thermal/thermal_camera.py +++ b/scripts/examples/Arduino/Nano 33 BLE Sense/04-Thermal/thermal_camera.py @@ -18,6 +18,9 @@ if (fir.type() == fir.FIR_MLX90621): elif (fir.type() == fir.FIR_MLX90640): w = w * 5 h = h * 5 +elif (fir.type() == fir.FIR_MLX90641): + w = w * 5 + h = h * 5 elif (fir.type() == fir.FIR_AMG8833): w = w * 10 h = h * 10 diff --git a/scripts/examples/OpenMV/12-Thermopile-Shield/thermal_camera.py b/scripts/examples/OpenMV/12-Thermopile-Shield/thermal_camera.py index 59249a416..c43ca4494 100644 --- a/scripts/examples/OpenMV/12-Thermopile-Shield/thermal_camera.py +++ b/scripts/examples/OpenMV/12-Thermopile-Shield/thermal_camera.py @@ -18,6 +18,9 @@ if (fir.type() == fir.FIR_MLX90621): elif (fir.type() == fir.FIR_MLX90640): w = w * 10 h = h * 10 +elif (fir.type() == fir.FIR_MLX90641): + w = w * 10 + h = h * 10 elif (fir.type() == fir.FIR_AMG8833): w = w * 20 h = h * 20 diff --git a/src/Makefile b/src/Makefile index 69847664d..aba10cc65 100755 --- a/src/Makefile +++ b/src/Makefile @@ -55,6 +55,7 @@ LSM6DS3_DIR=drivers/lsm6ds3 WINC1500_DIR=drivers/winc1500 MLX90621_DIR=drivers/mlx90621 MLX90640_DIR=drivers/mlx90640 +MLX90641_DIR=drivers/mlx90641 LIBPDM_DIR=lib/libpdm TENSORFLOW_DIR=lib/libtf OMV_BOARD_CONFIG_DIR=$(TOP_DIR)/$(OMV_DIR)/boards/$(TARGET)/ diff --git a/src/drivers/mlx90640/src/MLX90640_API.c b/src/drivers/mlx90640/src/MLX90640_API.c index 73a239d96..5e6179697 100644 --- a/src/drivers/mlx90640/src/MLX90640_API.c +++ b/src/drivers/mlx90640/src/MLX90640_API.c @@ -21,25 +21,25 @@ #define pow(a,b) powf(a,b) #define sqrt(x) sqrtf(x) -void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); -int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640); -int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2); -float GetMedian(float *values, int n); -int IsPixelBad(uint16_t pixel,paramsMLX90640 *params); -int ValidateFrameData(uint16_t *frameData); -int ValidateAuxData(uint16_t *auxData); +static void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640); +static int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640); +static int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2); +static float GetMedian(float *values, int n); +static int IsPixelBad(uint16_t pixel,paramsMLX90640 *params); +static int ValidateFrameData(uint16_t *frameData); +static int ValidateAuxData(uint16_t *auxData); int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData) { diff --git a/src/drivers/mlx90641/LICENSE b/src/drivers/mlx90641/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/drivers/mlx90641/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/drivers/mlx90641/Makefile b/src/drivers/mlx90641/Makefile new file mode 100644 index 000000000..626914d50 --- /dev/null +++ b/src/drivers/mlx90641/Makefile @@ -0,0 +1,25 @@ +# This file is part of the OpenMV project. +# +# Copyright (c) 2013-2021 Ibrahim Abdelkader +# Copyright (c) 2013-2021 Kwabena W. Agyeman +# +# This work is licensed under the MIT license, see the file LICENSE for details. +# +# MLX90641 Makefile +SRCS = $(wildcard src/*.c) +OBJS = $(addprefix $(BUILD)/, $(SRCS:.c=.o)) +OBJ_DIRS = $(sort $(dir $(OBJS))) + +all: | $(OBJ_DIRS) $(OBJS) +$(OBJ_DIRS): + $(MKDIR) -p $@ + +$(BUILD)/%.o : %.c + $(ECHO) "CC $<" + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/%.o : %.s + $(ECHO) "AS $<" + $(AS) $(AFLAGS) $< -o $@ + +-include $(OBJS:%.o=%.d) diff --git a/src/drivers/mlx90641/include/MLX90641_API.h b/src/drivers/mlx90641/include/MLX90641_API.h new file mode 100644 index 000000000..29a9d2781 --- /dev/null +++ b/src/drivers/mlx90641/include/MLX90641_API.h @@ -0,0 +1,69 @@ +/** + * @copyright (C) 2017 Melexis N.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef _MLX90641_API_H_ +#define _MLX90641_API_H_ + +#define SCALEALPHA 0.000001 + + 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[8]; + int16_t ct[8]; + uint16_t alpha[192]; + uint8_t alphaScale; + int16_t offset[2][192]; + int8_t kta[192]; + uint8_t ktaScale; + int8_t kv[192]; + uint8_t kvScale; + float cpAlpha; + int16_t cpOffset; + float emissivityEE; + uint16_t brokenPixel; + } paramsMLX90641; + + int MLX90641_DumpEE(uint8_t slaveAddr, uint16_t *eeData); + int MLX90641_SynchFrame(uint8_t slaveAddr); + int MLX90641_TriggerMeasurement(uint8_t slaveAddr); + int MLX90641_GetFrameData(uint8_t slaveAddr, uint16_t *frameData); + int MLX90641_ExtractParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); + float MLX90641_GetVdd(uint16_t *frameData, const paramsMLX90641 *params); + float MLX90641_GetTa(uint16_t *frameData, const paramsMLX90641 *params); + void MLX90641_GetImage(uint16_t *frameData, const paramsMLX90641 *params, float *result); + void MLX90641_CalculateTo(uint16_t *frameData, const paramsMLX90641 *params, float emissivity, float tr, float *result); + int MLX90641_SetResolution(uint8_t slaveAddr, uint8_t resolution); + int MLX90641_GetCurResolution(uint8_t slaveAddr); + int MLX90641_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate); + int MLX90641_GetRefreshRate(uint8_t slaveAddr); + int MLX90641_GetSubPageNumber(uint16_t *frameData); + float MLX90641_GetEmissivity(const paramsMLX90641 *mlx90641); + void MLX90641_BadPixelsCorrection(uint16_t pixel, float *to); + +#endif diff --git a/src/drivers/mlx90641/include/MLX90641_I2C_Driver.h b/src/drivers/mlx90641/include/MLX90641_I2C_Driver.h new file mode 100644 index 000000000..4df4ff76f --- /dev/null +++ b/src/drivers/mlx90641/include/MLX90641_I2C_Driver.h @@ -0,0 +1,25 @@ +/** + * @copyright (C) 2017 Melexis N.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef _MLX90641_I2C_Driver_H_ +#define _MLX90641_I2C_Driver_H_ +#include +void MLX90641_I2CInit(cambus_t *bus); +int MLX90641_I2CGeneralReset(void); +int MLX90641_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data); +int MLX90641_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data); +void MLX90641_I2CFreqSet(int freq); +#endif //_MLX90641_I2C_Driver_H_ diff --git a/src/drivers/mlx90641/src/MLX90641_API.c b/src/drivers/mlx90641/src/MLX90641_API.c new file mode 100644 index 000000000..80de76e03 --- /dev/null +++ b/src/drivers/mlx90641/src/MLX90641_API.c @@ -0,0 +1,1334 @@ +/** + * @copyright (C) 2017 Melexis N.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "cambus.h" +#include +#include +#include +#define fabs(x) fabsf(x) +#define pow(a,b) powf(a,b) +#define sqrt(x) sqrtf(x) + +static void ExtractVDDParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractPTATParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractGainParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractTgcParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractEmissivityParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractKsToParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static void ExtractCPParameters(uint16_t *eeData, paramsMLX90641 *mlx90641); +static int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90641 *mlx90641); +static int CheckEEPROMValid(uint16_t *eeData); +static int HammingDecode(uint16_t *eeData); +static int ValidateFrameData(uint16_t *frameData); +static int ValidateAuxData(uint16_t *auxData); + +//------------------------------------------------------------------------------ + +int MLX90641_DumpEE(uint8_t slaveAddr, uint16_t *eeData) +{ + int error = 1; + error = MLX90641_I2CRead(slaveAddr, 0x2400, 832, eeData); + if (error == 0) + { + error = HammingDecode(eeData); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int HammingDecode(uint16_t *eeData) +{ + int error = 0; + int16_t parity[5]; + int8_t D[16]; + int16_t check; + uint16_t data; + uint16_t mask; + + for (int addr=16; addr<832; addr++) + { + parity[0] = -1; + parity[1] = -1; + parity[2] = -1; + parity[3] = -1; + parity[4] = -1; + + data = eeData[addr]; + + mask = 1; + for( int i = 0; i < 16; i++) + { + D[i] = (data & mask) >> i; + mask = mask << 1; + } + + parity[0] = D[0]^D[1]^D[3]^D[4]^D[6]^D[8]^D[10]^D[11]; + parity[1] = D[0]^D[2]^D[3]^D[5]^D[6]^D[9]^D[10]^D[12]; + parity[2] = D[1]^D[2]^D[3]^D[7]^D[8]^D[9]^D[10]^D[13]; + parity[3] = D[4]^D[5]^D[6]^D[7]^D[8]^D[9]^D[10]^D[14]; + parity[4] = D[0]^D[1]^D[2]^D[3]^D[4]^D[5]^D[6]^D[7]^D[8]^D[9]^D[10]^D[11]^D[12]^D[13]^D[14]^D[15]; + + + if ((parity[0]!=0) || (parity[1]!=0) || (parity[2]!=0) || (parity[3]!=0) || (parity[4]!=0)) + { + check = (parity[0]<<0) + (parity[1]<<1) + (parity[2]<<2) + (parity[3]<<3) + (parity[4]<<4); + + if ((check > 15)&&(check < 32)) + { + switch (check) + { + case 16: + D[15] = 1 - D[15]; + break; + + case 24: + D[14] = 1 - D[14]; + break; + + case 20: + D[13] = 1 - D[13]; + break; + + case 18: + D[12] = 1 - D[12]; + break; + + case 17: + D[11] = 1 - D[11]; + break; + + case 31: + D[10] = 1 - D[10]; + break; + + case 30: + D[9] = 1 - D[9]; + break; + + case 29: + D[8] = 1 - D[8]; + break; + + case 28: + D[7] = 1 - D[7]; + break; + + case 27: + D[6] = 1 - D[6]; + break; + + case 26: + D[5] = 1 - D[5]; + break; + + case 25: + D[4] = 1 - D[4]; + break; + + case 23: + D[3] = 1 - D[3]; + break; + + case 22: + D[2] = 1 - D[2]; + break; + + case 21: + D[1] = 1 - D[1]; + break; + + case 19: + D[0] = 1 - D[0]; + break; + + } + + if(error == 0) + { + error = -9; + + } + + data = 0; + mask = 1; + for( int i = 0; i < 16; i++) + { + data = data + D[i]*mask; + mask = mask << 1; + } + + } + else + { + error = -10; + } + } + + eeData[addr] = data & 0x07FF; + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90641_SynchFrame(uint8_t slaveAddr) +{ + uint16_t dataReady = 0; + uint16_t statusRegister; + int error = 1; + + error = MLX90641_I2CWrite(slaveAddr, 0x8000, 0x0030); + if(error == -1) + { + return error; + } + + while(dataReady == 0) + { + error = MLX90641_I2CRead(slaveAddr, 0x8000, 1, &statusRegister); + if(error != 0) + { + return error; + } + dataReady = statusRegister & 0x0008; + } + + return 0; +} + +//------------------------------------------------------------------------------ + +int MLX90641_TriggerMeasurement(uint8_t slaveAddr) +{ + int error = 1; + uint16_t ctrlReg; + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg); + + if ( error != 0) + { + return error; + } + + ctrlReg |= 0x8000; + error = MLX90641_I2CWrite(slaveAddr, 0x800D, ctrlReg); + + if ( error != 0) + { + return error; + } + + error = MLX90641_I2CGeneralReset(); + + if ( error != 0) + { + return error; + } + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg); + + if ( error != 0) + { + return error; + } + + if ((ctrlReg & 0x8000) != 0) + { + return -11; + } + + return 0; +} + +//------------------------------------------------------------------------------ + +int MLX90641_GetFrameData(uint8_t slaveAddr, uint16_t *frameData) +{ + uint16_t dataReady = 1; + uint16_t controlRegister1; + uint16_t statusRegister; + uint16_t data[48]; + int error = 1; + uint8_t cnt = 0; + uint8_t subPage = 0; + + dataReady = 0; + while(dataReady == 0) + { + error = MLX90641_I2CRead(slaveAddr, 0x8000, 1, &statusRegister); + if(error != 0) + { + return error; + } + dataReady = statusRegister & 0x0008; + } + subPage = statusRegister & 0x0001; + + error = MLX90641_I2CWrite(slaveAddr, 0x8000, 0x0030); + if(error == -1) + { + return error; + } + + if(subPage == 0) + { + error = MLX90641_I2CRead(slaveAddr, 0x0400, 32, frameData); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0440, 32, frameData+32); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0480, 32, frameData+64); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x04C0, 32, frameData+96); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0500, 32, frameData+128); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0540, 32, frameData+160); + if(error != 0) + { + return error; + } + } + else + { + error = MLX90641_I2CRead(slaveAddr, 0x0420, 32, frameData); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0460, 32, frameData+32); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x04A0, 32, frameData+64); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x04E0, 32, frameData+96); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0520, 32, frameData+128); + if(error != 0) + { + return error; + } + error = MLX90641_I2CRead(slaveAddr, 0x0560, 32, frameData+160); + if(error != 0) + { + return error; + } + } + + error = MLX90641_I2CRead(slaveAddr, 0x0580, 48, data); + if(error != 0) + { + return error; + } + + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + frameData[240] = controlRegister1; + frameData[241] = subPage; + + if(error != 0) + { + return error; + } + + error = ValidateAuxData(data); + if(error == 0) + { + for(cnt=0; cnt<48; cnt++) + { + frameData[cnt+192] = data[cnt]; + } + } + + error = ValidateFrameData(frameData); + if (error != 0) + { + return error; + } + + return frameData[241]; +} + +int ValidateFrameData(uint16_t *frameData) +{ + uint8_t line = 0; + + for(int i=0; i<192; i+=16) + { + if(frameData[i] == 0x7FFF) return -8; + line = line + 1; + } + + return 0; +} + +int ValidateAuxData(uint16_t *auxData) +{ + + if(auxData[0] == 0x7FFF) return -8; + + for(int i=8; i<19; i++) + { + if(auxData[i] == 0x7FFF) return -8; + } + + for(int i=20; i<23; i++) + { + if(auxData[i] == 0x7FFF) return -8; + } + + for(int i=24; i<33; i++) + { + if(auxData[i] == 0x7FFF) return -8; + } + + for(int i=40; i<48; i++) + { + if(auxData[i] == 0x7FFF) return -8; + } + + return 0; + +} + +//------------------------------------------------------------------------------ + +int MLX90641_ExtractParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + int error = CheckEEPROMValid(eeData); + + if(error == 0) + { + ExtractVDDParameters(eeData, mlx90641); + ExtractPTATParameters(eeData, mlx90641); + ExtractGainParameters(eeData, mlx90641); + ExtractTgcParameters(eeData, mlx90641); + ExtractEmissivityParameters(eeData, mlx90641); + ExtractResolutionParameters(eeData, mlx90641); + ExtractKsTaParameters(eeData, mlx90641); + ExtractKsToParameters(eeData, mlx90641); + ExtractCPParameters(eeData, mlx90641); + ExtractAlphaParameters(eeData, mlx90641); + ExtractOffsetParameters(eeData, mlx90641); + ExtractKtaPixelParameters(eeData, mlx90641); + ExtractKvPixelParameters(eeData, mlx90641); + error = ExtractDeviatingPixels(eeData, mlx90641); + } + + return error; + +} + +//------------------------------------------------------------------------------ + +int MLX90641_SetResolution(uint8_t slaveAddr, uint8_t resolution) +{ + uint16_t controlRegister1; + int value; + int error; + + value = (resolution & 0x03) << 10; + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + + if(error == 0) + { + value = (controlRegister1 & 0xF3FF) | value; + error = MLX90641_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90641_GetCurResolution(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int resolutionRAM; + int error; + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error != 0) + { + return error; + } + resolutionRAM = (controlRegister1 & 0x0C00) >> 10; + + return resolutionRAM; +} + +//------------------------------------------------------------------------------ + +int MLX90641_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate) +{ + uint16_t controlRegister1; + int value; + int error; + + value = (refreshRate & 0x07)<<7; + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error == 0) + { + value = (controlRegister1 & 0xFC7F) | value; + error = MLX90641_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90641_GetRefreshRate(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int refreshRate; + int error; + + error = MLX90641_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error != 0) + { + return error; + } + refreshRate = (controlRegister1 & 0x0380) >> 7; + + return refreshRate; +} + +//------------------------------------------------------------------------------ + +void MLX90641_CalculateTo(uint16_t *frameData, const paramsMLX90641 *params, float emissivity, float tr, float *result) +{ + float vdd; + float ta; + float ta4; + float tr4; + float taTr; + float gain; + float irDataCP; + float irData; + float alphaCompensated; + float Sx; + float To; + float alphaCorrR[8]; + int8_t range; + uint16_t subPage; + float ktaScale; + float kvScale; + float alphaScale; + float kta; + float kv; + + subPage = frameData[241]; + vdd = MLX90641_GetVdd(frameData, params); + ta = MLX90641_GetTa(frameData, params); + 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 = pow(2,(double)params->ktaScale); + kvScale = pow(2,(double)params->kvScale); + alphaScale = pow(2,(double)params->alphaScale); + + alphaCorrR[1] = 1 / (1 + params->ksTo[1] * 20); + alphaCorrR[0] = alphaCorrR[1] / (1 + params->ksTo[0] * 20); + alphaCorrR[2] = 1 ; + alphaCorrR[3] = (1 + params->ksTo[2] * params->ct[3]); + alphaCorrR[4] = alphaCorrR[3] * (1 + params->ksTo[3] * (params->ct[4] - params->ct[3])); + alphaCorrR[5] = alphaCorrR[4] * (1 + params->ksTo[4] * (params->ct[5] - params->ct[4])); + alphaCorrR[6] = alphaCorrR[5] * (1 + params->ksTo[5] * (params->ct[6] - params->ct[5])); + alphaCorrR[7] = alphaCorrR[6] * (1 + params->ksTo[6] * (params->ct[7] - params->ct[6])); + +//------------------------- Gain calculation ----------------------------------- + gain = frameData[202]; + if(gain > 32767) + { + gain = gain - 65536; + } + + gain = params->gainEE / gain; + +//------------------------- To calculation ------------------------------------- + irDataCP = frameData[200]; + if(irDataCP > 32767) + { + irDataCP = irDataCP - 65536; + } + irDataCP = irDataCP * gain; + + irDataCP = irDataCP - params->cpOffset * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3)); + + for( int pixelNumber = 0; pixelNumber < 192; pixelNumber++) + { + irData = frameData[pixelNumber]; + if(irData > 32767) + { + irData = irData - 65536; + } + irData = irData * gain; + + kta = (float)params->kta[pixelNumber]/ktaScale; + kv = (float)params->kv[pixelNumber]/kvScale; + + irData = irData - params->offset[subPage][pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3)); + + irData = irData - params->tgc * irDataCP; + + 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[2]; + + To = sqrt(sqrt(irData/(alphaCompensated * (1 - params->ksTo[2] * 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 if(To < params->ct[4]) + { + range = 3; + } + else if(To < params->ct[5]) + { + range = 4; + } + else if(To < params->ct[6]) + { + range = 5; + } + else if(To < params->ct[7]) + { + range = 6; + } + else + { + range = 7; + } + + To = sqrt(sqrt(irData / (alphaCompensated * alphaCorrR[range] * (1 + params->ksTo[range] * (To - params->ct[range]))) + taTr)) - 273.15; + + result[pixelNumber] = To; + } +} + +//------------------------------------------------------------------------------ + +void MLX90641_GetImage(uint16_t *frameData, const paramsMLX90641 *params, float *result) +{ + float vdd; + float ta; + float gain; + float irDataCP; + float irData; + float alphaCompensated; + float image; + uint16_t subPage; + float ktaScale; + float kvScale; + float kta; + float kv; + + subPage = frameData[241]; + + vdd = MLX90641_GetVdd(frameData, params); + ta = MLX90641_GetTa(frameData, params); + ktaScale = pow(2,(double)params->ktaScale); + kvScale = pow(2,(double)params->kvScale); + +//------------------------- Gain calculation ----------------------------------- + gain = frameData[202]; + if(gain > 32767) + { + gain = gain - 65536; + } + + gain = params->gainEE / gain; + +//------------------------- Image calculation ------------------------------------- + irDataCP = frameData[200]; + if(irDataCP > 32767) + { + irDataCP = irDataCP - 65536; + } + irDataCP = irDataCP * gain; + + irDataCP = irDataCP - params->cpOffset * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3)); + + for( int pixelNumber = 0; pixelNumber < 192; pixelNumber++) + { + irData = frameData[pixelNumber]; + if(irData > 32767) + { + irData = irData - 65536; + } + irData = irData * gain; + + kta = (float)params->kta[pixelNumber]/ktaScale; + kv = (float)params->kv[pixelNumber]/kvScale; + + irData = irData - params->offset[subPage][pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3)); + + irData = irData - params->tgc * irDataCP; + + alphaCompensated = (params->alpha[pixelNumber] - params->tgc * params->cpAlpha); + + image = irData*alphaCompensated; + + result[pixelNumber] = image; + } +} + +//------------------------------------------------------------------------------ + +float MLX90641_GetVdd(uint16_t *frameData, const paramsMLX90641 *params) +{ + float vdd; + float resolutionCorrection; + + int resolutionRAM; + + vdd = frameData[234]; + if(vdd > 32767) + { + vdd = vdd - 65536; + } + resolutionRAM = (frameData[240] & 0x0C00) >> 10; + resolutionCorrection = pow(2, (double)params->resolutionEE) / pow(2, (double)resolutionRAM); + vdd = (resolutionCorrection * vdd - params->vdd25) / params->kVdd + 3.3; + + return vdd; +} + +//------------------------------------------------------------------------------ + +float MLX90641_GetTa(uint16_t *frameData, const paramsMLX90641 *params) +{ + float ptat; + float ptatArt; + float vdd; + float ta; + + vdd = MLX90641_GetVdd(frameData, params); + + ptat = frameData[224]; + if(ptat > 32767) + { + ptat = ptat - 65536; + } + + ptatArt = frameData[192]; + if(ptatArt > 32767) + { + ptatArt = ptatArt - 65536; + } + ptatArt = (ptat / (ptat * params->alphaPTAT + ptatArt)) * pow(2, (double)18); + + ta = (ptatArt / (1 + params->KvPTAT * (vdd - 3.3)) - params->vPTAT25); + ta = ta / params->KtPTAT + 25; + + return ta; +} + +//------------------------------------------------------------------------------ + +int MLX90641_GetSubPageNumber(uint16_t *frameData) +{ + return frameData[241]; + +} + +//------------------------------------------------------------------------------ +void MLX90641_BadPixelsCorrection(uint16_t pixel, float *to) +{ + float ap[2]; + uint8_t line; + uint8_t column; + + if(pixel<192) + { + line = pixel>>4; + column = pixel - (line<<4); + + if(column == 0) + { + to[pixel] = to[pixel+1]; + } + else if(column == 1 || column == 14) + { + to[pixel] = (to[pixel-1]+to[pixel+1])/2.0; + } + else if(column == 15) + { + to[pixel] = to[pixel-1]; + } + else + { + ap[0] = to[pixel+1] - to[pixel+2]; + ap[1] = to[pixel-1] - to[pixel-2]; + if(fabs(ap[0]) > fabs(ap[1])) + { + to[pixel] = to[pixel-1] + ap[1]; + } + else + { + to[pixel] = to[pixel+1] + ap[0]; + } + + } + } +} + +//------------------------------------------------------------------------------ +void ExtractVDDParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + int16_t kVdd; + int16_t vdd25; + + kVdd = eeData[39]; + if(kVdd > 1023) + { + kVdd = kVdd - 2048; + } + kVdd = 32 * kVdd; + + vdd25 = eeData[38]; + if(vdd25 > 1023) + { + vdd25 = vdd25 - 2048; + } + vdd25 = 32 * vdd25; + + mlx90641->kVdd = kVdd; + mlx90641->vdd25 = vdd25; +} + +//------------------------------------------------------------------------------ + +void ExtractPTATParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float KvPTAT; + float KtPTAT; + int16_t vPTAT25; + float alphaPTAT; + + KvPTAT = eeData[43]; + if(KvPTAT > 1023) + { + KvPTAT = KvPTAT - 2048; + } + KvPTAT = KvPTAT/4096; + + KtPTAT = eeData[42]; + if(KtPTAT > 1023) + { + KtPTAT = KtPTAT - 2048; + } + KtPTAT = KtPTAT/8; + + vPTAT25 = 32 * eeData[40] + eeData[41]; + + alphaPTAT = eeData[44] / 128.0f; + + mlx90641->KvPTAT = KvPTAT; + mlx90641->KtPTAT = KtPTAT; + mlx90641->vPTAT25 = vPTAT25; + mlx90641->alphaPTAT = alphaPTAT; +} + +//------------------------------------------------------------------------------ + +void ExtractGainParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + int16_t gainEE; + + gainEE = 32 * eeData[36] + eeData[37]; + + mlx90641->gainEE = gainEE; +} + +//------------------------------------------------------------------------------ + +void ExtractTgcParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float tgc; + tgc = eeData[51] & 0x01FF; + if(tgc > 255) + { + tgc = tgc - 512; + } + tgc = tgc / 64.0f; + + mlx90641->tgc = tgc; +} + +//------------------------------------------------------------------------------ + +void ExtractEmissivityParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float emissivity; + emissivity = eeData[35]; + + if(emissivity > 1023) + { + emissivity = emissivity - 2048; + } + emissivity = emissivity/512; + + mlx90641->emissivityEE = emissivity; +} + +//------------------------------------------------------------------------------ + +void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + uint8_t resolutionEE; + resolutionEE = (eeData[51] & 0x0600) >> 9; + + mlx90641->resolutionEE = resolutionEE; +} + +//------------------------------------------------------------------------------ + +void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float KsTa; + KsTa = eeData[34]; + if(KsTa > 1023) + { + KsTa = KsTa - 2048; + } + KsTa = KsTa / 32768.0f; + + mlx90641->KsTa = KsTa; +} + +//------------------------------------------------------------------------------ + +void ExtractKsToParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + int KsToScale; + + mlx90641->ct[0] = -40; + mlx90641->ct[1] = -20; + mlx90641->ct[2] = 0; + mlx90641->ct[3] = 80; + mlx90641->ct[4] = 120; + mlx90641->ct[5] = eeData[58]; + mlx90641->ct[6] = eeData[60]; + mlx90641->ct[7] = eeData[62]; + + KsToScale = eeData[52]; + KsToScale = 1 << KsToScale; + + mlx90641->ksTo[0] = eeData[53]; + mlx90641->ksTo[1] = eeData[54]; + mlx90641->ksTo[2] = eeData[55]; + mlx90641->ksTo[3] = eeData[56]; + mlx90641->ksTo[4] = eeData[57]; + mlx90641->ksTo[5] = eeData[59]; + mlx90641->ksTo[6] = eeData[61]; + mlx90641->ksTo[7] = eeData[63]; + + + for(int i = 0; i < 8; i++) + { + if(mlx90641->ksTo[i] > 1023) + { + mlx90641->ksTo[i] = mlx90641->ksTo[i] - 2048; + } + mlx90641->ksTo[i] = mlx90641->ksTo[i] / KsToScale; + } +} + +//------------------------------------------------------------------------------ + +void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float rowMaxAlphaNorm[6]; + uint16_t scaleRowAlpha[6]; + uint8_t alphaScale; + float alphaTemp[192]; + float temp; + int p = 0; + + scaleRowAlpha[0] = (eeData[25] >> 5) + 20; + scaleRowAlpha[1] = (eeData[25] & 0x001F) + 20; + scaleRowAlpha[2] = (eeData[26] >> 5) + 20; + scaleRowAlpha[3] = (eeData[26] & 0x001F) + 20; + scaleRowAlpha[4] = (eeData[27] >> 5) + 20; + scaleRowAlpha[5] = (eeData[27] & 0x001F) + 20; + + + for(int i = 0; i < 6; i++) + { + rowMaxAlphaNorm[i] = eeData[28 + i] / pow(2,(double)scaleRowAlpha[i]); + rowMaxAlphaNorm[i] = rowMaxAlphaNorm[i] / 2047.0f; + } + + for(int i = 0; i < 6; i++) + { + for(int j = 0; j < 32; j ++) + { + p = 32 * i +j; + alphaTemp[p] = eeData[256 + p] * rowMaxAlphaNorm[i]; + alphaTemp[p] = alphaTemp[p] - mlx90641->tgc * mlx90641->cpAlpha; + alphaTemp[p] = SCALEALPHA/alphaTemp[p]; + } + } + + temp = alphaTemp[0]; + for(int i = 1; i < 192; i++) + { + if (alphaTemp[i] > temp) + { + temp = alphaTemp[i]; + } + } + + alphaScale = 0; + while(temp < 32768) + { + temp = temp*2; + alphaScale = alphaScale + 1; + } + + for(int i = 0; i < 192; i++) + { + temp = alphaTemp[i] * pow(2,(double)alphaScale); + mlx90641->alpha[i] = (temp + 0.5); + + } + + mlx90641->alphaScale = alphaScale; +} + +//------------------------------------------------------------------------------ + +void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + int scaleOffset; + int16_t offsetRef; + int16_t tempOffset; + + scaleOffset = eeData[16] >> 5; + scaleOffset = 1 << scaleOffset; + + offsetRef = 32 * eeData[17] + eeData[18]; + if (offsetRef > 32767) + { + offsetRef = offsetRef - 65536; + } + + for(int i = 0; i < 192; i++) + { + tempOffset = eeData[64 + i]; + if(tempOffset > 1023) + { + tempOffset = eeData[64 + i] - 2048; + } + mlx90641->offset[0][i] = tempOffset * scaleOffset + offsetRef; + + tempOffset = eeData[640 + i]; + if(tempOffset > 1023) + { + tempOffset = eeData[640 + i] - 2048; + } + mlx90641->offset[1][i] = tempOffset * scaleOffset + offsetRef; + } +} + +//------------------------------------------------------------------------------ + +void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + uint8_t ktaScale1; + uint8_t ktaScale2; + int16_t ktaAvg; + int16_t tempKta; + float ktaTemp[192]; + float temp; + + ktaAvg = eeData[21]; + if (ktaAvg > 1023) + { + ktaAvg = ktaAvg - 2048; + } + + ktaScale1 = eeData[22] >> 5; + ktaScale2 = eeData[22] & 0x001F; + + for(int i = 0; i < 192; i++) + { + tempKta = (eeData[448 + i] >> 5); + if (tempKta > 31) + { + tempKta = tempKta - 64; + } + + ktaTemp[i] = tempKta * pow(2,(double)ktaScale2); + ktaTemp[i] = ktaTemp[i] + ktaAvg; + ktaTemp[i] = ktaTemp[i] / pow(2,(double)ktaScale1); + } + + temp = fabs(ktaTemp[0]); + for(int i = 1; i < 192; i++) + { + if (fabs(ktaTemp[i]) > temp) + { + temp = fabs(ktaTemp[i]); + } + } + + ktaScale1 = 0; + while(temp < 64) + { + temp = temp*2; + ktaScale1 = ktaScale1 + 1; + } + + for(int i = 0; i < 192; i++) + { + temp = ktaTemp[i] * pow(2,(double)ktaScale1); + if (temp < 0) + { + mlx90641->kta[i] = (temp - 0.5); + } + else + { + mlx90641->kta[i] = (temp + 0.5); + } + + } + + mlx90641->ktaScale = ktaScale1; +} + +//------------------------------------------------------------------------------ + +void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + uint8_t kvScale1; + uint8_t kvScale2; + int16_t kvAvg; + int16_t tempKv; + float kvTemp[192]; + float temp; + + kvAvg = eeData[23]; + if (kvAvg > 1023) + { + kvAvg = kvAvg - 2048; + } + + kvScale1 = eeData[24] >> 5; + kvScale2 = eeData[24] & 0x001F; + + for(int i = 0; i < 192; i++) + { + tempKv = (eeData[448 + i] & 0x001F); + if (tempKv > 15) + { + tempKv = tempKv - 32; + } + + kvTemp[i] = tempKv * pow(2,(double)kvScale2); + kvTemp[i] = kvTemp[i] + kvAvg; + kvTemp[i] = kvTemp[i] / pow(2,(double)kvScale1); + } + + temp = fabs(kvTemp[0]); + for(int i = 1; i < 192; i++) + { + if (fabs(kvTemp[i]) > temp) + { + temp = fabs(kvTemp[i]); + } + } + + kvScale1 = 0; + while(temp < 64) + { + temp = temp*2; + kvScale1 = kvScale1 + 1; + } + + for(int i = 0; i < 192; i++) + { + temp = kvTemp[i] * pow(2,(double)kvScale1); + if (temp < 0) + { + mlx90641->kv[i] = (temp - 0.5); + } + else + { + mlx90641->kv[i] = (temp + 0.5); + } + + } + + mlx90641->kvScale = kvScale1; +} + +//------------------------------------------------------------------------------ + +void ExtractCPParameters(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + float alphaCP; + int16_t offsetCP; + float cpKv; + float cpKta; + uint8_t alphaScale; + uint8_t ktaScale1; + uint8_t kvScale; + + alphaScale = eeData[46]; + + offsetCP = 32 * eeData[47] + eeData[48]; + if (offsetCP > 32767) + { + offsetCP = offsetCP - 65536; + } + + alphaCP = eeData[45]; + if (alphaCP > 1023) + { + alphaCP = alphaCP - 2048; + } + + alphaCP = alphaCP / pow(2,(double)alphaScale); + + + cpKta = eeData[49] & 0x001F; + if (cpKta > 31) + { + cpKta = cpKta - 64; + } + ktaScale1 = eeData[49] >> 6; + mlx90641->cpKta = cpKta / pow(2,(double)ktaScale1); + + cpKv = eeData[50] & 0x001F; + if (cpKv > 31) + { + cpKv = cpKv - 64; + } + kvScale = eeData[50] >> 6; + mlx90641->cpKv = cpKv / pow(2,(double)kvScale); + + mlx90641->cpAlpha = alphaCP; + mlx90641->cpOffset = offsetCP; +} + +//------------------------------------------------------------------------------ + +float MLX90641_GetEmissivity(const paramsMLX90641 *mlx90641) +{ + return mlx90641->emissivityEE; +} + +//------------------------------------------------------------------------------ + +int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90641 *mlx90641) +{ + uint16_t pixCnt = 0; + uint16_t brokenPixCnt = 0; + + int warn = 0; + + mlx90641->brokenPixel = 0xFFFF; + + pixCnt = 0; + while (pixCnt < 192 && brokenPixCnt < 2) + { + if((eeData[pixCnt+64] == 0) && (eeData[pixCnt+256] == 0) && (eeData[pixCnt+448] == 0) && (eeData[pixCnt+640] == 0)) + { + mlx90641->brokenPixel = pixCnt; + brokenPixCnt = brokenPixCnt + 1; + } + + pixCnt = pixCnt + 1; + } + + if(brokenPixCnt > 1) + { + warn = -3; + } + + return warn; + +} + + //------------------------------------------------------------------------------ + + int CheckEEPROMValid(uint16_t *eeData) + { + int deviceSelect; + deviceSelect = eeData[10] & 0x0040; + if(deviceSelect != 0) + { + return 0; + } + + return -7; + } \ No newline at end of file diff --git a/src/drivers/mlx90641/src/MLX90641_I2C_Driver.c b/src/drivers/mlx90641/src/MLX90641_I2C_Driver.c new file mode 100644 index 000000000..2b8bad46a --- /dev/null +++ b/src/drivers/mlx90641/src/MLX90641_I2C_Driver.c @@ -0,0 +1,69 @@ +/** + * @copyright (C) 2017 Melexis N.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include "cambus.h" +#include "MLX90641_I2C_Driver.h" +static cambus_t *bus; + +void MLX90641_I2CInit(cambus_t *hbus) +{ + bus = hbus; +} + +int MLX90641_I2CGeneralReset(void) +{ + if (cambus_gencall(bus, 0x06) != 0) { + return -1; + } + + return 0; +} + +int MLX90641_I2CRead(uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data) +{ + startAddress = __REVSH(startAddress); + + if (cambus_write_bytes(bus, (slaveAddr<<1), (uint8_t *) &startAddress, 2, CAMBUS_XFER_NO_STOP) != 0) { + return -1; + } + + + if (cambus_read_bytes(bus, (slaveAddr<<1), (uint8_t *) data, nMemAddressRead*2, CAMBUS_XFER_NO_FLAGS) != 0) { + return -1; + } + + for(int i=0; i= 0, + "Failed to read the MLX90641 sensor data!"); + *Ta = MLX90641_GetTa(data, fir_mlx_data); + MLX90641_CalculateTo(data, fir_mlx_data, 0.95f, *Ta - 8, To); + + fb_free(); +} +#endif + #if (OMV_ENABLE_FIR_AMG8833 == 1) static void fir_AMG8833_get_frame(float *Ta, float *To) { @@ -266,7 +293,7 @@ static mp_obj_t py_fir_deinit() fir_sensor = FIR_NONE; } - #if ((OMV_ENABLE_FIR_MLX90621 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1)) + #if ((OMV_ENABLE_FIR_MLX90621 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90641 == 1)) if (fir_mlx_data != NULL) { xfree(fir_mlx_data); fir_mlx_data = NULL; @@ -303,6 +330,12 @@ mp_obj_t py_fir_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) break; } #endif + #if (OMV_ENABLE_FIR_MLX90640 == 0) && (OMV_ENABLE_FIR_MLX90641 == 1) + case (MLX90641_ADDR << 1): { + type = FIR_MLX90641; + break; + } + #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case AMG8833_ADDR: { type = FIR_AMG8833; @@ -429,6 +462,52 @@ mp_obj_t py_fir_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) return mp_const_none; } #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: { + // parse refresh rate and ADC resolution + int ir_fresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 32); + int adc_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 19); + + // sanitize values + ir_fresh_rate = __CLZ(__RBIT((ir_fresh_rate > 64) ? 64 : ((ir_fresh_rate < 1) ? 1 : ir_fresh_rate))) + 1; + adc_resolution = ((adc_resolution > 19) ? 19 : ((adc_resolution < 16) ? 16 : adc_resolution)) - 16; + + fir_mlx_data = xalloc(sizeof(paramsMLX90641)); + + fir_sensor = FIR_MLX90641; + FIR_MLX90641_RETRY: + cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_SPEED_FULL); // The EEPROM must be read at <= 400KHz. + MLX90641_I2CInit(&fir_bus); + + fb_alloc_mark(); + uint16_t *eeprom = fb_alloc(MLX90641_EEPROM_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT); + int error = MLX90641_DumpEE(MLX90641_ADDR, eeprom); + error |= MLX90641_SetRefreshRate(MLX90641_ADDR, ir_fresh_rate); + error |= MLX90641_SetResolution(MLX90641_ADDR, adc_resolution); + error |= MLX90641_ExtractParameters(eeprom, fir_mlx_data); + fb_alloc_free_till_mark(); + + if (error != 0) { + if (first_init) { + first_init = false; + cambus_pulse_scl(&fir_bus); + goto FIR_MLX90641_RETRY; + } else { + py_fir_deinit(); + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the MLX90641!")); + } + } + + // Switch to FAST speed + cambus_deinit(&fir_bus); + cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_SPEED_FAST); + fir_width = MLX90641_WIDTH; + fir_height = MLX90641_HEIGHT; + fir_ir_fresh_rate = ir_fresh_rate; + fir_adc_resolution = adc_resolution; + return mp_const_none; + } + #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { fir_sensor = FIR_AMG8833; @@ -513,8 +592,8 @@ static mp_obj_t py_fir_refresh() #if (OMV_ENABLE_FIR_MLX90621 == 1) const int mlx_90621_refresh_rates[16] = {512, 512, 512, 512, 512, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0}; #endif - #if (OMV_ENABLE_FIR_MLX90640 == 1) - const int mlx_90640_refresh_rates[8] = {0, 1, 2, 4, 8, 16, 32, 64}; + #if (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90641 == 1) + const int mlx_90640_1_refresh_rates[8] = {0, 1, 2, 4, 8, 16, 32, 64}; #endif switch (fir_sensor) { case FIR_NONE: @@ -523,9 +602,14 @@ static mp_obj_t py_fir_refresh() case FIR_MLX90621: return mp_obj_new_int(mlx_90621_refresh_rates[fir_ir_fresh_rate]); #endif + #if (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) #if (OMV_ENABLE_FIR_MLX90640 == 1) case FIR_MLX90640: - return mp_obj_new_int(mlx_90640_refresh_rates[fir_ir_fresh_rate]); + #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: + #endif + return mp_obj_new_int(mlx_90640_1_refresh_rates[fir_ir_fresh_rate]); #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: @@ -550,8 +634,13 @@ static mp_obj_t py_fir_resolution() case FIR_MLX90621: return mp_obj_new_int(fir_adc_resolution + 15); #endif + #if (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) #if (OMV_ENABLE_FIR_MLX90640 == 1) case FIR_MLX90640: + #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: + #endif return mp_obj_new_int(fir_adc_resolution + 16); #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) @@ -630,6 +719,17 @@ mp_obj_t py_fir_read_ta() return result; } #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: { + fb_alloc_mark(); + uint16_t *data = fb_alloc(MLX90641_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT); + PY_ASSERT_TRUE_MSG(MLX90641_GetFrameData(MLX90641_ADDR, data) >= 0, + "Failed to read the MLX90641 sensor data!"); + mp_obj_t result = mp_obj_new_float(MLX90641_GetTa(data, fir_mlx_data)); + fb_alloc_free_till_mark(); + return result; + } + #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { int16_t temp; @@ -683,6 +783,17 @@ mp_obj_t py_fir_read_ir(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) return result; } #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: { + fb_alloc_mark(); + float Ta, *To = fb_alloc(MLX90641_WIDTH * MLX90641_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT); + fir_MLX90641_get_frame(&Ta, To); + mp_obj_t result = fir_get_ir(MLX90641_WIDTH, MLX90641_HEIGHT, Ta, To, + arg_hmirror ^ true, arg_vflip, fir_transposed, false); + fb_alloc_free_till_mark(); + return result; + } + #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { fb_alloc_mark(); @@ -1011,6 +1122,21 @@ mp_obj_t py_fir_snapshot(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) break; } #endif + #if (OMV_ENABLE_FIR_MLX90641 == 1) + case FIR_MLX90641: { + float Ta, *To = fb_alloc(MLX90641_WIDTH * MLX90641_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT); + fir_MLX90641_get_frame(&Ta, To); + + if (!scale_obj) { + fast_get_min_max(To, MLX90641_WIDTH * MLX90641_HEIGHT, &min, &max); + } + + imlib_fill_image_from_float(&src_img, MLX90641_WIDTH, MLX90641_HEIGHT, To, min, max, + arg_hmirror ^ true, arg_vflip, arg_transpose, false); + fb_free(); + break; + } + #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { float Ta, *To = fb_alloc(AMG8833_WIDTH * AMG8833_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT); @@ -1058,6 +1184,9 @@ STATIC const mp_rom_map_elem_t globals_dict_table[] = { #if (OMV_ENABLE_FIR_MLX90640 == 1) { MP_ROM_QSTR(MP_QSTR_FIR_MLX90640), MP_ROM_INT(FIR_MLX90640) }, #endif +#if (OMV_ENABLE_FIR_MLX90641 == 1) + { MP_ROM_QSTR(MP_QSTR_FIR_MLX90641), MP_ROM_INT(FIR_MLX90641) }, +#endif #if (OMV_ENABLE_FIR_AMG8833 == 1) { MP_ROM_QSTR(MP_QSTR_FIR_AMG8833), MP_ROM_INT(FIR_AMG8833) }, #endif diff --git a/src/omv/modules/qstrdefsomv.h b/src/omv/modules/qstrdefsomv.h index 77beb8123..988b291cf 100644 --- a/src/omv/modules/qstrdefsomv.h +++ b/src/omv/modules/qstrdefsomv.h @@ -1227,6 +1227,7 @@ Q(FIR_NONE) Q(FIR_SHIELD) Q(FIR_MLX90621) Q(FIR_MLX90640) +Q(FIR_MLX90641) Q(FIR_AMG8833) Q(FIR_LEPTON) // duplicate Q(init) diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index 53f4f6d50..e24a4e587 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -55,6 +55,7 @@ OMV_CFLAGS += -I$(TOP_DIR)/$(LSM6DS3_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(WINC1500_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90621_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90640_DIR)/include/ +OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90641_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(TENSORFLOW_DIR)/$(CPU)/ OMV_CFLAGS += -I$(TOP_DIR)/$(LIBPDM_DIR)/ @@ -81,6 +82,7 @@ FIRM_OBJ += $(wildcard $(BUILD)/$(WINC1500_DIR)/src/*.o) endif FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o) FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o) +FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o) #------------- OpenMV Objects ----------------# FIRM_OBJ += $(addprefix $(BUILD)/$(CMSIS_DIR)/src/, \ @@ -346,6 +348,7 @@ FIRMWARE_OBJS: | $(BUILD) $(FW_DIR) $(MAKE) -C $(HAL_DIR) BUILD=$(BUILD)/$(HAL_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(MLX90621_DIR) BUILD=$(BUILD)/$(MLX90621_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(MLX90640_DIR) BUILD=$(BUILD)/$(MLX90640_DIR) CFLAGS="$(CFLAGS) -MMD" + $(MAKE) -C $(MLX90641_DIR) BUILD=$(BUILD)/$(MLX90641_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(OMV_DIR) BUILD=$(BUILD)/$(OMV_DIR) CFLAGS="$(CFLAGS) -MMD" $(FIRMWARE): FIRMWARE_OBJS diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index a801b99b7..6623c481e 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -43,6 +43,7 @@ OMV_CFLAGS += -I$(TOP_DIR)/$(LSM6DS3_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(WINC1500_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90621_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90640_DIR)/include/ +OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90641_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(TENSORFLOW_DIR)/$(CPU)/ OMV_CFLAGS += -I$(TOP_DIR)/$(LIBPDM_DIR)/ @@ -91,6 +92,7 @@ FIRM_OBJ += $(wildcard $(BUILD)/$(WINC1500_DIR)/src/*.o) endif FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o) FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o) +FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o) #------------- OpenMV Objects ----------------# FIRM_OBJ += $(addprefix $(BUILD)/$(CMSIS_DIR)/src/, \ @@ -482,6 +484,7 @@ UVC_OBJ += $(wildcard $(BUILD)/$(LSM6DS3_DIR)/src/*.o) endif UVC_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o) UVC_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o) +UVC_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o) endif ################################################### @@ -527,6 +530,7 @@ ifeq ($(MICROPY_PY_WINC1500), 1) endif $(MAKE) -C $(MLX90621_DIR) BUILD=$(BUILD)/$(MLX90621_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(MLX90640_DIR) BUILD=$(BUILD)/$(MLX90640_DIR) CFLAGS="$(CFLAGS) -MMD" + $(MAKE) -C $(MLX90641_DIR) BUILD=$(BUILD)/$(MLX90641_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(OMV_DIR) BUILD=$(BUILD)/$(OMV_DIR) CFLAGS="$(CFLAGS) -MMD" ifeq ($(CUBEAI), 1) $(MAKE) -C $(CUBEAI_DIR) BUILD=$(BUILD)/$(CUBEAI_DIR) CFLAGS="$(CFLAGS) -fno-strict-aliasing -MMD"