Skip to content

Commit

Permalink
MLX90640: Driver and 32x32 LED matrix example
Browse files Browse the repository at this point in the history
  • Loading branch information
Gadgetoid committed Aug 22, 2022
1 parent 05ef0d7 commit 3991684
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@
path = drivers/vl53l5cx/src
url = https://github.com/ST-mirror/VL53L5CX_ULD_driver
branch = no-fw/lite/en
[submodule "drivers/mlx90640/src"]
path = drivers/mlx90640/src
url = https://github.com/melexis/mlx90640-library
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ add_subdirectory(vl53l5cx)
add_subdirectory(pcf85063a)
add_subdirectory(pms5003)
add_subdirectory(sh1107)
add_subdirectory(mlx90640)
1 change: 1 addition & 0 deletions drivers/mlx90640/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include(mlx90640.cmake)
50 changes: 50 additions & 0 deletions drivers/mlx90640/MLX90640_RP2040_I2C_Driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "src/headers/MLX90640_I2C_Driver.h"
#include "mlx90640.hpp"

#include "stdio.h"


static pimoroni::I2C *i2c;

void MLX90640_I2CConfigure(pimoroni::I2C *i2c_instance) {
i2c = i2c_instance;
}

void MLX90640_I2CInit()
{
// i2c->init(); // Called in constructor
}

int MLX90640_I2CGeneralReset(void)
{
return 0;
}

int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data)
{
uint8_t cmd[2] = {(char)(startAddress >> 8), (char)(startAddress & 0xFF)};

// Set 16-bit register pointer
i2c->write_blocking(slaveAddr, cmd, sizeof(cmd), true);
// Read result
i2c->read_blocking(slaveAddr, (uint8_t*)data, nMemAddressRead * sizeof(uint16_t), false);

for(auto n = 0u; n < nMemAddressRead; n++) {
data[n] = __builtin_bswap16(data[n]);
}

return 0;
}

void MLX90640_I2CFreqSet(int freq)
{
// We can't assume we own the I2C instance and can wiggle the baudrate ad-hoc
}

int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data)
{
uint8_t cmd[4] = {(char)(writeAddress >> 8), (char)(writeAddress & 0x00FF), (char)(data >> 8), (char)(data & 0x00FF)};
i2c->write_blocking(slaveAddr, cmd, sizeof(cmd), false);
return 0;
}

13 changes: 13 additions & 0 deletions drivers/mlx90640/mlx90640.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(DRIVER_NAME mlx90640)
add_library(${DRIVER_NAME} INTERFACE)

target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/src/functions/MLX90640_API.cpp
${CMAKE_CURRENT_LIST_DIR}/MLX90640_RP2040_I2C_Driver.cpp
${CMAKE_CURRENT_LIST_DIR}/mlx90640.cpp
)

target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c)

target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src/headers)
92 changes: 92 additions & 0 deletions drivers/mlx90640/mlx90640.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include <stdint.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <chrono>
#include <thread>
#include <math.h>
#include "src/headers/MLX90640_API.h"
#include "mlx90640.hpp"



namespace pimoroni {
MLX90640::MLX90640_Error MLX90640::setup(int fps){
MLX90640_I2CConfigure(i2c_instance);
//MLX90640_SetDeviceMode(i2c_address, 0);
//MLX90640_SetSubPageRepeat(i2c_address, 0);

switch(fps){
case 1:
MLX90640_SetRefreshRate(i2c_address, 0b001);
break;
case 2:
MLX90640_SetRefreshRate(i2c_address, 0b010);
break;
case 4:
MLX90640_SetRefreshRate(i2c_address, 0b011);
break;
case 8:
MLX90640_SetRefreshRate(i2c_address, 0b100);
break;
case 16:
MLX90640_SetRefreshRate(i2c_address, 0b101);
if(i2c_instance->get_baudrate() < 1000000) {
return INVALID_BAUDRATE;
}
break;
case 32:
MLX90640_SetRefreshRate(i2c_address, 0b110);
if(i2c_instance->get_baudrate() < 1000000) {
return INVALID_BAUDRATE;
}
break;
case 64:
MLX90640_SetRefreshRate(i2c_address, 0b111);
if(i2c_instance->get_baudrate() < 1000000) {
return INVALID_BAUDRATE;
}
break;
default:
#ifdef DEBUG
printf("Unsupported framerate: %d", fps);
#endif
return INVALID_FPS;
}
//MLX90640_SetChessMode(i2c_address);
MLX90640_SetInterleavedMode(i2c_address);
//MLX90640_SetResolution(i2c_address, 0);
MLX90640_DumpEE(i2c_address, eeMLX90640);
MLX90640_ExtractParameters(eeMLX90640, &mlx90640);

return OK;
}

int MLX90640::get_image(void){
MLX90640_I2CConfigure(i2c_instance);

MLX90640_GetFrameData(i2c_address, frame0);
sleep_us(1000);
MLX90640_GetFrameData(i2c_address, frame1);

MLX90640_GetImage(frame0, &mlx90640, mlx90640To);
MLX90640_GetImage(frame1, &mlx90640, mlx90640To);

return 0;
}

int MLX90640::get_frame(void){
MLX90640_I2CConfigure(i2c_instance);

MLX90640_GetFrameData(i2c_address, frame0);
sleep_us(1000);
MLX90640_GetFrameData(i2c_address, frame1);

int tr0 = MLX90640_GetTa(frame0, &mlx90640) - reflected_temperature;
MLX90640_CalculateTo(frame0, &mlx90640, emissivity, tr0, mlx90640To);
int tr1 = MLX90640_GetTa(frame1, &mlx90640) - reflected_temperature;
MLX90640_CalculateTo(frame1, &mlx90640, emissivity, tr1, mlx90640To);

return 0;
}
}
33 changes: 33 additions & 0 deletions drivers/mlx90640/mlx90640.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "src/headers/MLX90640_API.h"
#include "common/pimoroni_i2c.hpp"

