mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
* Sharing the VOSPI packet DMA buffer with line buffer does not work for OpenMV 4 PRO, because the packet buffer needs to be in D3. * This will automatically allocate a DMA buffer for VOSPI packet in the same domain as the DMA peripheral.
740 lines
24 KiB
C
740 lines
24 KiB
C
/*
|
|
* This file is part of the OpenMV project.
|
|
*
|
|
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
|
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
|
*
|
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
|
*
|
|
* Lepton driver.
|
|
*/
|
|
#include "omv_boardconfig.h"
|
|
#if (OMV_ENABLE_LEPTON == 1)
|
|
|
|
#include STM32_HAL_H
|
|
#include "irq.h"
|
|
#include "cambus.h"
|
|
#include "sensor.h"
|
|
#include "py/mphal.h"
|
|
#include "framebuffer.h"
|
|
#include "common.h"
|
|
#include "dma_alloc.h"
|
|
|
|
#include "crc16.h"
|
|
#include "LEPTON_SDK.h"
|
|
#include "LEPTON_AGC.h"
|
|
#include "LEPTON_SYS.h"
|
|
#include "LEPTON_VID.h"
|
|
#include "LEPTON_OEM.h"
|
|
#include "LEPTON_RAD.h"
|
|
#include "LEPTON_I2C_Reg.h"
|
|
|
|
#define VOSPI_LINE_PIXELS (80)
|
|
#define VOSPI_NUMBER_PACKETS (60)
|
|
#define VOSPI_SPECIAL_PACKET (20)
|
|
#define VOSPI_LINE_SIZE (80 * 2)
|
|
#define VOSPI_HEADER_SIZE (4)
|
|
#define VOSPI_PACKET_SIZE (VOSPI_HEADER_SIZE + VOSPI_LINE_SIZE)
|
|
#define VOSPI_HEADER_SEG(buf) (((buf[0] >> 4) & 0x7))
|
|
#define VOSPI_HEADER_PID(buf) (((buf[0] << 8) | (buf[1] << 0)) & 0x0FFF)
|
|
#define VOSPI_HEADER_CRC(buf) (((buf[2] << 8) | (buf[3] << 0)))
|
|
#define VOSPI_FIRST_PACKET (0)
|
|
#define VOSPI_FIRST_SEGMENT (1)
|
|
#define LEPTON_TIMEOUT (1000)
|
|
// Temperatures in Celsius
|
|
#define DEFAULT_MIN_TEMP (-10.0f)
|
|
#define DEFAULT_MAX_TEMP (40.0f)
|
|
#define LEPTON_MIN_TEMP_NORM (-10.0f)
|
|
#define LEPTON_MAX_TEMP_NORM (140.0f)
|
|
#define LEPTON_MIN_TEMP_HIGH (-10.0f)
|
|
#define LEPTON_MAX_TEMP_HIGH (600.0f)
|
|
|
|
static bool radiometry = false;
|
|
static int h_res = 0;
|
|
static int v_res = 0;
|
|
static bool v_flip = false;
|
|
static bool h_mirror = false;
|
|
static bool measurement_mode = false;
|
|
static bool high_temp_mode = false;
|
|
static float min_temp = DEFAULT_MIN_TEMP;
|
|
static float max_temp = DEFAULT_MAX_TEMP;
|
|
|
|
extern SPI_HandleTypeDef ISC_SPIHandle;
|
|
static DMA_HandleTypeDef DMAHandle;
|
|
LEP_CAMERA_PORT_DESC_T LEPHandle;
|
|
extern uint8_t _vospi_buf[];
|
|
|
|
static bool vospi_resync = true;
|
|
static uint8_t *vospi_packet = NULL;
|
|
static uint8_t *vospi_buffer = _vospi_buf;
|
|
static volatile uint32_t vospi_pid = 0;
|
|
static volatile uint32_t vospi_seg = 1;
|
|
static uint32_t vospi_packets = 60;
|
|
static int lepton_reset(sensor_t *sensor, bool measurement_mode, bool high_temp_mode);
|
|
|
|
static void lepton_sync()
|
|
{
|
|
HAL_SPI_Abort(&ISC_SPIHandle);
|
|
|
|
// Disable DMA IRQ
|
|
HAL_NVIC_DisableIRQ(ISC_SPI_DMA_IRQn);
|
|
|
|
debug_printf("resync...\n");
|
|
mp_hal_delay_ms(200);
|
|
|
|
vospi_resync = false;
|
|
vospi_pid = VOSPI_FIRST_PACKET;
|
|
vospi_seg = VOSPI_FIRST_SEGMENT;
|
|
|
|
HAL_NVIC_EnableIRQ(ISC_SPI_DMA_IRQn);
|
|
HAL_SPI_Receive_DMA(&ISC_SPIHandle, vospi_packet, VOSPI_PACKET_SIZE);
|
|
}
|
|
|
|
static uint16_t lepton_calc_crc(uint8_t *buf)
|
|
{
|
|
buf[0] &= 0x0F;
|
|
buf[1] &= 0xFF;
|
|
buf[2] = 0;
|
|
buf[3] = 0;
|
|
return CalcCRC16Bytes(VOSPI_PACKET_SIZE, (char *) buf);
|
|
}
|
|
|
|
static int sleep(sensor_t *sensor, int enable)
|
|
{
|
|
if (enable) {
|
|
DCMI_PWDN_LOW();
|
|
mp_hal_delay_ms(100);
|
|
} else {
|
|
DCMI_PWDN_HIGH();
|
|
mp_hal_delay_ms(100);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_reg(sensor_t *sensor, uint16_t reg_addr)
|
|
{
|
|
uint16_t reg_data;
|
|
if (cambus_readw2(&sensor->bus, sensor->slv_addr, reg_addr, ®_data)) {
|
|
return -1;
|
|
}
|
|
return reg_data;
|
|
}
|
|
|
|
static int write_reg(sensor_t *sensor, uint16_t reg_addr, uint16_t reg_data)
|
|
{
|
|
return cambus_writew2(&sensor->bus, sensor->slv_addr, reg_addr, reg_data);
|
|
}
|
|
|
|
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
|
{
|
|
return ((pixformat != PIXFORMAT_GRAYSCALE) && (pixformat != PIXFORMAT_RGB565)) ? - 1 : 0;
|
|
}
|
|
|
|
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_contrast(sensor_t *sensor, int level)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_brightness(sensor_t *sensor, int level)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_saturation(sensor_t *sensor, int level)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_gainceiling(sensor_t *sensor, gainceiling_t gainceiling)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_quality(sensor_t *sensor, int quality)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_colorbar(sensor_t *sensor, int enable)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_special_effect(sensor_t *sensor, sde_t sde)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain_db_ceiling)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int get_gain_db(sensor_t *sensor, float *gain_db)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int get_exposure_us(sensor_t *sensor, int *exposure_us)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, float g_gain_db, float b_gain_db)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int get_rgb_gain_db(sensor_t *sensor, float *r_gain_db, float *g_gain_db, float *b_gain_db)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int set_hmirror(sensor_t *sensor, int enable)
|
|
{
|
|
h_mirror = enable;
|
|
return 0;
|
|
}
|
|
|
|
static int set_vflip(sensor_t *sensor, int enable)
|
|
{
|
|
v_flip = enable;
|
|
return 0;
|
|
}
|
|
|
|
static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ioctl(sensor_t *sensor, int request, va_list ap)
|
|
{
|
|
int ret = 0;
|
|
|
|
if ((!h_res) || (!v_res)) {
|
|
return -1;
|
|
}
|
|
|
|
switch (request) {
|
|
case IOCTL_LEPTON_GET_WIDTH: {
|
|
int *width = va_arg(ap, int *);
|
|
*width = h_res;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_HEIGHT: {
|
|
int *height = va_arg(ap, int *);
|
|
*height = v_res;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_RADIOMETRY: {
|
|
int *type = va_arg(ap, int *);
|
|
*type = radiometry;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_REFRESH: {
|
|
int *refresh = va_arg(ap, int *);
|
|
*refresh = (h_res == 80) ? 27 : 9;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_RESOLUTION: {
|
|
int *resolution = va_arg(ap, int *);
|
|
*resolution = 14;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_RUN_COMMAND: {
|
|
int command = va_arg(ap, int);
|
|
ret = (LEP_RunCommand(&LEPHandle, command) == LEP_OK) ? 0 : -1;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_SET_ATTRIBUTE: {
|
|
int command = va_arg(ap, int);
|
|
uint16_t *data = va_arg(ap, uint16_t *);
|
|
size_t data_len = va_arg(ap, size_t);
|
|
ret = (LEP_SetAttribute(&LEPHandle, command, (LEP_ATTRIBUTE_T_PTR) data, data_len) == LEP_OK) ? 0 : -1;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_ATTRIBUTE: {
|
|
int command = va_arg(ap, int);
|
|
uint16_t *data = va_arg(ap, uint16_t *);
|
|
size_t data_len = va_arg(ap, size_t);
|
|
ret = (LEP_GetAttribute(&LEPHandle, command, (LEP_ATTRIBUTE_T_PTR) data, data_len) == LEP_OK) ? 0 : -1;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_FPA_TEMPERATURE: {
|
|
int *temp = va_arg(ap, int *);
|
|
LEP_SYS_FPA_TEMPERATURE_KELVIN_T tfpa;
|
|
ret = (LEP_GetSysFpaTemperatureKelvin(&LEPHandle, &tfpa) == LEP_OK) ? 0 : -1;
|
|
*temp = tfpa;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_AUX_TEMPERATURE: {
|
|
int *temp = va_arg(ap, int *);
|
|
LEP_SYS_AUX_TEMPERATURE_KELVIN_T taux;
|
|
ret = (LEP_GetSysAuxTemperatureKelvin(&LEPHandle, &taux) == LEP_OK) ? 0 : -1;
|
|
*temp = taux;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_SET_MEASUREMENT_MODE: {
|
|
int measurement_mode_in = va_arg(ap, int);
|
|
int high_temp_mode_in = va_arg(ap, int);
|
|
if (measurement_mode != measurement_mode_in) {
|
|
measurement_mode = measurement_mode_in;
|
|
high_temp_mode = high_temp_mode_in;
|
|
ret = lepton_reset(sensor, measurement_mode, high_temp_mode);
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_MEASUREMENT_MODE: {
|
|
int *measurement_mode_out = va_arg(ap, int *);
|
|
int *high_temp_mode_out = va_arg(ap, int *);
|
|
*measurement_mode_out = measurement_mode;
|
|
*high_temp_mode_out = high_temp_mode;
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_SET_MEASUREMENT_RANGE: {
|
|
float *arg_min_temp = va_arg(ap, float *);
|
|
float *arg_max_temp = va_arg(ap, float *);
|
|
float min_temp_range = (high_temp_mode) ? LEPTON_MIN_TEMP_HIGH : LEPTON_MIN_TEMP_NORM;
|
|
float max_temp_range = (high_temp_mode) ? LEPTON_MAX_TEMP_HIGH : LEPTON_MAX_TEMP_NORM;
|
|
min_temp = IM_MAX(IM_MIN(*arg_min_temp, *arg_max_temp), min_temp_range);
|
|
max_temp = IM_MIN(IM_MAX(*arg_max_temp, *arg_min_temp), max_temp_range);
|
|
break;
|
|
}
|
|
case IOCTL_LEPTON_GET_MEASUREMENT_RANGE: {
|
|
float *ptr_min_temp = va_arg(ap, float *);
|
|
float *ptr_max_temp = va_arg(ap, float *);
|
|
*ptr_min_temp = min_temp;
|
|
*ptr_max_temp = max_temp;
|
|
break;
|
|
}
|
|
default: {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lepton_reset(sensor_t *sensor, bool measurement_mode, bool high_temp_mode)
|
|
{
|
|
DCMI_PWDN_LOW();
|
|
mp_hal_delay_ms(10);
|
|
|
|
DCMI_PWDN_HIGH();
|
|
mp_hal_delay_ms(10);
|
|
|
|
DCMI_RESET_LOW();
|
|
mp_hal_delay_ms(10);
|
|
|
|
DCMI_RESET_HIGH();
|
|
mp_hal_delay_ms(1000);
|
|
|
|
LEP_RAD_ENABLE_E rad;
|
|
LEP_AGC_ROI_T roi;
|
|
memset(&LEPHandle, 0, sizeof(LEP_CAMERA_PORT_DESC_T));
|
|
|
|
for (mp_uint_t start = mp_hal_ticks_ms(); ;mp_hal_delay_ms(1)) {
|
|
if (LEP_OpenPort(&sensor->bus, LEP_CCI_TWI, 0, &LEPHandle) == LEP_OK) {
|
|
break;
|
|
}
|
|
if ((mp_hal_ticks_ms() - start) >= LEPTON_TIMEOUT) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (mp_uint_t start = mp_hal_ticks_ms(); ;mp_hal_delay_ms(1)) {
|
|
LEP_SDK_BOOT_STATUS_E status;
|
|
if (LEP_GetCameraBootStatus(&LEPHandle, &status) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
if (status == LEP_BOOT_STATUS_BOOTED) {
|
|
break;
|
|
}
|
|
if ((mp_hal_ticks_ms() - start) >= LEPTON_TIMEOUT) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (mp_uint_t start = mp_hal_ticks_ms(); ;mp_hal_delay_ms(1)) {
|
|
LEP_UINT16 status;
|
|
if (LEP_DirectReadRegister(&LEPHandle, LEP_I2C_STATUS_REG, &status) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
if (!(status & LEP_I2C_STATUS_BUSY_BIT_MASK)) {
|
|
break;
|
|
}
|
|
if ((mp_hal_ticks_ms() - start) >= LEPTON_TIMEOUT) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (LEP_GetRadEnableState(&LEPHandle, &rad) != LEP_OK
|
|
|| LEP_GetAgcROI(&LEPHandle, &roi) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
|
|
// Use the low gain mode to enable high temperature readings (~450C) on Lepton 3.5
|
|
LEP_SYS_GAIN_MODE_E gain_mode = high_temp_mode ? LEP_SYS_GAIN_MODE_LOW : LEP_SYS_GAIN_MODE_HIGH;
|
|
if (LEP_SetSysGainMode(&LEPHandle, gain_mode) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
|
|
if (!measurement_mode) {
|
|
if (LEP_SetRadEnableState(&LEPHandle, LEP_RAD_DISABLE) != LEP_OK
|
|
|| LEP_SetAgcEnableState(&LEPHandle, LEP_AGC_ENABLE) != LEP_OK
|
|
|| LEP_SetAgcCalcEnableState(&LEPHandle, LEP_AGC_ENABLE) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
h_res = roi.endCol + 1;
|
|
v_res = roi.endRow + 1;
|
|
radiometry = (rad == LEP_RAD_ENABLE);
|
|
|
|
if (v_res > 60) {
|
|
vospi_packets = 240;
|
|
} else {
|
|
vospi_packets = 60;
|
|
}
|
|
|
|
// resync and enable DMA before the first snapshot.
|
|
vospi_resync = true;
|
|
return 0;
|
|
}
|
|
|
|
static int reset(sensor_t *sensor)
|
|
{
|
|
h_res = 0;
|
|
v_res = 0;
|
|
v_flip = false;
|
|
h_mirror = false;
|
|
radiometry = false;
|
|
measurement_mode = false;
|
|
high_temp_mode = false;
|
|
min_temp = DEFAULT_MIN_TEMP;
|
|
max_temp = DEFAULT_MAX_TEMP;
|
|
return lepton_reset(sensor, false, false);
|
|
}
|
|
|
|
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
|
|
{
|
|
(void) lepton_calc_crc; // to shut the compiler up.
|
|
|
|
if (vospi_resync == true) {
|
|
return; // nothing to do here
|
|
}
|
|
|
|
if (vospi_pid < vospi_packets && (vospi_packet[0] & 0xF) != 0xF) {
|
|
uint32_t pid = VOSPI_HEADER_PID(vospi_packet);
|
|
uint32_t seg = VOSPI_HEADER_SEG(vospi_packet);
|
|
if (pid != (vospi_pid % VOSPI_NUMBER_PACKETS)) {
|
|
if (vospi_pid == VOSPI_FIRST_PACKET) {
|
|
// Wait for the first packet of the first segement.
|
|
vospi_pid = VOSPI_FIRST_PACKET;
|
|
vospi_seg = VOSPI_FIRST_SEGMENT;
|
|
} else { // lost sync
|
|
vospi_resync = true;
|
|
debug_printf("lost sync, packet id:%lu expected id:%lu \n", pid, vospi_pid);
|
|
}
|
|
} else if (vospi_packets > 60 && pid == VOSPI_SPECIAL_PACKET && seg != vospi_seg ) {
|
|
if (vospi_seg == VOSPI_FIRST_SEGMENT) {
|
|
// Wait for the first packet of the first segement.
|
|
vospi_pid = VOSPI_FIRST_PACKET;
|
|
vospi_seg = VOSPI_FIRST_SEGMENT;
|
|
} else { // lost sync
|
|
vospi_resync = true;
|
|
debug_printf("lost sync, segment id:%lu expected id:%lu\n", seg, vospi_seg);
|
|
}
|
|
} else {
|
|
memcpy(vospi_buffer + vospi_pid * VOSPI_LINE_SIZE,
|
|
vospi_packet + VOSPI_HEADER_SIZE, VOSPI_LINE_SIZE);
|
|
if ((++vospi_pid % VOSPI_NUMBER_PACKETS) == 0) {
|
|
vospi_seg++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int snapshot(sensor_t *sensor, image_t *image, uint32_t flags)
|
|
{
|
|
framebuffer_update_jpeg_buffer();
|
|
|
|
if (MAIN_FB()->n_buffers != 1) {
|
|
framebuffer_set_buffers(1);
|
|
}
|
|
|
|
if (sensor_check_framebuffer_size(sensor) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
if ((!h_res) || (!v_res) || (!sensor->framesize) || (!sensor->pixformat)) {
|
|
return -1;
|
|
}
|
|
|
|
framebuffer_free_current_buffer();
|
|
vbuffer_t *buffer = framebuffer_get_tail(FB_NO_FLAGS);
|
|
|
|
if (!buffer) {
|
|
return -1;
|
|
}
|
|
|
|
// The SPI DMA device is always clocking the FLIR Lepton in the background.
|
|
// The code below resets the vospi control values to let data be pulled in.
|
|
// If we need to re-sync we do it. Otherwise, after we finish pulling data
|
|
// in we exit and let the SPI bus keep running. Then on the next call to
|
|
// snapshot we read in more data and pull in the next frame.
|
|
HAL_NVIC_DisableIRQ(ISC_SPI_DMA_IRQn);
|
|
vospi_pid = VOSPI_FIRST_PACKET;
|
|
vospi_seg = VOSPI_FIRST_SEGMENT;
|
|
HAL_NVIC_EnableIRQ(ISC_SPI_DMA_IRQn);
|
|
|
|
// Snapshot start tick
|
|
mp_uint_t tick_start = mp_hal_ticks_ms();
|
|
bool reset_tried = false;
|
|
|
|
do {
|
|
if (vospi_resync == true) {
|
|
lepton_sync();
|
|
}
|
|
|
|
__WFI();
|
|
|
|
if ((mp_hal_ticks_ms() - tick_start) >= 20000) {
|
|
// Timeout error.
|
|
return -1;
|
|
}
|
|
|
|
if ((!reset_tried) && ((mp_hal_ticks_ms() - tick_start) >= 10000)) {
|
|
reset_tried = true;
|
|
|
|
// The FLIR lepton might have crashed so reset it (it does this).
|
|
bool temp_h_mirror = h_mirror;
|
|
bool temp_v_flip = v_flip;
|
|
int ret = lepton_reset(sensor, measurement_mode, high_temp_mode);
|
|
h_mirror = temp_h_mirror;
|
|
v_flip = temp_v_flip;
|
|
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
// Reset the VOSPI interface again.
|
|
HAL_NVIC_DisableIRQ(ISC_SPI_DMA_IRQn);
|
|
vospi_pid = VOSPI_FIRST_PACKET;
|
|
vospi_seg = VOSPI_FIRST_SEGMENT;
|
|
HAL_NVIC_EnableIRQ(ISC_SPI_DMA_IRQn);
|
|
}
|
|
} while (vospi_pid < vospi_packets); // only checking one volatile var so atomic.
|
|
|
|
MAIN_FB()->w = MAIN_FB()->u;
|
|
MAIN_FB()->h = MAIN_FB()->v;
|
|
MAIN_FB()->pixfmt = sensor->pixformat;
|
|
|
|
framebuffer_init_image(image);
|
|
|
|
uint16_t *src = (uint16_t*) vospi_buffer;
|
|
|
|
float x_scale = resolution[sensor->framesize][0] / ((float) h_res);
|
|
float y_scale = resolution[sensor->framesize][1] / ((float) v_res);
|
|
// MAX == KeepAspectRationByExpanding - MIN == KeepAspectRatio
|
|
float scale = IM_MAX(x_scale, y_scale), scale_inv = 1.0f / scale;
|
|
int x_offset = (resolution[sensor->framesize][0] - (h_res * scale)) / 2;
|
|
int y_offset = (resolution[sensor->framesize][1] - (v_res * scale)) / 2;
|
|
// The code below upscales the source image to the requested frame size
|
|
// and then crops it to the window set by the user.
|
|
|
|
LEP_SYS_FPA_TEMPERATURE_KELVIN_T kelvin;
|
|
if (measurement_mode && (!radiometry)) {
|
|
if (LEP_GetSysFpaTemperatureKelvin(&LEPHandle, &kelvin) != LEP_OK) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (int y = y_offset, yy = fast_ceilf(v_res * scale) + y_offset; y < yy; y++) {
|
|
if ((MAIN_FB()->y <= y) && (y < (MAIN_FB()->y + MAIN_FB()->v))) { // user window cropping
|
|
|
|
uint16_t *row_ptr = src + (fast_floorf(y * scale_inv) * h_res);
|
|
|
|
for (int x = x_offset, xx = fast_ceilf(h_res * scale) + x_offset; x < xx; x++) {
|
|
if ((MAIN_FB()->x <= x) && (x < (MAIN_FB()->x + MAIN_FB()->u))) { // user window cropping
|
|
|
|
// Value is the 14/16-bit value from the FLIR IR camera.
|
|
// However, with AGC enabled only the bottom 8-bits are non-zero.
|
|
int value = __REV16(row_ptr[fast_floorf(x * scale_inv)]);
|
|
|
|
if (measurement_mode) {
|
|
// Need to convert 14/16-bits to 8-bits ourselves...
|
|
if (!radiometry) value = (value - 8192) + kelvin;
|
|
float celsius = (value * 0.01f) - 273.15f;
|
|
celsius = IM_MAX(IM_MIN(celsius, max_temp), min_temp);
|
|
value = IM_MAX(IM_MIN(IM_DIV(((celsius - min_temp) * 255), (max_temp - min_temp)), 255), 0);
|
|
}
|
|
|
|
int t_x = x - MAIN_FB()->x;
|
|
int t_y = y - MAIN_FB()->y;
|
|
|
|
if (h_mirror) t_x = MAIN_FB()->u - t_x - 1;
|
|
if (v_flip) t_y = MAIN_FB()->v - t_y - 1;
|
|
|
|
switch (sensor->pixformat) {
|
|
case PIXFORMAT_GRAYSCALE: {
|
|
IMAGE_PUT_GRAYSCALE_PIXEL(image, t_x, t_y, value & 0xFF);
|
|
break;
|
|
}
|
|
case PIXFORMAT_RGB565: {
|
|
IMAGE_PUT_RGB565_PIXEL(image, t_x, t_y, sensor->color_palette[value & 0xFF]);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lepton_init(sensor_t *sensor)
|
|
{
|
|
sensor->reset = reset;
|
|
sensor->sleep = sleep;
|
|
sensor->snapshot = snapshot;
|
|
sensor->read_reg = read_reg;
|
|
sensor->write_reg = write_reg;
|
|
sensor->set_pixformat = set_pixformat;
|
|
sensor->set_framesize = set_framesize;
|
|
sensor->set_contrast = set_contrast;
|
|
sensor->set_brightness = set_brightness;
|
|
sensor->set_saturation = set_saturation;
|
|
sensor->set_gainceiling = set_gainceiling;
|
|
sensor->set_quality = set_quality;
|
|
sensor->set_colorbar = set_colorbar;
|
|
sensor->set_special_effect = set_special_effect;
|
|
sensor->set_auto_gain = set_auto_gain;
|
|
sensor->get_gain_db = get_gain_db;
|
|
sensor->set_auto_exposure = set_auto_exposure;
|
|
sensor->get_exposure_us = get_exposure_us;
|
|
sensor->set_auto_whitebal = set_auto_whitebal;
|
|
sensor->get_rgb_gain_db = get_rgb_gain_db;
|
|
sensor->set_hmirror = set_hmirror;
|
|
sensor->set_vflip = set_vflip;
|
|
sensor->set_lens_correction = set_lens_correction;
|
|
sensor->ioctl = ioctl;
|
|
|
|
sensor->hw_flags.vsync = 1;
|
|
sensor->hw_flags.hsync = 0;
|
|
sensor->hw_flags.pixck = 0;
|
|
sensor->hw_flags.fsync = 0;
|
|
sensor->hw_flags.jpege = 0;
|
|
sensor->hw_flags.gs_bpp = 1;
|
|
|
|
// Allocate packet buffer in the same domain as the DMA instance.
|
|
vospi_packet = dma_alloc(VOSPI_PACKET_SIZE, ISC_SPI_DMA_STREAM);
|
|
if (vospi_packet == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
// Configure the DMA handler for Transmission process
|
|
DMAHandle.Instance = ISC_SPI_DMA_STREAM;
|
|
DMAHandle.Init.Request = ISC_SPI_DMA_REQUEST;
|
|
DMAHandle.Init.Mode = DMA_CIRCULAR;
|
|
DMAHandle.Init.Priority = DMA_PRIORITY_HIGH;
|
|
DMAHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
|
// When the DMA is configured in direct mode (the FIFO is disabled), the source and
|
|
// destination transfer widths are equal, and both defined by PSIZE (MSIZE is ignored).
|
|
// Additionally, burst transfers are not possible (MBURST and PBURST are both ignored).
|
|
DMAHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
|
DMAHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
|
// Note MBURST and PBURST are ignored.
|
|
DMAHandle.Init.MemBurst = DMA_MBURST_INC4;
|
|
DMAHandle.Init.PeriphBurst = DMA_PBURST_INC4;
|
|
DMAHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
|
DMAHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
|
DMAHandle.Init.MemInc = DMA_MINC_ENABLE;
|
|
DMAHandle.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
|
|
// NVIC configuration for DMA transfer complete interrupt
|
|
NVIC_SetPriority(ISC_SPI_DMA_IRQn, IRQ_PRI_DMA21);
|
|
HAL_NVIC_DisableIRQ(ISC_SPI_DMA_IRQn);
|
|
|
|
#if defined(ISC_SPI_DMA_CLK_ENABLE)
|
|
ISC_SPI_DMA_CLK_ENABLE();
|
|
#endif
|
|
|
|
HAL_DMA_DeInit(&DMAHandle);
|
|
if (HAL_DMA_Init(&DMAHandle) != HAL_OK) {
|
|
// Initialization Error
|
|
return -1;
|
|
}
|
|
|
|
memset(&ISC_SPIHandle, 0, sizeof(ISC_SPIHandle));
|
|
ISC_SPIHandle.Instance = ISC_SPI;
|
|
ISC_SPIHandle.Init.NSS = SPI_NSS_HARD_OUTPUT;
|
|
ISC_SPIHandle.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
|
|
ISC_SPIHandle.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
|
|
ISC_SPIHandle.Init.Mode = SPI_MODE_MASTER;
|
|
ISC_SPIHandle.Init.TIMode = SPI_TIMODE_DISABLE;
|
|
ISC_SPIHandle.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
|
|
ISC_SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
|
|
ISC_SPIHandle.Init.FifoThreshold = SPI_FIFO_THRESHOLD_04DATA;
|
|
ISC_SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
|
ISC_SPIHandle.Init.CLKPhase = SPI_PHASE_2EDGE;
|
|
ISC_SPIHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
|
ISC_SPIHandle.Init.BaudRatePrescaler = ISC_SPI_PRESCALER;
|
|
// Recommanded setting to avoid glitches
|
|
ISC_SPIHandle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
|
|
|
|
if (HAL_SPI_Init(&ISC_SPIHandle) != HAL_OK) {
|
|
ISC_SPI_RESET();
|
|
ISC_SPI_RELEASE();
|
|
ISC_SPI_CLK_DISABLE();
|
|
return -1;
|
|
}
|
|
|
|
// Associate the initialized DMA handle to the the SPI handle
|
|
__HAL_LINKDMA(&ISC_SPIHandle, hdmarx, DMAHandle);
|
|
|
|
// NVIC configuration for SPI transfer complete interrupt
|
|
NVIC_SetPriority(ISC_SPI_IRQn, IRQ_PRI_DCMI);
|
|
HAL_NVIC_EnableIRQ(ISC_SPI_IRQn);
|
|
|
|
LEP_OEM_PART_NUMBER_T part;
|
|
if ((!reset(sensor))
|
|
&& (LEP_GetOemFlirPartNumber(&LEPHandle, &part) == LEP_OK)) {
|
|
// 500 == Lepton
|
|
// xxxx == Version
|
|
// 01/00 == Shutter/NoShutter
|
|
if (!strncmp(part.value, "500-0771", 8)) {
|
|
sensor->chip_id_w = LEPTON_3_5;
|
|
} else if (!strncmp(part.value, "500-0726", 8)) {
|
|
sensor->chip_id_w = LEPTON_3_0;
|
|
} else if (!strncmp(part.value, "500-0763", 8)) {
|
|
sensor->chip_id_w = LEPTON_2_5;
|
|
} else if (!strncmp(part.value, "500-0659", 8)) {
|
|
sensor->chip_id_w = LEPTON_2_0;
|
|
} else if (!strncmp(part.value, "500-0690", 8)) {
|
|
sensor->chip_id_w = LEPTON_1_6;
|
|
} else if (!strncmp(part.value, "500-0643", 8)) {
|
|
sensor->chip_id_w = LEPTON_1_5;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif // (OMV_ENABLE_LEPTON == 1)
|