LibreSatCam/Cypress_FX3_USB_Firmware/Source/sensor_imx219.c

638 lines
17 KiB
C

#include <cyu3system.h>
#include <cyu3os.h>
#include <cyu3dma.h>
#include <cyu3error.h>
#include <cyu3uart.h>
#include <cyu3i2c.h>
#include <cyu3types.h>
#include <cyu3gpio.h>
#include <cyu3utils.h>
#include "sensor_imx219.h"
#include "uvc_settings.h"
imgsensor_mode_t *selected_img_mode;
static const imx219_reg_t mode_default[]={ //default register settings, Resolution and FPS specific settings will be over written
{REG_MODE_SEL, 0x00},
{0x30EB, 0x05}, //access sequence
{0x30EB, 0x0C},
{0x300A, 0xFF},
{0x300B, 0xFF},
{0x30EB, 0x05},
{0x30EB, 0x09},
{REG_CSI_LANE, 0x01}, //3-> 4Lane 1-> 2Lane
{REG_DPHY_CTRL, 0x00}, //DPHY timing 0-> auot 1-> manual
{REG_EXCK_FREQ_MSB, 0x18}, //external oscillator frequncy 0x18 -> 24Mhz
{REG_EXCK_FREQ_LSB, 0x00},
{REG_FRAME_LEN_MSB, 0x06}, //frame length , Raspberry pi sends this commands continously when recording video @60fps ,writes come at interval of 32ms , Data 355 for resolution 1280x720 command 162 also comes along with data 0DE7 also 15A with data 0200
{REG_FRAME_LEN_LSB, 0xE3},
{REG_LINE_LEN_MSB, 0x0d}, //does not directly affect how many bits on wire in one line does affect how many clock between lines
{REG_LINE_LEN_LSB, 0x78}, //appears to be having step in value, not every LSb change will reflect on fps
{REG_X_ADD_STA_MSB, 0x02}, //x start
{REG_X_ADD_STA_LSB, 0xA8},
{REG_X_ADD_END_MSB, 0x0A}, //x end
{REG_X_ADD_END_LSB, 0x27},
{REG_Y_ADD_STA_MSB, 0x02}, //y start
{REG_Y_ADD_STA_LSB, 0xB4},
{REG_Y_ADD_END_MSB, 0x06}, //y end
{REG_Y_ADD_END_LSB, 0xEB},
{REG_X_OUT_SIZE_MSB, 0x07}, //resolution 1280 -> 5 00 , 1920 -> 780 , 2048 -> 0x8 0x00
{REG_X_OUT_SIZE_LSB, 0x80},
{REG_Y_OUT_SIZE_MSB, 0x04}, // 720 -> 0x02D0 | 1080 -> 0x438 | this setting changes how many line over wire does not affect frame rate
{REG_Y_OUT_SIZE_LSB, 0x38},
{REG_X_ODD_INC, 0x01}, //increment
{REG_Y_ODD_INC, 0x01}, //increment
{REG_BINNING_H, 0x00}, //binning H 0 off 1 x2 2 x4 3 x2 analog
{REG_BINNING_V, 0x00}, //binning H 0 off 1 x2 2 x4 3 x2 analog
{REG_CSI_FORMAT_C, 0x0A}, //CSI Data format A-> 10bit
{REG_CSI_FORMAT_D, 0x0A}, //CSI Data format
{REG_VTPXCK_DIV, 0x05}, //vtpxclkd_div 5 301
{REG_VTSYCK_DIV, 0x01}, //vtsclk _div 1 303
{REG_PREPLLCK_VT_DIV, 0x03}, //external oscillator /3
{REG_PREPLLCK_OP_DIV, 0x03}, //external oscillator /3
{REG_PLL_VT_MPY_MSB, 0x00}, //PLL_VT multiplizer
{REG_PLL_VT_MPY_LSB, 0x52}, //Changes Frame rate with , integration register 0x15a
{REG_OPPXCK_DIV, 0x0A}, //oppxck_div
{REG_OPSYCK_DIV, 0x01}, //opsysck_div
{REG_PLL_OP_MPY_MSB, 0x00}, //PLL_OP
{REG_PLL_OP_MPY_LSB, 0x32}, // 8Mhz x 0x57 ->696Mhz -> 348Mhz | 0x32 -> 200Mhz | 0x40 -> 256Mhz
{0x455E, 0x00}, //magic?
{0x471E, 0x4B},
{0x4767, 0x0F},
{0x4750, 0x14},
{0x4540, 0x00},
{0x47B4, 0x14},
{0x4713, 0x30},
{0x478B, 0x10},
{0x478F, 0x10},
{0x4793, 0x10},
{0x4797, 0x0E},
{0x479B, 0x0E},
{REG_TP_RED_MSB, 0x00},
{REG_TP_RED_LSB, 0x00},
{REG_TP_GREEN_MSB, 0x00},
{REG_TP_GREEN_LSB, 0x00},
{REG_TP_BLUE_MSB, 0x00},
{REG_TP_BLUE_LSB, 0x00},
{REG_TEST_PATTERN_MSB, 0x00}, //test pattern
{REG_TEST_PATTERN_LSB, 0x00},
{REG_TP_X_OFFSET_MSB, 0x00}, //tp offset x 0
{REG_TP_X_OFFSET_LSB, 0x00},
{REG_TP_Y_OFFSET_MSB, 0x00}, //tp offset y 0
{REG_TP_Y_OFFSET_LSB, 0x00},
{REG_TP_WIDTH_MSB, 0x05}, //TP width 1920 ->780 1280->500
{REG_TP_WIDTH_LSB, 0x00},
{REG_TP_HEIGHT_MSB, 0x02}, //TP height 1080 -> 438 720->2D0
{REG_TP_HEIGHT_LSB, 0xD0},
{REG_DIG_GAIN_GLOBAL_MSB, 0x01},
{REG_DIG_GAIN_GLOBAL_LSB, 0x00},
{REG_ANA_GAIN_GLOBAL, 0x80}, //analog gain , raspberry pi constinouly changes this depending on scense
{REG_INTEGRATION_TIME_MSB, 0x03}, //integration time , really important for frame rate
{REG_INTEGRATION_TIME_LSB, 0x51},
{REG_MODE_SEL, 0x01},
};
imgsensor_mode_t *sensor_config;
static imgsensor_mode_t sensor_config_2LANE[] = {
{ //200Mhz 2Lane
.pix_clk_mul = 0x2E,
.pix_clk_div = 0x4, //only 4 or 5 or 8 or 10
.integration = 258 - 4, //must be < (linelength- 4) to maintain frame rate by framelength or integration time will slow frame rate
.gain = 0x70,
.linelength = 3448, //Warning! This value need to be either 0xD78 or 0xDE7 regardless of frame size and FPS, other values will result undefined and ununderstanable issues in image
.framelength = 258, //decided how long is frame, basically frame rate with pix clock, it has second priority to integration time. absolute minimum is 255 for imx219
.startx = 1000,
.starty = 750,
.endx = 2280,
.endy = 1715, //this has to odd or bayer oder will change
.width = 640,
.height = 480, //each frame will have two extra line to compensate for debayer crop
.binning = 2,
.fps = 200,
.test_pattern = 0
},
{ //200Mhz mipi 2 lane
.pix_clk_mul = 0x2E,
.pix_clk_div = 0x4,
.integration = 862 - 4,
.gain = 0x80,
.linelength = 0xD78,
.framelength = 862,
.startx = 0x2A8,
.starty = 0x2B4,
.endx = 0xA27,
.endy = 0x6EB,
.width = 1280,
.height = 720,
.binning = 0,
.fps = 60,
.test_pattern = 0
},
{ //200Mhz 2Lane
.pix_clk_mul = 0x20,
.pix_clk_div = 0x4,
.integration = 1200 - 4,
.gain = 0x80,
.linelength = 0xD78,
.framelength = 1200,
.startx = 0x2A8,
.starty = 0x2B4,
.endx = 0xA27,
.endy = 0x6EB,
.width = 1920,
.height = 1080,
.binning = 0,
.fps = 30,
.test_pattern = 0
},
{ //200Mhz 2Lane
.pix_clk_mul = 0x2D,
.pix_clk_div = 0x4,
.integration = 56 - 4,
.gain = 200,
.linelength = 0xD78,
.framelength = 56,
.startx = 1320,
.starty = 990,
.endx = 2600,
.endy = 1561,
.width = 640,
.height = 80,
.binning = 2,
.fps = 900,
.test_pattern = 0
},
{ //200Mhz 2 Lane
.pix_clk_mul = 0x12,
.pix_clk_div = 0x4,
.integration = 2670 - 4,
.gain = 200,
.linelength = 0xD78, //3448
.framelength = 2670,
.startx = 0,
.starty = 0,
.endx = 3279,
.endy = 2463,
.width = 3280,
.height = 2464,
.binning = 0,
.fps = 5,
.test_pattern = 0
},
{ //200Mhz 2 Lane
.pix_clk_mul = 0x12,
.pix_clk_div = 0x4,
.integration = 2670 - 4,
.gain = 200,
.linelength = 0xD78, //3448
.framelength = 2670,
.startx = 0,
.starty = 0,
.endx = 3279,
.endy = 2463,
.width = 3280,
.height = 2464,
.binning = 0,
.fps = 7,
.test_pattern = 0
},
};
static imgsensor_mode_t sensor_config_4LANE[] = {
{
.pix_clk_mul = 0x53,
.pix_clk_div = 0x4, //only 4 or 5 or 8 or 10
.integration = 481 - 4, //must be < (linelength- 4) to maintain frame rate by framelength or integration time will slow frame rate
.gain = 0x70,
.linelength = 3448, //Warning! This value need to be either 0xD78 or 0xDE7 regardless of frame size and FPS, other values will result undefined and ununderstanable issues in image
.framelength = 481,
.startx = 1000,
.starty = 750,
.endx = 2280,
.endy = 1715, //this has to be odd or bayer oder will change
.width = 640,
.height = 482, //each frame will have two extra line to compensate for debayer crop
.binning = 2,
.fps = 200,
.test_pattern = 0
},
{ //Camera output @120
.pix_clk_mul = 0x58,
.pix_clk_div = 0x4,
.integration = 850 - 4,
.gain = 0x80,
.linelength = 0xD78,
.framelength = 850,
.startx = 0x2A8,
.starty = 0x2B4,
.endx = 0xA27,
.endy = 0x6EB,
.width = 1280,
.height = 722,
.binning = 0,
.fps = 120,
.test_pattern = 0
},
{ //camera output 1920x1080 @60FPS
.pix_clk_mul = 0x50,
.pix_clk_div = 0x5,
.integration = 1237 - 4,
.gain = 0x80,
.linelength = 0xD78,
.framelength = 1237,
.startx = 0x2A8,
.starty = 0x2B4,
.endx = 0xA27,
.endy = 0x6EB,
.width = 1920,
.height = 1082,
.binning = 0,
.fps = 60,
.test_pattern = 0
},
{ //camera output 640x80 pixel @1000 FPS
.pix_clk_mul = 0x53,
.pix_clk_div = 0x4,
.integration = 96 - 4,
.gain = 200,
.linelength = 0xD78,
.framelength = 96,
.startx = 1320,
.starty = 990,
.endx = 2600,
.endy = 1561,
.width = 640,
.height = 80,
.binning = 2,
.fps = 1000,
.test_pattern = 0
},
{ //full frame 3280x2464 @15FPS
.pix_clk_mul = 0x31,
.pix_clk_div = 0x5,
.integration = 3031 - 4,
.gain = 200,
.linelength = 0xD78,
.framelength = 3031,
.startx = 0,
.starty = 0,
.endx = 3279,
.endy = 2463,
.width = 3280,
.height = 2464,
.binning = 0,
.fps = 5,
.test_pattern = 0
},
{ //full frame 3280x2464 @15FPS
.pix_clk_mul = 0x31,
.pix_clk_div = 0x5,
.integration = 3031 - 4,
.gain = 200,
.linelength = 0xD78,
.framelength = 3031,
.startx = 0,
.starty = 0,
.endx = 3279,
.endy = 2463,
.width = 3280,
.height = 2464,
.binning = 0,
.fps = 15,
.test_pattern = 0
},
};
static void SensorI2CAccessDelay (CyU3PReturnStatus_t status)
{
/* Add a 10us delay if the I2C operation that preceded this call was successful. */
if (status == CY_U3P_SUCCESS)
CyU3PBusyWait (50);
}
CyU3PReturnStatus_t sensor_i2c_write(uint16_t reg_addr, uint8_t data)
{
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
CyU3PI2cPreamble_t preamble;
uint8_t buf[2];
/* Set the parameters for the I2C API access and then call the write API. */
preamble.buffer[0] = SENSOR_ADDR_WR;
preamble.buffer[1] = (reg_addr >> 8) & 0xFF;
preamble.buffer[2] = (reg_addr) & 0xFF;
preamble.length = 3; /* Three byte preamble. */
preamble.ctrlMask = 0x0000; /* No additional start and stop bits. */
buf[0] = data;
apiRetStatus = CyU3PI2cTransmitBytes (&preamble, buf, 1, 0);
SensorI2CAccessDelay (apiRetStatus);
return apiRetStatus;
}
CyU3PReturnStatus_t sensor_i2c_read(uint16_t reg_addr , uint8_t *buffer)
{
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
CyU3PI2cPreamble_t preamble;
preamble.length = 4;
preamble.buffer[0] = SENSOR_ADDR_RD & I2C_SLAVEADDR_MASK; /* Mask out the transfer type bit. */
preamble.buffer[1] = (reg_addr >> 8) & 0xFF;
preamble.buffer[2] = reg_addr & 0xFF;
preamble.buffer[3] = SENSOR_ADDR_RD ;
preamble.ctrlMask = 1<<2; /* Send start bit after third byte of preamble. */
apiRetStatus = CyU3PI2cReceiveBytes (&preamble, buffer, 1, 0);
SensorI2CAccessDelay (apiRetStatus);
return apiRetStatus;
}
static CyU3PReturnStatus_t camera_stream_on (uint8_t on)
{
return sensor_i2c_write(REG_MODE_SEL , on);
}
void SensorReset (void)
{
sensor_i2c_write(REG_SW_RESET , 0x01);
/* Wait for some time to allow proper reset. */
CyU3PThreadSleep (10);
/* Delay the allow the sensor to power up. */
sensor_i2c_write(REG_SW_RESET , 0x00);
CyU3PThreadSleep (10);
return;
}
static void set_mirror_flip(uint8_t image_mirror)
{
// CyU3PDebugPrint(4,"image_mirror = %d\n", image_mirror);
/********************************************************
*
* 0x3820[2] ISP Vertical flip
* 0x3820[1] Sensor Vertical flip
*
* 0x3821[2] ISP Horizontal mirror
* 0x3821[1] Sensor Horizontal mirror
*
* ISP and Sensor flip or mirror register bit should be the same!!
*
********************************************************/
uint8_t iTemp;
image_mirror = IMAGE_NORMAL;
sensor_i2c_read(REG_IMG_ORIENT , iTemp);
iTemp = iTemp & 0x03;
switch (image_mirror)
{
case IMAGE_NORMAL:
sensor_i2c_write(REG_IMG_ORIENT, iTemp | 0x03); //Set normal
break;
case IMAGE_V_MIRROR:
sensor_i2c_write(REG_IMG_ORIENT, iTemp | 0x01); //Set flip
break;
case IMAGE_H_MIRROR:
sensor_i2c_write(REG_IMG_ORIENT, iTemp | 0x02); //Set mirror
break;
case IMAGE_HV_MIRROR:
sensor_i2c_write(REG_IMG_ORIENT, iTemp); //Set mirror and flip
break;
}
}
void sensor_handle_uvc_control(uint8_t frame_index, uint32_t interval)
{
switch(frame_index)
{
case FRAME_MODE0:
{
if (interval == INTERVAL_MODE0)
{
selected_img_mode = &sensor_config[0];
}
}
break;
case FRAME_MODE1:
{
if (interval == INTERVAL_MODE1)
{
selected_img_mode = &sensor_config[1];
}
}
break;
case FRAME_MODE2:
{
if (interval == INTERVAL_MODE2)
{
selected_img_mode = &sensor_config[2];
}
}
break;
case FRAME_MODE3:
{
if (interval == INTERVAL_MODE3)
{
//selected_img_mode = &sensor_config->mode_3280x2464_15;
selected_img_mode = &sensor_config[3];
}
}
break;
case FRAME_MODE4:
{
if (interval == INTERVAL_MODE4_MIN)
{
selected_img_mode = &sensor_config[4];
}
else if (interval == INTERVAL_MODE4)
{
selected_img_mode = &sensor_config[5];
}
}
break;
default:
{
}
}
sensor_configure_mode (selected_img_mode);
}
void sensor_configure_mode(imgsensor_mode_t * mode)
{
set_mirror_flip(mode->mirror);
camera_stream_on(false);
sensor_i2c_write(REG_PLL_VT_MPY_MSB, GET_WORD_MSB(mode->pix_clk_mul));
sensor_i2c_write(REG_PLL_VT_MPY_LSB, GET_WORD_LSB(mode->pix_clk_mul));
sensor_i2c_write(REG_VTPXCK_DIV, GET_WORD_LSB(mode->pix_clk_div));
sensor_i2c_write(REG_INTEGRATION_TIME_MSB, GET_WORD_MSB(mode->integration));
sensor_i2c_write(REG_INTEGRATION_TIME_LSB, GET_WORD_LSB(mode->integration));
sensor_i2c_write(REG_ANALOG_GAIN, GET_WORD_LSB(mode->gain));
sensor_i2c_write(REG_LINE_LEN_MSB, GET_WORD_MSB(mode->linelength));
sensor_i2c_write(REG_LINE_LEN_LSB, GET_WORD_LSB(mode->linelength));
sensor_i2c_write(REG_FRAME_LEN_MSB, GET_WORD_MSB(mode->framelength));
sensor_i2c_write(REG_FRAME_LEN_LSB, GET_WORD_LSB(mode->framelength));
sensor_i2c_write(REG_X_ADD_STA_MSB, GET_WORD_MSB(mode->startx));
sensor_i2c_write(REG_X_ADD_STA_LSB, GET_WORD_LSB(mode->startx));
sensor_i2c_write(REG_Y_ADD_STA_MSB, GET_WORD_MSB(mode->starty));
sensor_i2c_write(REG_Y_ADD_STA_LSB, GET_WORD_LSB(mode->starty));
sensor_i2c_write(REG_X_ADD_END_MSB, GET_WORD_MSB(mode->endx));
sensor_i2c_write(REG_X_ADD_END_LSB, GET_WORD_LSB(mode->endx));
sensor_i2c_write(REG_Y_ADD_END_MSB, GET_WORD_MSB(mode->endy));
sensor_i2c_write(REG_Y_ADD_END_LSB, GET_WORD_LSB(mode->endy));
sensor_i2c_write(REG_X_OUT_SIZE_MSB, GET_WORD_MSB(mode->width));
sensor_i2c_write(REG_X_OUT_SIZE_LSB, GET_WORD_LSB(mode->width));
sensor_i2c_write(REG_Y_OUT_SIZE_MSB, GET_WORD_MSB(mode->height));
sensor_i2c_write(REG_Y_OUT_SIZE_LSB, GET_WORD_LSB(mode->height));
sensor_i2c_write(REG_TEST_PATTERN_LSB, (mode->test_pattern < 8)? mode->test_pattern : 0);
sensor_i2c_write(REG_TP_WIDTH_MSB, GET_WORD_MSB(mode->width));
sensor_i2c_write(REG_TP_WIDTH_LSB, GET_WORD_LSB(mode->width));
sensor_i2c_write(REG_TP_HEIGHT_MSB, GET_WORD_MSB(mode->height));
sensor_i2c_write(REG_TP_HEIGHT_LSB, GET_WORD_LSB(mode->height));
if ( mode->binning == 2)
{
sensor_i2c_write(REG_BINNING_H, 0x03);
sensor_i2c_write(REG_BINNING_V, 0x03);
}
else
{
sensor_i2c_write(REG_BINNING_H, 0x00);
sensor_i2c_write(REG_BINNING_V, 0x00);
}
camera_stream_on(1);
}
uint8_t SensorI2cBusTest (void)
{
uint8_t model_lsb;
uint8_t model_msb;
sensor_i2c_read (REG_MODEL_ID_MSB, &model_msb);
sensor_i2c_read (REG_MODEL_ID_LSB, &model_lsb);
if (((((uint16_t)model_msb & 0x0F) << 8) | model_lsb) == CAMERA_ID )
{
CyU3PDebugPrint(4,"I2C Sensor id: 0x%x\n", (((uint16_t)model_msb & 0x0F) << 8) | model_lsb);
return CY_U3P_SUCCESS;
}
return CY_U3P_ERROR_DMA_FAILURE;
}
void SensorInit (void)
{
if (SensorI2cBusTest() != CY_U3P_SUCCESS) /* Verify that the sensor is connected. */
{
CyU3PDebugPrint (4, "Error: Reading Sensor ID failed!\r\n");
return;
}
for (uint16_t i = 0; i < _countof(mode_default); i++)
{
//CyU3PDebugPrint (4, "Reg 0x%x val 0x%x\n", (mode_default + i)->address, (mode_default + i)->val);
sensor_i2c_write((mode_default + i)->address, (mode_default + i)->val);
}
#if LANES == 2
sensor_config = sensor_config_2LANE;
#else
sensor_config = sensor_config_4LANE;
#endif
sensor_configure_mode(&sensor_config_2LANE[5]);
}
uint8_t SensorGetBrightness (void)
{
return selected_img_mode->gain;
}
void SensorSetBrightness (uint8_t input)
{
selected_img_mode->gain = input;
sensor_i2c_write (REG_ANALOG_GAIN, input);
}
uint16_t sensor_get_min_exposure (void)
{
return 0;
}
uint16_t sensor_get_max_exposure (void)
{
return selected_img_mode->integration;
}
uint16_t sensor_get_def_exposure (void)
{
return selected_img_mode->integration;
}
uint16_t sensor_get_exposure (void)
{
return selected_img_mode->integration;
}
void sensor_set_exposure (uint16_t integration)
{
if (integration > selected_img_mode->integration)
{
integration = selected_img_mode->integration;
}
sensor_i2c_write (REG_INTEGRATION_TIME_MSB, (integration >> 8) & 0xFF);
sensor_i2c_write (REG_INTEGRATION_TIME_LSB, integration & 0xFF);
}
uint8_t sensor_get_test_pattern (void)
{
return selected_img_mode->test_pattern;
}
void sensor_set_test_pattern (uint8_t test_pattern)
{
if (test_pattern > 8)
{
test_pattern = 0;
}
selected_img_mode->test_pattern = test_pattern;
sensor_i2c_write (REG_TEST_PATTERN_LSB, test_pattern);
}