openmv/src/omv/sensor.c
iabdalkader 9603870e04 Use software cropping instead of DCMI HW cropping.
* This fixes issues with some DMA transfer sizes.
2018-04-25 23:43:30 +02:00

932 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Sensor abstraction layer.
*
*/
#include <stdlib.h>
#include <string.h>
#include "mp.h"
#include "irq.h"
#include "cambus.h"
#include "ov9650.h"
#include "ov2640.h"
#include "ov7725.h"
#include "mt9v034.h"
#include "lepton.h"
#include "sensor.h"
#include "systick.h"
#include "framebuffer.h"
#include "omv_boardconfig.h"
#define OV_CHIP_ID (0x0A)
#define ON_CHIP_ID (0x00)
#define MAX_XFER_SIZE (0xFFFC)
sensor_t sensor;
TIM_HandleTypeDef TIMHandle;
DMA_HandleTypeDef DMAHandle;
DCMI_HandleTypeDef DCMIHandle;
static volatile int line = 0;
extern uint8_t _line_buf;
const int resolution[][2] = {
{0, 0 },
// C/SIF Resolutions
{88, 72 }, /* QQCIF */
{176, 144 }, /* QCIF */
{352, 288 }, /* CIF */
{88, 60 }, /* QQSIF */
{176, 120 }, /* QSIF */
{352, 240 }, /* SIF */
// VGA Resolutions
{40, 30 }, /* QQQQVGA */
{80, 60 }, /* QQQVGA */
{160, 120 }, /* QQVGA */
{320, 240 }, /* QVGA */
{640, 480 }, /* VGA */
{60, 40 }, /* HQQQVGA */
{120, 80 }, /* HQQVGA */
{240, 160 }, /* HQVGA */
// FFT Resolutions
{64, 32 }, /* 64x32 */
{64, 64 }, /* 64x64 */
{128, 64 }, /* 128x64 */
{128, 128 }, /* 128x64 */
// Other
{128, 160 }, /* LCD */
{128, 160 }, /* QQVGA2 */
{800, 600 }, /* SVGA */
{1280, 1024}, /* SXGA */
{1600, 1200}, /* UXGA */
};
#if (OMV_XCLK_SOURCE == OMV_XCLK_TIM)
static int extclk_config(int frequency)
{
// Doubles PCLK
//__HAL_RCC_TIMCLKPRESCALER(RCC_TIMPRES_ACTIVATED);
/* TCLK (PCLK * 2) */
int tclk = DCMI_TIM_PCLK_FREQ() * 2;
/* Period should be even */
int period = (tclk / frequency) - 1;
/* Timer base configuration */
TIMHandle.Instance = DCMI_TIM;
TIMHandle.Init.Period = period;
TIMHandle.Init.Prescaler = 0;
TIMHandle.Init.ClockDivision = 0;
TIMHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
/* Timer channel configuration */
TIM_OC_InitTypeDef TIMOCHandle;
TIMOCHandle.Pulse = period/2;
TIMOCHandle.OCMode = TIM_OCMODE_PWM1;
TIMOCHandle.OCPolarity = TIM_OCPOLARITY_HIGH;
TIMOCHandle.OCFastMode = TIM_OCFAST_DISABLE;
TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET;
if (HAL_TIM_PWM_Init(&TIMHandle) != HAL_OK
|| HAL_TIM_PWM_ConfigChannel(&TIMHandle, &TIMOCHandle, DCMI_TIM_CHANNEL) != HAL_OK
|| HAL_TIM_PWM_Start(&TIMHandle, DCMI_TIM_CHANNEL) != HAL_OK) {
// Initialization Error
return -1;
}
return 0;
}
#endif // (OMV_XCLK_SOURCE == OMV_XCLK_TIM)
static int dcmi_config(uint32_t jpeg_mode)
{
// DCMI configuration
DCMIHandle.Instance = DCMI;
// VSYNC clock polarity
DCMIHandle.Init.VSPolarity = SENSOR_HW_FLAGS_GET(&sensor, SENSOR_HW_FLAGS_VSYNC) ?
DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW;
// HSYNC clock polarity
DCMIHandle.Init.HSPolarity = SENSOR_HW_FLAGS_GET(&sensor, SENSOR_HW_FLAGS_HSYNC) ?
DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW;
// PXCLK clock polarity
DCMIHandle.Init.PCKPolarity = SENSOR_HW_FLAGS_GET(&sensor, SENSOR_HW_FLAGS_PIXCK) ?
DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
DCMIHandle.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; // Enable Hardware synchronization
DCMIHandle.Init.CaptureRate = DCMI_CR_ALL_FRAME; // Capture rate all frames
DCMIHandle.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; // Capture 8 bits on every pixel clock
DCMIHandle.Init.JPEGMode = jpeg_mode; // Set JPEG Mode
#if defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
DCMIHandle.Init.ByteSelectMode = DCMI_BSM_ALL; // Capture all received bytes
DCMIHandle.Init.ByteSelectStart = DCMI_OEBS_ODD; // Ignored
DCMIHandle.Init.LineSelectMode = DCMI_LSM_ALL; // Capture all received lines
DCMIHandle.Init.LineSelectStart = DCMI_OELS_ODD; // Ignored
#endif
// Associate the DMA handle to the DCMI handle
__HAL_LINKDMA(&DCMIHandle, DMA_Handle, DMAHandle);
// Initialize the DCMI
HAL_DCMI_DeInit(&DCMIHandle);
if (HAL_DCMI_Init(&DCMIHandle) != HAL_OK) {
// Initialization Error
return -1;
}
// Configure and enable DCMI IRQ Channel
HAL_NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI, IRQ_SUBPRI_DCMI);
HAL_NVIC_EnableIRQ(DCMI_IRQn);
return 0;
}
static int dma_config()
{
// DMA Stream configuration
DMAHandle.Instance = DMA2_Stream1; /* Select the DMA instance */
#if defined(MCU_SERIES_H7)
DMAHandle.Init.Request = DMA_REQUEST_DCMI; /* DMA Channel */
#else
DMAHandle.Init.Channel = DMA_CHANNEL_1; /* DMA Channel */
#endif
DMAHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* Peripheral to memory transfer */
DMAHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */
DMAHandle.Init.PeriphInc = DMA_PINC_DISABLE; /* Peripheral increment mode Enable */
DMAHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* Peripheral data alignment : Word */
DMAHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* Memory data alignment : Word */
DMAHandle.Init.Mode = DMA_NORMAL; /* Normal DMA mode */
DMAHandle.Init.Priority = DMA_PRIORITY_HIGH; /* Priority level : high */
DMAHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* FIFO mode enabled */
DMAHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* FIFO threshold full */
DMAHandle.Init.MemBurst = DMA_MBURST_INC4; /* Memory burst */
DMAHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Peripheral burst */
// Configure and disable DMA IRQ Channel
HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21, IRQ_SUBPRI_DMA21);
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
// Initialize the DMA stream
HAL_DMA_DeInit(&DMAHandle);
if (HAL_DMA_Init(&DMAHandle) != HAL_OK) {
// Initialization Error
return -1;
}
return 0;
}
void sensor_init0()
{
// Init FB mutex
mutex_init(&JPEG_FB()->lock);
// Save fb_enabled flag state
int fb_enabled = JPEG_FB()->enabled;
// Clear framebuffers
memset(MAIN_FB(), 0, sizeof(*MAIN_FB()));
memset(JPEG_FB(), 0, sizeof(*JPEG_FB()));
// Set default quality
JPEG_FB()->quality = 35;
// Set fb_enabled
JPEG_FB()->enabled = fb_enabled;
}
int sensor_init()
{
int init_ret = 0;
/* Do a power cycle */
DCMI_PWDN_HIGH();
systick_sleep(10);
DCMI_PWDN_LOW();
systick_sleep(10);
// Initialize the camera bus.
cambus_init();
systick_sleep(10);
// Configure the sensor external clock (XCLK) to XCLK_FREQ.
//
// Max pixclk is 2.5 * HCLK:
// STM32F427@180MHz PCLK = 71.9999MHz
// STM32F769@216MHz PCLK = 86.4000MHz
//
// OV2640:
// The sensor's internal PLL (when CLKRC=0x80) doubles the XCLK_FREQ
// (XCLK=XCLK_FREQ*2), and the unscaled PIXCLK output is XCLK_FREQ*4
//
// OV7725 PCLK when prescalar is enabled (CLKRC[6]=0):
// Internal clock = Input clock × PLL multiplier / [(CLKRC[5:0] + 1) × 2]
//
// OV7725 PCLK when prescalar is disabled (CLKRC[6]=1):
// Internal clock = Input clock × PLL multiplier
//
#if (OMV_XCLK_SOURCE == OMV_XCLK_TIM)
// Configure external clock timer.
if (extclk_config(OMV_XCLK_FREQUENCY) != 0) {
// Timer problem
return -1;
}
#elif (OMV_XCLK_SOURCE == OMV_XCLK_MCO)
// Pass through the MCO1 clock with source input set to HSE (12MHz).
// Note MCO1 is multiplexed on OPENMV2/TIM1 only.
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
#else
#error "OMV_XCLK_SOURCE is not set!"
#endif
/* Reset the sesnor state */
memset(&sensor, 0, sizeof(sensor_t));
/* Some sensors have different reset polarities, and we can't know which sensor
is connected before initializing cambus and probing the sensor, which in turn
requires pulling the sensor out of the reset state. So we try to probe the
sensor with both polarities to determine line state. */
sensor.pwdn_pol = ACTIVE_HIGH;
sensor.reset_pol = ACTIVE_HIGH;
/* Reset the sensor */
DCMI_RESET_HIGH();
systick_sleep(10);
DCMI_RESET_LOW();
systick_sleep(10);
/* Probe the sensor */
sensor.slv_addr = cambus_scan();
if (sensor.slv_addr == 0) {
/* Sensor has been held in reset,
so the reset line is active low */
sensor.reset_pol = ACTIVE_LOW;
/* Pull the sensor out of the reset state */
DCMI_RESET_HIGH();
systick_sleep(10);
/* Probe again to set the slave addr */
sensor.slv_addr = cambus_scan();
if (sensor.slv_addr == 0) {
sensor.pwdn_pol = ACTIVE_LOW;
DCMI_PWDN_HIGH();
systick_sleep(10);
sensor.slv_addr = cambus_scan();
if (sensor.slv_addr == 0) {
sensor.reset_pol = ACTIVE_HIGH;
DCMI_RESET_LOW();
systick_sleep(10);
sensor.slv_addr = cambus_scan();
if (sensor.slv_addr == 0) {
return -2;
}
}
}
}
// Clear sensor chip ID.
sensor.chip_id = 0;
// Set default snapshot function.
sensor.snapshot = sensor_snapshot;
if (sensor.slv_addr == LEPTON_ID) {
sensor.chip_id = LEPTON_ID;
extclk_config(25000000);
init_ret = lepton_init(&sensor);
} else {
// Read ON semi sensor ID.
cambus_readb(sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id);
if (sensor.chip_id == MT9V034_ID) {
// On/Aptina MT requires 13-27MHz clock.
extclk_config(27000000);
// Only the MT9V034 is currently supported.
init_ret = mt9v034_init(&sensor);
} else { // Read OV sensor ID.
cambus_readb(sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id);
// Initialize sensor struct.
switch (sensor.chip_id) {
case OV9650_ID:
init_ret = ov9650_init(&sensor);
break;
case OV2640_ID:
init_ret = ov2640_init(&sensor);
break;
case OV7725_ID:
init_ret = ov7725_init(&sensor);
break;
default:
// Sensor is not supported.
return -3;
}
}
}
if (init_ret != 0 ) {
// Sensor init failed.
return -4;
}
/* Configure the DCMI DMA Stream */
if (dma_config() != 0) {
// DMA problem
return -5;
}
/* Configure the DCMI interface. This should be called
after ovxxx_init to set VSYNC/HSYNC/PCLK polarities */
if (dcmi_config(DCMI_JPEG_DISABLE) != 0){
// DCMI config failed
return -6;
}
// Disable VSYNC EXTI IRQ
HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN);
// Clear fb_enabled flag
// This is executed only once to initialize the FB enabled flag.
JPEG_FB()->enabled = 0;
/* All good! */
return 0;
}
int sensor_reset()
{
// Reset the sesnor state
sensor.sde = 0;
sensor.pixformat = 0;
sensor.framesize = 0;
sensor.framerate = 0;
sensor.gainceiling = 0;
sensor.vsync_gpio = NULL;
// Reset image filter
sensor_set_line_filter(NULL, NULL);
// Call sensor-specific reset function
if (sensor.reset(&sensor) != 0) {
return -1;
}
// Just in case there's a running DMA request.
HAL_DMA_Abort(&DMAHandle);
// Disable VSYNC EXTI IRQ
HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN);
return 0;
}
int sensor_get_id()
{
return sensor.chip_id;
}
int sensor_sleep(int enable)
{
if (sensor.sleep == NULL
|| sensor.sleep(&sensor, enable) != 0) {
// Operation not supported
return -1;
}
return 0;
}
int sensor_read_reg(uint8_t reg_addr)
{
if (sensor.read_reg == NULL) {
// Operation not supported
return -1;
}
return sensor.read_reg(&sensor, reg_addr);
}
int sensor_write_reg(uint8_t reg_addr, uint16_t reg_data)
{
if (sensor.write_reg == NULL) {
// Operation not supported
return -1;
}
return sensor.write_reg(&sensor, reg_addr, reg_data);
}
int sensor_set_pixformat(pixformat_t pixformat)
{
uint32_t jpeg_mode = DCMI_JPEG_DISABLE;
if (sensor.pixformat == pixformat) {
// No change
return 0;
}
if (sensor.set_pixformat == NULL
|| sensor.set_pixformat(&sensor, pixformat) != 0) {
// Operation not supported
return -1;
}
// Set pixel format
sensor.pixformat = pixformat;
// Set JPEG mode
if (pixformat == PIXFORMAT_JPEG) {
jpeg_mode = DCMI_JPEG_ENABLE;
}
// Skip the first frame.
MAIN_FB()->bpp = 0;
return dcmi_config(jpeg_mode);
}
int sensor_set_framesize(framesize_t framesize)
{
if (sensor.framesize == framesize) {
// No change
return 0;
}
// Call the sensor specific function
if (sensor.set_framesize == NULL
|| sensor.set_framesize(&sensor, framesize) != 0) {
// Operation not supported
return -1;
}
// Set framebuffer size
sensor.framesize = framesize;
// Skip the first frame.
MAIN_FB()->bpp = 0;
// Set MAIN FB x, y offset.
MAIN_FB()->x = 0;
MAIN_FB()->y = 0;
// Set MAIN FB width and height.
MAIN_FB()->w = resolution[framesize][0];
MAIN_FB()->h = resolution[framesize][1];
// Set MAIN FB backup width and height.
MAIN_FB()->u = resolution[framesize][0];
MAIN_FB()->v = resolution[framesize][1];
return 0;
}
int sensor_set_framerate(framerate_t framerate)
{
if (sensor.framerate == framerate) {
/* no change */
return 0;
}
/* call the sensor specific function */
if (sensor.set_framerate == NULL
|| sensor.set_framerate(&sensor, framerate) != 0) {
/* operation not supported */
return -1;
}
/* set the frame rate */
sensor.framerate = framerate;
return 0;
}
int sensor_set_windowing(int x, int y, int w, int h)
{
MAIN_FB()->x = x;
MAIN_FB()->y = y;
MAIN_FB()->w = MAIN_FB()->u = w;
MAIN_FB()->h = MAIN_FB()->v = h;
return 0;
}
int sensor_set_contrast(int level)
{
if (sensor.set_contrast != NULL) {
return sensor.set_contrast(&sensor, level);
}
return -1;
}
int sensor_set_brightness(int level)
{
if (sensor.set_brightness != NULL) {
return sensor.set_brightness(&sensor, level);
}
return -1;
}
int sensor_set_saturation(int level)
{
if (sensor.set_saturation != NULL) {
return sensor.set_saturation(&sensor, level);
}
return -1;
}
int sensor_set_gainceiling(gainceiling_t gainceiling)
{
if (sensor.gainceiling == gainceiling) {
/* no change */
return 0;
}
/* call the sensor specific function */
if (sensor.set_gainceiling == NULL
|| sensor.set_gainceiling(&sensor, gainceiling) != 0) {
/* operation not supported */
return -1;
}
sensor.gainceiling = gainceiling;
return 0;
}
int sensor_set_quality(int qs)
{
/* call the sensor specific function */
if (sensor.set_quality == NULL
|| sensor.set_quality(&sensor, qs) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_colorbar(int enable)
{
/* call the sensor specific function */
if (sensor.set_colorbar == NULL
|| sensor.set_colorbar(&sensor, enable) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_auto_gain(int enable, float gain_db, float gain_db_ceiling)
{
/* call the sensor specific function */
if (sensor.set_auto_gain == NULL
|| sensor.set_auto_gain(&sensor, enable, gain_db, gain_db_ceiling) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_get_gain_db(float *gain_db)
{
/* call the sensor specific function */
if (sensor.get_gain_db == NULL
|| sensor.get_gain_db(&sensor, gain_db) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_auto_exposure(int enable, int exposure_us)
{
/* call the sensor specific function */
if (sensor.set_auto_exposure == NULL
|| sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_get_exposure_us(int *exposure_us)
{
/* call the sensor specific function */
if (sensor.get_exposure_us == NULL
|| sensor.get_exposure_us(&sensor, exposure_us) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db)
{
/* call the sensor specific function */
if (sensor.set_auto_whitebal == NULL
|| sensor.set_auto_whitebal(&sensor, enable, r_gain_db, g_gain_db, b_gain_db) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db)
{
/* call the sensor specific function */
if (sensor.get_rgb_gain_db == NULL
|| sensor.get_rgb_gain_db(&sensor, r_gain_db, g_gain_db, b_gain_db) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_hmirror(int enable)
{
/* call the sensor specific function */
if (sensor.set_hmirror == NULL
|| sensor.set_hmirror(&sensor, enable) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_vflip(int enable)
{
/* call the sensor specific function */
if (sensor.set_vflip == NULL
|| sensor.set_vflip(&sensor, enable) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_special_effect(sde_t sde)
{
if (sensor.sde == sde) {
/* no change */
return 0;
}
/* call the sensor specific function */
if (sensor.set_special_effect == NULL
|| sensor.set_special_effect(&sensor, sde) != 0) {
/* operation not supported */
return -1;
}
sensor.sde = sde;
return 0;
}
int sensor_set_lens_correction(int enable, int radi, int coef)
{
/* call the sensor specific function */
if (sensor.set_lens_correction == NULL
|| sensor.set_lens_correction(&sensor, enable, radi, coef) != 0) {
/* operation not supported */
return -1;
}
return 0;
}
int sensor_set_line_filter(line_filter_t line_filter_func, void *line_filter_args)
{
// Set line pre-processing function and args
sensor.line_filter_func = line_filter_func;
sensor.line_filter_args = line_filter_args;
return 0;
}
int sensor_set_vsync_output(GPIO_TypeDef *gpio, uint32_t pin)
{
sensor.vsync_pin = pin;
sensor.vsync_gpio = gpio;
// Enable VSYNC EXTI IRQ
HAL_NVIC_SetPriority(DCMI_VSYNC_IRQN, IRQ_PRI_EXTINT, IRQ_SUBPRI_EXTINT);
HAL_NVIC_EnableIRQ(DCMI_VSYNC_IRQN);
return 0;
}
void DCMI_VsyncExtiCallback()
{
__HAL_GPIO_EXTI_CLEAR_FLAG(1 << DCMI_VSYNC_IRQ_LINE);
if (sensor.vsync_gpio != NULL) {
HAL_GPIO_WritePin(sensor.vsync_gpio, sensor.vsync_pin,
!HAL_GPIO_ReadPin(DCMI_VSYNC_PORT, DCMI_VSYNC_PIN));
}
}
static void sensor_check_buffsize()
{
int bpp=0;
switch (sensor.pixformat) {
case PIXFORMAT_BAYER:
case PIXFORMAT_GRAYSCALE:
bpp = 1;
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_RGB565:
bpp = 2;
break;
default:
break;
}
if ((MAIN_FB()->w * MAIN_FB()->h * bpp) > OMV_RAW_BUF_SIZE) {
if (sensor.pixformat == PIXFORMAT_GRAYSCALE) {
// Crop higher GS resolutions to QVGA
sensor_set_windowing(190, 120, 320, 240);
} else if (sensor.pixformat == PIXFORMAT_RGB565) {
// Switch to BAYER if the frame is too big to fit in RAM.
sensor_set_pixformat(PIXFORMAT_BAYER);
}
}
}
// This function is called back after each line transfer is complete,
// with a pointer to the line buffer that was used. At this point the
// DMA transfers the next line to the other half of the line buffer.
// Note: For JPEG this function is called once (and ignored) at the end of the transfer.
void DCMI_DMAConvCpltUser(uint32_t addr)
{
uint8_t *src = (uint8_t*) addr;
uint8_t *dst = MAIN_FB()->pixels;
uint16_t *src16 = (uint16_t*) addr;
uint16_t *dst16 = (uint16_t*) MAIN_FB()->pixels;
// Skip lines outside the window.
if (line >= MAIN_FB()->y && line <= (MAIN_FB()->y + MAIN_FB()->h)) {
switch (sensor.pixformat) {
case PIXFORMAT_BAYER:
dst += (line - MAIN_FB()->y) * MAIN_FB()->w;
for (int i=0; i<MAIN_FB()->w; i++) {
dst[i] = src[MAIN_FB()->x + i];
}
break;
case PIXFORMAT_GRAYSCALE:
dst += (line - MAIN_FB()->y) * MAIN_FB()->w;
if (sensor.gs_bpp == 1) {
// 1BPP GRAYSCALE.
for (int i=0; i<MAIN_FB()->w; i++) {
dst[i] = src[MAIN_FB()->x + i];
}
} else {
// Extract Y channel from YUV.
for (int i=0; i<MAIN_FB()->w; i++) {
dst[i] = src[MAIN_FB()->x * 2 + i * 2];
}
}
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_RGB565:
dst16 += (line - MAIN_FB()->y) * MAIN_FB()->w;
for (int i=0; i<MAIN_FB()->w; i++) {
dst16[i] = src16[MAIN_FB()->x + i];
}
break;
case PIXFORMAT_JPEG:
break;
default:
break;
}
}
line++;
}
// This is the default snapshot function, which can be replaced in sensor_init functions. This function
// uses the DCMI and DMA to capture frames and each line is processed in the DCMI_DMAConvCpltUser function.
int sensor_snapshot(sensor_t *sensor, image_t *image, line_filter_t line_filter_func, void *line_filter_args)
{
uint32_t addr, length, tick_start;
// Compress the framebuffer for the IDE preview, only if it's not the first frame,
// the framebuffer is enabled and the image sensor does not support JPEG encoding.
// Note: This doesn't run unless the IDE is connected and the framebuffer is enabled.
fb_update_jpeg_buffer();
// Make sure the raw frame fits into the FB. If it doesn't it will be cropped if
// the format is set to GS, otherwise the pixel format will be swicthed to BAYER.
sensor_check_buffsize();
// The user may have changed the MAIN_FB width or height on the last image so we need
// to restore that here. We don't have to restore bpp because that's taken care of
// already in the code below. Note that we do the JPEG compression above first to save
// the FB of whatever the user set it to and now we restore.
MAIN_FB()->w = MAIN_FB()->u;
MAIN_FB()->h = MAIN_FB()->v;
// We use the stored frame size to read the whole frame. Note that cropping is
// done in the line function using the diemensions stored in MAIN_FB()->x,y,w,h.
uint32_t w = resolution[sensor->framesize][0];
uint32_t h = resolution[sensor->framesize][1];
// Set line filter function and args.
sensor_set_line_filter(line_filter_func, line_filter_args);
// Setup the size and address of the transfer
switch (sensor->pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_YUV422:
// RGB/YUV read 2 bytes per pixel.
length = (w * h * 2)/4;
addr = (uint32_t) &_line_buf;
break;
case PIXFORMAT_BAYER:
// BAYER/RAW: 1 byte per pixel
length = (w * h * 1)/4;
addr = (uint32_t) &_line_buf;
break;
case PIXFORMAT_GRAYSCALE:
// 1/2BPP Grayscale.
length = (w * h * sensor->gs_bpp)/4;
addr = (uint32_t) &_line_buf;
break;
case PIXFORMAT_JPEG:
// Sensor has hardware JPEG set max frame size.
length = MAX_XFER_SIZE;
addr = (uint32_t) (MAIN_FB()->pixels);
break;
default:
return -1;
}
// Clear line counter
line = 0;
// Snapshot start tick
tick_start = HAL_GetTick();
// Enable DMA IRQ
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
if (sensor->pixformat == PIXFORMAT_JPEG) {
// Start a regular transfer
HAL_DCMI_Start_DMA(&DCMIHandle,
DCMI_MODE_SNAPSHOT, addr, length);
} else {
// Start a multibuffer transfer (line by line)
HAL_DCMI_Start_DMA_MB(&DCMIHandle,
DCMI_MODE_SNAPSHOT, addr, length, h);
}
// Wait for frame
while ((DCMI->CR & DCMI_CR_CAPTURE) != 0) {
// Wait for interrupt
__WFI();
if ((HAL_GetTick() - tick_start) >= 3000) {
// Sensor timeout, most likely a HW issue.
// Abort the DMA request.
HAL_DMA_Abort(&DMAHandle);
return -1;
}
}
// Abort DMA transfer.
// Note: In JPEG mode the DMA will still be waiting for data since
// the max frame size is set, so we need to abort the DMA transfer.
HAL_DMA_Abort(&DMAHandle);
// Disable DMA IRQ
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
// Fix the BPP
switch (sensor->pixformat) {
case PIXFORMAT_GRAYSCALE:
MAIN_FB()->bpp = 1;
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_RGB565:
MAIN_FB()->bpp = 2;
break;
case PIXFORMAT_BAYER:
MAIN_FB()->bpp = 3;
break;
case PIXFORMAT_JPEG:
// Read the number of data items transferred
MAIN_FB()->bpp = (MAX_XFER_SIZE - __HAL_DMA_GET_COUNTER(&DMAHandle))*4;
break;
default:
break;
}
// Set the user image.
if (image != NULL) {
image->w = MAIN_FB()->w;
image->h = MAIN_FB()->h;
image->bpp = MAIN_FB()->bpp;
image->pixels = MAIN_FB()->pixels;
}
return 0;
}