diff --git a/src/omv/sensors/mt9v034.c b/src/omv/sensors/mt9v034.c index eb9595427..47be21aa2 100644 --- a/src/omv/sensors/mt9v034.c +++ b/src/omv/sensors/mt9v034.c @@ -11,152 +11,97 @@ #include "omv_boardconfig.h" #if (OMV_ENABLE_MT9V034 == 1) +#include +#include +#include + #include "cambus.h" #include "sensor.h" #include "mt9v034.h" +#include "mt9v034_regs.h" #include "py/mphal.h" -#include "framebuffer.h" -#define MT9V034_MAX_HEIGHT (480) -#define MT9V034_MAX_WIDTH (752) -#define MT9V034_CHIP_VERSION (0x00) -#define MT9V034_COL_START (0x01) -#define MT9V034_COL_START_MIN (1) -#define MT9V034_COL_START_MAX (752) -#define MT9V034_ROW_START (0x02) -#define MT9V034_ROW_START_MIN (4) -#define MT9V034_ROW_START_MAX (482) -#define MT9V034_WINDOW_HEIGHT (0x03) -#define MT9V034_WINDOW_HEIGHT_MIN (1) -#define MT9V034_WINDOW_HEIGHT_MAX (480) -#define MT9V034_WINDOW_WIDTH (0x04) -#define MT9V034_WINDOW_WIDTH_MIN (1) -#define MT9V034_WINDOW_WIDTH_MAX (752) -#define MT9V034_HORIZONTAL_BLANKING (0x05) -#define MT9V034_HORIZONTAL_BLANKING_MIN (43) -#define MT9V034_HORIZONTAL_BLANKING_MAX (1023) -#define MT9V034_HORIZONTAL_BLANKING_DEF (94) -#define MT9V034_VERTICAL_BLANKING (0x06) -#define MT9V034_VERTICAL_BLANKING_MIN (4) -#define MT9V034_VERTICAL_BLANKING_MAX (3000) -#define MT9V034_VERTICAL_BLANKING_DEF (45) -#define MT9V034_CHIP_CONTROL (0x07) -#define MT9V034_CHIP_CONTROL_MASTER_MODE (1 << 3) -#define MT9V034_CHIP_CONTROL_SNAP_MODE (3 << 3) -#define MT9V034_CHIP_CONTROL_MODE_MASK (3 << 3) -#define MT9V034_CHIP_CONTROL_DOUT_ENABLE (1 << 7) -#define MT9V034_CHIP_CONTROL_SEQUENTIAL (1 << 8) -#define MT9V034_CHIP_CONTROL_RESERVED (1 << 9) -#define MT9V034_SHUTTER_WIDTH1 (0x08) -#define MT9V034_SHUTTER_WIDTH2 (0x09) -#define MT9V034_SHUTTER_WIDTH_CONTROL (0x0A) -#define MT9V034_TOTAL_SHUTTER_WIDTH (0x0B) -#define MT9V034_TOTAL_SHUTTER_WIDTH_MIN (1) -#define MT9V034_TOTAL_SHUTTER_WIDTH_MAX (32767) -#define MT9V034_RESET (0x0C) -#define MT9V034_READ_MODE (0x0D) -#define MT9V034_READ_MODE_ROW_BIN_2 (1 << 0) -#define MT9V034_READ_MODE_ROW_BIN_4 (1 << 1) -#define MT9V034_READ_MODE_COL_BIN_2 (1 << 2) -#define MT9V034_READ_MODE_COL_BIN_4 (1 << 3) -#define MT9V034_READ_MODE_ROW_FLIP (1 << 4) -#define MT9V034_READ_MODE_COL_FLIP (1 << 5) -#define MT9V034_READ_MODE_DARK_COLS (1 << 6) -#define MT9V034_READ_MODE_DARK_ROWS (1 << 7) -#define MT9V034_PIXEL_OPERATION_MODE (0x0F) -#define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) -#define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) -#define MT9V034_ANALOG_GAIN (0x35) -#define MT9V034_ANALOG_GAIN_MIN (16) -#define MT9V034_ANALOG_GAIN_MAX (64) -#define MT9V034_MAX_ANALOG_GAIN (0x36) -#define MT9V034_MAX_ANALOG_GAIN_MAX (127) -#define MT9V034_FRAME_DARK_AVERAGE (0x42) -#define MT9V034_DARK_AVG_THRESH (0x46) -#define MT9V034_DARK_AVG_LOW_THRESH_MASK (255 << 0) -#define MT9V034_DARK_AVG_LOW_THRESH_SHIFT (0) -#define MT9V034_DARK_AVG_HIGH_THRESH_MASK (255 << 8) -#define MT9V034_DARK_AVG_HIGH_THRESH_SHIFT (8) -#define MT9V034_ROW_NOISE_CORR_CONTROL (0x70) -#define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 5) -#define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) -#define MT9V034_PIXEL_CLOCK (0x72) -#define MT9V034_PIXEL_CLOCK_INV_LINE (1 << 0) -#define MT9V034_PIXEL_CLOCK_INV_FRAME (1 << 1) -#define MT9V034_PIXEL_CLOCK_XOR_LINE (1 << 2) -#define MT9V034_PIXEL_CLOCK_CONT_LINE (1 << 3) -#define MT9V034_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) -#define MT9V034_TEST_PATTERN (0x7F) -#define MT9V034_TEST_PATTERN_DATA_MASK (1023 << 0) -#define MT9V034_TEST_PATTERN_DATA_SHIFT (0) -#define MT9V034_TEST_PATTERN_USE_DATA (1 << 10) -#define MT9V034_TEST_PATTERN_GRAY_MASK (3 << 11) -#define MT9V034_TEST_PATTERN_GRAY_NONE (0 << 11) -#define MT9V034_TEST_PATTERN_GRAY_VERTICAL (1 << 11) -#define MT9V034_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) -#define MT9V034_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) -#define MT9V034_TEST_PATTERN_ENABLE (1 << 13) -#define MT9V034_TEST_PATTERN_FLIP (1 << 14) -#define MT9V034_AEC_AGC_ENABLE (0xAF) -#define MT9V034_AEC_ENABLE (1 << 0) -#define MT9V034_AGC_ENABLE (1 << 1) -#define MT9V034_THERMAL_INFO (0xC1) -#define MT9V034_ID_REG (0x6B) -#define MT9V034_MAX_GAIN (0xAB) -#define MT9V034_MAX_EXPOSE (0xAD) -#define MT9V034_PIXEL_COUNT (0xB0) -#define MT9V034_FINE_SHUTTER_WIDTH_TOTAL (0xD5) +#define ACTIVE_SENSOR_WIDTH (752) +#define ACTIVE_SENSOR_HEIGHT (480) -#define MICROSECOND_CLKS (1000000) +static int16_t readout_x = 0; +static int16_t readout_y = 0; static int reset(sensor_t *sensor) { - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(1); - - DCMI_PWDN_LOW(); - mp_hal_delay_ms(1); - - DCMI_RESET_LOW(); - mp_hal_delay_ms(1); - - DCMI_RESET_HIGH(); - mp_hal_delay_ms(1); - int ret = 0; + readout_x = 0; + readout_y = 0; + + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_RESET, MT9V034_RESET_SOFT_RESET); uint16_t chip_control; ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, (chip_control & (~MT9V034_CHIP_CONTROL_RESERVED))); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, + (chip_control & (~MT9V034_CHIP_CONTROL_RESERVED))); uint16_t read_mode; ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, &read_mode); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, read_mode | MT9V034_READ_MODE_ROW_FLIP | MT9V034_READ_MODE_COL_FLIP); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, + read_mode | MT9V034_READ_MODE_ROW_FLIP | MT9V034_READ_MODE_COL_FLIP); + + // We have to copy the differences from context A into context B registers so that we can + // ping-pong between them seamlessly... + + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, &read_mode); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, + read_mode | MT9V034_READ_MODE_ROW_FLIP | MT9V034_READ_MODE_COL_FLIP); + + uint16_t shutter_width1; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH1, &shutter_width1); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH1_B, shutter_width1); + + uint16_t shutter_width2; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH2, &shutter_width2); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH2_B, shutter_width2); + + uint16_t shutter_control; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH_CONTROL, &shutter_control); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_SHUTTER_WIDTH_CONTROL_B, shutter_control); + + uint16_t voltage_level_1; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_V1_CONTROL, &voltage_level_1); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_V1_CONTROL_B, voltage_level_1); + + uint16_t voltage_level_2; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_V2_CONTROL, &voltage_level_2); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_V2_CONTROL_B, voltage_level_2); + + uint16_t voltage_level_3; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_V3_CONTROL, &voltage_level_3); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_V3_CONTROL_B, voltage_level_3); + + uint16_t voltage_level_4; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_V4_CONTROL, &voltage_level_4); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_V4_CONTROL_B, voltage_level_4); + + uint16_t analog_gain; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN, &analog_gain); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN_B, analog_gain); + + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_PIXEL_OPERATION_MODE, + 0); + + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_ADC_COMPANDING_MODE, + MT9V034_ADC_COMPANDING_MODE_LINEAR | MT9V034_ADC_COMPANDING_MODE_LINEAR_B); + + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, + MT9V034_AEC_ENABLE | MT9V034_AEC_ENABLE_B | MT9V034_AGC_ENABLE | MT9V034_AGC_ENABLE_B); return ret; } -static int sleep(sensor_t *sensor, int enable) -{ - if (enable) { - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(1); - } else { - DCMI_PWDN_LOW(); - mp_hal_delay_ms(1); - } - - return 0; -} - static int read_reg(sensor_t *sensor, uint16_t reg_addr) { uint16_t reg_data; - if (cambus_readw(&sensor->bus, sensor->slv_addr, reg_addr, ®_data) != 0) { return -1; } - return reg_data; } @@ -170,43 +115,62 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) if (pixformat != PIXFORMAT_GRAYSCALE) { return -1; } + return 0; } static int set_framesize(sensor_t *sensor, framesize_t framesize) { - uint16_t width = resolution[framesize][0]; - uint16_t height = resolution[framesize][1]; + uint16_t chip_control, read_mode; + int ret = 0; + uint16_t w = resolution[framesize][0]; + uint16_t h = resolution[framesize][1]; - if ((width > MT9V034_MAX_WIDTH) || (height > MT9V034_MAX_HEIGHT)) { + if ((w > ACTIVE_SENSOR_WIDTH) || (h > ACTIVE_SENSOR_HEIGHT)) { return -1; } - uint16_t read_mode; + if (cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control) != 0) { + return -1; + } - if (cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, &read_mode) != 0) { + // EDIT: WORKS BETTER TO STAY IN CONTEXT A + // + // Determine which context to switch to... + int context = 1; // chip_control & MT9V034_CHIP_CONTROL_CONTEXT; + int read_mode_addr = context ? MT9V034_READ_MODE : MT9V034_READ_MODE_B; + int col_start_addr = context ? MT9V034_COL_START : MT9V034_COL_START_B; + int row_start_addr = context ? MT9V034_ROW_START : MT9V034_ROW_START_B; + int window_height_addr = context ? MT9V034_WINDOW_HEIGHT : MT9V034_WINDOW_HEIGHT_B; + int window_width_addr = context ? MT9V034_WINDOW_WIDTH : MT9V034_WINDOW_WIDTH_B; + int horizontal_blanking_addr = context ? MT9V034_HORIZONTAL_BLANKING : MT9V034_HORIZONTAL_BLANKING_B; + + if (cambus_readw(&sensor->bus, sensor->slv_addr, read_mode_addr, &read_mode) != 0) { return -1; } int read_mode_mul = 1; read_mode &= 0xFFF0; - if ((width <= (MT9V034_MAX_WIDTH / 4)) && (height <= (MT9V034_MAX_HEIGHT / 4))) { + if ((w <= (ACTIVE_SENSOR_WIDTH / 4)) && (h <= (ACTIVE_SENSOR_HEIGHT / 4))) { read_mode_mul = 4; read_mode |= MT9V034_READ_MODE_COL_BIN_4 | MT9V034_READ_MODE_ROW_BIN_4; - } else if ((width <= (MT9V034_MAX_WIDTH / 2)) && (height <= (MT9V034_MAX_HEIGHT / 2))) { + } else if ((w <= (ACTIVE_SENSOR_WIDTH / 2)) && (h <= (ACTIVE_SENSOR_HEIGHT / 2))) { read_mode_mul = 2; read_mode |= MT9V034_READ_MODE_COL_BIN_2 | MT9V034_READ_MODE_ROW_BIN_2; } - int ret = 0; + int readout_x_max = (ACTIVE_SENSOR_WIDTH - (w * read_mode_mul)) / 2; + int readout_y_max = (ACTIVE_SENSOR_HEIGHT - (h * read_mode_mul)) / 2; + readout_x = IM_MAX(IM_MIN(readout_x, readout_x_max), -readout_x_max); + readout_y = IM_MAX(IM_MIN(readout_y, readout_y_max), -readout_y_max); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_COL_START, - ((MT9V034_MAX_WIDTH - (width * read_mode_mul)) / 2) + MT9V034_COL_START_MIN); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_ROW_START, - ((MT9V034_MAX_HEIGHT - (height * read_mode_mul)) / 2) + MT9V034_ROW_START_MIN); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_WINDOW_WIDTH, width * read_mode_mul); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_WINDOW_HEIGHT, height * read_mode_mul); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, col_start_addr, + readout_x_max - readout_x + MT9V034_COL_START_MIN); // sensor is mirrored by default + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, row_start_addr, + readout_y_max - readout_y + MT9V034_ROW_START_MIN); // sensor is mirrored by default + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, window_width_addr, w * read_mode_mul); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, window_height_addr, h * read_mode_mul); // Notes: 1. The MT9V034 uses column parallel analog-digital converters, thus short row timing is not possible. // The minimum total row time is 690 columns (horizontal width + horizontal blanking). The minimum @@ -214,14 +178,21 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) // must be increased. // // The STM32H7 needs more than 94+(752-640) clocks between rows otherwise it can't keep up with the pixel rate. - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING, - MT9V034_HORIZONTAL_BLANKING_DEF + (MT9V034_MAX_WIDTH - IM_MIN(width * read_mode_mul, 640))); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, horizontal_blanking_addr, + MT9V034_HORIZONTAL_BLANKING_DEF + (ACTIVE_SENSOR_WIDTH - IM_MIN(w * read_mode_mul, 640))); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, read_mode); - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_PIXEL_COUNT, (width * height) / 8); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, read_mode_addr, read_mode); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_PIXEL_COUNT, (w * h) / 8); // We need more setup time for the pixel_clk at the full data rate... - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_PIXEL_CLOCK, (read_mode_mul == 1) ? MT9V034_PIXEL_CLOCK_INV_PXL_CLK : 0); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_PIXEL_CLOCK, + (read_mode_mul == 1) ? MT9V034_PIXEL_CLOCK_INV_PXL_CLK : 0); + + // EDIT: WORKS BETTER TO STAY IN CONTEXT A + // + // Flip the context. + // ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, + // chip_control ^ MT9V034_CHIP_CONTROL_CONTEXT); return ret; } @@ -242,16 +213,19 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain uint16_t reg; int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, - (reg & (~MT9V034_AGC_ENABLE)) | ((enable != 0) ? MT9V034_AGC_ENABLE : 0)); + (reg & (~(MT9V034_AGC_ENABLE | MT9V034_AGC_ENABLE_B))) | + ((enable != 0) ? (MT9V034_AGC_ENABLE | MT9V034_AGC_ENABLE_B) : 0)); ret |= sensor->snapshot(sensor, NULL, NULL); // Force shadow mode register to update... if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - int gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db / 20.0) * fast_log(10.0)) * 16.0), 127), 0); + int gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db / 20.0f) * fast_log(10.0f)) * 16.0f), 64), 16); ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN, ®); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN, (reg & 0xFF80) | gain); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN_B, ®); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN_B, (reg & 0xFF80) | gain); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - int gain_ceiling = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)) * 16.0), 127), 16); + int gain_ceiling = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db_ceiling / 20.0f) * fast_log(10.0f)) * 16.0f), 64), 16); ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_MAX_GAIN, ®); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_MAX_GAIN, (reg & 0xFF80) | gain_ceiling); @@ -262,42 +236,59 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain static int get_gain_db(sensor_t *sensor, float *gain_db) { - uint16_t gain; - int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_ANALOG_GAIN, &gain); + uint16_t chip_control, reg, gain; + int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control); + int context = chip_control & MT9V034_CHIP_CONTROL_CONTEXT; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); - *gain_db = 20.0 * (fast_log((gain & 0x7F) / 16.0) / fast_log(10.0)); + if (reg & (context ? MT9V034_AGC_ENABLE_B : MT9V034_AGC_ENABLE)) { + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AGC_GAIN_OUTPUT, &gain); + } else { + int analog_gain = context ? MT9V034_ANALOG_GAIN_B : MT9V034_ANALOG_GAIN; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, analog_gain, &gain); + } + *gain_db = 20.0 * (fast_log((gain & 0x7F) / 16.0f) / fast_log(10.0f)); return ret; } static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { - uint16_t reg, row_time_0, row_time_1; - int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); + uint16_t chip_control, reg, read_mode_reg, row_time_0, row_time_1; + int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control); + int context = chip_control & MT9V034_CHIP_CONTROL_CONTEXT; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, - (reg & (~MT9V034_AEC_ENABLE)) | ((enable != 0) ? MT9V034_AEC_ENABLE : 0)); + (reg & (~(MT9V034_AEC_ENABLE | MT9V034_AEC_ENABLE_B))) | + ((enable != 0) ? (MT9V034_AEC_ENABLE | MT9V034_AEC_ENABLE_B) : 0)); ret |= sensor->snapshot(sensor, NULL, NULL); // Force shadow mode register to update... + int read_mode = context ? MT9V034_READ_MODE_B : MT9V034_READ_MODE; + int window_width = context ? MT9V034_WINDOW_WIDTH_B : MT9V034_WINDOW_WIDTH; + int horizontal_blanking = context ? MT9V034_HORIZONTAL_BLANKING_B : MT9V034_HORIZONTAL_BLANKING; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, read_mode, &read_mode_reg); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, window_width, &row_time_0); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, horizontal_blanking, &row_time_1); + + int clock = MT9V034_XCLK_FREQ; + if (read_mode_reg & MT9V034_READ_MODE_COL_BIN_2) clock /= 2; + if (read_mode_reg & MT9V034_READ_MODE_COL_BIN_4) clock /= 4; + + int exposure = IM_MIN(exposure_us, MICROSECOND_CLKS / 2) * (clock / MICROSECOND_CLKS); + int row_time = row_time_0 + row_time_1; + int coarse_time = exposure / row_time; + int fine_time = exposure % row_time; + + // Fine shutter time is global. if ((enable == 0) && (exposure_us >= 0)) { - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_WINDOW_WIDTH, &row_time_0); - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING, &row_time_1); - - int exposure = IM_MIN(exposure_us, MICROSECOND_CLKS / 2) * (MT9V034_XCLK_FREQ / MICROSECOND_CLKS); - int row_time = row_time_0 + row_time_1; - int coarse_time = exposure / row_time; - int fine_time = exposure % row_time; - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_TOTAL_SHUTTER_WIDTH, coarse_time); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_TOTAL_SHUTTER_WIDTH_B, coarse_time); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL, fine_time); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL_B, fine_time); } else if ((enable != 0) && (exposure_us >= 0)) { - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_WINDOW_WIDTH, &row_time_0); - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING, &row_time_1); - - int exposure = IM_MIN(exposure_us, MICROSECOND_CLKS / 2) * (MT9V034_XCLK_FREQ / MICROSECOND_CLKS); - int row_time = row_time_0 + row_time_1; - int coarse_time = exposure / row_time; - ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_MAX_EXPOSE, coarse_time); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL, fine_time); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL_B, fine_time); } return ret; @@ -305,14 +296,32 @@ static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) static int get_exposure_us(sensor_t *sensor, int *exposure_us) { - uint16_t int_rows, int_pixels, row_time_0, row_time_1; - int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_TOTAL_SHUTTER_WIDTH, &int_rows); - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL, &int_pixels); - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_WINDOW_WIDTH, &row_time_0); - ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING, &row_time_1); + uint16_t chip_control, reg, read_mode_reg, row_time_0, row_time_1, int_pixels, int_rows; + int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control); + int context = chip_control & MT9V034_CHIP_CONTROL_CONTEXT; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); - *exposure_us = ((int_rows * (row_time_0 + row_time_1)) + int_pixels) / (MT9V034_XCLK_FREQ / MICROSECOND_CLKS); + int read_mode = context ? MT9V034_READ_MODE_B : MT9V034_READ_MODE; + int window_width = context ? MT9V034_WINDOW_WIDTH_B : MT9V034_WINDOW_WIDTH; + int horizontal_blanking = context ? MT9V034_HORIZONTAL_BLANKING_B : MT9V034_HORIZONTAL_BLANKING; + int fine_shutter_width_total = context ? MT9V034_FINE_SHUTTER_WIDTH_TOTAL_B : MT9V034_FINE_SHUTTER_WIDTH_TOTAL; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, read_mode, &read_mode_reg); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, window_width, &row_time_0); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, horizontal_blanking, &row_time_1); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, fine_shutter_width_total, &int_pixels); + int clock = MT9V034_XCLK_FREQ; + if (read_mode_reg & MT9V034_READ_MODE_COL_BIN_2) clock /= 2; + if (read_mode_reg & MT9V034_READ_MODE_COL_BIN_4) clock /= 4; + + if (reg & (context ? MT9V034_AEC_ENABLE_B : MT9V034_AEC_ENABLE)) { + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_AEC_EXPOSURE_OUTPUT, &int_rows); + } else { + int total_shutter_width = context ? MT9V034_TOTAL_SHUTTER_WIDTH_B : MT9V034_TOTAL_SHUTTER_WIDTH; + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, total_shutter_width, &int_rows); + } + + *exposure_us = ((int_rows * (row_time_0 + row_time_1)) + int_pixels) / (clock / MICROSECOND_CLKS); return ret; } @@ -322,6 +331,9 @@ static int set_hmirror(sensor_t *sensor, int enable) int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, &read_mode); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, // inverted behavior (read_mode & (~MT9V034_READ_MODE_COL_FLIP)) | ((enable == 0) ? MT9V034_READ_MODE_COL_FLIP : 0)); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, &read_mode); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, // inverted behavior + (read_mode & (~MT9V034_READ_MODE_COL_FLIP)) | ((enable == 0) ? MT9V034_READ_MODE_COL_FLIP : 0)); ret |= sensor->snapshot(sensor, NULL, NULL); // Force shadow mode register to update... return ret; } @@ -332,21 +344,59 @@ static int set_vflip(sensor_t *sensor, int enable) int ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, &read_mode); ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE, // inverted behavior (read_mode & (~MT9V034_READ_MODE_ROW_FLIP)) | ((enable == 0) ? MT9V034_READ_MODE_ROW_FLIP : 0)); + ret |= cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, &read_mode); + ret |= cambus_writew(&sensor->bus, sensor->slv_addr, MT9V034_READ_MODE_B, // inverted behavior + (read_mode & (~MT9V034_READ_MODE_ROW_FLIP)) | ((enable == 0) ? MT9V034_READ_MODE_ROW_FLIP : 0)); ret |= sensor->snapshot(sensor, NULL, NULL); // Force shadow mode register to update... return ret; } -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; uint16_t chip_control; + // The MT9V034 does not have a hardware scaler so the readout w/h must be equal to the + // framesize w/h. + int tmp_readout_w = resolution[sensor->framesize][0]; + int tmp_readout_h = resolution[sensor->framesize][1]; + if (sensor->framesize == FRAMESIZE_INVALID) { + tmp_readout_w = ACTIVE_SENSOR_WIDTH; + tmp_readout_h = ACTIVE_SENSOR_HEIGHT; + } + + if ((tmp_readout_w <= (ACTIVE_SENSOR_WIDTH / 4)) && (tmp_readout_h <= (ACTIVE_SENSOR_HEIGHT / 4))) { + tmp_readout_w *= 4; + tmp_readout_h *= 4; + } else if ((tmp_readout_w <= (ACTIVE_SENSOR_WIDTH / 2)) && (tmp_readout_h <= (ACTIVE_SENSOR_HEIGHT / 2))) { + tmp_readout_w *= 2; + tmp_readout_h *= 2; + } + switch (request) { + case IOCTL_SET_READOUT_WINDOW: { + int tmp_readout_x = va_arg(ap, int); + int tmp_readout_y = va_arg(ap, int); + int readout_x_max = (ACTIVE_SENSOR_WIDTH - tmp_readout_w) / 2; + int readout_y_max = (ACTIVE_SENSOR_HEIGHT - tmp_readout_h) / 2; + tmp_readout_x = IM_MAX(IM_MIN(tmp_readout_x, readout_x_max), -readout_x_max); + tmp_readout_y = IM_MAX(IM_MIN(tmp_readout_y, readout_y_max), -readout_y_max); + bool changed = (tmp_readout_x != readout_x) || + (tmp_readout_y != readout_y); + readout_x = tmp_readout_x; + readout_y = tmp_readout_y; + if (changed && (sensor->framesize != FRAMESIZE_INVALID)) { + ret |= set_framesize(sensor, sensor->framesize); + } + break; + } + case IOCTL_GET_READOUT_WINDOW: { + *va_arg(ap, int *) = readout_x; + *va_arg(ap, int *) = readout_y; + *va_arg(ap, int *) = tmp_readout_w; + *va_arg(ap, int *) = tmp_readout_h; + break; + } case IOCTL_SET_TRIGGERED_MODE: { int enable = va_arg(ap, int); ret = cambus_readw(&sensor->bus, sensor->slv_addr, MT9V034_CHIP_CONTROL, &chip_control); @@ -375,9 +425,9 @@ static int ioctl(sensor_t *sensor, int request, va_list ap) int mt9v034_init(sensor_t *sensor) { + // Initialize sensor structure. sensor->gs_bpp = sizeof(uint8_t); sensor->reset = reset; - sensor->sleep = sleep; sensor->read_reg = read_reg; sensor->write_reg = write_reg; sensor->set_pixformat = set_pixformat; @@ -389,15 +439,17 @@ int mt9v034_init(sensor_t *sensor) sensor->get_exposure_us = get_exposure_us; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; - sensor->set_lens_correction = set_lens_correction; sensor->ioctl = ioctl; + // Set sensor flags SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_VSYNC, 0); SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_HSYNC, 0); SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_PIXCK, 0); SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_FSYNC, 1); SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_JPEGE, 0); + SENSOR_HW_FLAGS_SET(sensor, SWNSOR_HW_FLAGS_RGB565_REV, 0); return 0; } + #endif // (OMV_ENABLE_MT9V034 == 1) diff --git a/src/omv/sensors/mt9v034.h b/src/omv/sensors/mt9v034.h index c37ca2d08..1aea084ef 100644 --- a/src/omv/sensors/mt9v034.h +++ b/src/omv/sensors/mt9v034.h @@ -10,6 +10,6 @@ */ #ifndef __MT9V034_H__ #define __MT9V034_H__ -#define MT9V034_XCLK_FREQ 27000000 +#define MT9V034_XCLK_FREQ 26666666 int mt9v034_init(sensor_t *sensor); #endif // __MT9V034_H__ diff --git a/src/omv/sensors/mt9v034_regs.h b/src/omv/sensors/mt9v034_regs.h new file mode 100644 index 000000000..1213052ec --- /dev/null +++ b/src/omv/sensors/mt9v034_regs.h @@ -0,0 +1,163 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * MT9V034 driver. + */ + +#define MT9V034_CHIP_VERSION (0x00) + +#define MT9V034_COL_START (0x01) +#define MT9V034_COL_START_B (0xC9) +#define MT9V034_COL_START_MIN (1) +#define MT9V034_COL_START_MAX (752) + +#define MT9V034_ROW_START (0x02) +#define MT9V034_ROW_START_B (0xCA) +#define MT9V034_ROW_START_MIN (4) +#define MT9V034_ROW_START_MAX (482) + +#define MT9V034_WINDOW_HEIGHT (0x03) +#define MT9V034_WINDOW_HEIGHT_B (0xCB) +#define MT9V034_WINDOW_HEIGHT_MIN (1) +#define MT9V034_WINDOW_HEIGHT_MAX (480) + +#define MT9V034_WINDOW_WIDTH (0x04) +#define MT9V034_WINDOW_WIDTH_B (0xCC) +#define MT9V034_WINDOW_WIDTH_MIN (1) +#define MT9V034_WINDOW_WIDTH_MAX (752) + +#define MT9V034_HORIZONTAL_BLANKING (0x05) +#define MT9V034_HORIZONTAL_BLANKING_B (0xCD) +#define MT9V034_HORIZONTAL_BLANKING_MIN_1 (61) +#define MT9V034_HORIZONTAL_BLANKING_MIN_2 (71) +#define MT9V034_HORIZONTAL_BLANKING_MIN_4 (91) +#define MT9V034_HORIZONTAL_BLANKING_MAX (1023) +#define MT9V034_HORIZONTAL_BLANKING_DEF (94) + +#define MT9V034_VERTICAL_BLANKING (0x06) +#define MT9V034_VERTICAL_BLANKING_B (0xCE) +#define MT9V034_VERTICAL_BLANKING_MIN (4) +#define MT9V034_VERTICAL_BLANKING_MAX (3000) +#define MT9V034_VERTICAL_BLANKING_DEF (45) + +#define MT9V034_CHIP_CONTROL (0x07) +#define MT9V034_CHIP_CONTROL_MASTER_MODE (1 << 3) +#define MT9V034_CHIP_CONTROL_SNAP_MODE (3 << 3) +#define MT9V034_CHIP_CONTROL_MODE_MASK (3 << 3) +#define MT9V034_CHIP_CONTROL_DOUT_ENABLE (1 << 7) +#define MT9V034_CHIP_CONTROL_SEQUENTIAL (1 << 8) +#define MT9V034_CHIP_CONTROL_RESERVED (1 << 9) +#define MT9V034_CHIP_CONTROL_CONTEXT (1 << 15) + +#define MT9V034_SHUTTER_WIDTH1 (0x08) +#define MT9V034_SHUTTER_WIDTH1_B (0xCF) + +#define MT9V034_SHUTTER_WIDTH2 (0x09) +#define MT9V034_SHUTTER_WIDTH2_B (0xD0) + +#define MT9V034_SHUTTER_WIDTH_CONTROL (0x0A) +#define MT9V034_SHUTTER_WIDTH_CONTROL_B (0xD1) + +#define MT9V034_TOTAL_SHUTTER_WIDTH (0x0B) +#define MT9V034_TOTAL_SHUTTER_WIDTH_B (0xD2) +#define MT9V034_TOTAL_SHUTTER_WIDTH_MIN (1) +#define MT9V034_TOTAL_SHUTTER_WIDTH_MAX (32767) + +#define MT9V034_RESET (0x0C) +#define MT9V034_RESET_SOFT_RESET (1 << 0) + +#define MT9V034_READ_MODE (0x0D) +#define MT9V034_READ_MODE_B (0x0E) +#define MT9V034_READ_MODE_ROW_BIN_2 (1 << 0) +#define MT9V034_READ_MODE_ROW_BIN_4 (1 << 1) +#define MT9V034_READ_MODE_COL_BIN_2 (1 << 2) +#define MT9V034_READ_MODE_COL_BIN_4 (1 << 3) +#define MT9V034_READ_MODE_ROW_FLIP (1 << 4) +#define MT9V034_READ_MODE_COL_FLIP (1 << 5) +#define MT9V034_READ_MODE_DARK_COLS (1 << 6) +#define MT9V034_READ_MODE_DARK_ROWS (1 << 7) + +#define MT9V034_PIXEL_OPERATION_MODE (0x0F) +#define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) +#define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) +#define MT9V034_PIXEL_OPERATION_MODE_HDR_B (1 << 8) + +#define MT9V034_ADC_COMPANDING_MODE (0x1C) +#define MT9V034_ADC_COMPANDING_MODE_LINEAR (2 << 0) +#define MT9V034_ADC_COMPANDING_MODE_LINEAR_B (2 << 8) + +#define MT9V034_ANALOG_GAIN (0x35) +#define MT9V034_ANALOG_GAIN_B (0x36) +#define MT9V034_ANALOG_GAIN_MIN (16) +#define MT9V034_ANALOG_GAIN_MAX (64) + +#define MT9V034_V1_CONTROL (0x31) +#define MT9V034_V1_CONTROL_B (0x39) + +#define MT9V034_V2_CONTROL (0x32) +#define MT9V034_V2_CONTROL_B (0x3A) + +#define MT9V034_V3_CONTROL (0x33) +#define MT9V034_V3_CONTROL_B (0x3B) + +#define MT9V034_V4_CONTROL (0x34) +#define MT9V034_V4_CONTROL_B (0x3C) + +#define MT9V034_FRAME_DARK_AVERAGE (0x42) + +#define MT9V034_DARK_AVG_THRESH (0x46) +#define MT9V034_DARK_AVG_LOW_THRESH_MASK (255 << 0) +#define MT9V034_DARK_AVG_LOW_THRESH_SHIFT (0) +#define MT9V034_DARK_AVG_HIGH_THRESH_MASK (255 << 8) +#define MT9V034_DARK_AVG_HIGH_THRESH_SHIFT (8) + +#define MT9V034_ROW_NOISE_CORR_CONTROL (0x70) +#define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 5) +#define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) + +#define MT9V034_PIXEL_CLOCK (0x72) +#define MT9V034_PIXEL_CLOCK_INV_LINE (1 << 0) +#define MT9V034_PIXEL_CLOCK_INV_FRAME (1 << 1) +#define MT9V034_PIXEL_CLOCK_XOR_LINE (1 << 2) +#define MT9V034_PIXEL_CLOCK_CONT_LINE (1 << 3) +#define MT9V034_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) + +#define MT9V034_TEST_PATTERN (0x7F) +#define MT9V034_TEST_PATTERN_DATA_MASK (1023 << 0) +#define MT9V034_TEST_PATTERN_DATA_SHIFT (0) +#define MT9V034_TEST_PATTERN_USE_DATA (1 << 10) +#define MT9V034_TEST_PATTERN_GRAY_MASK (3 << 11) +#define MT9V034_TEST_PATTERN_GRAY_NONE (0 << 11) +#define MT9V034_TEST_PATTERN_GRAY_VERTICAL (1 << 11) +#define MT9V034_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) +#define MT9V034_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) +#define MT9V034_TEST_PATTERN_ENABLE (1 << 13) +#define MT9V034_TEST_PATTERN_FLIP (1 << 14) + +#define MT9V034_AEC_AGC_ENABLE (0xAF) +#define MT9V034_AEC_ENABLE (1 << 0) +#define MT9V034_AEC_ENABLE_B (1 << 8) +#define MT9V034_AGC_ENABLE (1 << 1) +#define MT9V034_AGC_ENABLE_B (1 << 9) + +#define MT9V034_THERMAL_INFO (0xC1) + +#define MT9V034_ID_REG (0x6B) + +#define MT9V034_MAX_GAIN (0xAB) + +#define MT9V034_MAX_EXPOSE (0xAD) + +#define MT9V034_PIXEL_COUNT (0xB0) +#define MT9V034_AGC_GAIN_OUTPUT (0xBA) +#define MT9V034_AEC_EXPOSURE_OUTPUT (0xBB) + +#define MT9V034_FINE_SHUTTER_WIDTH_TOTAL (0xD5) +#define MT9V034_FINE_SHUTTER_WIDTH_TOTAL_B (0xD8) + +#define MICROSECOND_CLKS (1000000)