void MLX90640_I2CConfigure(pimoroni::I2C *i2c_instance);

#define MLX90640_DEFAULT_I2C_ADDRESS 0x33

namespace pimoroni {
class MLX90640 {
public:
enum MLX90640_Error {
OK = 0,
INVALID_BAUDRATE = 1,
INVALID_FPS = 2,
};

float mlx90640To[768] = {0.0f};
float emissivity = 1.0f;
float reflected_temperature = 8.0f;

MLX90640(pimoroni::I2C *i2c_instance, uint i2c_address=MLX90640_DEFAULT_I2C_ADDRESS) : i2c_instance(i2c_instance), i2c_address(i2c_address) {};
MLX90640_Error setup(int fps);
int get_image(void);
int get_frame(void);
private:
pimoroni::I2C *i2c_instance;
uint i2c_address = MLX90640_DEFAULT_I2C_ADDRESS;
paramsMLX90640 mlx90640;
uint16_t eeMLX90640[832] = {0};
uint16_t frame0[834] = {0};
uint16_t frame1[834] = {0};
};
}
1 change: 1 addition & 0 deletions drivers/mlx90640/src
Submodule src added at 4dbbc5
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_subdirectory(breakout_scd41)
add_subdirectory(breakout_vl53l5cx)
add_subdirectory(breakout_pms5003)
add_subdirectory(breakout_oled_128x128)
add_subdirectory(breakout_mlx90640)

add_subdirectory(pico_display)
add_subdirectory(pico_display_2)
Expand Down
12 changes: 12 additions & 0 deletions examples/breakout_mlx90640/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
set(OUTPUT_NAME mlx90460_demo)

add_executable(
${OUTPUT_NAME}
demo.cpp
)

# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib mlx90640 hub75 hardware_vreg)

# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})
104 changes: 104 additions & 0 deletions examples/breakout_mlx90640/demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "pico/stdlib.h"
#include "hardware/vreg.h"
#include "stdio.h"

#include <algorithm>
#include <cmath>

#include "mlx90640.hpp"

#include "hub75.hpp"

using namespace pimoroni;

// Display size in pixels
// Should be either 64x64 or 32x32 but perhaps 64x32 an other sizes will work.
// Note: this example uses only 5 address lines so it's limited to 32*2 pixels.
const uint8_t WIDTH = 32;
const uint8_t HEIGHT = 32;

// min and max temperature range (in degrees C) for scaling false colour
const float temp_min = 18.0f;
const float temp_max = 38.0f;

// colour brightness - crushes dynamic range so use wisely!
const float brightness = 0.5f;

Hub75 hub75(WIDTH, HEIGHT, nullptr);

// Dirty hack to overclock the Pico before class initialisation takes place
// since i2c uses the current clock frequency when determining baudrate.
class OC {
public:
OC(uint32_t freq_khz, vreg_voltage voltage) {
vreg_set_voltage(voltage);
sleep_us(100);
set_sys_clock_khz(freq_khz, true);
}
};

OC oc(266000, VREG_VOLTAGE_1_20);

// 1MHz i2c for higher framerates
I2C i2c(20, 21, 1000000UL);

MLX90640 mlx90640(&i2c);

void __isr dma_complete() {
hub75.dma_complete();
}

void set_pixel_false_colour(int x, int y, float v) {
const int colours = 8;
static float color[colours][3] = {
{0, 0, 0},
{0, 0, 255.0f},
{0, 255.0f,255.0f},
{0, 255.0f,0},
{255.0f,255.0f,0},
{255.0f,0, 0},
{255.0f,0, 255.0f},
{255.0f,255.0f,255.0f}
};
int idx1, idx2;
float blend = 0.0f;
const float temp_range = temp_max - temp_min;
v -= temp_min;
v /= temp_range;
if(v <= 0) {idx1 = idx2 = 0;}
else if(v >= 1) {idx1 = idx2 = colours - 1;}
else
{
v *= (colours - 1);
idx1 = std::floor(v);
idx2 = idx1 + 1;
blend = v - float(idx1);
}

int r = (int)((((color[idx2][0] - color[idx1][0]) * blend) + color[idx1][0]) * brightness);
int g = (int)((((color[idx2][1] - color[idx1][1]) * blend) + color[idx1][1]) * brightness);
int b = (int)((((color[idx2][2] - color[idx1][2]) * blend) + color[idx1][2]) * brightness);

hub75.set_rgb(x, y, r, g, b);
}

int main() {
stdio_init_all();
hub75.start(dma_complete);

mlx90640.setup(32);

while(true) {
mlx90640.get_frame();
for(auto y = 0u; y < 24; y++) {
for(auto x = 0u; x < 32; x++) {
int offset = y * 32 + x;
float v = mlx90640.mlx90640To[offset]; // / 30000.0f;
set_pixel_false_colour(x, y, v);
}
}
hub75.flip();
}

return 0;
}

0 comments on commit 3991684

Please sign in to comment.