diff --git a/scripts/examples/12-Thermopile-Shield/AMG8833_camera.py b/scripts/examples/12-Thermopile-Shield/AMG8833_camera.py new file mode 100644 index 000000000..e1c2418c0 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/AMG8833_camera.py @@ -0,0 +1,20 @@ +# AMG8833 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir + +# Initialize the thermal sensor +fir.init(type=fir.FIR_AMG8833) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/AMG8833_camera_lcd.py b/scripts/examples/12-Thermopile-Shield/AMG8833_camera_lcd.py new file mode 100644 index 000000000..77149d82c --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/AMG8833_camera_lcd.py @@ -0,0 +1,25 @@ +# AMG8833 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir, lcd + +# Initialize the thermal sensor +fir.init(type=fir.FIR_AMG8833) + +# Init the lcd. +lcd.init() + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + lcd.display(img) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/AMG8833_overlay.py b/scripts/examples/12-Thermopile-Shield/AMG8833_overlay.py new file mode 100644 index 000000000..af86cdfb2 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/AMG8833_overlay.py @@ -0,0 +1,55 @@ +# AMG8833 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_AMG8833) + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/AMG8833_overlay_lcd.py b/scripts/examples/12-Thermopile-Shield/AMG8833_overlay_lcd.py new file mode 100644 index 000000000..d05c9ce40 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/AMG8833_overlay_lcd.py @@ -0,0 +1,59 @@ +# AMG8833 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir, lcd + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA2) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_AMG8833) + +# Init the lcd. +lcd.init() + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + lcd.display(img) + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90621_camera.py b/scripts/examples/12-Thermopile-Shield/MLX90621_camera.py new file mode 100644 index 000000000..648bfd5f1 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90621_camera.py @@ -0,0 +1,20 @@ +# MLX90621 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90621) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90621_camera_lcd.py b/scripts/examples/12-Thermopile-Shield/MLX90621_camera_lcd.py new file mode 100644 index 000000000..545cff75d --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90621_camera_lcd.py @@ -0,0 +1,25 @@ +# MLX90621 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir, lcd + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90621) + +# Init the lcd. +lcd.init() + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + lcd.display(img) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90621_overlay.py b/scripts/examples/12-Thermopile-Shield/MLX90621_overlay.py new file mode 100644 index 000000000..83fbc7726 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90621_overlay.py @@ -0,0 +1,55 @@ +# MLX90621 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90621) + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90621_overlay_lcd.py b/scripts/examples/12-Thermopile-Shield/MLX90621_overlay_lcd.py new file mode 100644 index 000000000..6f098b25e --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90621_overlay_lcd.py @@ -0,0 +1,59 @@ +# MLX90621 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir, lcd + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA2) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90621) + +# Init the lcd. +lcd.init() + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + lcd.display(img) + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90640_camera.py b/scripts/examples/12-Thermopile-Shield/MLX90640_camera.py new file mode 100644 index 000000000..a3e5819cb --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90640_camera.py @@ -0,0 +1,20 @@ +# MLX90640 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90640, refresh=16) # Hz (higher end OpenMV Cam's may be able to run faster) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90640_camera_lcd.py b/scripts/examples/12-Thermopile-Shield/MLX90640_camera_lcd.py new file mode 100644 index 000000000..33818f95a --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90640_camera_lcd.py @@ -0,0 +1,25 @@ +# MLX90640 Camera Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import image, time, fir, lcd + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90640, refresh=16) # Hz (higher end OpenMV Cam's may be able to run faster) + +# Init the lcd. +lcd.init() + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + img = fir.snapshot(copy_to_fb=True) + + lcd.display(img) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90640_overlay.py b/scripts/examples/12-Thermopile-Shield/MLX90640_overlay.py new file mode 100644 index 000000000..76ad3ae01 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90640_overlay.py @@ -0,0 +1,55 @@ +# MLX90640 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90640, refresh=16) # Hz (higher end OpenMV Cam's may be able to run faster) + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/MLX90640_overlay_lcd.py b/scripts/examples/12-Thermopile-Shield/MLX90640_overlay_lcd.py new file mode 100644 index 000000000..297f045f9 --- /dev/null +++ b/scripts/examples/12-Thermopile-Shield/MLX90640_overlay_lcd.py @@ -0,0 +1,59 @@ +# MLX90640 Overlay Demo +# +# This example shows off how to overlay a heatmap onto your OpenMV Cam's +# live video output from the main camera. + +import sensor, image, time, fir, lcd + +ALT_OVERLAY = False # Set to True to allocate a second ir image. + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QQVGA2) +sensor.skip_frames(time = 2000) + +# Initialize the thermal sensor +fir.init(type=fir.FIR_MLX90640, refresh=16) # Hz (higher end OpenMV Cam's may be able to run faster) + +# Init the lcd. +lcd.init() + +# Allocate another frame buffer for smoother video. +extra_fb = sensor.alloc_extra_fb(sensor.width(), sensor.height(), sensor.RGB565) + +# FPS clock +clock = time.clock() + +while (True): + clock.tick() + + # Capture an image + img = sensor.snapshot() + + # Capture FIR data + # ta: Ambient temperature + # ir: Object temperatures (IR array) + # to_min: Minimum object temperature + # to_max: Maximum object temperature + ta, ir, to_min, to_max = fir.read_ir() + + if not ALT_OVERLAY: + # Scale the image and belnd it with the framebuffer + fir.draw_ir(img, ir) + else: + # Create a secondary image and then blend into the frame buffer. + extra_fb.clear() + fir.draw_ir(extra_fb, ir, alpha=256) + img.blend(extra_fb, alpha=128) + + # Draw ambient, min and max temperatures. + img.draw_string(8, 0, "Ta: %0.2f C" % ta, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 8, "To min: %0.2f C" % to_min, color = (255, 0, 0), mono_space = False) + img.draw_string(8, 16, "To max: %0.2f C"% to_max, color = (255, 0, 0), mono_space = False) + + lcd.display(img) + # Force high quality streaming... + img.compress(quality=90) + + # Print FPS. + print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/fir.py b/scripts/examples/12-Thermopile-Shield/fir.py deleted file mode 100644 index d3c56a49d..000000000 --- a/scripts/examples/12-Thermopile-Shield/fir.py +++ /dev/null @@ -1,55 +0,0 @@ -# Thermopile Shield Demo -# -# Note: To run this example you will need a Thermopile Shield for your OpenMV -# Cam. Also, please disable JPEG mode in the IDE. -# -# The Thermopile Shield allows your OpenMV Cam to see heat! - -import sensor, image, time, fir - -# Reset sensor -sensor.reset() - -# Set sensor settings -sensor.set_contrast(1) -sensor.set_brightness(0) -sensor.set_saturation(2) -sensor.set_pixformat(sensor.RGB565) -sensor.set_framesize(sensor.QQVGA) - -# The following registers fine-tune the image -# sensor window to align it with the FIR sensor. -if (sensor.get_id() == sensor.OV2640): - sensor.__write_reg(0xFF, 0x01) # switch to reg bank - sensor.__write_reg(0x17, 0x19) # set HSTART - sensor.__write_reg(0x18, 0x43) # set HSTOP - -# Initialize the thermal sensor -fir.init() - -# FPS clock -clock = time.clock() - -while (True): - clock.tick() - - # Capture an image - image = sensor.snapshot() - - # Capture FIR data - # ta: Ambient temperature - # ir: Object temperatures (IR array) - # to_min: Minimum object temperature - # to_max: Maximum object temperature - ta, ir, to_min, to_max = fir.read_ir() - - # Scale the image and belnd it with the framebuffer - fir.draw_ir(image, ir) - - # Draw ambient, min and max temperatures. - image.draw_string(0, 0, "Ta: %0.2f"%ta, color = (0xFF, 0x00, 0x00)) - image.draw_string(0, 8, "To min: %0.2f"%to_min, color = (0xFF, 0x00, 0x00)) - image.draw_string(0, 16, "To max: %0.2f"%to_max, color = (0xFF, 0x00, 0x00)) - - # Print FPS. - print(clock.fps()) diff --git a/scripts/examples/12-Thermopile-Shield/fir_lcd.py b/scripts/examples/12-Thermopile-Shield/fir_lcd.py deleted file mode 100644 index 560c0ebfb..000000000 --- a/scripts/examples/12-Thermopile-Shield/fir_lcd.py +++ /dev/null @@ -1,61 +0,0 @@ -# Thermopile Shield Demo with LCD -# -# Note: To run this example you will need a Thermopile Shield for your OpenMV -# Cam and a LCD Shield. Also, please disable JPEG mode in the IDE. -# -# The Thermopile Shield allows your OpenMV Cam to see heat! - -import sensor, image, time, fir, lcd - -# Reset sensor -sensor.reset() - -# Set sensor settings -sensor.set_contrast(1) -sensor.set_brightness(0) -sensor.set_saturation(2) -sensor.set_pixformat(sensor.RGB565) -sensor.set_framesize(sensor.QQVGA2) - -# The following registers fine-tune the image -# sensor window to align it with the FIR sensor. -if (sensor.get_id() == sensor.OV2640): - sensor.__write_reg(0xFF, 0x01) # switch to reg bank - sensor.__write_reg(0x17, 0x19) # set HSTART - sensor.__write_reg(0x18, 0x43) # set HSTOP - -# Initialize the thermal sensor -fir.init() - -# Initialize the lcd sensor -lcd.init() - -# FPS clock -clock = time.clock() - -while(True): - clock.tick() - - # Capture an image - image = sensor.snapshot() - - # Capture FIR data - # ta: Ambient temperature - # ir: Object temperatures (IR array) - # to_min: Minimum object temperature - # to_max: Maximum object temperature - ta, ir, to_min, to_max = fir.read_ir() - - # Draw IR data on the framebuffer - fir.draw_ir(image, ir) - - # Draw ambient, min and max temperatures. - image.draw_string(0, 0, "Ta: %0.2f"%ta, color = (0xFF, 0x00, 0x00)) - image.draw_string(0, 8, "To min: %0.2f"%to_min, color = (0xFF, 0x00, 0x00)) - image.draw_string(0, 16, "To max: %0.2f"%to_max, color = (0xFF, 0x00, 0x00)) - - # Display image on LCD - lcd.display(image) - - # Print FPS. - print(clock.fps()) diff --git a/src/Makefile b/src/Makefile index fdeafc853..7a1dbb471 100755 --- a/src/Makefile +++ b/src/Makefile @@ -38,6 +38,7 @@ STUSB_DIR=stusb MICROPY_DIR=micropython OMV_DIR=omv LEPTON_DIR=lepton +MLX_DIR=mlx WINC1500_DIR=winc1500 BOOTLDR_DIR=bootloader WEBCAM_DIR=webcam @@ -104,6 +105,7 @@ OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/nn/ OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/img/ OMV_CFLAGS += -I$(OMV_BOARD_CONFIG_DIR) OMV_CFLAGS += -I$(TOP_DIR)/$(LEPTON_DIR)/include/ +OMV_CFLAGS += -I$(TOP_DIR)/$(MLX_DIR)/include/ OMV_CFLAGS += -I$(TOP_DIR)/$(WINC1500_DIR)/include/ WEBCAM_CFLAGS = $(CFLAGS) @@ -147,6 +149,7 @@ FIRM_OBJ += $(wildcard $(BUILD)/$(CMSIS_DIR)/src/nn/SoftmaxFunctions/*.o) #FIRM_OBJ += $(wildcard $(BUILD)/$(CMSIS_DIR)/src/dsp/TransformFunctions/*.o) FIRM_OBJ += $(wildcard $(BUILD)/$(STHAL_DIR)/src/*.o) FIRM_OBJ += $(wildcard $(BUILD)/$(LEPTON_DIR)/src/*.o) +FIRM_OBJ += $(wildcard $(BUILD)/$(MLX_DIR)/src/*.o) FIRM_OBJ += $(wildcard $(BUILD)/$(WINC1500_DIR)/src/*.o) #------------- OpenMV Objects ----------------# @@ -449,6 +452,7 @@ WEBCAM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\ ) WEBCAM_OBJ += $(wildcard $(BUILD)/$(LEPTON_DIR)/src/*.o) +WEBCAM_OBJ += $(wildcard $(BUILD)/$(MLX_DIR)/src/*.o) ################################################### #Export Variables export Q @@ -484,6 +488,7 @@ FIRMWARE_OBJS: $(MAKE) -C $(STHAL_DIR) BUILD=$(BUILD)/$(STHAL_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(MICROPY_DIR)/ports/stm32 BUILD=$(BUILD)/$(MICROPY_DIR) BOARD=$(TARGET) QSTR_DEFS=$(OMV_QSTR_DEFS) $(MAKE) -C $(LEPTON_DIR) BUILD=$(BUILD)/$(LEPTON_DIR) CFLAGS="$(CFLAGS) -MMD" + $(MAKE) -C $(MLX_DIR) BUILD=$(BUILD)/$(MLX_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(WINC1500_DIR) BUILD=$(BUILD)/$(WINC1500_DIR) CFLAGS="$(CFLAGS) -MMD" $(MAKE) -C $(OMV_DIR) BUILD=$(BUILD)/$(OMV_DIR) CFLAGS="$(CFLAGS) -MMD" diff --git a/src/mlx/Makefile b/src/mlx/Makefile new file mode 100644 index 000000000..2a76ed499 --- /dev/null +++ b/src/mlx/Makefile @@ -0,0 +1,17 @@ +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/mlx/include/MLX90640_API.h b/src/mlx/include/MLX90640_API.h new file mode 100644 index 000000000..b8be27640 --- /dev/null +++ b/src/mlx/include/MLX90640_API.h @@ -0,0 +1,64 @@ +/** + * @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 diff --git a/src/mlx/include/MLX90640_I2C_Driver.h b/src/mlx/include/MLX90640_I2C_Driver.h new file mode 100644 index 000000000..ef795d597 --- /dev/null +++ b/src/mlx/include/MLX90640_I2C_Driver.h @@ -0,0 +1,26 @@ +/** + * @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 _MLX90640_I2C_Driver_H_ +#define _MLX90640_I2C_Driver_H_ + +#include + + void MLX90640_I2CInit(void); + int MLX90640_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data); + int MLX90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data); + void MLX90640_I2CFreqSet(int freq); +#endif \ No newline at end of file diff --git a/src/mlx/src/MLX90640_API.c b/src/mlx/src/MLX90640_API.c new file mode 100644 index 000000000..52a3e1158 --- /dev/null +++ b/src/mlx/src/MLX90640_API.c @@ -0,0 +1,1187 @@ +/** + * @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 +#include + +#define double float +#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); +int CheckEEPROMValid(uint16_t *eeData); + + +int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData) +{ + return MLX90640_I2CRead(slaveAddr, 0x2400, 832, eeData); +} + +int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData) +{ + uint16_t dataReady = 1; + uint16_t controlRegister1; + uint16_t statusRegister; + int error = 1; + uint8_t cnt = 0; + + dataReady = 0; + while(dataReady == 0) + { + error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister); + if(error != 0) + { + return error; + } + dataReady = statusRegister & 0x0008; + } + + while(dataReady != 0 && cnt < 5) + { + error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030); + if(error == -1) + { + return error; + } + + error = MLX90640_I2CRead(slaveAddr, 0x0400, 832, frameData); + if(error != 0) + { + return error; + } + + error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister); + if(error != 0) + { + return error; + } + dataReady = statusRegister & 0x0008; + cnt = cnt + 1; + } + + if(cnt > 4) + { + return -8; + } + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + frameData[832] = controlRegister1; + frameData[833] = statusRegister & 0x0001; + + if(error != 0) + { + return error; + } + + return frameData[833]; +} + +int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int error = CheckEEPROMValid(eeData); + + if(error == 0) + { + ExtractVDDParameters(eeData, mlx90640); + ExtractPTATParameters(eeData, mlx90640); + ExtractGainParameters(eeData, mlx90640); + ExtractTgcParameters(eeData, mlx90640); + ExtractResolutionParameters(eeData, mlx90640); + ExtractKsTaParameters(eeData, mlx90640); + ExtractKsToParameters(eeData, mlx90640); + ExtractAlphaParameters(eeData, mlx90640); + ExtractOffsetParameters(eeData, mlx90640); + ExtractKtaPixelParameters(eeData, mlx90640); + ExtractKvPixelParameters(eeData, mlx90640); + ExtractCPParameters(eeData, mlx90640); + ExtractCILCParameters(eeData, mlx90640); + error = ExtractDeviatingPixels(eeData, mlx90640); + } + + return error; + +} + +//------------------------------------------------------------------------------ + +int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution) +{ + uint16_t controlRegister1; + int value; + int error; + + value = (resolution & 0x03) << 10; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + + if(error == 0) + { + value = (controlRegister1 & 0xF3FF) | value; + error = MLX90640_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90640_GetCurResolution(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int resolutionRAM; + int error; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error != 0) + { + return error; + } + resolutionRAM = (controlRegister1 & 0x0C00) >> 10; + + return resolutionRAM; +} + +//------------------------------------------------------------------------------ + +int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate) +{ + uint16_t controlRegister1; + int value; + int error; + + value = (refreshRate & 0x07)<<7; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error == 0) + { + value = (controlRegister1 & 0xFC7F) | value; + error = MLX90640_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90640_GetRefreshRate(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int refreshRate; + int error; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error != 0) + { + return error; + } + refreshRate = (controlRegister1 & 0x0380) >> 7; + + return refreshRate; +} + +//------------------------------------------------------------------------------ + +int MLX90640_SetInterleavedMode(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int value; + int error; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + + if(error == 0) + { + value = (controlRegister1 & 0xEFFF); + error = MLX90640_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90640_SetChessMode(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int value; + int error; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + + if(error == 0) + { + value = (controlRegister1 | 0x1000); + error = MLX90640_I2CWrite(slaveAddr, 0x800D, value); + } + + return error; +} + +//------------------------------------------------------------------------------ + +int MLX90640_GetCurMode(uint8_t slaveAddr) +{ + uint16_t controlRegister1; + int modeRAM; + int error; + + error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1); + if(error != 0) + { + return error; + } + modeRAM = (controlRegister1 & 0x1000) >> 12; + + return modeRAM; +} + +//------------------------------------------------------------------------------ + +void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result) +{ + 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; + + subPage = frameData[833]; + vdd = MLX90640_GetVdd(frameData, params); + ta = MLX90640_GetTa(frameData, params); + ta4 = pow((ta + 273.15), (double)4); + tr4 = pow((tr + 273.15), (double)4); + taTr = tr4 - (tr4-ta4)/emissivity; + + alphaCorrR[0] = 1 / (1 + params->ksTo[0] * 40); + alphaCorrR[1] = 1 ; + alphaCorrR[2] = (1 + params->ksTo[2] * params->ct[2]); + alphaCorrR[3] = alphaCorrR[2] * (1 + params->ksTo[3] * (params->ct[3] - params->ct[2])); + +//------------------------- Gain calculation ----------------------------------- + gain = frameData[778]; + if(gain > 32767) + { + gain = gain - 65536; + } + + gain = params->gainEE / gain; + +//------------------------- To calculation ------------------------------------- + mode = (frameData[832] & 0x1000) >> 5; + + irDataCP[0] = frameData[776]; + irDataCP[1] = frameData[808]; + for( int i = 0; i < 2; i++) + { + if(irDataCP[i] > 32767) + { + irDataCP[i] = irDataCP[i] - 65536; + } + irDataCP[i] = irDataCP[i] * 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 = frameData[pixelNumber]; + if(irData > 32767) + { + irData = irData - 65536; + } + irData = irData * gain; + + irData = irData - params->offset[pixelNumber]*(1 + params->kta[pixelNumber]*(ta - 25))*(1 + params->kv[pixelNumber]*(vdd - 3.3)); + if(mode != params->calibrationModeEE) + { + irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern; + } + + irData = irData / emissivity; + + irData = irData - params->tgc * irDataCP[subPage]; + + alphaCompensated = (params->alpha[pixelNumber] - params->tgc * params->cpAlpha[subPage])*(1 + params->KsTa * (ta - 25)); + + Sx = pow((double)alphaCompensated, (double)3) * (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; + + subPage = frameData[833]; + vdd = MLX90640_GetVdd(frameData, params); + ta = MLX90640_GetTa(frameData, params); + +//------------------------- Gain calculation ----------------------------------- + gain = frameData[778]; + if(gain > 32767) + { + gain = gain - 65536; + } + + gain = params->gainEE / gain; + +//------------------------- Image calculation ------------------------------------- + mode = (frameData[832] & 0x1000) >> 5; + + irDataCP[0] = frameData[776]; + irDataCP[1] = frameData[808]; + for( int i = 0; i < 2; i++) + { + if(irDataCP[i] > 32767) + { + irDataCP[i] = irDataCP[i] - 65536; + } + irDataCP[i] = irDataCP[i] * 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 = frameData[pixelNumber]; + if(irData > 32767) + { + irData = irData - 65536; + } + irData = irData * gain; + + irData = irData - params->offset[pixelNumber]*(1 + params->kta[pixelNumber]*(ta - 25))*(1 + params->kv[pixelNumber]*(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] - params->tgc * params->cpAlpha[subPage])*(1 + params->KsTa * (ta - 25)); + + image = irData/alphaCompensated; + + result[pixelNumber] = image; + } + } +} + +//------------------------------------------------------------------------------ + +float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params) +{ + float vdd; + float resolutionCorrection; + + int resolutionRAM; + + vdd = frameData[810]; + if(vdd > 32767) + { + vdd = vdd - 65536; + } + resolutionRAM = (frameData[832] & 0x0C00) >> 10; + resolutionCorrection = pow(2, (double)params->resolutionEE) / pow(2, (double)resolutionRAM); + vdd = (resolutionCorrection * vdd - params->vdd25) / params->kVdd + 3.3; + + return vdd; +} + +//------------------------------------------------------------------------------ + +float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params) +{ + float ptat; + float ptatArt; + float vdd; + float ta; + + vdd = MLX90640_GetVdd(frameData, params); + + ptat = frameData[800]; + if(ptat > 32767) + { + ptat = ptat - 65536; + } + + ptatArt = frameData[768]; + 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 MLX90640_GetSubPageNumber(uint16_t *frameData) +{ + return frameData[833]; + +} + +//------------------------------------------------------------------------------ + +void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int16_t kVdd; + int16_t vdd25; + + kVdd = eeData[51]; + + kVdd = (eeData[51] & 0xFF00) >> 8; + if(kVdd > 127) + { + kVdd = kVdd - 256; + } + kVdd = 32 * kVdd; + vdd25 = eeData[51] & 0x00FF; + vdd25 = ((vdd25 - 256) << 5) - 8192; + + mlx90640->kVdd = kVdd; + mlx90640->vdd25 = vdd25; +} + +//------------------------------------------------------------------------------ + +void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + float KvPTAT; + float KtPTAT; + int16_t vPTAT25; + float alphaPTAT; + + KvPTAT = (eeData[50] & 0xFC00) >> 10; + if(KvPTAT > 31) + { + KvPTAT = KvPTAT - 64; + } + KvPTAT = KvPTAT/4096; + + KtPTAT = eeData[50] & 0x03FF; + if(KtPTAT > 511) + { + KtPTAT = KtPTAT - 1024; + } + KtPTAT = KtPTAT/8; + + vPTAT25 = eeData[49]; + + alphaPTAT = (eeData[16] & 0xF000) / pow(2, (double)14) + 8.0f; + + mlx90640->KvPTAT = KvPTAT; + mlx90640->KtPTAT = KtPTAT; + mlx90640->vPTAT25 = vPTAT25; + mlx90640->alphaPTAT = alphaPTAT; +} + +//------------------------------------------------------------------------------ + +void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int16_t gainEE; + + gainEE = eeData[48]; + if(gainEE > 32767) + { + gainEE = gainEE -65536; + } + + mlx90640->gainEE = gainEE; +} + +//------------------------------------------------------------------------------ + +void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + float tgc; + tgc = eeData[60] & 0x00FF; + if(tgc > 127) + { + tgc = tgc - 256; + } + tgc = tgc / 32.0f; + + mlx90640->tgc = tgc; +} + +//------------------------------------------------------------------------------ + +void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + uint8_t resolutionEE; + resolutionEE = (eeData[56] & 0x3000) >> 12; + + mlx90640->resolutionEE = resolutionEE; +} + +//------------------------------------------------------------------------------ + +void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + float KsTa; + KsTa = (eeData[60] & 0xFF00) >> 8; + if(KsTa > 127) + { + KsTa = KsTa -256; + } + KsTa = KsTa / 8192.0f; + + mlx90640->KsTa = KsTa; +} + +//------------------------------------------------------------------------------ + +void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int KsToScale; + int8_t step; + + step = ((eeData[63] & 0x3000) >> 12) * 10; + + mlx90640->ct[0] = -40; + mlx90640->ct[1] = 0; + mlx90640->ct[2] = (eeData[63] & 0x00F0) >> 4; + mlx90640->ct[3] = (eeData[63] & 0x0F00) >> 8; + + mlx90640->ct[2] = mlx90640->ct[2]*step; + mlx90640->ct[3] = mlx90640->ct[2] + mlx90640->ct[3]*step; + + KsToScale = (eeData[63] & 0x000F) + 8; + KsToScale = 1 << KsToScale; + + mlx90640->ksTo[0] = eeData[61] & 0x00FF; + mlx90640->ksTo[1] = (eeData[61] & 0xFF00) >> 8; + mlx90640->ksTo[2] = eeData[62] & 0x00FF; + mlx90640->ksTo[3] = (eeData[62] & 0xFF00) >> 8; + + + for(int i = 0; i < 4; i++) + { + if(mlx90640->ksTo[i] > 127) + { + mlx90640->ksTo[i] = mlx90640->ksTo[i] -256; + } + mlx90640->ksTo[i] = mlx90640->ksTo[i] / KsToScale; + } +} + +//------------------------------------------------------------------------------ + +void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int accRow[24]; + int accColumn[32]; + int p = 0; + int alphaRef; + uint8_t alphaScale; + uint8_t accRowScale; + uint8_t accColumnScale; + uint8_t accRemScale; + + + accRemScale = eeData[32] & 0x000F; + accColumnScale = (eeData[32] & 0x00F0) >> 4; + accRowScale = (eeData[32] & 0x0F00) >> 8; + alphaScale = ((eeData[32] & 0xF000) >> 12) + 30; + alphaRef = eeData[33]; + + for(int i = 0; i < 6; i++) + { + p = i * 4; + accRow[p + 0] = (eeData[34 + i] & 0x000F); + accRow[p + 1] = (eeData[34 + i] & 0x00F0) >> 4; + accRow[p + 2] = (eeData[34 + i] & 0x0F00) >> 8; + accRow[p + 3] = (eeData[34 + i] & 0xF000) >> 12; + } + + for(int i = 0; i < 24; i++) + { + if (accRow[i] > 7) + { + accRow[i] = accRow[i] - 16; + } + } + + for(int i = 0; i < 8; i++) + { + p = i * 4; + accColumn[p + 0] = (eeData[40 + i] & 0x000F); + accColumn[p + 1] = (eeData[40 + i] & 0x00F0) >> 4; + accColumn[p + 2] = (eeData[40 + i] & 0x0F00) >> 8; + accColumn[p + 3] = (eeData[40 + i] & 0xF000) >> 12; + } + + for(int i = 0; i < 32; i ++) + { + if (accColumn[i] > 7) + { + accColumn[i] = accColumn[i] - 16; + } + } + + for(int i = 0; i < 24; i++) + { + for(int j = 0; j < 32; j ++) + { + p = 32 * i +j; + mlx90640->alpha[p] = (eeData[64 + p] & 0x03F0) >> 4; + if (mlx90640->alpha[p] > 31) + { + mlx90640->alpha[p] = mlx90640->alpha[p] - 64; + } + mlx90640->alpha[p] = mlx90640->alpha[p]*(1 << accRemScale); + mlx90640->alpha[p] = (alphaRef + (accRow[i] << accRowScale) + (accColumn[j] << accColumnScale) + mlx90640->alpha[p]); + mlx90640->alpha[p] = mlx90640->alpha[p] / pow(2,(double)alphaScale); + } + } +} + +//------------------------------------------------------------------------------ + +void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int occRow[24]; + int occColumn[32]; + int p = 0; + int16_t offsetRef; + uint8_t occRowScale; + uint8_t occColumnScale; + uint8_t occRemScale; + + + occRemScale = (eeData[16] & 0x000F); + occColumnScale = (eeData[16] & 0x00F0) >> 4; + occRowScale = (eeData[16] & 0x0F00) >> 8; + offsetRef = eeData[17]; + if (offsetRef > 32767) + { + offsetRef = offsetRef - 65536; + } + + for(int i = 0; i < 6; i++) + { + p = i * 4; + occRow[p + 0] = (eeData[18 + i] & 0x000F); + occRow[p + 1] = (eeData[18 + i] & 0x00F0) >> 4; + occRow[p + 2] = (eeData[18 + i] & 0x0F00) >> 8; + occRow[p + 3] = (eeData[18 + i] & 0xF000) >> 12; + } + + for(int i = 0; i < 24; i++) + { + if (occRow[i] > 7) + { + occRow[i] = occRow[i] - 16; + } + } + + for(int i = 0; i < 8; i++) + { + p = i * 4; + occColumn[p + 0] = (eeData[24 + i] & 0x000F); + occColumn[p + 1] = (eeData[24 + i] & 0x00F0) >> 4; + occColumn[p + 2] = (eeData[24 + i] & 0x0F00) >> 8; + occColumn[p + 3] = (eeData[24 + i] & 0xF000) >> 12; + } + + for(int i = 0; i < 32; i ++) + { + if (occColumn[i] > 7) + { + occColumn[i] = occColumn[i] - 16; + } + } + + for(int i = 0; i < 24; i++) + { + for(int j = 0; j < 32; j ++) + { + p = 32 * i +j; + mlx90640->offset[p] = (eeData[64 + p] & 0xFC00) >> 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]); + } + } +} + +//------------------------------------------------------------------------------ + +void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + int p = 0; + int8_t KtaRC[4]; + int8_t KtaRoCo; + int8_t KtaRoCe; + int8_t KtaReCo; + int8_t KtaReCe; + uint8_t ktaScale1; + uint8_t ktaScale2; + uint8_t split; + + KtaRoCo = (eeData[54] & 0xFF00) >> 8; + if (KtaRoCo > 127) + { + KtaRoCo = KtaRoCo - 256; + } + KtaRC[0] = KtaRoCo; + + KtaReCo = (eeData[54] & 0x00FF); + if (KtaReCo > 127) + { + KtaReCo = KtaReCo - 256; + } + KtaRC[2] = KtaReCo; + + KtaRoCe = (eeData[55] & 0xFF00) >> 8; + if (KtaRoCe > 127) + { + KtaRoCe = KtaRoCe - 256; + } + KtaRC[1] = KtaRoCe; + + KtaReCe = (eeData[55] & 0x00FF); + if (KtaReCe > 127) + { + KtaReCe = KtaReCe - 256; + } + KtaRC[3] = KtaReCe; + + ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8; + ktaScale2 = (eeData[56] & 0x000F); + + for(int i = 0; i < 24; i++) + { + for(int j = 0; j < 32; j ++) + { + p = 32 * i +j; + split = 2*(p/32 - (p/64)*2) + p%2; + mlx90640->kta[p] = (eeData[64 + p] & 0x000E) >> 1; + if (mlx90640->kta[p] > 3) + { + mlx90640->kta[p] = mlx90640->kta[p] - 8; + } + mlx90640->kta[p] = mlx90640->kta[p] * (1 << ktaScale2); + mlx90640->kta[p] = KtaRC[split] + mlx90640->kta[p]; + mlx90640->kta[p] = mlx90640->kta[p] / pow(2,(double)ktaScale1); + } + } +} + +//------------------------------------------------------------------------------ + +void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + 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; + + KvRoCo = (eeData[52] & 0xF000) >> 12; + if (KvRoCo > 7) + { + KvRoCo = KvRoCo - 16; + } + KvT[0] = KvRoCo; + + KvReCo = (eeData[52] & 0x0F00) >> 8; + if (KvReCo > 7) + { + KvReCo = KvReCo - 16; + } + KvT[2] = KvReCo; + + KvRoCe = (eeData[52] & 0x00F0) >> 4; + if (KvRoCe > 7) + { + KvRoCe = KvRoCe - 16; + } + KvT[1] = KvRoCe; + + KvReCe = (eeData[52] & 0x000F); + if (KvReCe > 7) + { + KvReCe = KvReCe - 16; + } + KvT[3] = KvReCe; + + kvScale = (eeData[56] & 0x0F00) >> 8; + + + for(int i = 0; i < 24; i++) + { + for(int j = 0; j < 32; j ++) + { + p = 32 * i +j; + split = 2*(p/32 - (p/64)*2) + p%2; + mlx90640->kv[p] = KvT[split]; + mlx90640->kv[p] = mlx90640->kv[p] / pow(2,(double)kvScale); + } + } +} + +//------------------------------------------------------------------------------ + +void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + float alphaSP[2]; + int16_t offsetSP[2]; + float cpKv; + float cpKta; + uint8_t alphaScale; + uint8_t ktaScale1; + uint8_t kvScale; + + alphaScale = ((eeData[32] & 0xF000) >> 12) + 27; + + offsetSP[0] = (eeData[58] & 0x03FF); + if (offsetSP[0] > 511) + { + offsetSP[0] = offsetSP[0] - 1024; + } + + offsetSP[1] = (eeData[58] & 0xFC00) >> 10; + if (offsetSP[1] > 31) + { + offsetSP[1] = offsetSP[1] - 64; + } + offsetSP[1] = offsetSP[1] + offsetSP[0]; + + alphaSP[0] = (eeData[57] & 0x03FF); + if (alphaSP[0] > 511) + { + alphaSP[0] = alphaSP[0] - 1024; + } + alphaSP[0] = alphaSP[0] / pow(2,(double)alphaScale); + + alphaSP[1] = (eeData[57] & 0xFC00) >> 10; + if (alphaSP[1] > 31) + { + alphaSP[1] = alphaSP[1] - 64; + } + alphaSP[1] = (1 + alphaSP[1]/128) * alphaSP[0]; + + cpKta = (eeData[59] & 0x00FF); + if (cpKta > 127) + { + cpKta = cpKta - 256; + } + ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8; + mlx90640->cpKta = cpKta / pow(2,(double)ktaScale1); + + cpKv = (eeData[59] & 0xFF00) >> 8; + if (cpKv > 127) + { + cpKv = cpKv - 256; + } + kvScale = (eeData[56] & 0x0F00) >> 8; + mlx90640->cpKv = cpKv / pow(2,(double)kvScale); + + mlx90640->cpAlpha[0] = alphaSP[0]; + mlx90640->cpAlpha[1] = alphaSP[1]; + mlx90640->cpOffset[0] = offsetSP[0]; + mlx90640->cpOffset[1] = offsetSP[1]; +} + +//------------------------------------------------------------------------------ + +void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + 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]; +} + +//------------------------------------------------------------------------------ + +int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640) +{ + 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 < 768 && 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 = -3; + } + else if(outlierPixCnt > 4) + { + warn = -4; + } + else if((brokenPixCnt + outlierPixCnt) > 4) + { + warn = -5; + } + else + { + for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->brokenPixels[i]); + if(warn != 0) + { + return warn; + } + } + } + + for(pixCnt=0; pixCntoutlierPixels[pixCnt],mlx90640->outlierPixels[i]); + if(warn != 0) + { + return warn; + } + } + } + + for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->outlierPixels[i]); + if(warn != 0) + { + return warn; + } + } + } + + } + + + return warn; + +} + +//------------------------------------------------------------------------------ + + int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2) + { + int pixPosDif; + + pixPosDif = pix1 - pix2; + if(pixPosDif > -34 && pixPosDif < -30) + { + return -6; + } + if(pixPosDif > -2 && pixPosDif < 2) + { + return -6; + } + if(pixPosDif > 30 && pixPosDif < 34) + { + return -6; + } + + return 0; + } + + //------------------------------------------------------------------------------ + + int CheckEEPROMValid(uint16_t *eeData) + { + int deviceSelect; + deviceSelect = eeData[10] & 0x0040; + if(deviceSelect == 0) + { + return 0; + } + + return -7; + } diff --git a/src/mlx/src/MLX90640_I2C_Driver.c b/src/mlx/src/MLX90640_I2C_Driver.c new file mode 100644 index 000000000..5eae81d38 --- /dev/null +++ b/src/mlx/src/MLX90640_I2C_Driver.c @@ -0,0 +1,107 @@ +/** + * @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 "mbed.h" +#include "MLX90640_I2C_Driver.h" + +I2C i2c(p9, p10); + +void MLX90640_I2CInit() +{ + i2c.stop(); +} + +int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data) +{ + uint8_t sa; + int ack = 0; + int cnt = 0; + int i = 0; + char cmd[2] = {0,0}; + char i2cData[1664] = {0}; + uint16_t *p; + + p = data; + sa = (slaveAddr << 1); + cmd[0] = startAddress >> 8; + cmd[1] = startAddress & 0x00FF; + + i2c.stop(); + wait_us(5); + ack = i2c.write(sa, cmd, 2, 1); + + if (ack != 0x00) + { + return -1; + } + + sa = sa | 0x01; + ack = i2c.read(sa, i2cData, 2*nMemAddressRead, 0); + + if (ack != 0x00) + { + return -1; + } + i2c.stop(); + + for(cnt=0; cnt < nMemAddressRead; cnt++) + { + i = cnt << 1; + *p++ = (uint16_t)i2cData[i]*256 + (uint16_t)i2cData[i+1]; + } + + return 0; +} + +void MLX90640_I2CFreqSet(int freq) +{ + i2c.frequency(1000*freq); +} + +int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data) +{ + uint8_t sa; + int ack = 0; + char cmd[4] = {0,0,0,0}; + uint16_t dataCheck; + + + sa = (slaveAddr << 1); + cmd[0] = writeAddress >> 8; + cmd[1] = writeAddress & 0x00FF; + cmd[2] = data >> 8; + cmd[3] = data & 0x00FF; + + i2c.stop(); + wait_us(5); + ack = i2c.write(sa, cmd, 4, 0); + + if (ack != 0x00) + { + return -1; + } + i2c.stop(); + + MLX90640_I2CRead(slaveAddr,writeAddress,1, &dataCheck); + + if ( dataCheck != data) + { + return -2; + } + + return 0; +}*/ + diff --git a/src/mlx/src/MLX90640_SWI2C_Driver.c b/src/mlx/src/MLX90640_SWI2C_Driver.c new file mode 100644 index 000000000..9d9de4597 --- /dev/null +++ b/src/mlx/src/MLX90640_SWI2C_Driver.c @@ -0,0 +1,334 @@ +/** + * @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. + * + */ + /** + * As the timings depend heavily on the MCU in use, it is recommended + * to make sure that the proper timings are achieved. For that purpose + * an oscilloscope might be needed to strobe the SCL and SDA signals. + * The Wait(int) function could be modified in order to better + * trim the frequency. For coarse setting of the frequency or + * dynamic frequency change using the default function implementation, + * ‘freqCnt’ argument should be changed – lower value results in + * higher frequency. + */ + +//#include "mbed.h" +#include "MLX90640_I2C_Driver.h" +#include STM32_HAL_H +#include "omv_boardconfig.h" + +//DigitalInOut sda(p9); +#define sda I2C_SIOD_READ() +//DigitalOut scl(p10); + +#define LOW 0; +#define HIGH 1; + +//#define SCL_HIGH scl = HIGH; +#define SCL_HIGH I2C_SIOC_H() +//#define SCL_LOW scl = LOW; +#define SCL_LOW I2C_SIOC_L() +//#define SDA_HIGH sda.input(); +#define SDA_HIGH I2C_SIOD_H() +//#define SDA_LOW sda.output(); sda = LOW; +#define SDA_LOW I2C_SIOD_L() + +int I2CSendByte(int8_t); +void I2CReadBytes(int, char *); +void I2CStart(void); +void I2CStop(void); +void I2CRepeatedStart(void); +void I2CSendACK(void); +void I2CSendNack(void); +int I2CReceiveAck(void); +void Wait(int); + +static int freqCnt; + +void MLX90640_I2CInit() +{ + I2CStop(); +} + +int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress,uint16_t nMemAddressRead, uint16_t *data) +{ + uint8_t sa; + int ack = 0; + int cnt = 0; + int i = 0; + char cmd[2] = {0,0}; + char i2cData[1664] = {0}; + uint16_t *p; + + p = data; + sa = (slaveAddr << 1); + cmd[0] = startAddress >> 8; + cmd[1] = startAddress & 0x00FF; + + I2CStop(); + Wait(freqCnt); + I2CStart(); + Wait(freqCnt); + + ack = I2CSendByte(sa)!=0; + if(ack != 0) + { + return -1; + } + + ack = I2CSendByte(cmd[0])!=0; + if(ack != 0) + { + return -1; + } + + ack = I2CSendByte(cmd[1])!=0; + if(ack != 0) + { + return -1; + } + + I2CRepeatedStart(); + + sa = sa | 0x01; + + ack = I2CSendByte(sa); + if(ack != 0) + { + return -1; + } + + I2CReadBytes((nMemAddressRead << 1), i2cData); + + I2CStop(); + + for(cnt=0; cnt < nMemAddressRead; cnt++) + { + i = cnt << 1; + *p++ = (int)i2cData[i]*256 + (int)i2cData[i+1]; + } + return 0; + +} + +void MLX90640_I2CFreqSet(int freq) +{ + freqCnt = freq>>1; +} + +int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data) +{ + uint8_t sa; + int ack = 0; + char cmd[4] = {0,0,0,0}; + uint16_t dataCheck; + + sa = (slaveAddr << 1); + cmd[0] = writeAddress >> 8; + cmd[1] = writeAddress & 0x00FF; + cmd[2] = data >> 8; + cmd[3] = data & 0x00FF; + + I2CStop(); + Wait(freqCnt); + I2CStart(); + ack = I2CSendByte(sa); + if (ack != 0x00) + { + return 1; + } + + for(int i = 0; i<4; i++) + { + ack = I2CSendByte(cmd[i]); + + if (ack != 0x00) + { + return -1; + } + } + I2CStop(); + + MLX90640_I2CRead(slaveAddr,writeAddress,1, &dataCheck); + + if ( dataCheck != data) + { + return -2; + } + + return 0; +} + +int I2CSendByte(int8_t data) +{ + int ack = 1; + int8_t byte = data; + + for(int i=0;i<8;i++) + { + Wait(freqCnt); + + if(byte & 0x80) + { + SDA_HIGH; + } + else + { + SDA_LOW; + } + Wait(freqCnt); + SCL_HIGH; + Wait(freqCnt); + Wait(freqCnt); + SCL_LOW; + byte = byte<<1; + } + + Wait(freqCnt); + ack = I2CReceiveAck(); + + return ack; +} + +void I2CReadBytes(int nBytes, char *dataP) +{ + char data; + for(int j=0;j #include "imlib.h" #include "fb_alloc.h" +#ifdef IMLIB_ENABLE_BINARY_OPS typedef struct gvec { uint16_t t; @@ -151,3 +152,4 @@ void imlib_edge_canny(image_t *src, rectangle_t *roi, int low_thresh, int high_t fb_free(); } +#endif diff --git a/src/omv/py/py_fir.c b/src/omv/py/py_fir.c index 420e29393..67eb6f869 100644 --- a/src/omv/py/py_fir.c +++ b/src/omv/py/py_fir.c @@ -3,18 +3,18 @@ * Copyright (c) 2013/2014 Ibrahim Abdelkader * This work is licensed under the MIT license, see the file LICENSE for details. * - * MLX90621 Python module. + * MLX Python module. * */ -#include -#include -#include +#include #include "soft_i2c.h" -#include "fb_alloc.h" -#include "xalloc.h" -#include "py_assert.h" -#include "py_image.h" +#include "MLX90640_I2C_Driver.h" +#include "MLX90640_API.h" +#include "omv_boardconfig.h" +#include "framebuffer.h" +#include "sensor.h" #include "py_helper.h" +#include "py_image.h" #include "py_fir.h" #define FIR_EEPROM_ADDR 0xA0 @@ -71,6 +71,10 @@ #define CAL_BCP 0xD5 +#define MLX90640_ADDR 0x33 + +#define AMG8833_ADDR 0xD2 + #define MAP(OldValue, OldMin, OldMax, NewMin, NewMax) \ ({ __typeof__ (OldValue) _OldValue = (OldValue); \ __typeof__ (OldMin) _OldMin = (OldMin); \ @@ -89,73 +93,77 @@ static float *b_ij = NULL; static float *alpha_ij = NULL; static float v_th, k_t1, k_t2, tgc, emissivity, ksta, alpha_cp, ks4, a_cp, b_cp; -static int width = 0; -static int height = 0; -static enum { FIR_NONE, FIR_SHIELD } type = FIR_NONE; +static uint8_t width = 0; +static uint8_t height = 0; +static enum { FIR_NONE, FIR_SHIELD, FIR_MLX90640, FIR_AMG8833 } type = FIR_NONE; +static uint8_t IR_refresh_rate = 0; +static uint8_t ADC_resolution = 0; + +static void test_ack(int ret) +{ + PY_ASSERT_TRUE_MSG(ret == 0, "I2C Bus communication error - missing ACK!"); +} static float calculate_Ta() // ambient temp { // Code to handle dealing with brown-out conditions. { uint16_t config_reg; - soft_i2c_write_bytes(FIR_MODULE_ADDR, - (uint8_t [4]) {FIR_READ_CMD, 0x92, 0x00, 0x01}, 4, false); - soft_i2c_read_bytes(FIR_MODULE_ADDR, - (uint8_t*) &config_reg, 2, true); + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, + (uint8_t [4]) {FIR_READ_CMD, 0x92, 0x00, 0x01}, 4, false)); + test_ack(soft_i2c_read_bytes(FIR_MODULE_ADDR, + (uint8_t*) &config_reg, 2, true)); if (!(config_reg & 0x0400)) { // brown out uint8_t eeprom; // Read the eeprom. - soft_i2c_write_bytes(FIR_EEPROM_ADDR, - (uint8_t [1]){CAL_OSC_TRIM}, 1, false); - soft_i2c_read_bytes(FIR_EEPROM_ADDR, - &eeprom, 1, true); + test_ack(soft_i2c_write_bytes(FIR_EEPROM_ADDR, + (uint8_t [1]){CAL_OSC_TRIM}, 1, false)); + test_ack(soft_i2c_read_bytes(FIR_EEPROM_ADDR, + &eeprom, 1, true)); // Write oscillator trimming value. soft_i2c_write_bytes(FIR_MODULE_ADDR, (uint8_t [5]){FIR_WR_TRIM_CMD, (uint8_t)(eeprom-0xAA), eeprom, - (uint8_t)(0x00-0xAA), 0x00}, 5, true); + (uint8_t)(0x00-0xAA), 0x00}, 5, true); // no ack here // Write device configuration value. - uint8_t IR_refresh_rate = 0x8; // 64 Hz - uint8_t ADC_resolution = 0x3; // 18-bits uint8_t lsb = (ADC_resolution << 4) | IR_refresh_rate; // Normal Operation Mode - Continuous Measurment Mode - // ADC set to 18 bit resolution - IR Refresh rate = 64 Hz uint8_t msb = 0x44; // ADC low reference enabled - EEPROM enabled // I2C FM+ enabled - soft_i2c_write_bytes(FIR_MODULE_ADDR, + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, (uint8_t [5]){FIR_WR_CFG_REG, (uint8_t)(lsb-0x55), lsb, - (uint8_t)(msb-0x55), msb}, 5, true); + (uint8_t)(msb-0x55), msb}, 5, true)); } } uint16_t ptat; - soft_i2c_write_bytes(FIR_MODULE_ADDR, - (uint8_t [4]) {FIR_READ_CMD, 0x40, 0x00, 0x01}, 4, false); - soft_i2c_read_bytes(FIR_MODULE_ADDR, - (uint8_t*) &ptat, 2, true); - return (((-k_t1)+fast_sqrtf((k_t1*k_t1)-(4*k_t2*(v_th-ptat))))/(2*k_t2))+25; + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, + (uint8_t [4]) {FIR_READ_CMD, 0x40, 0x00, 0x01}, 4, false)); + test_ack(soft_i2c_read_bytes(FIR_MODULE_ADDR, + (uint8_t*) &ptat, 2, true)); + return (((-k_t1)+sqrtf((k_t1*k_t1)-(4*k_t2*(v_th-ptat))))/(2*k_t2))+25; } static void calculate_To(float Ta, float *To) { - int16_t v_ir[64]; + int16_t *v_ir = fb_alloc(64 * sizeof(int16_t)); // Read IR sensor result - soft_i2c_write_bytes(FIR_MODULE_ADDR, - (uint8_t [4]){FIR_READ_CMD, 0x00, 0x01, 0x40}, 4, false); - soft_i2c_read_bytes(FIR_MODULE_ADDR, - (uint8_t*) v_ir, 128, true); + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, + (uint8_t [4]){FIR_READ_CMD, 0x00, 0x01, 0x40}, 4, false)); + test_ack(soft_i2c_read_bytes(FIR_MODULE_ADDR, + (uint8_t*) v_ir, 128, true)); int16_t v_cp; // Read compensation pixel result - soft_i2c_write_bytes(FIR_MODULE_ADDR, - (uint8_t [4]){FIR_READ_CMD, 0x41, 0x00, 0x01}, 4, false); - soft_i2c_read_bytes(FIR_MODULE_ADDR, - (uint8_t*) &v_cp, 2, true); + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, + (uint8_t [4]){FIR_READ_CMD, 0x41, 0x00, 0x01}, 4, false)); + test_ack(soft_i2c_read_bytes(FIR_MODULE_ADDR, + (uint8_t*) &v_cp, 2, true)); // Calculate Thermal Gradien Compensation (TGC) float v_ir_cp_off_comp = v_cp-(a_cp+(b_cp*(Ta-25))); @@ -181,6 +189,7 @@ static void calculate_To(float Ta, float *To) // To[i] = sqrtf(sqrtf((v_ir_comp/((alpha_comp_ij*(1-(ks4*273.15f)))+sx))+Ta4))-273.15f; To[i] = sqrtf(sqrtf((v_ir_comp/alpha_comp_ij)+Tak4))-273.15f; } + fb_free(); } static mp_obj_t py_fir_deinit() @@ -189,11 +198,14 @@ static mp_obj_t py_fir_deinit() case FIR_NONE: return mp_const_none; case FIR_SHIELD: + case FIR_MLX90640: + case FIR_AMG8833: soft_i2c_deinit(); width = 0; height = 0; type = FIR_NONE; - + IR_refresh_rate = 0; + ADC_resolution = 0; if (a_ij) { a_ij = NULL; } @@ -207,6 +219,7 @@ static mp_obj_t py_fir_deinit() } return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_deinit_obj, py_fir_deinit); /* Allows the refresh rate to be set in the range 1Hz and 512Hz, in powers of 2. (64Hz default) @@ -225,7 +238,9 @@ mp_obj_t py_fir_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) py_fir_deinit(); switch (py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_type), FIR_SHIELD)) { case FIR_NONE: + { return mp_const_none; + } case FIR_SHIELD: { width = 16; @@ -233,43 +248,42 @@ mp_obj_t py_fir_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) type = FIR_SHIELD; soft_i2c_init(); - // pasre refresh rate and ADC resolution - uint32_t IR_refresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 64); // 64Hz - uint32_t ADC_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 18); // 18-bits + // parse refresh rate and ADC resolution + IR_refresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 64); // 64Hz + ADC_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 18); // 18-bits // sanitize values - ADC_resolution = ((ADC_resolution > 18)? 18:(ADC_resolution < 15)? 15:ADC_resolution) - 15; - IR_refresh_rate = 14 - __CLZ(__RBIT((IR_refresh_rate > 512) ? 512:(IR_refresh_rate < 1)? 1:IR_refresh_rate)); + ADC_resolution = ((ADC_resolution > 18) ? 18 : (ADC_resolution < 15) ? 15 : ADC_resolution) - 15; + IR_refresh_rate = 14 - __CLZ(__RBIT((IR_refresh_rate > 512) ? 512 : (IR_refresh_rate < 1) ? 1 : IR_refresh_rate)); a_ij = xalloc(64 * sizeof(*a_ij)); b_ij = xalloc(64 * sizeof(*b_ij)); alpha_ij = xalloc(64 * sizeof(*alpha_ij)); - uint8_t eeprom[256]; + uint8_t *eeprom = fb_alloc(256 * sizeof(uint8_t)); // Read the whole eeprom. - soft_i2c_write_bytes(FIR_EEPROM_ADDR, - (uint8_t [1]){0x00}, 1, false); - soft_i2c_read_bytes(FIR_EEPROM_ADDR, - eeprom, 256, true); + test_ack(soft_i2c_write_bytes(FIR_EEPROM_ADDR, + (uint8_t [1]){0x00}, 1, false)); + test_ack(soft_i2c_read_bytes(FIR_EEPROM_ADDR, + eeprom, 256, true)); // Write oscillator trimming value. soft_i2c_write_bytes(FIR_MODULE_ADDR, (uint8_t [5]){FIR_WR_TRIM_CMD, (uint8_t)(eeprom[CAL_OSC_TRIM]-0xAA), eeprom[CAL_OSC_TRIM], - (uint8_t)(0x00-0xAA), 0x00}, 5, true); + (uint8_t)(0x00-0xAA), 0x00}, 5, true); // no ack here // Write device configuration value. // assignment of IR_refresh_rate and ADC_resolution now done above uint8_t lsb = (ADC_resolution << 4) | IR_refresh_rate; // Normal Operation Mode - Continuous Measurment Mode - // ADC set to 18 bit resolution - IR Refresh rate = 64 Hz uint8_t msb = 0x44; // ADC low reference enabled - EEPROM enabled // I2C FM+ enabled - soft_i2c_write_bytes(FIR_MODULE_ADDR, + test_ack(soft_i2c_write_bytes(FIR_MODULE_ADDR, (uint8_t [5]){FIR_WR_CFG_REG, (uint8_t)(lsb-0x55), lsb, - (uint8_t)(msb-0x55), msb}, 5, true); + (uint8_t)(msb-0x55), msb}, 5, true)); v_th = ((int16_t)((eeprom[CAL_VTH_H]<<8)|eeprom[CAL_VTH_L])) / powf(2,3-ADC_resolution); @@ -321,83 +335,255 @@ mp_obj_t py_fir_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) b_cp = ((int8_t)eeprom[CAL_BCP]) / powf(2,b_i_scale+(3-ADC_resolution)); + fb_free(); + return mp_const_none; + } + case FIR_MLX90640: + { + width = 32; + height = 24; + type = FIR_MLX90640; + soft_i2c_init(); + + // parse refresh rate and ADC resolution + IR_refresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 32); // 32Hz + ADC_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 19); // 19-bits + + // sanitize values + ADC_resolution = ((ADC_resolution > 19) ? 19 : (ADC_resolution < 16) ? 16 : ADC_resolution) - 16; + IR_refresh_rate = __CLZ(__RBIT((IR_refresh_rate > 64) ? 64 : (IR_refresh_rate < 1) ? 1 : IR_refresh_rate)) + 1; + + alpha_ij = xalloc(sizeof(paramsMLX90640)); + + MLX90640_I2CFreqSet(I2C_SPIN_DELAY); + MLX90640_I2CInit(); + + int error = 0; + + error |= MLX90640_SetResolution(MLX90640_ADDR, ADC_resolution); + error |= MLX90640_SetRefreshRate(MLX90640_ADDR, IR_refresh_rate); + + uint16_t *eeprom = fb_alloc(832 * sizeof(uint16_t)); + error |= MLX90640_DumpEE(MLX90640_ADDR, eeprom); + error |= MLX90640_ExtractParameters(eeprom, (paramsMLX90640 *) alpha_ij); + + PY_ASSERT_TRUE_MSG(error == 0, "Failed to init the MLX90640!"); + + fb_free(); + return mp_const_none; + } + case FIR_AMG8833: + { + width = 8; + height = 8; + type = FIR_AMG8833; + soft_i2c_init(); + + IR_refresh_rate = 10; + ADC_resolution = 12; + + test_ack(soft_i2c_write_bytes(AMG8833_ADDR, (uint8_t [2]){0x01, 0x3F}, 2, true)); + return mp_const_none; } } return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_init_obj, 0, py_fir_init); static mp_obj_t py_fir_width() { if (type == FIR_NONE) return mp_const_none; return mp_obj_new_int(width); } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_width_obj, py_fir_width); static mp_obj_t py_fir_height() { if (type == FIR_NONE) return mp_const_none; return mp_obj_new_int(height); } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_height_obj, py_fir_height); static mp_obj_t py_fir_type() { if (type == FIR_NONE) return mp_const_none; return mp_obj_new_int(type); } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_type_obj, py_fir_type); + +static mp_obj_t py_fir_refresh() +{ + const int mlx_90621_refresh_rates[16] = {512, 512, 512, 512, 512, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0}; + const int mlx_90640_refresh_rates[8] = {0, 1, 2, 4, 8, 16, 32, 64}; + if (type == FIR_NONE) return mp_const_none; + if (type == FIR_SHIELD) return mp_obj_new_int(mlx_90621_refresh_rates[IR_refresh_rate]); + if (type == FIR_MLX90640) return mp_obj_new_int(mlx_90640_refresh_rates[IR_refresh_rate]); + if (type == FIR_AMG8833) return mp_obj_new_int(IR_refresh_rate); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_refresh_obj, py_fir_refresh); + +static mp_obj_t py_fir_resolution() +{ + if (type == FIR_NONE) return mp_const_none; + if (type == FIR_SHIELD) return mp_obj_new_int(ADC_resolution + 15); + if (type == FIR_MLX90640) return mp_obj_new_int(ADC_resolution + 16); + if (type == FIR_AMG8833) return mp_obj_new_int(ADC_resolution); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_resolution_obj, py_fir_resolution); mp_obj_t py_fir_read_ta() { - if (type == FIR_NONE) return mp_const_none; - return mp_obj_new_float(calculate_Ta()); + switch(type) { + case FIR_NONE: return mp_const_none; + case FIR_SHIELD: return mp_obj_new_float(calculate_Ta()); + case FIR_MLX90640: + { + uint16_t *data = fb_alloc(834 * sizeof(uint16_t)); + PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0, + "Failed to read the MLX90640 sensor data!"); + mp_obj_t result = mp_obj_new_float(MLX90640_GetTa(data, (paramsMLX90640 *) alpha_ij)); + fb_free(); + return result; + } + case FIR_AMG8833: + { + test_ack(soft_i2c_write_bytes(AMG8833_ADDR, (uint8_t [1]){0x0E}, 1, true)); + int16_t temp; + test_ack(soft_i2c_read_bytes(AMG8833_ADDR, (uint8_t *) &temp, 2, true)); + if ((temp >> 11) & 1) temp |= 1 << 15; + temp &= 0x87FF; + return mp_obj_new_float(temp * 0.0625); + } + } + return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_read_ta_obj, py_fir_read_ta); mp_obj_t py_fir_read_ir() { - if (type == FIR_NONE) { - return mp_const_none; - } + switch(type) { + case FIR_NONE: return mp_const_none; + case FIR_SHIELD: + { + float *To = fb_alloc(64 * sizeof(float)), *To_rot = fb_alloc(64 * sizeof(float)); + float Ta = calculate_Ta(); + float min = FLT_MAX, max = FLT_MIN; - float To[64], To_rot[64]; - float Ta = calculate_Ta(); - float min = FLT_MAX, max = FLT_MIN; + // Calculate object temperatures + calculate_To(Ta, To_rot); - // Calculate object temperatures - calculate_To(Ta, To_rot); + // Rotate temperatures array (sensor memory is read column wise). + for (int x=15, r=0; x>=0; x--) { + for (int y=0; y<4; y++) { + float temp = To[y*16 + x] = To_rot[r++]; + min = IM_MIN(min, temp); + max = IM_MAX(max, temp); + } + } - // Rotate temperatures array (sensor memory is read column wise). - for (int x=15, r=0; x>=0; x--) { - for (int y=0; y<4; y++) { - float temp = To[y*16 + x] = To_rot[r++]; - min = IM_MIN(min, temp); - max = IM_MAX(max, temp); + mp_obj_t tuple[4]; + tuple[0] = mp_obj_new_float(Ta); + tuple[1] = mp_obj_new_list(0, NULL); + tuple[2] = mp_obj_new_float(min); + tuple[3] = mp_obj_new_float(max); + + for (int i=0; i<64; i++) { + mp_obj_list_append(tuple[1], mp_obj_new_float(To[i])); + } + + fb_free(); + fb_free(); + return mp_obj_new_tuple(4, tuple); + } + case FIR_MLX90640: + { + uint16_t *data = fb_alloc(834 * sizeof(uint16_t)); + // Calculate 1st sub-frame... + PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0, + "Failed to read the MLX90640 sensor data!"); + float Ta = MLX90640_GetTa(data, (paramsMLX90640 *) alpha_ij); + float *To = fb_alloc0(768 * sizeof(float)); + MLX90640_CalculateTo(data, (paramsMLX90640 *) alpha_ij, 0.95, Ta - 8, To); + // Calculate 2nd sub-frame... + PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0, + "Failed to read the MLX90640 sensor data!"); + Ta = MLX90640_GetTa(data, (paramsMLX90640 *) alpha_ij); + MLX90640_CalculateTo(data, (paramsMLX90640 *) alpha_ij, 0.95, Ta - 8, To); + float min = FLT_MAX, max = FLT_MIN; + + for (int i=0; i<768; i++) { + min = IM_MIN(min, To[i]); + max = IM_MAX(max, To[i]); + } + + mp_obj_t tuple[4]; + tuple[0] = mp_obj_new_float(Ta); + tuple[1] = mp_obj_new_list(0, NULL); + tuple[2] = mp_obj_new_float(min); + tuple[3] = mp_obj_new_float(max); + + for (int i=0; i<768; i++) { + mp_obj_list_append(tuple[1], mp_obj_new_float(To[i])); + } + + fb_free(); + fb_free(); + return mp_obj_new_tuple(4, tuple); + } + case FIR_AMG8833: + { + test_ack(soft_i2c_write_bytes(AMG8833_ADDR, (uint8_t [1]){0x0E}, 1, true)); + int16_t temp; + test_ack(soft_i2c_read_bytes(AMG8833_ADDR, (uint8_t *) &temp, 2, true)); + if ((temp >> 11) & 1) temp |= 1 << 15; + temp &= 0x87FF; + float Ta = temp * 0.0625; + + test_ack(soft_i2c_write_bytes(AMG8833_ADDR, (uint8_t [1]){0x80}, 1, true)); + int16_t *data = fb_alloc(64 * sizeof(int16_t)); + test_ack(soft_i2c_read_bytes(AMG8833_ADDR, (uint8_t *) data, 128, true)); + float To[64], min = FLT_MAX, max = FLT_MIN; + for (int i = 0; i < 64; i++) { + if ((data[i] >> 11) & 1) data[i] |= 1 << 15; + data[i] &= 0x87FF; + To[i] = data[i] * 0.25; + min = IM_MIN(min, To[i]); + max = IM_MAX(max, To[i]); + } + + mp_obj_t tuple[4]; + tuple[0] = mp_obj_new_float(Ta); + tuple[1] = mp_obj_new_list(0, NULL); + tuple[2] = mp_obj_new_float(min); + tuple[3] = mp_obj_new_float(max); + + for (int i=0; i<64; i++) { + mp_obj_list_append(tuple[1], mp_obj_new_float(To[i])); + } + + fb_free(); + return mp_obj_new_tuple(4, tuple); } } - - mp_obj_t tuple[4]; - tuple[0] = mp_obj_new_float(Ta); - tuple[1] = mp_obj_new_list(0, NULL); - tuple[2] = mp_obj_new_float(min); - tuple[3] = mp_obj_new_float(max); - - for (int i=0; i<64; i++) { - mp_obj_list_append(tuple[1], mp_obj_new_float(To[i])); - } - return mp_obj_new_tuple(4, tuple); + return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_read_ir_obj, py_fir_read_ir); mp_obj_t py_fir_draw_ta(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { if (type == FIR_NONE) return mp_const_none; - image_t *arg_img = py_image_cobj(args[0]); - PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); + image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); float Ta = mp_obj_get_float(args[1]); float min = -17.7778, max = 37.7778; // 0F to 100F - int alpha = IM_MIN(IM_MAX(py_helper_keyword_int(n_args, args, 2, kw_args, - MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 128), 0), 256); + int alpha = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 128); + PY_ASSERT_TRUE_MSG((0 <= alpha) && (alpha <= 256), "Error: 0 <= alpha <= 256!"); - mp_obj_t scale_obj = py_helper_keyword_object(n_args, args, 3, kw_args, + mp_obj_t scale_obj = py_helper_keyword_object(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_scale)); if (scale_obj) { mp_obj_t *arg_scale; @@ -407,50 +593,65 @@ mp_obj_t py_fir_draw_ta(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) } uint8_t gs_ta = IM_MIN(IM_MAX(MAP(Ta, min, max, 0, 255), 0), 255); - uint16_t r_ta = IM_R565(rainbow_table[gs_ta]); - uint16_t g_ta = IM_G565(rainbow_table[gs_ta]); - uint16_t b_ta = IM_B565(rainbow_table[gs_ta]); + uint16_t r_ta = COLOR_RGB565_TO_R5(rainbow_table[gs_ta]); + uint16_t g_ta = COLOR_RGB565_TO_G6(rainbow_table[gs_ta]); + uint16_t b_ta = COLOR_RGB565_TO_B5(rainbow_table[gs_ta]); uint32_t va = __PKHBT((256-alpha), alpha, 16); for (int y=0; yh; y++) { for (int x=0; xw; x++) { - if (IM_IS_GS(arg_img)) { - uint8_t pixel = IM_GET_GS_PIXEL(arg_img, x, y); - uint32_t vgs = __PKHBT(pixel, gs_ta, 16); - uint32_t gs = __SMUAD(va, vgs)>>8; - IM_SET_GS_PIXEL(arg_img, x, y, gs); - } else { - uint16_t pixel = IM_GET_RGB565_PIXEL(arg_img, x, y); - uint32_t vr = __PKHBT(IM_R565(pixel), r_ta, 16); - uint32_t vg = __PKHBT(IM_G565(pixel), g_ta, 16); - uint32_t vb = __PKHBT(IM_B565(pixel), b_ta, 16); - uint32_t r = __SMUAD(va, vr)>>8; - uint32_t g = __SMUAD(va, vg)>>8; - uint32_t b = __SMUAD(va, vb)>>8; - IM_SET_RGB565_PIXEL(arg_img, x, y, IM_RGB565(r, g, b)); + switch (arg_img->bpp) { + case IMAGE_BPP_BINARY: + { + uint8_t pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL(arg_img, x, y)); + uint32_t vgs = __PKHBT(pixel, gs_ta, 16); + uint32_t gs = __SMUAD(va, vgs)>>8; + IMAGE_PUT_BINARY_PIXEL(arg_img, x, y, COLOR_GRAYSCALE_TO_BINARY(gs)); + break; + } + case IMAGE_BPP_GRAYSCALE: + { + uint8_t pixel = IMAGE_GET_GRAYSCALE_PIXEL(arg_img, x, y); + uint32_t vgs = __PKHBT(pixel, gs_ta, 16); + uint32_t gs = __SMUAD(va, vgs)>>8; + IMAGE_PUT_GRAYSCALE_PIXEL(arg_img, x, y, gs); + break; + } + case IMAGE_BPP_RGB565: { + uint16_t pixel = IMAGE_GET_RGB565_PIXEL(arg_img, x, y); + uint32_t vr = __PKHBT(COLOR_RGB565_TO_R5(pixel), r_ta, 16); + uint32_t vg = __PKHBT(COLOR_RGB565_TO_G6(pixel), g_ta, 16); + uint32_t vb = __PKHBT(COLOR_RGB565_TO_B5(pixel), b_ta, 16); + uint32_t r = __SMUAD(va, vr)>>8; + uint32_t g = __SMUAD(va, vg)>>8; + uint32_t b = __SMUAD(va, vb)>>8; + IMAGE_PUT_RGB565_PIXEL(arg_img, x, y, COLOR_R5_G6_B5_TO_RGB565(r, g, b)); + break; + } + default: break; } } } return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_draw_ta_obj, 2, py_fir_draw_ta); mp_obj_t py_fir_draw_ir(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { if (type == FIR_NONE) return mp_const_none; - image_t *arg_img = py_image_cobj(args[0]); - PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); + image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); mp_obj_t *arg_To; - mp_obj_get_array_fixed_n(args[1], 64, &arg_To); + mp_obj_get_array_fixed_n(args[1], width*height, &arg_To); - float To[64], min = FLT_MAX, max = FLT_MIN; - for (int i=0; i<64; i++) { + float *To = fb_alloc(width*height * sizeof(float)), min = FLT_MAX, max = FLT_MIN; + for (int i=0; iw / width; - int x_offset = (arg_img->w - (width * x_scale)) / 2; - int y_scale = x_scale; // keep aspect ratio - int y_offset = (arg_img->h - (height * y_scale)) / 2; + int x_scale = arg_img->w / width, y_scale = arg_img->h / height; + int scale = IM_MIN(x_scale, y_scale); + int x_offset = (arg_img->w - (width * scale)) / 2; + int y_offset = (arg_img->h - (height * scale)) / 2; uint32_t va = __PKHBT((256-alpha), alpha, 16); - for (int y=y_offset; y>8; - IM_SET_GS_PIXEL(arg_img, x, y, gs); - } else { - uint16_t pixel = IM_GET_RGB565_PIXEL(arg_img, x, y); - uint32_t vr = __PKHBT(IM_R565(pixel), r_to, 16); - uint32_t vg = __PKHBT(IM_G565(pixel), g_to, 16); - uint32_t vb = __PKHBT(IM_B565(pixel), b_to, 16); - uint32_t r = __SMUAD(va, vr)>>8; - uint32_t g = __SMUAD(va, vg)>>8; - uint32_t b = __SMUAD(va, vb)>>8; - IM_SET_RGB565_PIXEL(arg_img, x, y, IM_RGB565(r, g, b)); + uint16_t r_to = COLOR_RGB565_TO_R5(rainbow_table[gs_to]); + uint16_t g_to = COLOR_RGB565_TO_G6(rainbow_table[gs_to]); + uint16_t b_to = COLOR_RGB565_TO_B5(rainbow_table[gs_to]); + switch (arg_img->bpp) { + case IMAGE_BPP_BINARY: + { + uint8_t pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL(arg_img, x, y)); + uint32_t vgs = __PKHBT(pixel, gs_to, 16); + uint32_t gs = __SMUAD(va, vgs)>>8; + IMAGE_PUT_BINARY_PIXEL(arg_img, x, y, COLOR_GRAYSCALE_TO_BINARY(gs)); + break; + } + case IMAGE_BPP_GRAYSCALE: + { + uint8_t pixel = IMAGE_GET_GRAYSCALE_PIXEL(arg_img, x, y); + uint32_t vgs = __PKHBT(pixel, gs_to, 16); + uint32_t gs = __SMUAD(va, vgs)>>8; + IMAGE_PUT_GRAYSCALE_PIXEL(arg_img, x, y, gs); + break; + } + case IMAGE_BPP_RGB565: { + uint16_t pixel = IMAGE_GET_RGB565_PIXEL(arg_img, x, y); + uint32_t vr = __PKHBT(COLOR_RGB565_TO_R5(pixel), r_to, 16); + uint32_t vg = __PKHBT(COLOR_RGB565_TO_G6(pixel), g_to, 16); + uint32_t vb = __PKHBT(COLOR_RGB565_TO_B5(pixel), b_to, 16); + uint32_t r = __SMUAD(va, vr)>>8; + uint32_t g = __SMUAD(va, vg)>>8; + uint32_t b = __SMUAD(va, vb)>>8; + IMAGE_PUT_RGB565_PIXEL(arg_img, x, y, COLOR_R5_G6_B5_TO_RGB565(r, g, b)); + break; + } + default: break; } } } + fb_free(); return mp_const_none; } - -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_init_obj, 0, py_fir_init); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_deinit_obj, py_fir_deinit); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_width_obj, py_fir_width); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_height_obj, py_fir_height); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_type_obj, py_fir_type); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_read_ta_obj, py_fir_read_ta); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_read_ir_obj, py_fir_read_ir); -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_draw_ta_obj, 2, py_fir_draw_ta); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_draw_ir_obj, 2, py_fir_draw_ir); -static const mp_map_elem_t globals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fir) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&py_fir_init_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&py_fir_deinit_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_width), (mp_obj_t)&py_fir_width_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_height), (mp_obj_t)&py_fir_height_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_type), (mp_obj_t)&py_fir_type_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_read_ta), (mp_obj_t)&py_fir_read_ta_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_read_ir), (mp_obj_t)&py_fir_read_ir_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_draw_ta), (mp_obj_t)&py_fir_draw_ta_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_draw_ir), (mp_obj_t)&py_fir_draw_ir_obj }, - { NULL, NULL }, + +mp_obj_t py_fir_snapshot(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + if (type == FIR_NONE) return mp_const_none; + mp_obj_t ir = py_fir_read_ir(); + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(ir, &len, &items); + + int pixformat = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_pixformat), PIXFORMAT_RGB565); + PY_ASSERT_TRUE_MSG((pixformat == PIXFORMAT_GRAYSCALE) || (pixformat == PIXFORMAT_RGB565), "Invalid Pixformat!"); + + bool copy_to_fb = py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy_to_fb), false); + if (copy_to_fb) fb_update_jpeg_buffer(); + + image_t image; + image.w = width; + image.h = height; + image.bpp = (pixformat == PIXFORMAT_RGB565) ? IMAGE_BPP_RGB565 : IMAGE_BPP_GRAYSCALE; + image.data = NULL; + + if (copy_to_fb) { + PY_ASSERT_TRUE_MSG((image_size(&image) <= OMV_RAW_BUF_SIZE), "FB Overflow!"); + MAIN_FB()->w = image.w; + MAIN_FB()->h = image.h; + MAIN_FB()->bpp = image.bpp; + image.data = MAIN_FB()->pixels; + } else { + image.data = xalloc(image_size(&image)); + } + + mp_obj_t snapshot = py_image_from_struct(&image); + + mp_obj_t *new_args = xalloc((2 + n_args) * sizeof(mp_obj_t)); + new_args[0] = snapshot; + new_args[1] = items[1]; // ir array + + for (uint i = 0; i < n_args; i++) { + new_args[2+i] = args[i]; + } + + py_fir_draw_ir(2 + n_args, new_args, kw_args); + gc_collect(); + + return snapshot; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_snapshot_obj, 0, py_fir_snapshot); + +STATIC const mp_rom_map_elem_t globals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fir) }, + { MP_ROM_QSTR(MP_QSTR_FIR_NONE), MP_ROM_INT(FIR_NONE) }, + { MP_ROM_QSTR(MP_QSTR_FIR_SHIELD), MP_ROM_INT(FIR_SHIELD) }, + { MP_ROM_QSTR(MP_QSTR_FIR_MLX90620), MP_ROM_INT(FIR_SHIELD) }, // == FIR_SHIELD + { MP_ROM_QSTR(MP_QSTR_FIR_MLX90621), MP_ROM_INT(FIR_SHIELD) }, // == FIR_SHIELD + { MP_ROM_QSTR(MP_QSTR_FIR_MLX90640), MP_ROM_INT(FIR_MLX90640) }, + { MP_ROM_QSTR(MP_QSTR_FIR_AMG8833), MP_ROM_INT(FIR_AMG8833) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&py_fir_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&py_fir_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&py_fir_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&py_fir_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&py_fir_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&py_fir_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_resolution), MP_ROM_PTR(&py_fir_resolution_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_ta), MP_ROM_PTR(&py_fir_read_ta_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_ir), MP_ROM_PTR(&py_fir_read_ir_obj) }, + { MP_ROM_QSTR(MP_QSTR_draw_ta), MP_ROM_PTR(&py_fir_draw_ta_obj) }, + { MP_ROM_QSTR(MP_QSTR_draw_ir), MP_ROM_PTR(&py_fir_draw_ir_obj) }, + { MP_ROM_QSTR(MP_QSTR_snapshot), MP_ROM_PTR(&py_fir_snapshot_obj) } }; + STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table); const mp_obj_module_t fir_module = { .base = { &mp_type_module }, - .globals = (mp_obj_t)&globals_dict, + .globals = (mp_obj_t) &globals_dict, }; void py_fir_init0() diff --git a/src/omv/py/py_fir.h b/src/omv/py/py_fir.h index 2f58f44c2..00dec0fb6 100644 --- a/src/omv/py/py_fir.h +++ b/src/omv/py/py_fir.h @@ -3,7 +3,7 @@ * Copyright (c) 2013/2014 Ibrahim Abdelkader * This work is licensed under the MIT license, see the file LICENSE for details. * - * MLX90621 Python module. + * MLX Python module. * */ #ifndef __PY_FIR_H__ diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index 7c7c83f76..b2ce212b3 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -1489,6 +1489,7 @@ STATIC mp_obj_t py_image_flood_fill(uint n_args, const mp_obj_t *args, mp_map_t STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_flood_fill_obj, 2, py_image_flood_fill); #endif // IMLIB_ENABLE_FLOOD_FILL +#ifdef IMLIB_ENABLE_BINARY_OPS ///////////////// // Binary Methods ///////////////// @@ -1758,6 +1759,7 @@ STATIC mp_obj_t py_image_close(uint n_args, const mp_obj_t *args, mp_map_t *kw_a return args[0]; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_close_obj, 2, py_image_close); +#endif // IMLIB_ENABLE_BINARY_OPS #ifdef IMLIB_ENABLE_MATH_OPS /////////////// @@ -5157,6 +5159,7 @@ static mp_obj_t py_image_find_keypoints(uint n_args, const mp_obj_t *args, mp_ma } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_keypoints_obj, 1, py_image_find_keypoints); +#ifdef IMLIB_ENABLE_BINARY_OPS static mp_obj_t py_image_find_edges(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { image_t *arg_img = py_helper_arg_to_image_grayscale(args[0]); @@ -5190,6 +5193,7 @@ static mp_obj_t py_image_find_edges(uint n_args, const mp_obj_t *args, mp_map_t return args[0]; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_edges_obj, 2, py_image_find_edges); +#endif #ifdef IMLIB_ENABLE_HOG static mp_obj_t py_image_find_hog(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) @@ -5276,6 +5280,7 @@ static const mp_rom_map_elem_t locals_dict_table[] = { #endif {MP_ROM_QSTR(MP_QSTR_draw_keypoints), MP_ROM_PTR(&py_image_draw_keypoints_obj)}, /* Binary Methods */ +#ifdef IMLIB_ENABLE_BINARY_OPS {MP_ROM_QSTR(MP_QSTR_binary), MP_ROM_PTR(&py_image_binary_obj)}, {MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&py_image_invert_obj)}, {MP_ROM_QSTR(MP_QSTR_and), MP_ROM_PTR(&py_image_b_and_obj)}, @@ -5294,6 +5299,26 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_dilate), MP_ROM_PTR(&py_image_dilate_obj)}, {MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&py_image_open_obj)}, {MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&py_image_close_obj)}, +#else + {MP_ROM_QSTR(MP_QSTR_binary), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_and), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_and), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_nand), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_nand), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_or), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_or), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_nor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_nor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_xor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_xor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_xnor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_b_xnor), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_erode), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_dilate), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&py_func_unavailable_obj)}, +#endif #ifdef IMLIB_ENABLE_MATH_OPS {MP_ROM_QSTR(MP_QSTR_top_hat), MP_ROM_PTR(&py_image_top_hat_obj)}, {MP_ROM_QSTR(MP_QSTR_black_hat), MP_ROM_PTR(&py_image_black_hat_obj)}, @@ -5471,7 +5496,11 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_find_eye), MP_ROM_PTR(&py_image_find_eye_obj)}, {MP_ROM_QSTR(MP_QSTR_find_lbp), MP_ROM_PTR(&py_image_find_lbp_obj)}, {MP_ROM_QSTR(MP_QSTR_find_keypoints), MP_ROM_PTR(&py_image_find_keypoints_obj)}, +#ifdef IMLIB_ENABLE_BINARY_OPS {MP_ROM_QSTR(MP_QSTR_find_edges), MP_ROM_PTR(&py_image_find_edges_obj)}, +#else + {MP_ROM_QSTR(MP_QSTR_find_edges), MP_ROM_PTR(&py_func_unavailable_obj)}, +#endif #ifdef IMLIB_ENABLE_HOG {MP_ROM_QSTR(MP_QSTR_find_hog), MP_ROM_PTR(&py_image_find_hog_obj)}, #else diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index d87ef842b..4211fd1a0 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -79,17 +79,6 @@ Q(get_backlight) Q(display) Q(clear) -// FIR Module -Q(fir) -Q(read_ta) -Q(read_ir) -Q(draw_ta) -Q(draw_ir) -Q(alpha) -Q(scale) -Q(refresh) -Q(resolution) - // Gif module Q(gif) Q(Gif) @@ -412,7 +401,7 @@ Q(draw_circle) // Draw String Q(draw_string) // duplicate Q(color) -// duplicate Q(scale) +Q(scale) Q(x_spacing) Q(y_spacing) Q(mono_space) @@ -564,7 +553,7 @@ Q(difference) // Blend Q(blend) -// duplicate Q(alpha) +Q(alpha) // duplicate Q(mask) // Histogram Equalization @@ -1006,3 +995,32 @@ Q(next_frame) // duplicate Q(copy_to_fb) // duplicate Q(loop) // duplicate Q(close) + +// FIR Module +Q(fir) +// duplicate Q(init) +Q(FIR_NONE) +Q(FIR_SHIELD) +Q(FIR_MLX90620) +Q(FIR_MLX90621) +Q(FIR_MLX90640) +Q(FIR_AMG8833) +Q(refresh) +Q(resolution) +// duplicate Q(deinit) +// duplicate Q(width) +// duplicate Q(height) +// duplicate Q(type) +Q(read_ta) +Q(read_ir) +Q(draw_ta) +// duplicate Q(alpha) +// duplicate Q(scale) +Q(draw_ir) +// duplicate Q(alpha) +// duplicate Q(scale) +// duplicate Q(snapshot) +// duplicate Q(alpha) +// duplicate Q(scale) +Q(pixformat) +// duplciate Q(copy_to_fb) diff --git a/src/omv/soft_i2c.c b/src/omv/soft_i2c.c index 4b441efda..1ec4e500f 100644 --- a/src/omv/soft_i2c.c +++ b/src/omv/soft_i2c.c @@ -8,26 +8,14 @@ */ #include #include "soft_i2c.h" - -#define I2C_PORT GPIOB -#define I2C_SIOC_PIN GPIO_PIN_10 -#define I2C_SIOD_PIN GPIO_PIN_11 - -#define I2C_SIOC_H() HAL_GPIO_WritePin(I2C_PORT, I2C_SIOC_PIN, GPIO_PIN_SET) -#define I2C_SIOC_L() HAL_GPIO_WritePin(I2C_PORT, I2C_SIOC_PIN, GPIO_PIN_RESET) - -#define I2C_SIOD_H() HAL_GPIO_WritePin(I2C_PORT, I2C_SIOD_PIN, GPIO_PIN_SET) -#define I2C_SIOD_L() HAL_GPIO_WritePin(I2C_PORT, I2C_SIOD_PIN, GPIO_PIN_RESET) - -#define I2C_SIOD_READ() HAL_GPIO_ReadPin(I2C_PORT, I2C_SIOD_PIN) -#define I2C_SIOD_WRITE(bit) HAL_GPIO_WritePin(I2C_PORT, I2C_SIOD_PIN, bit); +#include "omv_boardconfig.h" #define ACK 0 #define NACK 1 -static void delay(void) // TODO: Update with clock speed knowledge for M7. +static void delay(void) { - for(volatile int i=0; i<16; i++); + for(volatile int i=0; i