From 7c99afef6ae9e23c01e034ce82e5e62ddc46189a Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sat, 19 Aug 2017 01:45:30 +0200 Subject: [PATCH] Implemented OV5640 --- tracker/software/Makefile | 1 + tracker/software/board/board.h | 2 +- tracker/software/config.c | 31 +- tracker/software/config.h | 2 +- tracker/software/drivers/ov2640.c | 340 +------ tracker/software/drivers/ov5640.c | 1205 +++++++++++++++++++++++ tracker/software/drivers/ov5640.h | 22 + tracker/software/drivers/wrapper/pi2c.c | 15 + tracker/software/drivers/wrapper/pi2c.h | 3 + tracker/software/mcuconf.h | 4 +- tracker/software/modules/image.c | 173 ++-- tracker/software/types.h | 1 - 12 files changed, 1387 insertions(+), 412 deletions(-) create mode 100644 tracker/software/drivers/ov5640.c create mode 100644 tracker/software/drivers/ov5640.h diff --git a/tracker/software/Makefile b/tracker/software/Makefile index fe9c709..531864b 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -135,6 +135,7 @@ CSRC = $(STARTUPSRC) \ drivers/bme280.c \ drivers/pac1720.c \ drivers/ov2640.c \ + drivers/ov5640.c \ drivers/flash/flash.c \ drivers/flash/helper.c \ drivers/flash/ihex.c \ diff --git a/tracker/software/board/board.h b/tracker/software/board/board.h index c95fb4f..e9974c2 100644 --- a/tracker/software/board/board.h +++ b/tracker/software/board/board.h @@ -272,7 +272,7 @@ // Camera pins #define LINE_CAM_XCLK PAL_LINE(GPIOA, 8U) -#define LINE_CAM_PCLK PAL_LINE(GPIOC, 6U) +#define LINE_CAM_PCLK PAL_LINE(GPIOC, 0U) #define LINE_CAM_HREF PAL_LINE(GPIOC, 2U) #define LINE_CAM_VSYNC PAL_LINE(GPIOC, 1U) #define LINE_CAM_D2 PAL_LINE(GPIOA, 0U) diff --git a/tracker/software/config.c b/tracker/software/config.c index 899e8d8..698a844 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -3,8 +3,7 @@ #include "debug.h" module_conf_t config[9]; -uint8_t ssdv_buffer[65535]; -uint8_t ssdv_buffer2[65535]; +uint8_t ssdv_buffer[65535] __attribute__((aligned(1024))); /* * Position module configuration description @@ -174,12 +173,12 @@ void start_user_modules(void) // Module POSITION, APRS 2m AFSK config[0].power = 127; // Power 20 dBm config[0].protocol = PROT_APRS_AFSK; // Protocol APRS, modulation AFSK - config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation - config[0].frequency.hz = 144800000; // Default frequency 144.800 MHz + config[0].frequency.type = FREQ_STATIC; // Dynamic frequency allocation + config[0].frequency.hz = 144390000; // Default frequency 144.800 MHz config[0].init_delay = 0; // Module startup delay in msec config[0].trigger.type = TRIG_NEW_POINT; // Trigger when new track point released chsnprintf(config[0].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign - config[0].aprs_conf.ssid = 12; // APRS SSID + config[0].aprs_conf.ssid = 11; // APRS SSID config[0].aprs_conf.symbol = SYM_BALLOON; // APRS Symbol chsnprintf(config[0].aprs_conf.path, 16, "WIDE1-1"); // APRS Path config[0].aprs_conf.preamble = 300; // APRS Preamble @@ -191,7 +190,7 @@ void start_user_modules(void) config[0].aprs_conf.tel_enc = TRUE; // Transmit Telemetry encoding information activated config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec chsnprintf(config[0].aprs_conf.tel_comment, 30, "http://ssdv.habhub.org/DL7AD");// Telemetry comment - start_position_thread(&config[0]); + //start_position_thread(&config[0]); // Module POSITION, APRS 2m 2GFSK /*config[1].power = 127; // Power 10 dBm @@ -235,23 +234,23 @@ void start_user_modules(void) // Module IMAGE, APRS 2m AFSK low-duty cycle config[3].power = 127; // Power 20 dBm config[3].protocol = PROT_APRS_AFSK; // Protocol APRS SSDV, modulation AFSK - config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation - config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz + config[3].frequency.type = FREQ_STATIC; // Dynamic frequency allocation + config[3].frequency.hz = 144390000; // Transmission frequency 144.800 MHz config[3].init_delay = 0; // Module startup delay in msec - config[3].packet_spacing = 15000; // Packet spacing in ms + //config[3].packet_spacing = 30000; // Packet spacing in ms //config[3].sleep_conf.type = SLEEP_WHEN_ISOL_BELOW_THRES; //config[3].sleep_conf.isol_thres = 3; config[3].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling) config[3].trigger.timeout = 10; // Timeout 10 sec chsnprintf(config[3].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign - config[3].aprs_conf.ssid = 12; // APRS SSID + config[3].aprs_conf.ssid = 11; // APRS SSID config[3].aprs_conf.preamble = 300; // APRS Preamble chsnprintf(config[3].ssdv_conf.callsign, 7, "DL7AD"); // SSDV Callsign config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size config[3].ssdv_conf.res = RES_QVGA; // Resolution VGA config[3].ssdv_conf.redundantTx = true; // Transmit packets twice - start_image_thread(&config[3]); + //start_image_thread(&config[3]); // Module POSITION, Morse 2m OOK /*config[4].power = 127; // Power 10 dBm @@ -272,7 +271,7 @@ void start_user_modules(void) config[5].gfsk_conf.speed = 9600; // 2GFSK Speed config[5].frequency.type = FREQ_STATIC; // Static frequency allocation config[5].frequency.hz = 144860000; // Transmission frequency 144.860 MHz - config[5].init_delay = 60000; // Module startup delay in msec + //config[5].init_delay = 60000; // Module startup delay in msec config[5].sleep_conf.type = SLEEP_WHEN_VBAT_BELOW_THRES; config[5].sleep_conf.vbat_thres = 4000; config[5].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling) @@ -281,11 +280,11 @@ void start_user_modules(void) config[5].aprs_conf.ssid = 12; // APRS SSID config[5].aprs_conf.preamble = 100; // APRS Preamble chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign - config[5].ssdv_conf.ram_buffer = ssdv_buffer2; // Camera buffer - config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer2); // Buffer size + config[5].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer + config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size config[5].ssdv_conf.res = RES_QVGA; // Resolution XGA - config[5].ssdv_conf.redundantTx = true; // Transmit packets twice - //start_image_thread(&config[5]); + //config[5].ssdv_conf.redundantTx = true; // Transmit packets twice + start_image_thread(&config[5]); // Module IMAGE, SSDV 2m 2FSK /*config[6].power = 127; // Power 20 dBm diff --git a/tracker/software/config.h b/tracker/software/config.h index cbaddad..327972c 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -8,7 +8,7 @@ #include "radio.h" #include "sleep.h" -#define TRACK_CYCLE_TIME 30 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */ +#define TRACK_CYCLE_TIME 60 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */ #define LOG_CYCLE_TIME 30 /* Log cycle time in seconds */ #define LOG_FLASH_ADDR1 0x080C0000 /* Log flash memory address 1 */ diff --git a/tracker/software/drivers/ov2640.c b/tracker/software/drivers/ov2640.c index f3e48bf..6244e4f 100644 --- a/tracker/software/drivers/ov2640.c +++ b/tracker/software/drivers/ov2640.c @@ -312,12 +312,13 @@ static const struct regval_list ov2640_init_regs[] = { { 0x2e, 0xdf }, { BANK_SEL, BANK_SEL_SENS }, { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(4) }, + { CLKRC, CLKRC_DIV_SET(8) }, { COM2, COM2_OCAP_Nx_SET(3) }, { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN }, + { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, //~ { AEC, 0x00 }, { COM9, COM9_AGC_GAIN_8x | 0x08}, + //{ COM10, COM10_PCLK_RISE }, { 0x2c, 0x0c }, { 0x33, 0x78 }, { 0x3a, 0x33 }, @@ -648,313 +649,20 @@ bool OV2640_Snapshot2RAM(void) bool OV2640_BufferOverflow(void) { - return ov2640_conf->image_buffer[0] != 0xFF || ov2640_conf->image_buffer[1] != 0xD8; // Check for JPEG SOI header + return ov2640_conf->ram_buffer[0] != 0xFF || ov2640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header } uint32_t OV2640_getBuffer(uint8_t** buffer) { - *buffer = ov2640_conf->ram_buffer[0]; + *buffer = ov2640_conf->ram_buffer; return ov2640_conf->size_sampled; } -#if 1 - -const stm32_dma_stream_t *dmastp; -static bool image_finished; -uint8_t dma_index; - -inline int32_t dma_start(void) { - /* Clear any pending inerrupts. */ - dmaStreamClearInterrupt(dmastp); - dmaStreamEnable(dmastp); - return 0; -} - -inline int32_t dma_stop(void) { - dmaStreamDisable(dmastp); - dmaStreamRelease(dmastp); - return 0; -} - -/** - * @brief Get DMA stream current target. - * @note This function can be invoked in both ISR or thread context. - * @pre The stream must have been allocated using @p dmaStreamAllocate(). - * @post After use the stream can be released using @p dmaStreamRelease(). - * - * @param[in] dmastp pointer to a stm32_dma_stream_t structure - * @return Current target index - * - * @special - */ -#define dmaStreamGetCurrentTarget(dmastp) \ - ((uint8_t)(((dmastp)->stream->CR >> DMA_SxCR_CT_Pos) & 1U)) - -static void dma_interrupt(void *p, uint32_t flags) { - /* No parameter passed. */ - (void)p; - - if (flags & STM32_DMA_ISR_HTIF) { - /* - * Half transfer complete. - * Check if DMA is writing to the last buffer. - */ - if (dma_index == (DMA_BUFFERS - 1)) { - /* - * This is the last buffer so we have to terminate DMA. - * Because the DBM switch is done in h/w. - * Stopping DMA in interrupt would be too late. - * DMA would write beyond buffer or overwrite prior buffer (if MxAR not updated). - * - * So stop DMA and TIM DMA trigger. - */ - dmaStreamClearInterrupt(dmastp); - chSysLockFromISR(); - dma_stop(); - TIM1->DIER &= ~TIM_DIER_TDE; - image_finished = true; - /* Disable HSYNC and VYSNC edge interrupts. */ - nvicDisableVector(EXTI1_IRQn); - nvicDisableVector(EXTI2_IRQn); - chSysUnlockFromISR(); - return; - } - /* - * Else Safe to allow buffer to fill. - * DMA DBM will switch buffers in h/w this one is full. - * Just clear the interrupt and wait for TCIF. - */ - dmaStreamClearInterrupt(dmastp); - return; - } - if (flags & STM32_DMA_ISR_TCIF) { - /* - * Full transfer complete. - * Update non-active memory address register. - * DMA will use new address at h/w DBM switch. - */ - dmaStreamClearInterrupt(dmastp); - - if (dmaStreamGetCurrentTarget(dmastp) == 1) { - dmaStreamSetMemory0(dmastp, ram_buffer[++dma_index]); - } else { - dmaStreamSetMemory1(dmastp, ram_buffer[++dma_index]); - } - return; - } -} - -// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c) - -CH_FAST_IRQ_HANDLER(Vector60) { - //CH_IRQ_PROLOGUE(); - - //uint8_t gpioc = GPIOC->IDR; - // HREF handling - if(GPIOC->IDR & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or continuation of dma. - * Set TIM8_CH1 capture input rising edge as DMA trigger. - */ - TIM8->DIER |= TIM_DIER_CC1DE; - } else { - /* On falling edge remove TIM8 trigger for DMA. */ - TIM8->DIER &= ~TIM_DIER_CC1DE; - } - - EXTI->PR |= EXTI_PR_PR2; - //CH_IRQ_EPILOGUE(); -} - -bool vsync = false; - -// This is the vector for EXTI1 (stolen from hal_ext_lld_isr.c) -/* - * Note: VSYNC is a pulse the full length of a frame. - * This is contrary to the OV2640 datasheet which shows VSYNC as pulses. -*/ -CH_FAST_IRQ_HANDLER(Vector5C) { - //CH_IRQ_PROLOGUE(); - - // VSYNC handling - if(!vsync && palReadLine(LINE_CAM_VSYNC)) { - /* - * Rising edge of VSYNC . - */ - - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - vsync = true; - } else if(vsync && !palReadLine(LINE_CAM_VSYNC)) { - /* VSYNC falling edge - end of JPEG frame. - * Stop & release the DMA channel. - * Disable TIM8 trigger of DMA - * These should have already been disabled in DMA interrupt if was filled. - */ - dma_stop(); - TIM8->DIER &= ~TIM_DIER_CC1DE; - - /* Disable HSYNC & VYSNC edge interrupts. */ - nvicDisableVector(EXTI1_IRQn); - nvicDisableVector(EXTI2_IRQn); - /* Turn on capture LED and signal the semaphore (data can be processed). */ - palSetLine(LINE_IO_LED1); - image_finished = true; - vsync = false; - } - - palToggleLine(LINE_IO_LED1); - - EXTI->PR |= EXTI_PR_PR1; - //CH_IRQ_EPILOGUE(); -} - -uint8_t ram_buffer[DMA_BUFFERS][DMA_SIZE] __attribute__((aligned(DMA_ALIGNMENT))); - -bool OV2640_Capture(void) -{ - /* - * Note: - * If there are no Chibios devices enabled that use DMA then... - * In makefile add entry to UDEFS: - * UDEFS = -DSTM32_DMA_REQUIRED - */ - - /* - * Setup DMA for transfer on TIM8_CH1 - DMA2 stream 2, channel 7. - * Direction is peripheral (GPIO) to memory. - * Set highest priority (3). - * Further down we set DMA FIFO to write to memory on FIFO full (4 words). - * GPIO is 8 bit, memory is 32 bit, memory burst write using 4 beats. - */ - dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 2)); - uint32_t dmamode = STM32_DMA_CR_CHSEL(7) | - STM32_DMA_CR_PL(3) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_WORD | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DBM | - STM32_DMA_CR_MBURST_INCR4; - - chSysLock(); - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - /* - * Buffer address must be word aligned. - * Also note requirement for burst transfers. - * Bursts must not cross a 1K address boundary. - * See RM0430 9.3.12 - * DMA buffers for ov2640_conf should be defined like this... - * #define DMA_ALIGNMENT 1024 - * #define DMA_SIZE DMA_ALIGNMENT - * #define DMA_BUFFERS 6 - * uint8_t ram_buffer[DMA_BUFFERS][DMA_SIZE] __attribute__((aligned(DMA_ALIGNMENT))); - * - */ - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer[0]); - dmaStreamSetMemory1(dmastp, ov2640_conf->ram_buffer[1]); - /* - * Transfer size (NDT) must be a multiple of 4 bytes. - * See RM0430 9.3.11 - */ - dmaStreamSetTransactionSize(dmastp, DMA_SIZE); - //dmaStreamSetTransactionSize(dmastp, (ov2640_conf->ram_size)); - - /* Setup DMA... - * Disable direct mode (enable FIFO). - * Set FIFO to transfer at full. - * Clear any outstanding interrupt. - */ - dmaStreamSetMode(dmastp, dmamode); - dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); - dmaStreamClearInterrupt(dmastp); - /* - * Setup timer to trigger DMA. - * We have to use TIM8 because... - * - TIM8_CH1 is in DMA2 and we need DMA2 for peripheral -> memory transfer - */ - rccResetTIM8(); - rccEnableTIM8(FALSE); - - TIM8->CCMR1 = TIM_CCMR1_CC1S_0; // Select channel 1 as input - TIM8->CCER |= TIM_CCER_CC1E; // Enable capture channel - TIM8->CCER |= TIM_CCER_CC1P; // Select trailing edge - - - - // Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF) - SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC | SYSCFG_EXTICR1_EXTI2_PC; - - EXTI->IMR = EXTI_IMR_MR1 | EXTI_IMR_MR2; // Activate interrupt for chan1 (=>PC1) and chan2 (=>PC2) - EXTI->RTSR = EXTI_RTSR_TR1 | EXTI_RTSR_TR2; // Listen on rising edge - EXTI->FTSR = EXTI_FTSR_TR1 | EXTI_FTSR_TR2; // Listen on falling edge too - /* Reset any pending interrupt. */ - EXTI->PR |= EXTI_PR_PR1; - EXTI->PR |= EXTI_PR_PR2; - /* Enable interrupt vectors. */ - nvicEnableVector(EXTI1_IRQn, 2); - nvicEnableVector(EXTI2_IRQn, 1); - /* Start DMA and timer DMA. */ - dma_start(); - TIM8->DIER |= TIM_DIER_CC1DE; - - image_finished = false; - dma_index = 0; - chSysUnlock(); - - do { - osalThdSleepMilliseconds(100); - } while(!image_finished); - - unit8_t *image_buffer = ov2640_conf->ram_buffer[0]; - int32_t size=65535; - while(ov2640_conf->image_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->image_buffer[0], ov2640_conf->image_buffer[1], ov2640_conf->image_buffer[2], ov2640_conf->image_buffer[3]); - - TRACE_DEBUG("CAM > Have a look for SOI"); - uint32_t soi; // Start of Image - for(soi=0; soi<65533; soi++) - { - if(ov2640_conf->image_buffer[soi] == 0xFF && ov2640_conf->image_buffer[soi+1] == 0xD8) - break; - } - - if(soi == 65533) { - TRACE_ERROR("CAM > Could not find SOI flag"); - return false; // We failed to sample the picture correctly because we didn't find the JPEG SOI flag - } - - TRACE_DEBUG("SOI=%d", soi) - - // Found SOI, move bytes - for(uint32_t i=0; i<65535; i++) - ov2640_conf->image_buffer[i] = ov2640_conf->image_buffer[i+soi]; - - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->image_buffer[0], ov2640_conf->image_buffer[1], ov2640_conf->image_buffer[2], ov2640_conf->image_buffer[3]); - TRACE_INFO("CAM > Capture finished"); - - return true; -} -#else - -bool image_finished; -bool sample_byte; - bool OV2640_Capture(void) { TRACE_INFO("CAM > Start capture"); - while(palReadLine(LINE_CAM_VSYNC)); while(!palReadLine(LINE_CAM_VSYNC)); - uint8_t gpioc; uint8_t gpioa; ov2640_conf->size_sampled = 0; @@ -963,9 +671,7 @@ bool OV2640_Capture(void) do { gpioc = GPIOC->IDR & 0x7; } while((gpioc & 0x1) != 0x1); // Wait for PCLK to rise - gpioa = GPIOA->IDR; - switch(gpioc) { case 0x3: break; @@ -975,41 +681,32 @@ bool OV2640_Capture(void) default: return true; } - // Wait for falling edge while(GPIOC->IDR & 0x1); } } - - -#endif - /** - * Initializes GPIO (for pseudo DCMI) + * Initializes GPIO (for DCMI) * The high speed clock supports communication by I2C (XCLK = 16MHz) */ void OV2640_InitGPIO(void) { - palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - /* PC6 AF3 = TIM8_CH1. */ - palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(3)); - palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(1)); + palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); palSetLineMode(LINE_CAM_XCLK, PAL_MODE_ALTERNATE(0)); - palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST); palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL); palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL); - - palSetPadMode(GPIOC, 0, PAL_MODE_INPUT); - palSetPadMode(GPIOB, 15, PAL_MODE_INPUT); } void OV2640_TransmitConfig(void) @@ -1076,7 +773,7 @@ void OV2640_init(ssdv_conf_t *config) { // Clearing buffer uint32_t i; for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[0][i] = 0; + ov2640_conf->ram_buffer[i] = 0; TRACE_INFO("CAM > Init pins"); OV2640_InitGPIO(); @@ -1145,3 +842,4 @@ bool OV2640_isAvailable(void) return ret; } + diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c new file mode 100644 index 0000000..df74c99 --- /dev/null +++ b/tracker/software/drivers/ov5640.c @@ -0,0 +1,1205 @@ +#include "ch.h" +#include "hal.h" +#include "ov5640.h" +#include "pi2c.h" +#include "board.h" +#include "defines.h" +#include "debug.h" +#include + +#define OV5640_I2C_ADR 0x3C + + + +struct regval_list { + uint16_t reg; + uint8_t val; +}; + + +static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = +{ + { 0x4740, 0x24 }, + + { 0x4050, 0x6e }, + { 0x4051, 0x8f }, + + { 0x3008, 0x42 }, + { 0x3103, 0x03 }, + { 0x3017, 0x7f }, + { 0x3018, 0xff }, + { 0x302c, 0x02 }, + { 0x3108, 0x01 }, + { 0x3630, 0x2e },//2e + { 0x3632, 0xe2 }, + { 0x3633, 0x23 },//23 + { 0x3621, 0xe0 }, + { 0x3704, 0xa0 }, + { 0x3703, 0x5a }, + { 0x3715, 0x78 }, + { 0x3717, 0x01 }, + { 0x370b, 0x60 }, + { 0x3705, 0x1a }, + { 0x3905, 0x02 }, + { 0x3906, 0x10 }, + { 0x3901, 0x0a }, + { 0x3731, 0x12 }, + { 0x3600, 0x08 }, + { 0x3601, 0x33 }, + { 0x302d, 0x60 }, + { 0x3620, 0x52 }, + { 0x371b, 0x20 }, + { 0x471c, 0x50 }, + + { 0x3a18, 0x00 }, + { 0x3a19, 0xf8 }, + + { 0x3635, 0x1c },//1c + { 0x3634, 0x40 }, + { 0x3622, 0x01 }, + + { 0x3c04, 0x28 }, + { 0x3c05, 0x98 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x08 }, + { 0x3c08, 0x00 }, + { 0x3c09, 0x1c }, + { 0x3c0a, 0x9c }, + { 0x3c0b, 0x40 }, + + { 0x3820, 0x41 }, + { 0x3821, 0x01 }, //07 + + //windows setup + { 0x3800, 0x00 }, + { 0x3801, 0x00 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x0a }, + { 0x3805, 0x3f }, + { 0x3806, 0x07 }, + { 0x3807, 0x9b }, + { 0x3808, 0x05 }, + { 0x3809, 0x00 }, + { 0x380a, 0x03 }, + { 0x380b, 0xc0 }, + { 0x3810, 0x00 }, + { 0x3811, 0x10 }, + { 0x3812, 0x00 }, + { 0x3813, 0x06 }, + { 0x3814, 0x31 }, + { 0x3815, 0x31 }, + + { 0x3034, 0x1a }, + { 0x3035, 0x41 }, //15fps + { 0x3036, 0x46 }, + { 0x3037, 0x13 }, + { 0x3038, 0x00 }, + { 0x3039, 0x00 }, + + { 0x380c, 0x07 }, + { 0x380d, 0x68 }, + { 0x380e, 0x03 }, //03 + { 0x380f, 0xd8 }, //d8 + + { 0x3c01, 0xb4 }, + { 0x3c00, 0x04 }, + { 0x3a08, 0x00 }, + { 0x3a09, 0x93 }, + { 0x3a0e, 0x06 }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0x7b }, + { 0x3a0d, 0x08 }, + + { 0x3a00, 0x3c }, //15fps-10fps + { 0x3a02, 0x05 }, + { 0x3a03, 0xc4 }, + { 0x3a14, 0x05 }, + { 0x3a15, 0xc4 }, + + { 0x3618, 0x00 }, + { 0x3612, 0x29 }, + { 0x3708, 0x64 }, + { 0x3709, 0x52 }, + { 0x370c, 0x03 }, + + { 0x4001, 0x02 }, + { 0x4004, 0x02 }, + { 0x3000, 0x00 }, + { 0x3002, 0x1c }, + { 0x3004, 0xff }, + { 0x3006, 0xc3 }, + { 0x300e, 0x58 }, + { 0x302e, 0x00 }, + { 0x4300, 0x30 }, + { 0x501f, 0x00 }, + { 0x4713, 0x03 }, + { 0x4407, 0x04 }, + { 0x460b, 0x35 }, + { 0x460c, 0x22 },//add by bright + { 0x3824, 0x01 },//add by bright + { 0x5001, 0xa3 }, + + { 0x3406, 0x01 },//awbinit + { 0x3400, 0x06 }, + { 0x3401, 0x80 }, + { 0x3402, 0x04 }, + { 0x3403, 0x00 }, + { 0x3404, 0x06 }, + { 0x3405, 0x00 }, + //awb + { 0x5180, 0xff }, + { 0x5181, 0xf2 }, + { 0x5182, 0x00 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x16 }, + { 0x5187, 0x16 }, + { 0x5188, 0x16 }, + { 0x5189, 0x62 }, + { 0x518a, 0x62 }, + { 0x518b, 0xf0 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x50 }, + { 0x518e, 0x30 }, + { 0x518f, 0x30 }, + { 0x5190, 0x50 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x04 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x03 }, + { 0x5197, 0x01 }, + { 0x5198, 0x04 }, + { 0x5199, 0x12 }, + { 0x519a, 0x04 }, + { 0x519b, 0x00 }, + { 0x519c, 0x06 }, + { 0x519d, 0x82 }, + { 0x519e, 0x38 }, + //color matrix + { 0x5381, 0x1e }, + { 0x5382, 0x5b }, + { 0x5383, 0x14 }, + { 0x5384, 0x06 }, + { 0x5385, 0x82 }, + { 0x5386, 0x88 }, + { 0x5387, 0x7c }, + { 0x5388, 0x60 }, + { 0x5389, 0x1c }, + { 0x538a, 0x01 }, + { 0x538b, 0x98 }, + //sharp&noise + { 0x5300, 0x08 }, + { 0x5301, 0x30 }, + { 0x5302, 0x3f }, + { 0x5303, 0x10 }, + { 0x5304, 0x08 }, + { 0x5305, 0x30 }, + { 0x5306, 0x18 }, + { 0x5307, 0x28 }, + { 0x5309, 0x08 }, + { 0x530a, 0x30 }, + { 0x530b, 0x04 }, + { 0x530c, 0x06 }, + //gamma + { 0x5480, 0x01 }, + { 0x5481, 0x06 }, + { 0x5482, 0x12 }, + { 0x5483, 0x24 }, + { 0x5484, 0x4a }, + { 0x5485, 0x58 }, + { 0x5486, 0x65 }, + { 0x5487, 0x72 }, + { 0x5488, 0x7d }, + { 0x5489, 0x88 }, + { 0x548a, 0x92 }, + { 0x548b, 0xa3 }, + { 0x548c, 0xb2 }, + { 0x548d, 0xc8 }, + { 0x548e, 0xdd }, + { 0x548f, 0xf0 }, + { 0x5490, 0x15 }, + //UV adjust + { 0x5580, 0x06 }, + { 0x5583, 0x40 }, + { 0x5584, 0x20 }, + { 0x5589, 0x10 }, + { 0x558a, 0x00 }, + { 0x558b, 0xf8 }, + //lens shading + { 0x5000, 0xa7 }, + { 0x5800, 0x20 }, + { 0x5801, 0x19 }, + { 0x5802, 0x17 }, + { 0x5803, 0x16 }, + { 0x5804, 0x18 }, + { 0x5805, 0x21 }, + { 0x5806, 0x0F }, + { 0x5807, 0x0A }, + { 0x5808, 0x07 }, + { 0x5809, 0x07 }, + { 0x580a, 0x0A }, + { 0x580b, 0x0C }, + { 0x580c, 0x0A }, + { 0x580d, 0x03 }, + { 0x580e, 0x01 }, + { 0x580f, 0x01 }, + { 0x5810, 0x03 }, + { 0x5811, 0x09 }, + { 0x5812, 0x0A }, + { 0x5813, 0x03 }, + { 0x5814, 0x01 }, + { 0x5815, 0x01 }, + { 0x5816, 0x03 }, + { 0x5817, 0x08 }, + { 0x5818, 0x10 }, + { 0x5819, 0x0A }, + { 0x581a, 0x06 }, + { 0x581b, 0x06 }, + { 0x581c, 0x08 }, + { 0x581d, 0x0E }, + { 0x581e, 0x22 }, + { 0x581f, 0x18 }, + { 0x5820, 0x13 }, + { 0x5821, 0x12 }, + { 0x5822, 0x16 }, + { 0x5823, 0x1E }, + { 0x5824, 0x64 }, + { 0x5825, 0x2A }, + { 0x5826, 0x2C }, + { 0x5827, 0x2A }, + { 0x5828, 0x46 }, + { 0x5829, 0x2A }, + { 0x582a, 0x26 }, + { 0x582b, 0x24 }, + { 0x582c, 0x26 }, + { 0x582d, 0x2A }, + { 0x582e, 0x28 }, + { 0x582f, 0x42 }, + { 0x5830, 0x40 }, + { 0x5831, 0x42 }, + { 0x5832, 0x08 }, + { 0x5833, 0x28 }, + { 0x5834, 0x26 }, + { 0x5835, 0x24 }, + { 0x5836, 0x26 }, + { 0x5837, 0x2A }, + { 0x5838, 0x44 }, + { 0x5839, 0x4A }, + { 0x583a, 0x2C }, + { 0x583b, 0x2a }, + { 0x583c, 0x46 }, + { 0x583d, 0xCE }, + + { 0x5688, 0x22 }, + { 0x5689, 0x22 }, + { 0x568a, 0x42 }, + { 0x568b, 0x24 }, + { 0x568c, 0x42 }, + { 0x568d, 0x24 }, + { 0x568e, 0x22 }, + { 0x568f, 0x22 }, + + { 0x5025, 0x00 }, + + { 0x3a0f, 0x30 }, + { 0x3a10, 0x28 }, + { 0x3a1b, 0x30 }, + { 0x3a1e, 0x28 }, + { 0x3a11, 0x61 }, + { 0x3a1f, 0x10 }, + + { 0x4005, 0x1a }, + { 0x3406, 0x00 },//awbinit + { 0x3503, 0x00 },//awbinit + { 0x3008, 0x02 }, + { 0xffff, 0xff }, +}; + + + +static const struct regval_list ov5640_vga_preview[] = +{ + // YUV VGA 30fps, night mode 5fps + // Input Clock = 24Mhz, PCLK = 56MHz + { 0x3035, 0x41 }, // PLL + { 0x3036, 0x46 }, // PLL + { 0x3c07, 0x08 }, // light meter 1 threshold [7:0] + { 0x3820, 0x41 }, // Sensor flip off, ISP flip on + { 0x3821, 0x01 }, // Sensor mirror on, ISP mirror on, H binning on + { 0x3814, 0x31 }, // X INC + { 0x3815, 0x31 }, // Y INC + { 0x3800, 0x00 }, // HS + { 0x3801, 0x00 }, // HS + { 0x3802, 0x00 }, // VS + { 0x3803, 0x04 }, // VS + { 0x3804, 0x0a }, // HW (HE) + { 0x3805, 0x3f }, // HW (HE) + { 0x3806, 0x07 }, // VH (VE) + { 0x3807, 0x9b }, // VH (VE) + { 0x3808, 0x02 }, // DVPHO + { 0x3809, 0x80 }, // DVPHO + { 0x380a, 0x01 }, // DVPVO + { 0x380b, 0xe0 }, // DVPVO + { 0x380c, 0x07 }, // HTS + { 0x380d, 0x68 }, // HTS + { 0x380e, 0x03 }, // VTS + { 0x380f, 0xd8 }, // VTS + { 0x3813, 0x06 }, // Timing Voffset + { 0x3618, 0x00 }, + { 0x3612, 0x29 }, + { 0x3709, 0x52 }, + { 0x370c, 0x03 }, + { 0x3a02, 0x17 }, // 60Hz max exposure, night mode 5fps + { 0x3a03, 0x10 }, // 60Hz max exposure + // banding filters are calculated automatically in camera driver + //{ 0x3a08, 0x01 }, // B50 step + //{ 0x3a09, 0x27 }, // B50 step + //{ 0x3a0a, 0x00 }, // B60 step + //{ 0x3a0b, 0xf6 }, // B60 step + //{ 0x3a0e, 0x03 }, // 50Hz max band + //{ 0x3a0d, 0x04 }, // 60Hz max band + { 0x3a14, 0x17 }, // 50Hz max exposure, night mode 5fps + { 0x3a15, 0x10 }, // 50Hz max exposure + { 0x4004, 0x02 }, // BLC 2 lines + { 0x3002, 0x1c }, // reset JFIFO, SFIFO, JPEG + { 0x3006, 0xc3 }, // disable clock of JPEG2x, JPEG + { 0x4713, 0x03 }, // JPEG mode 3 + { 0x4407, 0x04 }, // Quantization scale + { 0x460b, 0x35 }, + { 0x460c, 0x22 }, + { 0x4837, 0x22 }, // DVP CLK divider + { 0x3824, 0x02 }, // DVP CLK divider + { 0x5001, 0xa3 }, // SDE on, scale on, UV average off, color matrix on, AWB on + { 0x3503, 0x00 }, // AEC/AGC on +}; + +static const struct regval_list OV5640_RGB_QVGA[] = +{ + {0x3008, 0x02}, + {0x3035, 0x41}, + {0x4740, 0x21}, + {0x4300, 0x61}, + {0x3808, 0x01}, + {0x3809, 0x40}, + {0x380a, 0x00}, + {0x380b, 0xf0}, + {0x501f, 0x01}, + {0xffff, 0xff}, +}; + +//2592x1944 QSXGA +static const struct regval_list OV5640_JPEG_QSXGA[] = +{ + {0x3820 ,0x40}, + {0x3821 ,0x26}, + {0x3814 ,0x11}, + {0x3815 ,0x11}, + {0x3803 ,0x00}, + {0x3807 ,0x9f}, + {0x3808 ,0x0a}, + {0x3809 ,0x20}, + {0x380a ,0x07}, + {0x380b ,0x98}, + {0x380c ,0x0b}, + {0x380d ,0x1c}, + {0x380e ,0x07}, + {0x380f ,0xb0}, + {0x3813 ,0x04}, + {0x3618 ,0x04}, + {0x3612 ,0x4b}, + {0x3708 ,0x64}, + {0x3709 ,0x12}, + {0x370c ,0x00}, + {0x3a02 ,0x07}, + {0x3a03 ,0xb0}, + {0x3a0e ,0x06}, + {0x3a0d ,0x08}, + {0x3a14 ,0x07}, + {0x3a15 ,0xb0}, + {0x4001 ,0x02}, + {0x4004 ,0x06}, + {0x3002 ,0x00}, + {0x3006 ,0xff}, + {0x3824 ,0x04}, + {0x5001 ,0x83}, + {0x3036 ,0x69}, + {0x3035 ,0x41}, + {0x4005 ,0x1A}, + {0xffff, 0xff}, +}; + +//5MP +static const struct regval_list OV5640_5MP_JPEG[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0xA }, + {0x3809 ,0x20}, + {0x380a ,0x7 }, + {0x380b ,0x98}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//320x240 QVGA +static const struct regval_list OV5640_QSXGA2QVGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x1 }, + {0x3809 ,0x40}, + {0x380a ,0x0 }, + {0x380b ,0xf0}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//640x480 VGA +static const struct regval_list OV5640_QSXGA2VGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x2 }, + {0x3809 ,0x80}, + {0x380a ,0x1 }, + {0x380b ,0xe0}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//800x480 WVGA +static const struct regval_list OV5640_QSXGA2WVGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x3 }, + {0x3809 ,0x20}, + {0x380a ,0x1 }, + {0x380b ,0xe0}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x3810, 0x00}, + {0x3811, 0x10}, + {0x3812, 0x01}, + {0x3813, 0x48}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//352x288 CIF +static const struct regval_list OV5640_QSXGA2CIF[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x1 }, + {0x3809 ,0x60}, + {0x380a ,0x1 }, + {0x380b ,0x20}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x3810, 0x00}, + {0x3811, 0x10}, + {0x3812, 0x00}, + {0x3813, 0x70}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//1280x960 SXGA +static const struct regval_list OV5640_QSXGA2SXGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x5 }, + {0x3809 ,0x0 }, + {0x380a ,0x3 }, + {0x380b ,0xc0}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//2048x1536 QXGA +static const struct regval_list OV5640_QSXGA2QXGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x8 }, + {0x3809 ,0x0 }, + {0x380a ,0x6 }, + {0x380b ,0x0 }, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + + +//1600x1200 UXGA +static const struct regval_list OV5640_QSXGA2UXGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x6 }, + {0x3809 ,0x40}, + {0x380a ,0x4 }, + {0x380b ,0xb0}, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + +//1024x768 XGA +static const struct regval_list OV5640_QSXGA2XGA[] = +{ + {0x3800 ,0x00}, + {0x3801 ,0x00}, + {0x3802 ,0x00}, + {0x3803 ,0x00}, + {0x3804 ,0xA }, + {0x3805 ,0x3f}, + {0x3806 ,0x7 }, + {0x3807 ,0x9f}, + {0x3808 ,0x4 }, + {0x3809 ,0x0 }, + {0x380a ,0x3 }, + {0x380b ,0x0 }, + {0x380c ,0xc }, + {0x380d ,0x80}, + {0x380e ,0x7 }, + {0x380f ,0xd0}, + {0x5001 ,0xa3}, + {0x5680 ,0x0 }, + {0x5681 ,0x0 }, + {0x5682 ,0xA }, + {0x5683 ,0x20}, + {0x5684 ,0x0 }, + {0x5685 ,0x0 }, + {0x5686 ,0x7 }, + {0x5687 ,0x98}, + {0xffff, 0xff}, +}; + + + +static ssdv_conf_t *ov5640_conf; + +/** + * Captures an image from the camera. + */ +bool OV5640_Snapshot2RAM(void) +{ + // Capture enable + TRACE_INFO("CAM > Capture image"); + OV5640_Capture(); + + return true; +} + +bool OV5640_BufferOverflow(void) +{ + return ov5640_conf->ram_buffer[0] != 0xFF || ov5640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header +} + +uint32_t OV5640_getBuffer(uint8_t** buffer) { + *buffer = ov5640_conf->ram_buffer; + return ov5640_conf->size_sampled; +} + +const stm32_dma_stream_t *dmastp; + +inline int32_t dma_start(void) { + /* Clear any pending inerrupts. */ + dmaStreamClearInterrupt(dmastp); + dmaStreamEnable(dmastp); + return 0; +} + +inline int32_t dma_stop(void) { + dmaStreamDisable(dmastp); + dmaStreamRelease(dmastp); + return 0; +} + +static void dma_interrupt(void *p, uint32_t flags) { + (void)p; + + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Deprecate - Nothing really to do at half way point. */ + return; + } + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* End of transfer. */ + palClearLine(LINE_IO_LED1); + + /* + * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. + * Stop and release DMA channel. + * Either DMA count full or VSNC traling edge can terminate frame capture + */ + TIM1->DIER &= ~TIM_DIER_TDE; + LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; + dma_stop(); + return; + } + /* + * TODO: Anything else is an error. + * Maybe set an error flag? + */ +} + +/* + * The TIM1 interrupt handler (to be deprecated - not used). + */ + + /* Not defined by Chibios. */ +#define STM32_TIM1_TRG_HANDLER VectorA8 +#define STM32_TIM1_UP_NUMBER 25 + +OSAL_IRQ_HANDLER(STM32_TIM1_TRG_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + TIM1->SR &= ~TIM_SR_TIF; + + OSAL_IRQ_EPILOGUE(); + +} + +static bool LptimRdy; +static bool image_finished; +static bool vsync; + +/* + * The LPTIM interrupt handler. + */ +OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) { + + /* Note: + * STM32F4 vectors defined by Chibios currently stop at 98. + * Need to allocate more space in vector table for LPTIM1. + * LPTIM1 is vector 97. Vector table is expanded in increments of 8. + * Change CORTEX_NUM_PARAMS in cmparams.h to 106. + */ + OSAL_IRQ_PROLOGUE(); + /* Reset interrupt flag for ARR. */ + LPTIM1->ICR = LPTIM_ICR_ARRMCF; + /* + * LPTIM interrupts can be disabled at this stage. + * We don't need this interrupt again until a new capture is started. + */ + LPTIM1->IER &= ~LPTIM_IER_ARRMIE; + + /* + * The first interrupt indicates LPTIM core has been initialized by PCLK. + * This flag is used to synchronise capture start. + * If on leading edge of VSYNC lptim_rdy is true then capture can start. + * If not wait for the next VSYNC. + * This is needed because of LPTIM core setup requirement when clocked externally. + */ + LptimRdy = true; + + OSAL_IRQ_EPILOGUE(); +} + +/* + * Note: VSYNC is a pulse the full length of a frame. + * This is contrary to the OV5640 datasheet which shows VSYNC as pulses. +*/ +CH_IRQ_HANDLER(Vector5C) { + CH_IRQ_PROLOGUE(); + + if (LptimRdy) { + // VSYNC handling + if(!vsync) { + /* + * Rising edge of VSYNC after LPTIM1 has been initiualized. + * Start DMA channel. + * Enable TIM1 trigger of DMA. + */ + dma_start(); + TIM1->DIER |= TIM_DIER_TDE; + vsync = true; + } else { + /* VSYNC falling edge - end of JPEG frame. + * Stop & release the DMA channel. + * Disable TIM1 trigger of DMA and stop PCLK via LPTIM1 + * These should have already been disabled in DMA interrupt if was filled. + */ + dma_stop(); + TIM1->DIER &= ~TIM_DIER_TDE; + LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; + + /* Disable VYSNC edge interrupts. */ + nvicDisableVector(EXTI1_IRQn); + image_finished = true; + vsync = false; + } + } else { + /* + * LPTIM1 is not yet initialised. + * So we enable LPTIM1 to start counting. + * The PCLK should be low at the leading edge of VSYNC. + * Thus we get a clean start of LPTIM1 clocking on next leading edge of PCLK. + * After the LPTIM core is initialised PCLK and LPTIM_OUT >should< be synchronous. + * This needs to be verified as the ST RM document is not precise on this matter. + */ + LPTIM1->CR |= LPTIM_CR_CNTSTRT; + } + + EXTI->PR |= EXTI_PR_PR1; + CH_IRQ_EPILOGUE(); +} + +bool OV5640_Capture(void) +{ + /* + * Note: + * If there are no Chibios devices enabled that use DMA then... + * In makefile add entry to UDEFS: + * UDEFS = -DSTM32_DMA_REQUIRED + */ + + /* Setup DMA for transfer on TIM1_TRIG - DMA2 stream 0, channel 6 */ + dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 0)); + uint32_t dmamode = STM32_DMA_CR_CHSEL(6) | + STM32_DMA_CR_PL(3) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MSIZE_WORD | + STM32_DMA_CR_MBURST_INCR4 | + STM32_DMA_CR_PSIZE_BYTE | + STM32_DMA_CR_MINC | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE | + STM32_DMA_CR_TCIE; + + dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); + + dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here + dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer); // Thats the buffer address + dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size); // Thats the buffer size + + dmaStreamSetMode(dmastp, dmamode); // Setup DMA + dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); + dmaStreamClearInterrupt(dmastp); + + // Setup timer for PCLCK + rccResetLPTIM1(); + rccEnableLPTIM1(FALSE); + + /* + * The setting of CKSEL & COUNTMODE are not completely clear. + * The change below switches to using internal clock sampling the external clock. + */ + LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1); + LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; + LPTIM1->CR |= LPTIM_CR_ENABLE; + LPTIM1->IER |= LPTIM_IER_ARRMIE; // We need this interrupt to fire only once when the LPTIM kernel is ready + + LPTIM1->CMP = 0; + LPTIM1->ARR = 1; + + /* Set vector and clear flag. */ + nvicEnableVector(LPTIM1_IRQn, 7); // Enable interrupt + LptimRdy = false; + + /* + * Setup slave timer to trigger DMA. + * We have to use TIM1 because... + * > it can be triggered from LPTIM1 + * > and TIM1_TRIG is in DMA2 and we need DMA2 for peripheral -> memory transfer + */ + rccResetTIM1(); + rccEnableTIM1(FALSE); + + TIM1->SMCR = TIM_SMCR_TS_1; // Select ITR2 as trigger + TIM1->SMCR |= TIM_SMCR_SMS_2; // Set timer in reset mode + /* + * IC1 is mapped to TRC which means we are in Input mode. + * The timer will reset and trigger DMA on the leading edge of LPTIM1_OUT. + * The counter will count up while LPTIM1_OUT is high. + * We don't care what the count is. + * We just use the DMA initiated by the trigger which is independant of counting. + */ + TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); + + image_finished = false; + vsync = false; + + // Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF) + SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC; + EXTI->IMR = EXTI_IMR_MR1; // Activate interrupt for chan1 (=>PC1) and chan2 (=>PC2) + EXTI->RTSR = EXTI_RTSR_TR1; // Listen on rising edge + nvicEnableVector(EXTI1_IRQn, 1); // Enable interrupt + + do { // Have a look for some bytes in memory for testing if capturing works + TRACE_INFO("CAM > Capturing"); + chThdSleepMilliseconds(100); + } while(!image_finished); + + uint32_t soi; // Start of Image + for(soi=0; soi<65533; soi++) + if(ov5640_conf->ram_buffer[soi] == 0xFF && ov5640_conf->ram_buffer[soi+1] == 0xE0) + break; + + if(soi == 65533) { + TRACE_ERROR("CAM > Could not find SOI flag"); + return false; // We failed to sample the picture correctly because we didn't find the JPEG SOI flag + } + + // Found SOI, move bytes + for(uint32_t i=0; i<65535; i++) + ov5640_conf->ram_buffer[i] = ov5640_conf->ram_buffer[i+soi]; + + TRACE_INFO("CAM > Capture finished"); + + return true; +} + + + +/** + * Initializes GPIO (for pseudo DCMI) + * The high speed clock supports communication by I2C (XCLK = 16MHz) + */ +void OV5640_InitGPIO(void) +{ + palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + /* PC6 AF3 = TIM8_CH1. */ + palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(1)); + palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_XCLK, PAL_MODE_ALTERNATE(0)); + palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST); + + palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL); + palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL); +} + +void OV5640_TransmitConfig(void) +{ + chThdSleepMilliseconds(1000); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3103, 0x11); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3008, 0x82); + chThdSleepMilliseconds(100); + + for(uint32_t i=0; (OV5640YUV_Sensor_Dvp_Init[i].reg != 0xffff) || (OV5640YUV_Sensor_Dvp_Init[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640YUV_Sensor_Dvp_Init[i].reg, OV5640YUV_Sensor_Dvp_Init[i].val); + + chThdSleepMilliseconds(500); + + for(uint32_t i=0; (OV5640_JPEG_QSXGA[i].reg != 0xffff) || (OV5640_JPEG_QSXGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_JPEG_QSXGA[i].reg, OV5640_JPEG_QSXGA[i].val); + + + switch(ov5640_conf->res) { + case RES_QVGA: + for(uint32_t i=0; (OV5640_QSXGA2QVGA[i].reg != 0xffff) || (OV5640_QSXGA2QVGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val); + break; + + case RES_VGA: + for(uint32_t i=0; (OV5640_QSXGA2VGA[i].reg != 0xffff) || (OV5640_QSXGA2VGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_QSXGA2VGA[i].reg, OV5640_QSXGA2VGA[i].val); + break; + + case RES_XGA: + for(uint32_t i=0; (OV5640_QSXGA2XGA[i].reg != 0xffff) || (OV5640_QSXGA2XGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_QSXGA2XGA[i].reg, OV5640_QSXGA2XGA[i].val); + break; + + case RES_UXGA: + for(uint32_t i=0; (OV5640_QSXGA2UXGA[i].reg != 0xffff) || (OV5640_QSXGA2UXGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_QSXGA2UXGA[i].reg, OV5640_QSXGA2UXGA[i].val); + break; + + default: // Default QVGA + for(uint32_t i=0; (OV5640_QSXGA2QVGA[i].reg != 0xffff) || (OV5640_QSXGA2QVGA[i].val != 0xff); i++) + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val); + } + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3406, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3400, 0x04); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3401, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3402, 0x04); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3403, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3404, 0x04); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3405, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0xa3); // lanuch group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5183 ,0x0 ); + + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5381, 0x1c); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5382, 0x5a); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5383, 0x06); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5384, 0x1a); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5385, 0x66); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5386, 0x80); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5387, 0x82); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5388, 0x80); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5389, 0x02); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x538b, 0x98); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x538a, 0x01); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5587, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5588, 0x01); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5586, 0x20); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5585, 0x00); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5580, 0x06); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5583, 0x40); // sat U + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5584, 0x10); // sat V + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x5003, 0x08); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group + + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a0f, 0x38); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a10, 0x30); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a11, 0x61); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a1b, 0x38); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a1e, 0x30); + I2C_write8_16bitreg_locked(OV5640_I2C_ADR, 0x3a1f, 0x10); +} + +void OV5640_init(ssdv_conf_t *config) { + ov5640_conf = config; + + // Take I2C (due to silicon bug of OV5640, it interferes if byte 0x30 transmitted on I2C bus) + I2C_lock(); + + // Clearing buffer + uint32_t i; + for(i=0; iram_size; i++) + ov5640_conf->ram_buffer[i] = 0; + + TRACE_INFO("CAM > Init pins"); + OV5640_InitGPIO(); + + // Power on OV5640 + TRACE_INFO("CAM > Switch on"); + palSetLine(LINE_CAM_EN); // Switch on camera + palSetLine(LINE_CAM_RESET); // Toggle reset + + // Send settings to OV5640 + TRACE_INFO("CAM > Transmit config to camera"); + OV5640_TransmitConfig(); + + chThdSleepMilliseconds(3000); +} + +void OV5640_deinit(void) { + // Power off OV5640 + TRACE_INFO("CAM > Switch off"); + + palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_PCLK, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT); + + palSetLineMode(LINE_CAM_XCLK, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT); + + palSetLineMode(LINE_CAM_EN, PAL_MODE_INPUT); + palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT); + + // Release I2C (due to silicon bug of OV5640, it interferes if byte 0x30 transmitted on I2C bus) + I2C_unlock(); +} + +bool OV5640_isAvailable(void) +{ + I2C_lock(); + + // Configure pins + OV5640_InitGPIO(); + + // Switch on camera + palSetLine(LINE_CAM_EN); // Switch on camera + palSetLine(LINE_CAM_RESET); // Toggle reset + + chThdSleepMilliseconds(100); + + uint8_t val, val2; + bool ret; + + if(I2C_read8_16bitreg_locked(OV5640_I2C_ADR, 0x300A, &val) && I2C_read8_16bitreg_locked(OV5640_I2C_ADR, 0x300B, &val2)) { + ret = val == 0x56 && val2 == 0x40; + } else { + ret = false; + } + + palClearLine(LINE_CAM_EN); // Switch off camera + palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT); // CAM_RESET + I2C_unlock(); + + return ret; +} + diff --git a/tracker/software/drivers/ov5640.h b/tracker/software/drivers/ov5640.h new file mode 100644 index 0000000..3b4ba99 --- /dev/null +++ b/tracker/software/drivers/ov5640.h @@ -0,0 +1,22 @@ +/** + * This is the OV2640 driver + */ + +#ifndef __OV5640_H__ +#define __OV5640_H__ + +#include "ch.h" +#include "hal.h" +#include "types.h" + +bool OV5640_Snapshot2RAM(void); +bool OV5640_Capture(void); +void OV5640_InitGPIO(void); +uint32_t OV5640_getBuffer(uint8_t** buffer); +bool OV5640_BufferOverflow(void); +void OV5640_TransmitConfig(void); +void OV5640_init(ssdv_conf_t *config); +void OV5640_deinit(void); +bool OV5640_isAvailable(void); + +#endif diff --git a/tracker/software/drivers/wrapper/pi2c.c b/tracker/software/drivers/wrapper/pi2c.c index 441ac10..b21eaa6 100644 --- a/tracker/software/drivers/wrapper/pi2c.c +++ b/tracker/software/drivers/wrapper/pi2c.c @@ -52,6 +52,12 @@ bool I2C_write8_locked(uint8_t address, uint8_t reg, uint8_t value) return I2C_send(address, txbuf, 2, NULL, 0, MS2ST(100)); } +bool I2C_write8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t value) // 16bit register (for OV5640) +{ + uint8_t txbuf[] = {reg >> 8, reg & 0xFF, value}; + return I2C_send(address, txbuf, 3, NULL, 0, MS2ST(100)); +} + bool I2C_writeN_locked(uint8_t address, uint8_t *txbuf, uint32_t length) { return I2C_send(address, txbuf, length, NULL, 0, MS2ST(100)); @@ -66,6 +72,15 @@ bool I2C_read8_locked(uint8_t address, uint8_t reg, uint8_t *val) return ret; } +bool I2C_read8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t *val) // 16bit register (for OV5640) +{ + uint8_t txbuf[] = {reg >> 8, reg & 0xFF}; + uint8_t rxbuf[1]; + bool ret = I2C_send(address, txbuf, 2, rxbuf, 1, MS2ST(100)); + *val = rxbuf[0]; + return ret; +} + bool I2C_read16_locked(uint8_t address, uint8_t reg, uint16_t *val) { uint8_t txbuf[] = {reg}; diff --git a/tracker/software/drivers/wrapper/pi2c.h b/tracker/software/drivers/wrapper/pi2c.h index 2356d0d..c437db9 100644 --- a/tracker/software/drivers/wrapper/pi2c.h +++ b/tracker/software/drivers/wrapper/pi2c.h @@ -19,6 +19,9 @@ bool I2C_writeN_locked(uint8_t address, uint8_t *txbuf, uint32_t length); bool I2C_read8_locked(uint8_t address, uint8_t reg, uint8_t *val); bool I2C_read16_locked(uint8_t address, uint8_t reg, uint16_t *val); +bool I2C_write8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t value); // 16bit register (for OV5640) +bool I2C_read8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t *val); // 16bit register (for OV5640) + // I2C Mutex unlocked access functions bool I2C_write8(uint8_t address, uint8_t reg, uint8_t value); bool I2C_writeN(uint8_t address, uint8_t *txbuf, uint32_t length); diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index 8f7c03f..d9ef2ba 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -49,8 +49,8 @@ #define STM32_PLLP_VALUE 2 #define STM32_PLLQ_VALUE 4 #define STM32_HPRE STM32_HPRE_DIV1 -#define STM32_PPRE1 STM32_PPRE1_DIV4 -#define STM32_PPRE2 STM32_PPRE2_DIV4 +#define STM32_PPRE1 STM32_PPRE1_DIV2 +#define STM32_PPRE2 STM32_PPRE2_DIV1 #define STM32_RTCSEL STM32_RTCSEL_LSI #define STM32_RTCPRE_VALUE 8 #define STM32_MCO1SEL STM32_MCO1SEL_HSE diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index 9ee31e0..a0fd96c 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -4,6 +4,7 @@ #include "debug.h" #include "modules.h" #include "ov2640.h" +#include "ov5640.h" #include "pi2c.h" #include "ssdv.h" #include "aprs.h" @@ -15,7 +16,7 @@ #include "watchdog.h" #include "flash.h" -static uint8_t gimage_id; // Global image ID (for all image threads) +static uint8_t gimage_id = 2; // Global image ID (for all image threads) mutex_t camera_mtx; void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx) @@ -146,49 +147,29 @@ THD_FUNCTION(imgThread, arg) { uint32_t image_len = 0; uint8_t *image; - // Take photo if camera activated (if camera disabled, camera buffer is probably shared in config file) - if(!conf->ssdv_conf.no_camera) + // Lock camera + TRACE_INFO("IMG > Lock camera"); + chMtxLock(&camera_mtx); + TRACE_INFO("IMG > Locked camera"); + + // Lock RADIO from producing interferences + TRACE_INFO("IMG > Lock radio"); + chMtxLock(&interference_mtx); + TRACE_INFO("IMG > Locked radio"); + + uint8_t tries; + bool status = false; + + // Detect camera + if(OV2640_isAvailable()) // OV2640 available { - // Lock camera - TRACE_INFO("IMG > Lock camera"); - chMtxLock(&camera_mtx); - TRACE_INFO("IMG > Locked camera"); + TRACE_INFO("IMG > OV2640 found"); - // Lock RADIO from producing interferences - TRACE_INFO("IMG > Lock radio"); - chMtxLock(&interference_mtx); - TRACE_INFO("IMG > Locked radio"); - - uint8_t tries; - bool status = false; - - // Detect camera - if(OV2640_isAvailable()) // OV2640 available + if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory) { - TRACE_INFO("IMG > OV2640 found"); + conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution - if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory) - { - conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution - - do { - - // Init camera - OV2640_init(&conf->ssdv_conf); - - // Sample data from DCMI through DMA into RAM - tries = 5; // Try 5 times at maximum - do { // Try capturing image until capture successful - status = OV2640_Snapshot2RAM(); - } while(!status && --tries); - - conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false) - - } while(OV2640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA); - - conf->ssdv_conf.res = RES_MAX; // Revert register - - } else { // Static resolution + do { // Init camera OV2640_init(&conf->ssdv_conf); @@ -199,49 +180,101 @@ THD_FUNCTION(imgThread, arg) { status = OV2640_Snapshot2RAM(); } while(!status && --tries); - } + conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false) - // Switch off camera - OV2640_deinit(); + } while(OV2640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA); - // Get image - image_len = OV2640_getBuffer(&image); - TRACE_INFO("IMG > Image size: %d bytes", image_len); + conf->ssdv_conf.res = RES_MAX; // Revert register - } else { // Camera error + } else { // Static resolution - TRACE_ERROR("IMG > No camera found"); + // Init camera + OV2640_init(&conf->ssdv_conf); + + // Sample data from DCMI through DMA into RAM + tries = 5; // Try 5 times at maximum + do { // Try capturing image until capture successful + status = OV2640_Snapshot2RAM(); + } while(!status && --tries); } - // Unlock radio - TRACE_INFO("IMG > Unlock radio"); - chMtxUnlock(&interference_mtx); - TRACE_INFO("IMG > Unlocked radio"); - - // Unlock camera - TRACE_INFO("IMG > Unlock camera"); - chMtxUnlock(&camera_mtx); - TRACE_INFO("IMG > Unlocked camera"); - - // Encode/Transmit SSDV if image sampled successfully - if(status) - { - gimage_id++; - TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1); - encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx); - } - - } else { + // Switch off camera + OV2640_deinit(); + // Get image image_len = OV2640_getBuffer(&image); TRACE_INFO("IMG > Image size: %d bytes", image_len); - TRACE_INFO("IMG > Camera disabled"); - TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id); - encode_ssdv(image, image_len, conf, gimage_id, conf->ssdv_conf.redundantTx); + } else if(OV5640_isAvailable()) { // OV5640 available + + TRACE_INFO("IMG > OV5640 found"); + + if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory) + { + conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution + + do { + + // Init camera + OV5640_init(&conf->ssdv_conf); + + // Sample data from DCMI through DMA into RAM + tries = 5; // Try 5 times at maximum + do { // Try capturing image until capture successful + status = OV5640_Snapshot2RAM(); + } while(!status && --tries); + + conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false) + + } while(OV5640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA); + + conf->ssdv_conf.res = RES_MAX; // Revert register + + } else { // Static resolution + + // Init camera + OV5640_init(&conf->ssdv_conf); + + // Sample data from DCMI through DMA into RAM + tries = 5; // Try 5 times at maximum + do { // Try capturing image until capture successful + status = OV5640_Snapshot2RAM(); + } while(!status && --tries); + + } + + // Switch off camera + OV5640_deinit(); + + // Get image + image_len = OV5640_getBuffer(&image); + TRACE_INFO("IMG > Image size: %d bytes", image_len); + + } else { // Camera error + + TRACE_ERROR("IMG > No camera found"); } + + // Unlock radio + TRACE_INFO("IMG > Unlock radio"); + chMtxUnlock(&interference_mtx); + TRACE_INFO("IMG > Unlocked radio"); + + // Unlock camera + TRACE_INFO("IMG > Unlock camera"); + chMtxUnlock(&camera_mtx); + TRACE_INFO("IMG > Unlocked camera"); + + // Encode/Transmit SSDV if image sampled successfully + if(status) + { + gimage_id++; + TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1); + encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx); + } + } time = waitForTrigger(time, &conf->trigger); diff --git a/tracker/software/types.h b/tracker/software/types.h index b9e6a73..090800b 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -114,7 +114,6 @@ typedef struct { uint8_t *ram_buffer; // Camera Buffer uint16_t ram_size; // Size of buffer uint16_t size_sampled; // Actual image data size (do not set in config) - bool no_camera; // Camera disabled bool redundantTx; // Redundand packet transmission (APRS only) } ssdv_conf_t;