openmv/drivers/sensors/pag7920.c
iabdalkader 2958b854ef drivers/sensors: Rename header files.
Old header files are no longer needed.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-06-27 20:17:03 +02:00

673 lines
21 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2023 Lake Fu <lake_fu@pixart.com> for PixArt Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* PAG7920 driver.
*/
#include "omv_boardconfig.h"
#if (OMV_PAG7920_ENABLE == 1)
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include "py/mphal.h"
#include "omv_csi.h"
#include "framebuffer.h"
#include "omv_i2c.h"
#include "pag7920.h"
#include "sensor_config.h"
#define SENSOR_I2C_DEBUG (0)
#define US (1000000) // microsecond
#ifndef M_LN29
#define M_LN29 (3.36729582998647402718)
#endif
// Grayscale/QVGA
const uint16_t default_regs [][2] = {
{0xEF, 0x00},
{0x1D, 0x41},
{0x64, 0x01},
{0x45, 0x44},
{0x12, 0x20},
{0x0C, 0xC8},
{0x11, 0xC8},
{0x42, 0xC8},
{0x43, 0xC8},
{0x44, 0xC8},
{0xAF, 0x31},
{0x69, 0x14},
{0xEF, 0x00},
{0x4C, 0x00},
{0x4D, 0x35},
{0x4E, 0x0C},
{0x4F, 0x00},
{0xEF, 0x02},
{0x02, 0x64},
{0x03, 0x08},
{0xEF, 0x04},
{0x2C, 0xC6},
{0x2D, 0x2D},
{0x2E, 0x00},
{0x30, 0x31},
{0x31, 0x80},
{0x40, 0x1D},
{0x41, 0x00},
{0x42, 0xD0},
{0x43, 0x01},
{0x44, 0xF0},
{0x45, 0x00},
{0x46, 0x00},
{0x47, 0x00},
{0x48, 0xF0},
{0x49, 0x0D},
{0x4A, 0x0C},
{0x4B, 0x00},
{0x51, 0x1D},
{0x52, 0x00},
{0x53, 0xC0},
{0x54, 0xD4},
{0x55, 0x01},
{0x56, 0x00},
{0xEF, 0x01},
{0xC4, 0x02},
{0xC6, 0x40},
{0xC7, 0x01},
{0xC8, 0xF0},
{0xC9, 0x00},
{0xEF, 0x02},
{0x11, 0x03},
{0x19, 0xFC},
{0x1A, 0x00},
{0x21, 0x40},
{0x22, 0x01},
{0x23, 0xF0},
{0x24, 0x00},
{0x27, 0x0C},
{0x28, 0x00},
{0x2D, 0xF0},
{0x2E, 0x00},
{0x56, 0x40},
{0x57, 0x01},
{0x58, 0xFE},
{0x59, 0x00},
{0xEF, 0x01},
{0x33, 0x46},
{0x3B, 0x30},
{0x40, 0x96},
{0xD9, 0x32},
{0xDB, 0x64},
{0xDD, 0x64},
{0xEF, 0x02},
{0xA5, 0x00},
{0xA6, 0x96},
{0xEF, 0x00},
{0x09, 0x10},
{0x18, 0x01},
{0x2F, 0x44},
{0x37, 0x04},
{0x38, 0x06},
{0x3F, 0x01},
{0x55, 0x01},
{0x66, 0x01},
{0xEF, 0x01},
{0x03, 0x00},
{0x04, 0xAB},
{0x07, 0x02},
{0x0A, 0x00},
{0x0B, 0x40},
{0x0F, 0x0B},
{0x11, 0x0A},
{0x13, 0x0C},
{0x16, 0x00},
{0x17, 0xA9},
{0x36, 0x00},
{0x37, 0x02},
{0x4D, 0x02},
{0x56, 0xB8},
{0x57, 0x01},
{0x58, 0xB8},
{0x59, 0x01},
{0x62, 0x00},
{0x63, 0x06},
{0x69, 0x1C},
{0x6A, 0x1D},
{0x6B, 0x68},
{0x6C, 0x67},
{0x76, 0x06},
{0x77, 0x09},
{0x78, 0x02},
{0x79, 0x03},
{0x84, 0x19},
{0x86, 0x14},
{0x87, 0x19},
{0x89, 0x14},
{0x8A, 0x23},
{0x8C, 0x1E},
{0x8D, 0x23},
{0x8F, 0x1E},
{0x9D, 0x0A},
{0xA0, 0x00},
{0xD1, 0xC8},
{0xD2, 0x00},
{0xEF, 0x02},
{0x92, 0x11},
{0x93, 0x01},
{0xC3, 0xC8},
{0xC4, 0x00},
{0xC5, 0xC8},
{0xC6, 0x00},
{0xD1, 0x45},
{0xEF, 0x00},
{0xEB, 0x80},
{0xEF, 0x00},
{0x30, 0x01},
// OpenMV typical end token.
{0x00, 0x00},
};
typedef union {
struct {
uint8_t b0 : 1;
uint8_t b1 : 1;
uint8_t b2 : 1;
uint8_t b3 : 1;
uint8_t b4 : 1;
uint8_t b5 : 1;
uint8_t b6 : 1;
uint8_t b7 : 1;
} bits;
uint8_t byte_val;
} bitplane;
extern uint8_t _line_buf;
static int8_t g_bank_cache = -1;
static bool g_f_hflip = false;
static bool g_f_vflip = false;
static int switch_bank(omv_csi_t *csi, uint8_t bank) {
if (g_bank_cache != bank) {
debug_printf("W Reg: 0x%02X 0x%02X\r\n", REG_BANK, bank);
int res = omv_i2c_writeb(csi->i2c, csi->slv_addr, REG_BANK, bank);
if (res) {
g_bank_cache = -1; // Encountered an unknown error, clear cache.
printf("switch bank failed, res = %d\n", res);
} else {
g_bank_cache = bank;
}
return res;
}
return 0;
}
static int write_reg_w_bank(omv_csi_t *csi, uint8_t bank, uint8_t addr,
uint8_t val) {
if (switch_bank(csi, bank)) {
printf("write_reg failed.\n");
return -1;
}
if (addr == REG_BANK) {
g_bank_cache = -1;
}
debug_printf("W Reg: 0x%02X 0x%02X\r\n", addr, val);
return omv_i2c_writeb(csi->i2c, csi->slv_addr, addr, val);
}
static int read_reg_w_bank(omv_csi_t *csi, uint8_t bank, uint8_t addr,
uint8_t *p_val) {
if (switch_bank(csi, bank)) {
printf("read_reg_w_bank() failed.\n");
return -1;
}
debug_printf("R Reg: 0x%02X\r\n", addr);
return omv_i2c_readb(csi->i2c, csi->slv_addr, addr, p_val);
}
static int init_csi(omv_csi_t *csi) {
write_reg_w_bank(csi, BANK_0, R_GLOBAL_RESET, V_GLOBAL_RESET);
mp_hal_delay_ms(50);
#define ta_seq default_regs
for (int i = 0; ta_seq[i][0]; i++) {
debug_printf("W Reg: 0x%02X 0x%02X\r\n", ta_seq[i][0], ta_seq[i][1]);
int res = omv_i2c_writeb(csi->i2c, csi->slv_addr, ta_seq[i][0],
ta_seq[i][1]);
if (res) {
return res;
}
}
#undef ta_seq
return 0;
}
static int reset(omv_csi_t *csi) {
// Reset internal flag.
g_f_hflip = g_f_vflip = false;
g_bank_cache = -1;
if (init_csi(csi) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
return 0;
}
static int sleep(omv_csi_t *csi, int enable) {
int ret = write_reg_w_bank(csi, 0, TG_En,
enable == 1 ? TG_En_V2 : TG_En_V1);
if (ret) {
printf("sleep() failed.\n");
}
return ret;
}
static int read_reg(omv_csi_t *csi, uint16_t reg_addr) {
uint8_t val = 0;
if (omv_i2c_readb(csi->i2c, csi->slv_addr, (uint8_t) reg_addr, &val)) {
return -1;
}
return val;
}
static int write_reg(omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data) {
int ret;
ret = omv_i2c_writeb(csi->i2c, csi->slv_addr, (uint8_t) reg_addr,
(uint8_t) reg_data);
if (ret == 0 && reg_addr == REG_BANK) {
g_bank_cache = (uint8_t) reg_data;
}
return ret;
}
static int set_pixformat(omv_csi_t *csi, pixformat_t pixformat) {
switch (pixformat) {
case PIXFORMAT_GRAYSCALE:
break;
default:
return -1;
}
return 0;
}
static int set_framesize(omv_csi_t *csi, omv_csi_framesize_t framesize) {
int res = 0;
uint8_t val_R0_15, val_R0_16, val_R0_1C,
val_R0_3E_B2, val_R1_4B_B0,
val_R1_4B_B4, val_R1_70, val_R1_C2,
val_R_VSize, val_R_ABC_2_Start, val_R_WOI_VSize,
val_R_WOI_VStart, val_R_ABC_grad1, val_R2_D9_B0, val_R2_D9_B1,
val_R_AE_Size__div4_X, val_R_AE_Size__div4_Y, val_R009_H;
switch (framesize) {
case OMV_CSI_FRAMESIZE_QVGA:
val_R0_15 = 0;
val_R0_16 = 0;
val_R0_1C = 0;
val_R0_3E_B2 = 0;
val_R1_4B_B0 = 0;
val_R1_4B_B4 = 0;
val_R1_70 = 0;
val_R1_C2 = g_f_vflip ? V_R1C2_Q_V_ON : V_R1C2_Q_V_OFF;
val_R_VSize = 240;
val_R_ABC_2_Start = 252;
val_R_WOI_VSize = 240;
val_R_WOI_VStart = 12;
val_R_ABC_grad1 = 240;
val_R2_D9_B0 = 0;
val_R009_H = g_f_hflip ? V_R009_Q_H_ON : V_R009_Q_H_OFF;
val_R2_D9_B1 = g_f_hflip ? V_R2D9_Q_H_ON : V_R2D9_Q_H_OFF;
val_R_AE_Size__div4_X = 80;
val_R_AE_Size__div4_Y = 60;
break;
case OMV_CSI_FRAMESIZE_QQVGA:
val_R0_15 = 1;
val_R0_16 = 1;
val_R0_1C = 1;
val_R0_3E_B2 = 1;
val_R1_4B_B0 = 1;
val_R1_4B_B4 = 1;
val_R1_70 = 1;
val_R1_C2 = g_f_vflip?V_R1C2_QQ_V_ON:V_R1C2_QQ_V_OFF;
val_R_VSize = 120;
val_R_ABC_2_Start = 129;
val_R_WOI_VSize = 120;
val_R_WOI_VStart = 9;
val_R_ABC_grad1 = 120;
val_R2_D9_B0 = 1;
val_R009_H = g_f_hflip ? V_R009_QQ_H_ON : V_R009_QQ_H_OFF;
val_R2_D9_B1 = 0;
val_R_AE_Size__div4_X = 40;
val_R_AE_Size__div4_Y = 30;
break;
default:
return -1;
}
bitplane tmp = {.byte_val = 0};
res |= read_reg_w_bank(csi, BANK_0, R0_15, &tmp.byte_val);
tmp.bits.b5 = val_R0_15;
res |= write_reg_w_bank(csi, BANK_0, R0_15, tmp.byte_val);
res |= read_reg_w_bank(csi, BANK_0, R0_16, &tmp.byte_val);
tmp.bits.b0 = val_R0_16;
res |= write_reg_w_bank(csi, BANK_0, R0_16, tmp.byte_val);
res |= read_reg_w_bank(csi, BANK_0, R0_1C, &tmp.byte_val);
tmp.bits.b1 = val_R0_1C;
res |= write_reg_w_bank(csi, BANK_0, R0_1C, tmp.byte_val);
res |= read_reg_w_bank(csi, BANK_0, R0_3E_B2, &tmp.byte_val);
tmp.bits.b2 = val_R0_3E_B2;
//tmp.bits.b5 = val_R0_3E_B5;
res |= write_reg_w_bank(csi, BANK_0, R0_3E_B2, tmp.byte_val);
res |= write_reg_w_bank(csi, BANK_0, R0_09_B1, val_R009_H);
res |= read_reg_w_bank(csi, BANK_1, R1_4B_B0, &tmp.byte_val);
tmp.bits.b0 = val_R1_4B_B0;
tmp.bits.b4 = val_R1_4B_B4 & 0x01;
tmp.bits.b5 = (val_R1_4B_B4 >> 1) & 0x01;
res |= write_reg_w_bank(csi, BANK_1, R1_4B_B0, tmp.byte_val);
res |= read_reg_w_bank(csi, BANK_1, R1_70, &tmp.byte_val);
tmp.bits.b4 = val_R1_70;
res |= write_reg_w_bank(csi, BANK_1, R1_70, tmp.byte_val);
tmp.byte_val = val_R1_C2;
res |= write_reg_w_bank(csi, BANK_1, R1_C2, tmp.byte_val);
tmp.byte_val = val_R_VSize;
res |= write_reg_w_bank(csi, BANK_1, R1_C8, tmp.byte_val);
tmp.byte_val = val_R_ABC_2_Start;
res |= write_reg_w_bank(csi, BANK_2, R2_19, tmp.byte_val);
tmp.byte_val = val_R_WOI_VSize;
res |= write_reg_w_bank(csi, BANK_2, R2_23, tmp.byte_val);
tmp.byte_val = val_R_WOI_VStart;
res |= write_reg_w_bank(csi, BANK_2, R2_27, tmp.byte_val);
tmp.byte_val = val_R_ABC_grad1;
res |= write_reg_w_bank(csi, BANK_2, R2_2D, tmp.byte_val);
res |= read_reg_w_bank(csi, BANK_2, R2_D9_B0, &tmp.byte_val);
tmp.bits.b0 = val_R2_D9_B0;
tmp.bits.b1 = val_R2_D9_B1;
res |= write_reg_w_bank(csi, BANK_2, R2_D9_B0, tmp.byte_val);
res |=
write_reg_w_bank(csi, BANK_4, R_AE_Size__div4_X, val_R_AE_Size__div4_X);
res |=
write_reg_w_bank(csi, BANK_4, R_AE_Size__div4_Y, val_R_AE_Size__div4_Y);
res |= write_reg_w_bank(csi, BANK_0, R_UPDATE_FLAG, V_UPDATE_VALUE);
res |= write_reg_w_bank(csi, BANK_0, TG_En, 1);
return res;
}
static int set_gainceiling(omv_csi_t *csi, omv_csi_gainceiling_t gainceiling) {
return 0;
}
static int set_auto_gain(omv_csi_t *csi, int enable, float gain_db,
float gain_db_ceiling) {
static const uint32_t digital = (OMV_PAG7920_CLK_FREQ / US) / 2;
uint32_t frame_time;
read_reg_w_bank(csi, BANK_0, R_Frame_Time_0, (uint8_t *) &frame_time);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_1, (uint8_t *) (&frame_time) + 1);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_2, (uint8_t *) (&frame_time) + 2);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_3, (uint8_t *) (&frame_time) + 3);
uint16_t gain_code;
if (!enable && (!isnanf(gain_db)) && (!isinff(gain_db))) {
float exponent = gain_db / 20.0f;
gain_code = IM_MIN(fast_roundf(expf(M_LN29 + (exponent * M_LN10))), 464);
write_reg_w_bank(csi, BANK_4, R_AE_Gain_manual_L,
*((uint8_t *) &gain_code));
write_reg_w_bank(csi, BANK_4, R_AE_Gain_manual_H,
*((uint8_t *) &gain_code + 1));
write_reg_w_bank(csi, BANK_0, R_UPDATE_FLAG, V_UPDATE_VALUE);
// AE disable.
write_reg_w_bank(csi, BANK_4, R_AE_EnH, 0x32);
// Delay a frame time.
mp_hal_delay_ms((frame_time / digital) / 1000);
write_reg_w_bank(csi, BANK_4, R_AE_MinGain_L, *((uint8_t *) &gain_code));
write_reg_w_bank(csi, BANK_4, R_AE_MinGain_H, *((uint8_t *) &gain_code + 1));
write_reg_w_bank(csi, BANK_4, R_AE_MaxGain_L, *((uint8_t *) &gain_code));
write_reg_w_bank(csi, BANK_4, R_AE_MaxGain_H, *((uint8_t *) &gain_code + 1));
} else if (enable && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) {
float exponent = gain_db_ceiling / 20.0f;
gain_code = IM_MIN(fast_roundf(expf(M_LN29 + (exponent * M_LN10))), 464);
// Min gain code = 29
write_reg_w_bank(csi, BANK_4, R_AE_MinGain_L, 0x1D);
write_reg_w_bank(csi, BANK_4, R_AE_MinGain_H, 0);
write_reg_w_bank(csi, BANK_4, R_AE_MaxGain_L, *((uint8_t *) &gain_code));
write_reg_w_bank(csi, BANK_4, R_AE_MaxGain_H, *((uint8_t *) &gain_code + 1));
}
// AE enable.
write_reg_w_bank(csi, BANK_4, R_AE_EnH, 0x31);
return 0;
}
static int get_gain_db(omv_csi_t *csi, float *gain_db) {
uint16_t gain_code;
read_reg_w_bank(csi, BANK_4, AE_Total_Gain_L, (uint8_t *) &gain_code);
read_reg_w_bank(csi, BANK_4, AE_Total_Gain_H, (uint8_t *) &gain_code + 1);
gain_code &= 0x07ff;
*gain_db = 20.0f * log10f(gain_code / 29.0f);
return 0;
}
static int set_auto_exposure(omv_csi_t *csi, int enable, int expo_us) {
const uint32_t digital = (OMV_PAG7920_CLK_FREQ / US) / 2;
uint32_t frame_time, expo_max_us, expo_min_us;
read_reg_w_bank(csi, BANK_0, R_Frame_Time_0, (uint8_t *) &frame_time);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_1, (uint8_t *) (&frame_time) + 1);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_2, (uint8_t *) (&frame_time) + 2);
read_reg_w_bank(csi, BANK_0, R_Frame_Time_3, (uint8_t *) (&frame_time) + 3);
uint32_t expo_max_factor = (frame_time - 10000);
const uint32_t expo_min_factor = 240;
expo_max_us = expo_max_factor / digital;
expo_min_us = expo_min_factor / digital;
if (!enable) {
uint32_t expo_manual_factor;
if (expo_us > expo_max_us) {
expo_manual_factor = expo_max_factor;
} else if (expo_us < expo_min_us) {
expo_manual_factor = expo_min_factor;
} else {
expo_manual_factor = expo_us * digital;
}
write_reg_w_bank(csi, BANK_4, R_AE_Expo_manual_0,
*((uint8_t *) &expo_manual_factor));
write_reg_w_bank(csi, BANK_4, R_AE_Expo_manual_1,
*((uint8_t *) &expo_manual_factor + 1));
write_reg_w_bank(csi, BANK_4, R_AE_Expo_manual_2,
*((uint8_t *) &expo_manual_factor + 2));
write_reg_w_bank(csi, BANK_4, R_AE_Expo_manual_3,
*((uint8_t *) &expo_manual_factor + 3));
write_reg_w_bank(csi, BANK_0, R_UPDATE_FLAG, V_UPDATE_VALUE);
// AE disable.
write_reg_w_bank(csi, BANK_4, R_AE_EnH, 0x32);
// Delay a frame time.
mp_hal_delay_ms(((frame_time / digital) / 1000));
// Bundle AE upbound and lowbound.
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_0, *((uint8_t *) &expo_manual_factor));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_1,
*((uint8_t *) &expo_manual_factor + 1));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_2,
*((uint8_t *) &expo_manual_factor + 2));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_3,
*((uint8_t *) &expo_manual_factor + 3));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_0, *((uint8_t *) &expo_manual_factor));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_1,
*((uint8_t *) &expo_manual_factor + 1));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_2,
*((uint8_t *) &expo_manual_factor + 2));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_3,
*((uint8_t *) &expo_manual_factor + 3));
// AE enable.
write_reg_w_bank(csi, BANK_4, R_AE_EnH, 0x31);
} else {
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_0, *((uint8_t *) &expo_min_factor));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_1,
*((uint8_t *) &expo_min_factor + 1));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_2,
*((uint8_t *) &expo_min_factor + 2));
write_reg_w_bank(csi, BANK_4, R_AE_MinExpo_3,
*((uint8_t *) &expo_min_factor + 3));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_0, *((uint8_t *) &expo_max_factor));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_1,
*((uint8_t *) &expo_max_factor + 1));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_2,
*((uint8_t *) &expo_max_factor + 2));
write_reg_w_bank(csi, BANK_4, R_AE_MaxExpo_3,
*((uint8_t *) &expo_max_factor + 3));
}
return 0;
}
static int get_exposure_us(omv_csi_t *csi, int *exposure_us) {
static const uint32_t digital = (OMV_PAG7920_CLK_FREQ / US) / 2;
uint32_t clk_num;
read_reg_w_bank(csi, BANK_4, Reg_ExpPxclkNum_0, (uint8_t *) &clk_num);
read_reg_w_bank(csi, BANK_4, Reg_ExpPxclkNum_1, (uint8_t *) (&clk_num) + 1);
read_reg_w_bank(csi, BANK_4, Reg_ExpPxclkNum_2, (uint8_t *) (&clk_num) + 2);
read_reg_w_bank(csi, BANK_4, Reg_ExpPxclkNum_3, (uint8_t *) (&clk_num) + 3);
clk_num &= 0x0fffffff;
debug_printf("Reg_ExpPxclkNum = %ld\n", clk_num);
*exposure_us = (int) clk_num / digital;
return 0;
}
static int set_auto_whitebal(omv_csi_t *csi, int enable, float r_gain_db,
float g_gain_db, float b_gain_db) {
return 0;
}
static int get_rgb_gain_db(omv_csi_t *csi, float *r_gain_db, float *g_gain_db,
float *b_gain_db) {
return 0;
}
static int set_hmirror(omv_csi_t *csi, int enable) {
int res = 0;
switch (csi->framesize) {
case OMV_CSI_FRAMESIZE_QVGA:
res |= write_reg_w_bank(csi, BANK_0, R0_09_B1,
enable?V_R009_Q_H_ON:V_R009_Q_H_OFF);
res |= write_reg_w_bank(csi, BANK_2, R2_D9_B1,
enable?V_R2D9_Q_H_ON:V_R2D9_Q_H_OFF);
break;
case OMV_CSI_FRAMESIZE_QQVGA:
res |= write_reg_w_bank(csi, BANK_0, R0_09_B1,
enable?V_R009_QQ_H_ON:V_R009_QQ_H_OFF);
break;
default:
return -1;
}
res |= write_reg_w_bank(csi, BANK_0, R_UPDATE_FLAG, V_UPDATE_VALUE);
res |= write_reg_w_bank(csi, BANK_0, TG_En, 1);
if (res == 0) {
g_f_hflip = enable ? true : false;
}
return res;
}
static int set_vflip(omv_csi_t *csi, int enable) {
int res = 0;
switch (csi->framesize) {
case OMV_CSI_FRAMESIZE_QVGA:
res |= write_reg_w_bank(csi, BANK_1, R1_C2,
enable?V_R1C2_Q_V_ON:V_R1C2_Q_V_OFF);
break;
case OMV_CSI_FRAMESIZE_QQVGA:
res |= write_reg_w_bank(csi, BANK_1, R1_C2,
enable?V_R1C2_QQ_V_ON:V_R1C2_QQ_V_OFF);
break;
default:
return -1;
}
res |= write_reg_w_bank(csi, BANK_1, R1_CB, enable?0xF3:0x04);
bitplane tmp = {.byte_val = 0};
res |= read_reg_w_bank(csi, BANK_1, R1_CE_B2, &tmp.byte_val);
tmp.bits.b2 = enable ? 1 : 0;
res |= write_reg_w_bank(csi, BANK_1, R1_CE_B2, tmp.byte_val);
res |= write_reg_w_bank(csi, BANK_0, R_UPDATE_FLAG, V_UPDATE_VALUE);
res |= write_reg_w_bank(csi, BANK_0, TG_En, 1);
if (res == 0) {
g_f_vflip = enable ? true : false;
}
return res;
}
int pag7920_init(omv_csi_t *csi) {
// Initialize csi structure.
csi->reset = reset;
csi->sleep = sleep;
csi->read_reg = read_reg;
csi->write_reg = write_reg;
csi->set_pixformat = set_pixformat;
csi->set_framesize = set_framesize;
csi->set_gainceiling = set_gainceiling;
csi->set_auto_gain = set_auto_gain;
csi->get_gain_db = get_gain_db;
csi->set_auto_exposure = set_auto_exposure;
csi->get_exposure_us = get_exposure_us;
csi->set_auto_whitebal = set_auto_whitebal;
csi->get_rgb_gain_db = get_rgb_gain_db;
csi->set_hmirror = set_hmirror;
csi->set_vflip = set_vflip;
// Set csi flags
csi->vsync_pol = 0;
csi->hsync_pol = 0;
csi->pixck_pol = 1;
csi->frame_sync = 1;
csi->mono_bpp = 1;
if (init_csi(csi) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
return 0;
}
#endif