mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Support PixArt image sensor PAJ6100 (#1365)
* Support PixArt image sensor PAJ6100
This commit is contained in:
parent
42245a75cc
commit
15264b7dac
@ -57,6 +57,7 @@ WINC1500_DIR=drivers/winc1500
|
||||
MLX90621_DIR=drivers/mlx90621
|
||||
MLX90640_DIR=drivers/mlx90640
|
||||
MLX90641_DIR=drivers/mlx90641
|
||||
PIXART_DIR=drivers/pixart
|
||||
LIBPDM_DIR=lib/libpdm
|
||||
TENSORFLOW_DIR=lib/libtf
|
||||
OMV_BOARD_CONFIG_DIR=$(TOP_DIR)/$(OMV_DIR)/boards/$(TARGET)/
|
||||
|
||||
41
src/drivers/pixart/Makefile
Normal file
41
src/drivers/pixart/Makefile
Normal file
@ -0,0 +1,41 @@
|
||||
###
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021 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.
|
||||
###
|
||||
|
||||
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)
|
||||
9
src/drivers/pixart/include/pixspi.h
Normal file
9
src/drivers/pixart/include/pixspi.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __PIXSPI__
|
||||
#define __PIXSPI__
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
bool pixspi_init();
|
||||
void pixspi_release();
|
||||
int pixspi_regs_read(uint8_t addr, uint8_t * data, uint16_t length);
|
||||
int pixspi_regs_write(uint8_t addr, const uint8_t * data, uint16_t length);
|
||||
#endif
|
||||
145
src/drivers/pixart/src/pixspi.c
Normal file
145
src/drivers/pixart/src/pixspi.c
Normal file
@ -0,0 +1,145 @@
|
||||
#include "pixspi.h"
|
||||
#include <stdbool.h>
|
||||
#include STM32_HAL_H
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "omv_boardconfig.h"
|
||||
#include <py/mpconfig.h>
|
||||
|
||||
#ifdef ISC_SPI
|
||||
#define SPI_TIMEOUT (5000) /* in ms */
|
||||
|
||||
#define CS_PORT ISC_SPI_SSEL_PORT
|
||||
#define CS_PIN ISC_SPI_SSEL_PIN
|
||||
#define W_CS_LOW() HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET)
|
||||
#define W_CS_HIGH() HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET)
|
||||
|
||||
extern SPI_HandleTypeDef ISC_SPIHandle;
|
||||
|
||||
static bool spi_send(uint8_t *data, uint16_t len)
|
||||
{
|
||||
HAL_StatusTypeDef status = HAL_OK;
|
||||
W_CS_LOW();
|
||||
//mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
status = HAL_SPI_Transmit(
|
||||
&ISC_SPIHandle, data, len, SPI_TIMEOUT);
|
||||
//MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
W_CS_HIGH();
|
||||
|
||||
return (status == HAL_OK);
|
||||
}
|
||||
|
||||
static bool spi_send_recv(uint8_t *txData, uint8_t *rxData, uint16_t len)
|
||||
{
|
||||
W_CS_LOW();
|
||||
//mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
bool res = (HAL_SPI_Transmit(&ISC_SPIHandle, txData, 1, SPI_TIMEOUT) == HAL_OK);
|
||||
res = (HAL_SPI_Receive(&ISC_SPIHandle, rxData, len, SPI_TIMEOUT) == HAL_OK);
|
||||
//MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
W_CS_HIGH();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool pixspi_init()
|
||||
{
|
||||
// Init SPI
|
||||
memset(&ISC_SPIHandle, 0, sizeof(ISC_SPIHandle));
|
||||
ISC_SPIHandle.Instance = ISC_SPI;
|
||||
ISC_SPIHandle.Init.Mode = SPI_MODE_MASTER;
|
||||
ISC_SPIHandle.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
ISC_SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
ISC_SPIHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
||||
ISC_SPIHandle.Init.CLKPhase = SPI_PHASE_2EDGE;
|
||||
ISC_SPIHandle.Init.NSS = SPI_NSS_SOFT;
|
||||
ISC_SPIHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32 ;
|
||||
ISC_SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
ISC_SPIHandle.Init.TIMode = SPI_TIMODE_DISABLED;
|
||||
ISC_SPIHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
|
||||
ISC_SPIHandle.Init.CRCPolynomial = 0;
|
||||
|
||||
if (HAL_SPI_Init(&ISC_SPIHandle) != HAL_OK)
|
||||
{
|
||||
/* Initialization Error */
|
||||
return false;
|
||||
}
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
// Re-Init GPIO for SPI SS(CS) pin, software control.
|
||||
GPIO_InitStructure.Pin = ISC_SPI_SSEL_PIN;
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStructure.Pull = GPIO_PULLUP;
|
||||
GPIO_InitStructure.Alternate = ISC_SPI_SSEL_AF;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
HAL_GPIO_Init(ISC_SPI_SSEL_PORT, &GPIO_InitStructure);
|
||||
|
||||
// Default high.
|
||||
W_CS_HIGH();
|
||||
return true;
|
||||
}
|
||||
|
||||
void pixspi_release()
|
||||
{
|
||||
W_CS_LOW();
|
||||
HAL_SPI_DeInit(&ISC_SPIHandle);
|
||||
}
|
||||
|
||||
int pixspi_regs_read(uint8_t addr, uint8_t * data, uint16_t length)
|
||||
{
|
||||
if ((addr & 0x80))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("pixspi_regs_read() address (0x%x) overflow.\n", addr);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
addr |= 0x80;
|
||||
if (!spi_send_recv(&addr, data, length))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("spi_send_recv() failed.\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pixspi_regs_write(uint8_t addr, const uint8_t * data, uint16_t length)
|
||||
{
|
||||
uint8_t buff[64] = {};
|
||||
if ((addr & 0x80) == 0x80)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("pixspi_regs_read() address (0x%x) overflow.\n", addr);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
int32_t remaining = length;
|
||||
|
||||
const static uint16_t MAX_LENGTH = 255;
|
||||
do
|
||||
{
|
||||
uint16_t len = remaining>MAX_LENGTH?MAX_LENGTH:remaining;
|
||||
buff[0] = addr;
|
||||
memcpy(buff+1, data, len);
|
||||
bool res = spi_send(buff, len + 1);
|
||||
if (!res)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("spi_send() failed.\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
remaining = remaining - MAX_LENGTH;
|
||||
}
|
||||
while (remaining > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
bool pixspi_init() { return false; }
|
||||
void pixspi_release() {}
|
||||
int pixspi_regs_read(uint8_t addr, uint8_t * data, uint16_t length) { return -1; }
|
||||
int pixspi_regs_write(uint8_t addr, const uint8_t * data, uint16_t length) { return -1; }
|
||||
#endif
|
||||
@ -37,6 +37,7 @@ SRCS += $(addprefix sensors/, \
|
||||
lepton.c \
|
||||
hm01b0.c \
|
||||
gc2145.c \
|
||||
paj6100.c \
|
||||
)
|
||||
|
||||
SRCS += $(addprefix modules/, \
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (0)
|
||||
#define OMV_ENABLE_LEPTON (0)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (0)
|
||||
|
||||
// Set which OV767x sensor is used
|
||||
#define OMV_OV7670_VERSION (75)
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (0)
|
||||
#define OMV_ENABLE_LEPTON (0)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (0)
|
||||
|
||||
// Enable sensor features
|
||||
#define OMV_ENABLE_OV5640_AF (0)
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (0)
|
||||
#define OMV_ENABLE_LEPTON (0)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (0)
|
||||
|
||||
// Enable sensor features
|
||||
#define OMV_ENABLE_OV5640_AF (0)
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (1)
|
||||
#define OMV_ENABLE_LEPTON (1)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (1)
|
||||
|
||||
// Enable sensor features
|
||||
#define OMV_ENABLE_OV5640_AF (0)
|
||||
|
||||
@ -66,6 +66,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (1)
|
||||
#define OMV_ENABLE_LEPTON (1)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (1)
|
||||
|
||||
// Enable sensor features
|
||||
#define OMV_ENABLE_OV5640_AF (0)
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
#define OMV_ENABLE_MT9V034 (0)
|
||||
#define OMV_ENABLE_LEPTON (0)
|
||||
#define OMV_ENABLE_HM01B0 (0)
|
||||
#define OMV_ENABLE_PAJ6100 (0)
|
||||
|
||||
// Enable sensor features
|
||||
#define OMV_ENABLE_OV5640_AF (1)
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
#define LEPTON_ID (0x54)
|
||||
#define HM01B0_ID (0xB0)
|
||||
#define GC2145_ID (0x21)
|
||||
// Wide ID
|
||||
#define PAJ6100_ID (0x6100)
|
||||
|
||||
typedef enum {
|
||||
FRAMESIZE_INVALID = 0,
|
||||
|
||||
@ -44,6 +44,7 @@ OMV_CFLAGS += -I$(TOP_DIR)/$(WINC1500_DIR)/include/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90621_DIR)/include/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90640_DIR)/include/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90641_DIR)/include/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(PIXART_DIR)/include/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(TENSORFLOW_DIR)/$(CPU)/
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(LIBPDM_DIR)/
|
||||
|
||||
@ -113,6 +114,7 @@ endif
|
||||
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o)
|
||||
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o)
|
||||
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o)
|
||||
FIRM_OBJ += $(wildcard $(BUILD)/$(PIXART_DIR)/src/*.o)
|
||||
|
||||
#------------- OpenMV Objects ----------------#
|
||||
FIRM_OBJ += $(addprefix $(BUILD)/$(CMSIS_DIR)/src/, \
|
||||
@ -150,6 +152,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \
|
||||
lepton.o \
|
||||
hm01b0.o \
|
||||
gc2145.o \
|
||||
paj6100.o \
|
||||
)
|
||||
|
||||
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/modules/, \
|
||||
@ -514,6 +517,7 @@ UVC_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \
|
||||
lepton.o \
|
||||
hm01b0.o \
|
||||
gc2145.o \
|
||||
paj6100.o \
|
||||
)
|
||||
|
||||
UVC_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/,\
|
||||
@ -541,6 +545,7 @@ endif
|
||||
UVC_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o)
|
||||
UVC_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o)
|
||||
UVC_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o)
|
||||
UVC_OBJ += $(wildcard $(BUILD)/$(PIXART_DIR)/src/*.o)
|
||||
endif
|
||||
|
||||
ifeq ($(OMV_ENABLE_CM4), 1)
|
||||
@ -600,6 +605,7 @@ endif
|
||||
$(MAKE) -C $(MLX90621_DIR) BUILD=$(BUILD)/$(MLX90621_DIR) CFLAGS="$(CFLAGS) -MMD"
|
||||
$(MAKE) -C $(MLX90640_DIR) BUILD=$(BUILD)/$(MLX90640_DIR) CFLAGS="$(CFLAGS) -MMD"
|
||||
$(MAKE) -C $(MLX90641_DIR) BUILD=$(BUILD)/$(MLX90641_DIR) CFLAGS="$(CFLAGS) -MMD"
|
||||
$(MAKE) -C $(PIXART_DIR) BUILD=$(BUILD)/$(PIXART_DIR) CFLAGS="$(CFLAGS) -MMD"
|
||||
$(MAKE) -C $(OMV_DIR) BUILD=$(BUILD)/$(OMV_DIR) CFLAGS="$(CFLAGS) -MMD"
|
||||
ifeq ($(CUBEAI), 1)
|
||||
$(MAKE) -C $(CUBEAI_DIR) BUILD=$(BUILD)/$(CUBEAI_DIR) CFLAGS="$(CFLAGS) -fno-strict-aliasing -MMD"
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "mt9m114.h"
|
||||
#include "lepton.h"
|
||||
#include "hm01b0.h"
|
||||
#include "paj6100.h"
|
||||
#include "gc2145.h"
|
||||
#include "systick.h"
|
||||
#include "framebuffer.h"
|
||||
@ -42,6 +43,10 @@
|
||||
#define OMV_ENABLE_SENSOR_MDMA_TOTAL_OFFLOAD 1
|
||||
#endif
|
||||
|
||||
#if (OMV_ENABLE_PAJ6100 == 1)
|
||||
#define OMV_ENABLE_NONI2CIS
|
||||
#endif
|
||||
|
||||
sensor_t sensor = {};
|
||||
static TIM_HandleTypeDef TIMHandle = {.Instance = DCMI_TIM};
|
||||
static DMA_HandleTypeDef DMAHandle = {.Instance = DMA2_Stream1};
|
||||
@ -386,20 +391,35 @@ int sensor_init()
|
||||
systick_sleep(10);
|
||||
|
||||
sensor.slv_addr = cambus_scan(&sensor.bus);
|
||||
#ifndef OMV_ENABLE_NONI2CIS
|
||||
if (sensor.slv_addr == 0) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear sensor chip ID.
|
||||
sensor.chip_id = 0;
|
||||
sensor.chip_id_w = 0;
|
||||
|
||||
// Set default snapshot function.
|
||||
sensor.snapshot = sensor_snapshot;
|
||||
|
||||
switch (sensor.slv_addr) {
|
||||
#if (OMV_ENABLE_PAJ6100 == 1)
|
||||
case 0:
|
||||
if (paj6100_detect(&sensor)) {
|
||||
// Found PixArt PAJ6100
|
||||
sensor.chip_id_w = PAJ6100_ID;
|
||||
sensor.pwdn_pol = ACTIVE_LOW;
|
||||
sensor.reset_pol = ACTIVE_LOW;
|
||||
break;
|
||||
}
|
||||
// Okay, there is not any sensor be detected.
|
||||
return -2;
|
||||
#endif
|
||||
|
||||
#if (OMV_ENABLE_OV2640 == 1)
|
||||
case OV2640_SLV_ADDR: // Or OV9650.
|
||||
cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id);
|
||||
@ -452,8 +472,7 @@ int sensor_init()
|
||||
return -3;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sensor.chip_id) {
|
||||
switch (sensor.chip_id_w) {
|
||||
#if (OMV_ENABLE_OV2640 == 1)
|
||||
case OV2640_ID:
|
||||
if (extclk_config(OV2640_XCLK_FREQ) != 0) {
|
||||
@ -544,6 +563,15 @@ int sensor_init()
|
||||
break;
|
||||
#endif //(OMV_ENABLE_GC2145 == 1)
|
||||
|
||||
#if (OMV_ENABLE_PAJ6100 == 1)
|
||||
case PAJ6100_ID:
|
||||
if (extclk_config(PAJ6100_XCLK_FREQ) != 0) {
|
||||
return -3;
|
||||
}
|
||||
init_ret = paj6100_init(&sensor);
|
||||
break;
|
||||
#endif // (OMV_ENABLE_PAJ6100 == 1)
|
||||
|
||||
default:
|
||||
return -3;
|
||||
break;
|
||||
@ -639,7 +667,7 @@ int sensor_reset()
|
||||
|
||||
int sensor_get_id()
|
||||
{
|
||||
return sensor.chip_id;
|
||||
return sensor.chip_id_w;
|
||||
}
|
||||
|
||||
bool sensor_is_detected()
|
||||
|
||||
788
src/omv/sensors/paj6100.c
Normal file
788
src/omv/sensors/paj6100.c
Normal file
@ -0,0 +1,788 @@
|
||||
/*
|
||||
* File: paj6100.c
|
||||
* Created Date: Tuesday, May 25th 2021, 10:45:35 am
|
||||
* Author: Lake Fu
|
||||
* -----
|
||||
* Last Modified: Tuesday June 15th 2021 2:54:43 pm
|
||||
* Modified By: Lake Fu at <lake_fu@pixart.com>
|
||||
* -----
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 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.
|
||||
* -----
|
||||
* HISTORY:
|
||||
* Date By Comments
|
||||
* ---------- --- ----------------------------------------------------------
|
||||
*/
|
||||
#include "omv_boardconfig.h"
|
||||
#if (OMV_ENABLE_PAJ6100 == 1)
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "systick.h"
|
||||
#include "sensor.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
#include "paj6100.h"
|
||||
#include "paj6100_reg.h"
|
||||
#include "pixspi.h"
|
||||
|
||||
#define CACHE_BANK
|
||||
|
||||
#ifdef DEBUG
|
||||
// For dump initial result before connect to IDE,
|
||||
// we use a global variable to store it.
|
||||
static int8_t init_res;
|
||||
#endif
|
||||
|
||||
static int16_t bank_cache = -1;
|
||||
|
||||
// Exposure time related paramaters +
|
||||
#define QVGA_MAX_EXPO_PA 85161
|
||||
#define QQVGA_MAX_EXPO_PA 25497
|
||||
|
||||
#define R_AE_MinGain 1
|
||||
#define R_AE_MaxGain 8
|
||||
|
||||
static float R_FrameTime = 6000000/30;
|
||||
static long R_AE_MaxExpoTime;
|
||||
static long R_AE_MinExpoTime = 120/6 + 0.5f;
|
||||
|
||||
#define CAL_MAX_EXPO_TIME(PA) (fast_roundf(((float)(R_FrameTime-PA))/6))
|
||||
|
||||
#define L_TARGET 127
|
||||
#define AE_LOCK_RANGE_IN 8
|
||||
#define AE_LOCK_RANGE_OUT 16
|
||||
|
||||
static uint32_t l_total = 0;
|
||||
static uint32_t l_target_total = 0;
|
||||
static uint32_t lt_lockrange_in_ubound, lt_lockrange_in_lbound;
|
||||
static uint32_t lt_lockrange_out_ubound, lt_lockrange_out_lbound;
|
||||
static bool ae_converged_flag = false;
|
||||
|
||||
static uint8_t skip_frame = 0;
|
||||
|
||||
static bool is_ae_enabled = true;
|
||||
static int exp_us_cache = -1;
|
||||
// Exposure time related paramaters -
|
||||
|
||||
static int set_auto_gain(sensor_t *sensor, int enable,
|
||||
float gain_db, float gain_db_ceiling);
|
||||
static int get_gain_db(sensor_t *sensor, float *gain_db);
|
||||
|
||||
static int bank_switch(uint8_t bank)
|
||||
{
|
||||
#ifdef CACHE_BANK
|
||||
if (bank_cache == bank)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (pixspi_regs_write(REG_BANK_SWITCH, &bank, 1))
|
||||
{
|
||||
printf("Bank switch failed.\n");
|
||||
return -1;
|
||||
}
|
||||
bank_cache = bank;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_regs_w_bank(uint8_t bank, uint8_t addr, uint8_t * buff, uint16_t len)
|
||||
{
|
||||
if (bank_switch(bank)) return -1;
|
||||
return pixspi_regs_read(addr, buff, len);
|
||||
}
|
||||
|
||||
static int write_regs_w_bank(uint8_t bank, uint8_t addr, uint8_t * buff, uint16_t len)
|
||||
{
|
||||
if (bank_switch(bank)) return -1;
|
||||
return pixspi_regs_write(addr, buff, len);
|
||||
}
|
||||
|
||||
static int init_sensor(sensor_t * sensor)
|
||||
{
|
||||
#define ta_seq low_active_R_ABC_Avg_UBx50_T_BLACINV_EnHx1_T_SIG_REFx1
|
||||
|
||||
int arr_size = sizeof(ta_seq)/sizeof(ta_seq[0]);
|
||||
for (int i=0; i<arr_size; i+=2)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
int
|
||||
#endif
|
||||
init_res = pixspi_regs_write(ta_seq[i], &ta_seq[i+1], 1);
|
||||
if (init_res)
|
||||
{
|
||||
return init_res;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear bank cache.
|
||||
bank_cache = -1;
|
||||
return 0;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static int set_exposure(sensor_t *sensor, int exp_us, bool protected)
|
||||
{
|
||||
// 3642T
|
||||
static const uint32_t EXPOSURE_BASE = 3642 / (PAJ6100_XCLK_FREQ/1000000.0f) + 0.5f;
|
||||
int ret;
|
||||
uint32_t cmd_expo /* 24-bit available */;
|
||||
uint16_t exp_offset;
|
||||
|
||||
#ifdef DEBUG_AE
|
||||
printf("set_exposure() = %d\n", exp_us);
|
||||
#endif
|
||||
if (exp_us == 0)
|
||||
return 0;
|
||||
|
||||
if (exp_us >= EXPOSURE_BASE)
|
||||
{
|
||||
int param;
|
||||
if (sensor->framesize == FRAMESIZE_QVGA)
|
||||
param = 88803;
|
||||
else if (sensor->framesize == FRAMESIZE_QQVGA)
|
||||
param = 29139; // QQVGA
|
||||
else
|
||||
param = 88803;
|
||||
|
||||
int max_cmd_expo = R_FrameTime - param;
|
||||
cmd_expo = (exp_us - EXPOSURE_BASE) * ((float)PAJ6100_XCLK_FREQ/1000000) + 0.5f;
|
||||
if (protected)
|
||||
{
|
||||
if (cmd_expo > max_cmd_expo)
|
||||
{
|
||||
cmd_expo = max_cmd_expo;
|
||||
exp_us = (cmd_expo + 3642) / ((float)PAJ6100_XCLK_FREQ/1000000) + 0.5f;
|
||||
printf("Exposure time overflow, reset to %dus.\n", exp_us);
|
||||
}
|
||||
}
|
||||
exp_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd_expo = 0;
|
||||
exp_offset = (EXPOSURE_BASE - exp_us) * ((float)PAJ6100_XCLK_FREQ/1000000) + 0.5;
|
||||
}
|
||||
#ifdef DEBUG_AE
|
||||
printf("target - cmd_exp: %ld, exp_offset: %d\n", cmd_expo, exp_offset);
|
||||
#endif
|
||||
uint8_t buff[3] = {
|
||||
(uint8_t)(cmd_expo & 0xff),
|
||||
(uint8_t)((cmd_expo >> 8) & 0xff),
|
||||
(uint8_t)((cmd_expo >> 16) & 0xff),
|
||||
};
|
||||
ret = write_regs_w_bank(BANK_0, REG_CMD_EXPO_L, buff, 3);
|
||||
|
||||
buff[0] = (uint8_t)(exp_offset & 0xff);
|
||||
buff[1] = (uint8_t)((exp_offset >> 8) & 0xff);
|
||||
ret |= write_regs_w_bank(BANK_0, REG_EXP_OFFSET_L, buff, 2);
|
||||
buff[0] = 1;
|
||||
ret |= write_regs_w_bank(BANK_1, REG_ISP_UPDATE, buff, 1);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
printf("Failed to write cmd_expo or exp_offset.\n");
|
||||
return -1;
|
||||
}
|
||||
exp_us_cache = exp_us;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_exposure(sensor_t *sensor)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buff[3] = {};
|
||||
uint32_t cmd_expo /* 24-bit available */;
|
||||
uint16_t exp_offset;
|
||||
|
||||
if (exp_us_cache >= 0)
|
||||
return exp_us_cache;
|
||||
|
||||
ret = read_regs_w_bank(BANK_0, REG_CMD_EXPO_L, buff, 3);
|
||||
if (ret)
|
||||
{
|
||||
printf("Read cmd_expo failed.\n");
|
||||
return -1;
|
||||
}
|
||||
cmd_expo = buff[0] + (((uint32_t)buff[1]) << 8) + (((uint32_t)buff[2]) << 16);
|
||||
|
||||
ret = read_regs_w_bank(BANK_0, REG_EXP_OFFSET_L, buff, 2);
|
||||
if (ret)
|
||||
{
|
||||
printf("Read exp_offset failed.\n");
|
||||
return -1;
|
||||
}
|
||||
exp_offset = buff[0] + (((uint16_t)buff[1]) << 8);
|
||||
|
||||
#ifdef DEBUG_AE
|
||||
printf("cmd_exp: %ld, exp_offset: %d\n", cmd_expo, exp_offset);
|
||||
#endif
|
||||
|
||||
if (exp_offset == 0)
|
||||
{
|
||||
exp_us_cache = (cmd_expo + 3642) / (PAJ6100_XCLK_FREQ/1000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
exp_us_cache = (3642 - exp_offset) / (PAJ6100_XCLK_FREQ/1000000);
|
||||
}
|
||||
|
||||
return exp_us_cache;
|
||||
}
|
||||
|
||||
static void blc_freeze(bool enable)
|
||||
{
|
||||
uint8_t tmp;
|
||||
tmp = enable?2:0;
|
||||
write_regs_w_bank(0, 0x0E, &tmp, 1);
|
||||
tmp = 1;
|
||||
write_regs_w_bank(1, 0x00, &tmp, 1); // Update flag
|
||||
}
|
||||
|
||||
static void auto_exposure(sensor_t *sensor)
|
||||
{
|
||||
if (!is_ae_enabled)
|
||||
return;
|
||||
|
||||
if (skip_frame)
|
||||
{
|
||||
--skip_frame;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((lt_lockrange_in_lbound <= l_total) && (l_total <= lt_lockrange_in_ubound))
|
||||
{
|
||||
ae_converged_flag = true;
|
||||
}
|
||||
else if ((l_total > lt_lockrange_out_ubound) || (l_total < lt_lockrange_out_lbound))
|
||||
{
|
||||
ae_converged_flag = false;
|
||||
}
|
||||
blc_freeze(ae_converged_flag);
|
||||
|
||||
if (ae_converged_flag == false)
|
||||
{
|
||||
float gain_db = 0;
|
||||
if (get_gain_db(sensor, &gain_db))
|
||||
{
|
||||
printf("Get Gain DB failed.\n");
|
||||
return;
|
||||
}
|
||||
uint32_t gain;
|
||||
if (gain_db < 6) {
|
||||
// 1x gain
|
||||
gain = 1;
|
||||
} else if (gain_db < 12) {
|
||||
// 2x gain
|
||||
gain = 2;
|
||||
} else if (gain_db < 18) {
|
||||
// 4x gain
|
||||
gain = 4;
|
||||
} else {
|
||||
// 8x gain
|
||||
gain = 8;
|
||||
}
|
||||
|
||||
int expo = get_exposure(sensor);
|
||||
if (expo < 0)
|
||||
{
|
||||
printf("Get exposure failed.\n");
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG_AE
|
||||
printf("Current Gain: %ldx, Expo: %d\n", gain, expo);
|
||||
#endif
|
||||
|
||||
uint32_t GEP = gain * expo;
|
||||
float l_ratio = ((float)l_target_total) / l_total;
|
||||
float GEP_target = GEP * l_ratio;
|
||||
uint32_t expo_target = IM_MIN(IM_MAX(fast_roundf(GEP_target / R_AE_MinGain), R_AE_MinExpoTime), R_AE_MaxExpoTime);
|
||||
|
||||
#ifdef DEBUG_AE
|
||||
printf("GEP: %ld ", GEP);
|
||||
printf("GEP_target: %d (x1000), L_Ratio: %d (x1000)\n",
|
||||
(int)(GEP_target*1000), (int)(l_ratio*1000));
|
||||
printf("1st Expo Target: %ld (max: %ld) ", expo_target, R_AE_MaxExpoTime);
|
||||
#endif
|
||||
uint32_t gain_target;
|
||||
float tmp_gain = GEP_target/expo_target;
|
||||
if (tmp_gain <= 1) gain_target = 1;
|
||||
else if (tmp_gain <= 2) gain_target = 2;
|
||||
else if (tmp_gain <= 4) gain_target = 4;
|
||||
else gain_target = 8;
|
||||
//gain_target = IM_MIN(IM_MAX(fast_ceilf(GEP_target/expo_target), R_AE_MinGain), R_AE_MaxGain);
|
||||
gain_db = 20 * (fast_log((float)gain_target)/fast_log(10.0));
|
||||
expo_target = IM_MIN(IM_MAX(fast_roundf(GEP_target / gain_target), R_AE_MinExpoTime), R_AE_MaxExpoTime);
|
||||
#ifdef DEBUG_AE
|
||||
printf("Gain Target: %ld (%d DB (x1000))\n", gain_target, (int)(gain_db*1000));
|
||||
printf("Final Expo Target: %ld (max: %ld) \n", expo_target, R_AE_MaxExpoTime);
|
||||
#endif
|
||||
set_exposure(sensor, expo_target, false);
|
||||
set_auto_gain(sensor, false, gain_db, 0);
|
||||
skip_frame = 0;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static int sleep(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val;
|
||||
|
||||
if (enable) {
|
||||
ret = read_regs_w_bank(BANK_1, REG_CMD_SENSOR_MODE, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to read REG_CMD_SENSOR_MODE.\n");
|
||||
return -1;
|
||||
}
|
||||
val |= (1 << 7);
|
||||
ret = write_regs_w_bank(BANK_1, REG_CMD_SENSOR_MODE, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to write REG_CMD_SENSOR_MODE.\n");
|
||||
return -1;
|
||||
}
|
||||
// Sleep 30ms
|
||||
systick_sleep(30);
|
||||
ret = read_regs_w_bank(BANK_1, REG_CMD_LPM_ENH, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to read REG_CMD_SENSOR_MODE.\n");
|
||||
return -1;
|
||||
}
|
||||
val |= (1 << 6);
|
||||
ret = write_regs_w_bank(BANK_1, REG_CMD_LPM_ENH, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to write REG_CMD_LPM_ENH.\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ret = read_regs_w_bank(BANK_1, REG_CMD_SENSOR_MODE, &val, 1);
|
||||
if (ret) {
|
||||
#ifdef DEBUG
|
||||
printf("Failed to read REG_CMD_SENSOR_MODE.\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
val &= ~(1 << 7);
|
||||
val &= ~(1 << 6);
|
||||
ret = write_regs_w_bank(BANK_1, REG_CMD_SENSOR_MODE, &val, 1);
|
||||
if (ret) {
|
||||
#ifdef DEBUG
|
||||
printf("Failed to write REG_CMD_SENSOR_MODE.\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_reg(sensor_t *sensor, uint16_t reg_addr)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
if (pixspi_regs_read((uint8_t)reg_addr, &data, 1))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
static int write_reg(sensor_t *sensor, uint16_t reg_addr, uint16_t reg_data)
|
||||
{
|
||||
uint8_t data = reg_data;
|
||||
|
||||
// Clear bank cache.
|
||||
bank_cache = -1;
|
||||
|
||||
return pixspi_regs_write((uint8_t)reg_addr, &data, 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
uint16_t w = resolution[framesize][0];
|
||||
uint16_t h = resolution[framesize][1];
|
||||
|
||||
uint8_t aavg_VnH, abc_start_line, voffset, abc_sample_size;
|
||||
ret |= read_regs_w_bank(BANK_0, REG_CMD_AAVG_V /* REG_CMD_AAVG_H */, &aavg_VnH, 1);
|
||||
ret |= read_regs_w_bank(BANK_0, REG_ABC_START_LINE, &abc_start_line, 1);
|
||||
ret |= read_regs_w_bank(BANK_1, REG_ABC_SAMPLE_SIZE, &abc_sample_size, 1);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
printf("Failed to read Average mode registers.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (framesize)
|
||||
{
|
||||
case FRAMESIZE_QVGA:
|
||||
aavg_VnH &= ~((1 << 3) | (1 << 2));
|
||||
abc_start_line = (abc_start_line & ~(0x07 << 1)) | (2 << 1);
|
||||
voffset = 2;
|
||||
abc_sample_size = (abc_sample_size & ~(0x07)) | 6;
|
||||
|
||||
R_AE_MaxExpoTime = CAL_MAX_EXPO_TIME(QVGA_MAX_EXPO_PA);
|
||||
break;
|
||||
case FRAMESIZE_QQVGA:
|
||||
aavg_VnH |= (1 << 3) | (1 << 2);
|
||||
abc_start_line = (abc_start_line & ~(0x07 << 1)) | (1 << 1);
|
||||
voffset = 1;
|
||||
abc_sample_size = (abc_sample_size & ~(0x07)) | 5;
|
||||
|
||||
R_AE_MaxExpoTime = CAL_MAX_EXPO_TIME(QQVGA_MAX_EXPO_PA);
|
||||
break;
|
||||
default:
|
||||
printf("PAJ6100 only support QVGA & QQVGA now.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret |= write_regs_w_bank(BANK_0, REG_CMD_AAVG_V /* REG_CMD_AAVG_H */, &aavg_VnH, 1);
|
||||
ret |= write_regs_w_bank(BANK_0, REG_ABC_START_LINE, &abc_start_line, 1);
|
||||
ret |= write_regs_w_bank(BANK_1, REG_CP_WOI_VOFFSET, &voffset, 1);
|
||||
ret |= write_regs_w_bank(BANK_1, REG_ABC_SAMPLE_SIZE, &abc_sample_size, 1);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
printf("Failed to write Average mode registers.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
l_target_total = L_TARGET * w * h;
|
||||
lt_lockrange_in_ubound = (L_TARGET + AE_LOCK_RANGE_IN) * w * h;
|
||||
lt_lockrange_in_lbound = (L_TARGET - AE_LOCK_RANGE_IN) * w * h;
|
||||
lt_lockrange_out_ubound = (L_TARGET + AE_LOCK_RANGE_OUT) * w * h;
|
||||
lt_lockrange_out_lbound = (L_TARGET - AE_LOCK_RANGE_OUT) * w * h;
|
||||
|
||||
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_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)
|
||||
{
|
||||
if (enable)
|
||||
return 0;
|
||||
int ret = 0;
|
||||
uint8_t val, fgh, ggh;
|
||||
|
||||
if (gain_db < 6) {
|
||||
// 1x gain
|
||||
fgh = 0;
|
||||
ggh = 0;
|
||||
} else if (gain_db < 12) {
|
||||
// 2x gain
|
||||
fgh = 0;
|
||||
ggh = 1;
|
||||
} else if (gain_db < 18) {
|
||||
// 4x gain
|
||||
fgh = 2;
|
||||
ggh = 1;
|
||||
} else {
|
||||
// 8x gain
|
||||
fgh = 3;
|
||||
ggh = 1;
|
||||
}
|
||||
|
||||
ret = read_regs_w_bank(BANK_0, REG_FGH, &val, 1);
|
||||
fgh = (val & ~(3 << 4)) | (fgh << 4); // fgh[5:4] = 0
|
||||
ret |= read_regs_w_bank(BANK_0, REG_GGH, &val, 1);
|
||||
ggh = (val & ~(1 << 7)) | (ggh << 7); // ggh[7] = 1
|
||||
if (ret) {
|
||||
printf("Failed to read FGH or GGH.\n");
|
||||
} else {
|
||||
write_regs_w_bank(BANK_0, REG_FGH, &fgh, 1);
|
||||
write_regs_w_bank(BANK_0, REG_GGH, &ggh, 1);
|
||||
val = 1;
|
||||
write_regs_w_bank(BANK_1, REG_ISP_UPDATE, &val, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_gain_db(sensor_t *sensor, float *gain_db)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t val;
|
||||
uint8_t fgh, ggh;
|
||||
|
||||
ret |= read_regs_w_bank(BANK_0, REG_FGH, &val, 1);
|
||||
fgh = (val >> 4) & 0x03 ;
|
||||
ret |= read_regs_w_bank(BANK_0, REG_GGH, &val, 1);
|
||||
ggh = (val >> 7) & 0x01 ;
|
||||
|
||||
if (ret) {
|
||||
printf("Failed to read FGH or GGH.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ggh == 0 && fgh == 0) {
|
||||
*gain_db = 0;
|
||||
} else if (ggh == 1) {
|
||||
switch (fgh)
|
||||
{
|
||||
case 0:
|
||||
*gain_db = 6;
|
||||
break;
|
||||
case 2:
|
||||
*gain_db = 12;
|
||||
break;
|
||||
case 3:
|
||||
*gain_db = 18;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
printf("Read a undefined FGH value (%d).\n", fgh);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us)
|
||||
{
|
||||
if (enable) {
|
||||
is_ae_enabled = true;
|
||||
return 0;
|
||||
} else {
|
||||
is_ae_enabled = false;
|
||||
return set_exposure(sensor, exposure_us, false);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_exposure_us(sensor_t *sensor, int *exposure_us) {
|
||||
int ret = get_exposure(sensor);
|
||||
if (ret >= 0)
|
||||
{
|
||||
*exposure_us = ret;
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val;
|
||||
ret = read_regs_w_bank(BANK_0, REG_CMD_HSYNC_INV, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to read REG_CMD_HSYNC_INV.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
val = enable==1?val|(0x01):val&~(0x01);
|
||||
|
||||
ret = write_regs_w_bank(BANK_0, REG_CMD_HSYNC_INV, &val, 1);
|
||||
ret = write_regs_w_bank(BANK_1, REG_ISP_UPDATE, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to write REG_CMD_HSYNC_INV.\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret;
|
||||
uint8_t val;
|
||||
ret = read_regs_w_bank(BANK_0, REG_CMD_VSYNC_INV, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to read REG_CMD_VSYNC_INV.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
val = enable==1?val|(0x01<<1):val&~(0x01<<1);
|
||||
|
||||
ret = write_regs_w_bank(BANK_0, REG_CMD_VSYNC_INV, &val, 1);
|
||||
ret = write_regs_w_bank(BANK_1, REG_ISP_UPDATE, &val, 1);
|
||||
if (ret) {
|
||||
printf("Failed to write REG_CMD_VSYNC_INV.\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret;
|
||||
bank_cache = -1;
|
||||
exp_us_cache = -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint8_t part_id_l = 0, part_id_h = 0;
|
||||
read_regs_w_bank(0, 0x00, &part_id_l, 1);
|
||||
read_regs_w_bank(0, 0x01, &part_id_h, 1);
|
||||
printf("Part ID 0x%x 0x%x\n", part_id_l, part_id_h);
|
||||
printf("init_res: %d\n", init_res);
|
||||
#endif
|
||||
|
||||
// Re-init sensor every time.
|
||||
init_sensor(sensor);
|
||||
|
||||
// Fetch default R_Frame_Time
|
||||
uint8_t buff[3] = {};
|
||||
ret = read_regs_w_bank(BANK_0, REG_FRAME_TIME_L, buff, 3);
|
||||
if (ret) {
|
||||
printf("Read cmd_expo failed.\n");
|
||||
} else {
|
||||
R_FrameTime = buff[0] + (((uint32_t)buff[1]) << 8) + (((uint32_t)buff[2]) << 16);
|
||||
}
|
||||
|
||||
// Default Gain 2x
|
||||
uint8_t val, fgh, ggh;
|
||||
ret = read_regs_w_bank(BANK_0, REG_FGH, &val, 1);
|
||||
fgh = (val & ~(3 << 4)); // fgh[5:4] = 0
|
||||
ret |= read_regs_w_bank(BANK_0, REG_GGH, &val, 1);
|
||||
ggh = (val & ~(1 << 7)) | (0x01 << 7); // ggh[7] = 1
|
||||
if (ret) {
|
||||
printf("Failed to read FGH or GGH.\n");
|
||||
} else {
|
||||
write_regs_w_bank(BANK_0, REG_FGH, &fgh, 1);
|
||||
write_regs_w_bank(BANK_0, REG_GGH, &ggh, 1);
|
||||
val = 1;
|
||||
write_regs_w_bank(BANK_1, REG_ISP_UPDATE, &val, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj6100_snapshot(sensor_t *sensor, image_t *image, uint32_t flags)
|
||||
{
|
||||
int res = sensor_snapshot(sensor, image, flags);
|
||||
if (res == 0)
|
||||
{
|
||||
l_total = 0;
|
||||
int iml = image->h * image->w;
|
||||
for (int i=0; i<iml; ++i)
|
||||
l_total += image->data[i];
|
||||
// PAJ6100 doesn't support HW auto-exposure,
|
||||
// we provide a software implementation.
|
||||
auto_exposure(sensor);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int paj6100_init(sensor_t *sensor)
|
||||
{
|
||||
// Initialize sensor structure.
|
||||
sensor->gs_bpp = 1;
|
||||
sensor->reset = reset;
|
||||
sensor->sleep = sleep;
|
||||
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_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_special_effect = set_special_effect;
|
||||
sensor->set_lens_correction = set_lens_correction;
|
||||
|
||||
sensor->snapshot = paj6100_snapshot;
|
||||
|
||||
// Set sensor flags
|
||||
SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_VSYNC, 1);
|
||||
SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_HSYNC, 1);
|
||||
SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_PIXCK, 1);
|
||||
SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_FSYNC, 0);
|
||||
SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_JPEGE, 0);
|
||||
|
||||
init_sensor(sensor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool paj6100_detect(sensor_t *sensor)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t part_id_l, part_id_h;
|
||||
|
||||
DCMI_RESET_HIGH();
|
||||
systick_sleep(10);
|
||||
|
||||
if (!pixspi_init())
|
||||
{
|
||||
printf("Initial pixspi failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ret |= pixspi_regs_read(0x00, &part_id_l, 1);
|
||||
ret |= pixspi_regs_read(0x01, &part_id_h, 1);
|
||||
#ifdef DEBUG
|
||||
printf("Part ID 0x%x 0x%x\n", part_id_l, part_id_h);
|
||||
#endif
|
||||
if (ret == 0 &&
|
||||
(part_id_l == 0x00 && part_id_h == 0x61))
|
||||
return true; // Got you.
|
||||
|
||||
pixspi_release();
|
||||
return false;
|
||||
}
|
||||
#endif //(OMV_ENABLE_PAJ6100 == 1)
|
||||
16
src/omv/sensors/paj6100.h
Normal file
16
src/omv/sensors/paj6100.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2019 Lake Fu <lake_fu@pixart.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* PAJ6100 driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __PAJ6100_H__
|
||||
#define __PAJ6100_H__
|
||||
|
||||
#define PAJ6100_XCLK_FREQ 6000000
|
||||
|
||||
bool paj6100_detect(sensor_t *sensor);
|
||||
int paj6100_init(sensor_t *sensor);
|
||||
#endif
|
||||
146
src/omv/sensors/paj6100_reg.h
Normal file
146
src/omv/sensors/paj6100_reg.h
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* File: paj6100u6_reg.h
|
||||
* Created Date: Tuesday, May 25th 2021, 10:45:35 am
|
||||
* Author: Lake Fu
|
||||
* -----
|
||||
* Last Modified: Sunday May 30th 2021 2:39:45 pm
|
||||
* Modified By: Lake Fu at <lake_fu@pixart.com>
|
||||
* -----
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 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.
|
||||
* -----
|
||||
* HISTORY:
|
||||
* Date By Comments
|
||||
* ---------- --- ----------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_BANK_SWITCH 0x7F
|
||||
|
||||
// =======================================
|
||||
#define BANK_0 0x00
|
||||
// ---------------------------------------
|
||||
#define REG_CMD_HSYNC_INV 0X07 // bit7
|
||||
#define REG_CMD_VSYNC_INV 0X07 // bit6
|
||||
|
||||
#define REG_CMD_AAVG_V 0X07 // bit3
|
||||
#define REG_CMD_AAVG_H 0X07 // bit2
|
||||
|
||||
#define REG_ABC_START_LINE 0x0D // bit3:1
|
||||
|
||||
|
||||
#define REG_FGH 0x11 // bit5:4
|
||||
#define REG_GGH 0x12 // bit7
|
||||
|
||||
#define REG_EXP_OFFSET_L 0x18
|
||||
#define REG_EXP_OFFSET_H 0x19
|
||||
#define REG_CMD_EXPO_L 0x48
|
||||
#define REG_CMD_EXPO_H 0x49
|
||||
#define REG_CMD_EXPO_2H 0x4A
|
||||
|
||||
#define REG_FRAME_TIME_L 0x7A
|
||||
#define REG_FRAME_TIME_H 0x7B
|
||||
#define REG_FRAME_TIME_2H 0x7C
|
||||
// =======================================
|
||||
#define BANK_1 0x01
|
||||
// ---------------------------------------
|
||||
#define REG_ISP_UPDATE 0x00
|
||||
|
||||
#define REG_CMD_SENSOR_MODE 0X23 // bit7
|
||||
#define REG_CMD_LPM_ENH 0x23 // bit6
|
||||
|
||||
#define REG_CP_WOI_VOFFSET 0x45
|
||||
#define REG_ABC_SAMPLE_SIZE 0x46 // bit2:0
|
||||
|
||||
|
||||
// 20180504
|
||||
static const uint8_t low_active_R_ABC_Avg_UBx50_T_BLACINV_EnHx1_T_SIG_REFx1[] = {
|
||||
//address, value
|
||||
0x7F, 0x00,
|
||||
0x0C, 0x1D, // software reset
|
||||
0x7F, 0x00,
|
||||
0x07, 0x00, // low active
|
||||
0x08, 0x03,
|
||||
0x0B, 0x0E,
|
||||
0x0D, 0xC5,
|
||||
0x0F, 0x32, //R_ABC_Avg_UB=50
|
||||
0x11, 0x41, //R_global=1
|
||||
0x13, 0xD2,
|
||||
0x14, 0xFE,
|
||||
0x15, 0x00, // R_fg_fast=0
|
||||
0x17, 0x02, //03
|
||||
0x1A, 0x07,
|
||||
0x1B, 0x08,
|
||||
0x20, 0x00, //R_fast_powerdn_WakeupTime=0
|
||||
0x25, 0x78,
|
||||
0x29, 0x28,
|
||||
0x2B, 0x06,
|
||||
0x2F, 0x0E,
|
||||
0x30, 0x0E,
|
||||
0x34, 0x0F,
|
||||
0x35, 0x0F,
|
||||
0x3A, 0x28,
|
||||
0x45, 0x17,
|
||||
0x46, 0x17,
|
||||
0x48, 0x10, //EXP
|
||||
0x49, 0x27, //EXP
|
||||
0x4A, 0x00, //EXP
|
||||
0x4D, 0x0D,
|
||||
0x4E, 0x20,
|
||||
0x62, 0x12,
|
||||
0x64, 0x02,
|
||||
0x67, 0x0A,
|
||||
0x69, 0x0A,
|
||||
0x6C, 0x0B,
|
||||
0x6E, 0x0B,
|
||||
0x71, 0x0A,
|
||||
0x73, 0x1D,
|
||||
0x75, 0x1E,
|
||||
0x77, 0x0B,
|
||||
0x7F, 0x01,
|
||||
0x01, 0x14,
|
||||
0x02, 0x02, //
|
||||
0x04, 0x96,
|
||||
0x05, 0x03,
|
||||
0x06, 0x46,
|
||||
0x0D, 0x9F, //T_BLACINV_EnH=1
|
||||
0x0E, 0x11, //T_SIG_REF=1
|
||||
0x0F, 0x48,
|
||||
0x10, 0x10, //T_vcom_lvl=0
|
||||
0x11, 0x00,
|
||||
0x12, 0x05,
|
||||
0x15, 0x00,
|
||||
0x16, 0x01, //schmitt trigger
|
||||
0x17, 0x67,
|
||||
0x18, 0xD0, //T_vrt_lvl=T_vrb_lvl=2
|
||||
0x21, 0x14,
|
||||
0x22, 0x80, //
|
||||
0x2F, 0x30,
|
||||
0x35, 0x64,
|
||||
0x39, 0x03,
|
||||
0x3A, 0x03,
|
||||
0x46, 0x06,
|
||||
0x4A, 0x00, //FX2
|
||||
0x4B, 0x00, //FX2
|
||||
0x00, 0x01, //updated flag
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user