针对移植,我们首先需要找到相关原厂驱动地址,如下所示:(包含API手册以及驱动程序)
其中的相关内容如下图所示:说明相关驱动层以及应用层的代码
针对于MLX90640_I2C_Driver.h的移植,由于本身不用到MLX90640_I2CGeneralReset这个函数,这个函数的用意是,实现 I2C 总线的标准复位操作,根据 I2C 协议规范,用于重置总线上支持该复位条件的所有设备。
其次就是平台兼容的问题了,为不同硬件平台设置合适的 I2C 缓冲区大小,使得代码可以通用。
避免因为平台硬件特性(如内存大小或 I2C 硬件实现差异)导致的问题。
接着就是MLX90640_I2C_Driver.cpp了硬件IIC,如下所示:(这边仅仅展示读跟写)
//Read a number of words from startAddress. Store into Data array.
//Returns 0 if successful, -1 if error
int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data)
{//Caller passes number of 'unsigned ints to read', increase this to 'bytes to read'uint16_t bytesRemaining = nWordsRead * 2;//It doesn't look like sequential read works. Do we need to re-issue the address command each time?uint16_t dataSpot = 0; //Start at beginning of array//Setup a series of chunked I2C_BUFFER_LENGTH byte readswhile (bytesRemaining > 0){Wire.beginTransmission(_deviceAddress);Wire.write(startAddress >> 8); //MSBWire.write(startAddress & 0xFF); //LSBif (Wire.endTransmission(false) != 0) //Do not release bus{Serial.println("No ack read");return (0); //Sensor did not ACK}uint16_t numberOfBytesToRead = bytesRemaining;if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH;Wire.requestFrom((uint8_t)_deviceAddress, (uint8_t)numberOfBytesToRead);if (Wire.available()){for (uint16_t x = 0 ; x < numberOfBytesToRead / 2; x++){//Store data into arraydata[dataSpot] = Wire.read() << 8; //MSBdata[dataSpot] |= Wire.read(); //LSBdataSpot++;}}bytesRemaining -= numberOfBytesToRead;startAddress += numberOfBytesToRead / 2;}return (0); //Success
}
_deviceAddress
: I2C 从设备地址。startAddress
: 起始地址,指示从哪里开始读取数据。nWordsRead
: 要读取的 16 位数据(uint16_t
)数量。data
: 存储读取数据的指针(调用者提供的数组)。
Wire.beginTransmission(_deviceAddress)
开始与从设备通信。Wire.write(startAddress >> 8)
和Wire.write(startAddress & 0xFF)
将 16 位起始地址分为高字节和低字节,依次发送。Wire.endTransmission(false)
将命令传输给设备,但不释放总线(false
参数)。
- 功能: 每次读取的字节数不能超过 I2C 缓冲区的大小(
I2C_BUFFER_LENGTH
)。 - 如果剩余的字节数超出缓冲区大小,就读取
I2C_BUFFER_LENGTH
,否则读取剩余字节。 - 使用
Wire.requestFrom
请求指定数量的字节数据。 - 确保缓冲区中有可用数据(
Wire.available()
)。 - 将读取的高字节(
MSB
)和低字节(LSB
)组合成一个 16 位数据,存入data
数组。
//Write two bytes to a two byte address
int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data)
{Wire.beginTransmission((uint8_t)_deviceAddress);Wire.write(writeAddress >> 8); //MSBWire.write(writeAddress & 0xFF); //LSBWire.write(data >> 8); //MSBWire.write(data & 0xFF); //LSBif (Wire.endTransmission() != 0){//Sensor did not ACKSerial.println("Error: Sensor did not ack");return (-1);}uint16_t dataCheck;MLX90640_I2CRead(_deviceAddress, writeAddress, 1, &dataCheck);if (dataCheck != data){//Serial.println("The write request didn't stick");return -2;}
追根溯源,其实就是平台API的替换嘛,哪里报错改哪里。
最后就是MLX90640_API.h与MLX90640_API.cpp了,如下所示原厂驱动:
如下是我移植后的,如下所示:
/*** @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 _MLX640_API_H_
#define _MLX640_API_H_typedef struct{int16_t kVdd;int16_t vdd25;float KvPTAT;float KtPTAT;uint16_t vPTAT25;float alphaPTAT;int16_t gainEE;float tgc;float cpKv;float cpKta;uint8_t resolutionEE;uint8_t calibrationModeEE;float KsTa;float ksTo[4];int16_t ct[4];float alpha[768]; int16_t offset[768]; float kta[768]; float kv[768];float cpAlpha[2];int16_t cpOffset[2];float ilChessC[3]; uint16_t brokenPixels[5];uint16_t outlierPixels[5]; } paramsMLX90640;int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData);int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData);int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params);float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params);void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result);void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result);int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution);int MLX90640_GetCurResolution(uint8_t slaveAddr);int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate); int MLX90640_GetRefreshRate(uint8_t slaveAddr); int MLX90640_GetSubPageNumber(uint16_t *frameData);int MLX90640_GetCurMode(uint8_t slaveAddr); int MLX90640_SetInterleavedMode(uint8_t slaveAddr);int MLX90640_SetChessMode(uint8_t slaveAddr);#endif
相关宏去除,直接写在了cpp里面。
因此我们如何进行app层调用,如下所示:
接下来就是STM32以及ESP32的相关代码了,如下所示:
如下是esp32你需要注意的:
因为跟RP2040同一个平台其实本质就是兼容arduino的API仅此而已。