From dcaa8e0bce5a6a8bc2667c9d5a7ac2e223eda4ad Mon Sep 17 00:00:00 2001 From: CInsights Date: Sat, 19 Aug 2017 19:51:15 +1000 Subject: [PATCH 01/37] Revision of ov5640.c dma interrupt handling. --- tracker/software/ChibiOS | 1 - tracker/software/ChibiOS_old | 1 - tracker/software/drivers/ov5640.c | 145 +++++++++++++++++++++--------- 3 files changed, 104 insertions(+), 43 deletions(-) delete mode 160000 tracker/software/ChibiOS delete mode 160000 tracker/software/ChibiOS_old diff --git a/tracker/software/ChibiOS b/tracker/software/ChibiOS deleted file mode 160000 index 5d82b6f..0000000 --- a/tracker/software/ChibiOS +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5d82b6fa504595ef93ce16fedd064bf992c7eb15 diff --git a/tracker/software/ChibiOS_old b/tracker/software/ChibiOS_old deleted file mode 160000 index 0e546f8..0000000 --- a/tracker/software/ChibiOS_old +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0e546f8e46684ebc9d0081c9d19919c93a403058 diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 26db8e6..7b349ba 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -723,11 +723,15 @@ static const struct regval_list OV5640_QSXGA2XGA[] = }; - static ssdv_conf_t *ov5640_conf; + +/* TODO: Implement a state machine instead of multiple flags. */ static bool LptimRdy; -static bool image_finished; +static bool capture_finished; +static bool capture_error; static bool vsync; +static bool dma_fault; +static bool dma_overrun; /** * Captures an image from the camera. @@ -754,38 +758,51 @@ uint32_t OV5640_getBuffer(uint8_t** buffer) { const stm32_dma_stream_t *dmastp; inline int32_t dma_start(void) { - /* Clear any pending inerrupts. */ + /* Clear any pending interrupts. */ dmaStreamClearInterrupt(dmastp); - dmaStreamEnable(dmastp); - return 0; + dmaStreamEnable(dmastp); + return 0; } -inline int32_t dma_stop(void) { +/* + * Stop DMA release stream and return count remaining. + * Note that any DMA FIFO transfer will complete. + * The Chibios DMAV2 driver waits for EN to clear before proceeding. + */ +inline uint16_t dma_stop(void) { dmaStreamDisable(dmastp); + uint16_t transfer = dmaStreamGetTransactionSize(dmastp); dmaStreamRelease(dmastp); - return 0; + return transfer; } 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. */ + /* + * Nothing really to do at half way point for now. + * Implementing DBM will use HTIF. + */ return; } if ((flags & STM32_DMA_ISR_TCIF) != 0) { /* Disable VYSNC edge interrupts. */ - nvicDisableVector(EXTI1_IRQn); - image_finished = true; + //nvicDisableVector(EXTI1_IRQn); + //capture_finished = true; /* + * If DMA has run to end within a frame then this is an error. + * In single buffer mode DMA should always be terminated by VSYNC. + * * 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 + * Wait for next VSYNC leading edge to tear down DMA stream. */ TIM1->DIER &= ~TIM_DIER_TDE; LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; - dma_stop(); + dma_overrun = true; + capture_error = true; + //dma_stop(); return; } /* @@ -845,8 +862,8 @@ OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) { } /* - * Note: VSYNC is a pulse the full length of a frame. - * This is contrary to the OV5640 datasheet which shows VSYNC as pulses. + * Note: VSYNC is a pulse at the start of each frame. + * This is unlike the OV2640 where VSYNC is active for the entire frame. */ CH_IRQ_HANDLER(Vector5C) { CH_IRQ_PROLOGUE(); @@ -855,7 +872,7 @@ CH_IRQ_HANDLER(Vector5C) { // VSYNC handling if(!vsync) { /* - * Rising edge of VSYNC after LPTIM1 has been initiualized. + * Rising edge of VSYNC after LPTIM1 has been initialised. * Start DMA channel. * Enable TIM1 trigger of DMA. */ @@ -863,28 +880,29 @@ CH_IRQ_HANDLER(Vector5C) { TIM1->DIER |= TIM_DIER_TDE; vsync = true; } else { - /* VSYNC falling edge - end of JPEG frame. + /* VSYNC leading with vsync true. + * This means end of capture for the 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. + * Disable TIM1 trigger of DMA and stop PCLK counting on LPTIM1. + * If buffer was filled in DMA then that is an error. + * We check that here. */ dma_stop(); TIM1->DIER &= ~TIM_DIER_TDE; LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; - /* Disable VYSNC edge interrupts. */ + /* + * Disable VSYNC edge interrupts. + * Flag image capture complete. + */ nvicDisableVector(EXTI1_IRQn); - image_finished = true; - vsync = false; + capture_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; } @@ -925,19 +943,58 @@ bool OV5640_Capture(void) dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); dmaStreamClearInterrupt(dmastp); - // Setup timer for PCLCK + dma_overrun = false; + dma_error = false; + + // Setup timer for PCLK 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 is run in external count mode (CKSEL = 0, COUNTMODE = 1). + * CKPOL is set so leading and trailing edge of PCLK increment the counter. + * The internal clocking (checking edges of LPTIM1_IN) is set to use APB. + * The internal clock must be >4 times the frequency of the input (PCLK). + * NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK. + * Depending on PCLK state when LPTIM1 is enabled, LPMTIM1_OUT be inverted. + * + * Possible fix... + * Using CKSEL = 1 where PCLK is the actual clock may still be possible. + * This would ensure coincidence between LPTIM1_OUT and PCLK. + * If using CKSEL = 1 LPTIM1 needs 5 external clocks to reach kernel ready. + * Using CKSEL = 1 only allows for leading or trailing edge counting. + * Thus we would be sure which edge of PCLK incremented the LPTIM1 counter. + * Have to test to see if CMP and ARR interrupts work when CKSEL = 1. + * + * Continuing... + * LPTIM1 is enabled on the leading edge of VSYNC. + * After enabling LPTIM1 wait for the first interrupt (ARRIF). + * Waiting for ARRIF indicates that LPTIM1 kernel is ready. + * Note that waiting for interrupt when using COUNTMODE is redundant. + * The ST RM says a delay of only 2 counter (APB) clocks are required. + * But leave the interrupt check in place for now as it does no harm. + * + * The interrupt must be disabled on the first interrupt (else flood). + * + * LPTIM1_OUT is gated to TIM1 internal trigger input 2. */ 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->IER |= LPTIM_IER_ARRMIE; + /* + * TODO: When using COUNTMODE CMP and ARR should be 1 & 2? + * It is intended that after counter start CNT = 0. + * Then CNT reaches 1 on first PCLK edge and 2 on the second edge. + * Using 0 and 1 means LPTIM1_OUT gets CMP match as soon as LPMTIM1 is ready. + * This means LPTIM1_OUT will be set and TIM1 will be triggered immediately. + * A DMA transfer will then occur. + * The next edge of PCLK will make CNT = 2 and ARR will match. + * LPTIM1 will then be reset (synchronous with APB presumably). + * LPTIM1_OUT will clear briefly prior to setting again on reset CMP match. + * This will allow TIM1 to be re-triggered. + */ LPTIM1->CMP = 0; LPTIM1->ARR = 1; @@ -954,18 +1011,20 @@ bool OV5640_Capture(void) 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_IC1 is mapped to TRC which means we are in trigger input mode. + * TIM1 is set in slave reset mode with input from ITR2. + * 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 about the count. + * We simply use the DMA initiated by the trigger input. + */ + TIM1->SMCR = TIM_SMCR_TS_1; + TIM1->SMCR |= TIM_SMCR_SMS_2; TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); - image_finished = false; + capture_finished = false; + capture_error = false; vsync = false; // Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF) @@ -977,8 +1036,12 @@ bool OV5640_Capture(void) do { // Have a look for some bytes in memory for testing if capturing works TRACE_INFO("CAM > Capturing"); chThdSleepMilliseconds(100); - } while(!image_finished); + } while(!capture_finished && !capture_error); + if (capture_error) { + TRACE_ERROR("CAM > Error capturing image"); + return false; + } 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) From ae83ddc9aea56c959b17c2193a1f11ff15846541 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sat, 19 Aug 2017 13:37:17 +0200 Subject: [PATCH 02/37] Fixed variable name --- tracker/software/drivers/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 7b349ba..53b1c1b 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -944,7 +944,7 @@ bool OV5640_Capture(void) dmaStreamClearInterrupt(dmastp); dma_overrun = false; - dma_error = false; + dma_fault = false; // Setup timer for PCLK rccResetLPTIM1(); From 43d72d041c42bdcf325c97a689897035894e6d73 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sat, 19 Aug 2017 14:14:04 +0200 Subject: [PATCH 03/37] Removed SOI search --- tracker/software/drivers/ov5640.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 53b1c1b..a384e2d 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -1042,19 +1042,6 @@ bool OV5640_Capture(void) TRACE_ERROR("CAM > Error capturing image"); return false; } - 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"); From 35f7164a7d081b6c0160aef5bbb70dba93027a76 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sun, 20 Aug 2017 03:17:42 +0200 Subject: [PATCH 04/37] Fixed DMA release error --- tracker/software/drivers/si4464.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tracker/software/drivers/si4464.c b/tracker/software/drivers/si4464.c index 011d87b..896f330 100644 --- a/tracker/software/drivers/si4464.c +++ b/tracker/software/drivers/si4464.c @@ -85,6 +85,7 @@ void Si4464_write(uint8_t* txData, uint32_t len) { spiSelect(&SPID1); spiExchange(&SPID1, len, txData, rxData); spiUnselect(&SPID1); + spiStop(&SPID1); spiReleaseBus(&SPID1); // Reqest ACK by Si4464 @@ -101,6 +102,7 @@ void Si4464_write(uint8_t* txData, uint32_t len) { spiSelect(&SPID1); spiExchange(&SPID1, 3, rx_ready, rxData); spiUnselect(&SPID1); + spiStop(&SPID1); spiReleaseBus(&SPID1); } } @@ -117,6 +119,7 @@ void Si4464_read(uint8_t* txData, uint32_t txlen, uint8_t* rxData, uint32_t rxle spiSelect(&SPID1); spiExchange(&SPID1, txlen, txData, null_spi); spiUnselect(&SPID1); + spiStop(&SPID1); spiReleaseBus(&SPID1); // Reqest ACK by Si4464 @@ -134,6 +137,7 @@ void Si4464_read(uint8_t* txData, uint32_t txlen, uint8_t* rxData, uint32_t rxle spiSelect(&SPID1); spiExchange(&SPID1, rxlen, rx_ready, rxData); spiUnselect(&SPID1); + spiStop(&SPID1); spiReleaseBus(&SPID1); } } From a7e9147ed3b36d352d802ec9cc6cbcff13d3a17e Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sun, 20 Aug 2017 05:22:16 +0200 Subject: [PATCH 05/37] Reduced clocking speed --- tracker/software/drivers/ov5640.c | 32 ++++++------------------------- tracker/software/mcuconf.h | 2 +- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index a384e2d..af92892 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -31,7 +31,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3017, 0x7f }, { 0x3018, 0xff }, { 0x302c, 0x02 }, - { 0x3108, 0x01 }, + { 0x3108, 0x31 }, { 0x3630, 0x2e },//2e { 0x3632, 0xe2 }, { 0x3633, 0x23 },//23 @@ -396,7 +396,7 @@ static const struct regval_list OV5640_RGB_QVGA[] = //2592x1944 QSXGA static const struct regval_list OV5640_JPEG_QSXGA[] = { - {0x3820 ,0x40}, + {0x3820 ,0x41}, {0x3821 ,0x26}, {0x3814 ,0x11}, {0x3815 ,0x11}, @@ -796,13 +796,12 @@ static void dma_interrupt(void *p, uint32_t flags) { * In single buffer mode DMA should always be terminated by VSYNC. * * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. - * Wait for next VSYNC leading edge to tear down DMA stream. + * Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC. */ TIM1->DIER &= ~TIM_DIER_TDE; LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; dma_overrun = true; capture_error = true; - //dma_stop(); return; } /* @@ -811,24 +810,6 @@ static void dma_interrupt(void *p, uint32_t flags) { */ } -/* - * 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(); - -} - /* * The LPTIM interrupt handler. */ @@ -897,7 +878,6 @@ CH_IRQ_HANDLER(Vector5C) { */ nvicDisableVector(EXTI1_IRQn); capture_finished = true; - //vsync = false; } } else { /* @@ -921,8 +901,8 @@ bool OV5640_Capture(void) */ /* 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) | + 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 | @@ -978,7 +958,7 @@ bool OV5640_Capture(void) * * LPTIM1_OUT is gated to TIM1 internal trigger input 2. */ - LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1); + LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL); LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; LPTIM1->CR |= LPTIM_CR_ENABLE; LPTIM1->IER |= LPTIM_IER_ARRMIE; diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index d9ef2ba..885ed41 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -46,7 +46,7 @@ #define STM32_PLLSRC STM32_PLLSRC_HSE #define STM32_PLLM_VALUE 26 #define STM32_PLLN_VALUE 192 -#define STM32_PLLP_VALUE 2 +#define STM32_PLLP_VALUE 4 #define STM32_PLLQ_VALUE 4 #define STM32_HPRE STM32_HPRE_DIV1 #define STM32_PPRE1 STM32_PPRE1_DIV2 From 59cd4be1379caeafeb4bfed5d3eb6529ba6d7db6 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sun, 20 Aug 2017 14:33:46 +0200 Subject: [PATCH 06/37] Adjusted clock speeds --- tracker/software/drivers/ov5640.c | 8 ++++---- tracker/software/modules/image.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index af92892..3f45cff 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -93,7 +93,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3815, 0x31 }, { 0x3034, 0x1a }, - { 0x3035, 0x41 }, //15fps + { 0x3035, 0x11 }, //15fps { 0x3036, 0x46 }, { 0x3037, 0x13 }, { 0x3038, 0x00 }, @@ -327,7 +327,7 @@ static const struct regval_list ov5640_vga_preview[] = { // YUV VGA 30fps, night mode 5fps // Input Clock = 24Mhz, PCLK = 56MHz - { 0x3035, 0x41 }, // PLL + { 0x3035, 0x11 }, // PLL { 0x3036, 0x46 }, // PLL { 0x3c07, 0x08 }, // light meter 1 threshold [7:0] { 0x3820, 0x41 }, // Sensor flip off, ISP flip on @@ -382,7 +382,7 @@ static const struct regval_list ov5640_vga_preview[] = static const struct regval_list OV5640_RGB_QVGA[] = { {0x3008, 0x02}, - {0x3035, 0x41}, + {0x3035, 0x11}, {0x4740, 0x21}, {0x4300, 0x61}, {0x3808, 0x01}, @@ -429,7 +429,7 @@ static const struct regval_list OV5640_JPEG_QSXGA[] = {0x3824 ,0x04}, {0x5001 ,0x83}, {0x3036 ,0x69}, - {0x3035 ,0x41}, + {0x3035 ,0x11}, {0x4005 ,0x1A}, {0xffff, 0xff}, }; diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index e65084d..670037b 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -16,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 = 10; // 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) From a855c4e7d8720e6488402e75121d81a32e0d34f9 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 21 Aug 2017 03:51:03 +0200 Subject: [PATCH 07/37] Flipped PCLK polarity, added TIM8 patch file --- tracker/software/drivers/ov5640.c | 2 +- .../drivers/ov5640_working_patch_tim8.c | 1234 +++++++++++++++++ tracker/software/modules/image.c | 2 +- 3 files changed, 1236 insertions(+), 2 deletions(-) create mode 100644 tracker/software/drivers/ov5640_working_patch_tim8.c diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 3f45cff..696a286 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -21,7 +21,7 @@ struct regval_list { static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { - { 0x4740, 0x24 }, + { 0x4740, 0x04 }, { 0x4050, 0x6e }, { 0x4051, 0x8f }, diff --git a/tracker/software/drivers/ov5640_working_patch_tim8.c b/tracker/software/drivers/ov5640_working_patch_tim8.c new file mode 100644 index 0000000..dc08908 --- /dev/null +++ b/tracker/software/drivers/ov5640_working_patch_tim8.c @@ -0,0 +1,1234 @@ +/* + * Registers by Arducam https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov5640_regs.h + * https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ArduCAM.cpp + */ + +#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, 0x04 }, + + { 0x4050, 0x6e }, + { 0x4051, 0x8f }, + + { 0x3008, 0x42 }, + { 0x3103, 0x03 }, + { 0x3017, 0x7f }, + { 0x3018, 0xff }, + { 0x302c, 0x02 }, + { 0x3108, 0x31 }, + { 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, 0x11 }, //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, 0x11 }, // 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, 0x11}, + {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 ,0x41}, + {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 ,0x11}, + {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; + +/* TODO: Implement a state machine instead of multiple flags. */ +//static bool LptimRdy; +static bool capture_finished; +static bool capture_error; +static bool vsync; +static bool dma_fault; +static bool dma_overrun; + +/** + * 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 interrupts. */ + dmaStreamClearInterrupt(dmastp); + dmaStreamEnable(dmastp); + return 0; +} + +/* + * Stop DMA release stream and return count remaining. + * Note that any DMA FIFO transfer will complete. + * The Chibios DMAV2 driver waits for EN to clear before proceeding. + */ +inline uint16_t dma_stop(void) { + dmaStreamDisable(dmastp); + uint16_t transfer = dmaStreamGetTransactionSize(dmastp); + dmaStreamRelease(dmastp); + return transfer; +} + +static void dma_interrupt(void *p, uint32_t flags) { + (void)p; + + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* + * Nothing really to do at half way point for now. + * Implementing DBM will use HTIF. + */ + return; + } + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Disable VYSNC edge interrupts. */ + //nvicDisableVector(EXTI1_IRQn); + //capture_finished = true; + + /* + * If DMA has run to end within a frame then this is an error. + * In single buffer mode DMA should always be terminated by VSYNC. + * + * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. + * Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC. + */ + TIM8->DIER &= ~TIM_DIER_CC1DE; + TIM8->CCER &= ~TIM_CCER_CC1E; + //LPTIM8->CR &= ~LPTIM_CR_CNTSTRT; + dma_overrun = true; + capture_error = true; + return; + } + /* + * TODO: Anything else is an error. + * Maybe set an error flag? + */ +} + +/* + * 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 at the start of each frame. + * This is unlike the OV2640 where VSYNC is active for the entire frame. +*/ +CH_IRQ_HANDLER(Vector5C) { + CH_IRQ_PROLOGUE(); + + //if (LptimRdy) { + // VSYNC handling + if(!vsync) { + /* + * Rising edge of VSYNC after LPTIM1 has been initialised. + * Start DMA channel. + * Enable TIM1 trigger of DMA. + */ + dma_start(); + TIM8->DIER |= TIM_DIER_CC1DE; + vsync = true; + } else { + /* VSYNC leading with vsync true. + * This means end of capture for the frame. + * Stop & release the DMA channel. + * Disable TIM1 trigger of DMA and stop PCLK counting on LPTIM1. + * If buffer was filled in DMA then that is an error. + * We check that here. + */ + dma_stop(); + TIM8->DIER &= ~TIM_DIER_CC1DE; + TIM8->CCER &= ~TIM_CCER_CC1E; + //LPTIM8->CR &= ~LPTIM_CR_CNTSTRT; + + /* + * Disable VSYNC edge interrupts. + * Flag image capture complete. + */ + nvicDisableVector(EXTI1_IRQn); + capture_finished = true; + } + //} else { + /* + * LPTIM1 is not yet initialised. + * So we enable LPTIM1 to start counting. + */ + //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, 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_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); + + dma_overrun = false; + dma_fault = false; + + // Setup timer for PCLK + //rccResetLPTIM1(); + //rccEnableLPTIM1(FALSE); + + /* + * LPTIM1 is run in external count mode (CKSEL = 0, COUNTMODE = 1). + * CKPOL is set so leading and trailing edge of PCLK increment the counter. + * The internal clocking (checking edges of LPTIM1_IN) is set to use APB. + * The internal clock must be >4 times the frequency of the input (PCLK). + * NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK. + * Depending on PCLK state when LPTIM1 is enabled, LPMTIM1_OUT be inverted. + * + * Possible fix... + * Using CKSEL = 1 where PCLK is the actual clock may still be possible. + * This would ensure coincidence between LPTIM1_OUT and PCLK. + * If using CKSEL = 1 LPTIM1 needs 5 external clocks to reach kernel ready. + * Using CKSEL = 1 only allows for leading or trailing edge counting. + * Thus we would be sure which edge of PCLK incremented the LPTIM1 counter. + * Have to test to see if CMP and ARR interrupts work when CKSEL = 1. + * + * Continuing... + * LPTIM1 is enabled on the leading edge of VSYNC. + * After enabling LPTIM1 wait for the first interrupt (ARRIF). + * Waiting for ARRIF indicates that LPTIM1 kernel is ready. + * Note that waiting for interrupt when using COUNTMODE is redundant. + * The ST RM says a delay of only 2 counter (APB) clocks are required. + * But leave the interrupt check in place for now as it does no harm. + * + * The interrupt must be disabled on the first interrupt (else flood). + * + * LPTIM1_OUT is gated to TIM1 internal trigger input 2. + */ + //LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL); + //LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; + //LPTIM1->CR |= LPTIM_CR_ENABLE; + //LPTIM1->IER |= LPTIM_IER_ARRMIE; + + /* + * TODO: When using COUNTMODE CMP and ARR should be 1 & 2? + * It is intended that after counter start CNT = 0. + * Then CNT reaches 1 on first PCLK edge and 2 on the second edge. + * Using 0 and 1 means LPTIM1_OUT gets CMP match as soon as LPMTIM1 is ready. + * This means LPTIM1_OUT will be set and TIM1 will be triggered immediately. + * A DMA transfer will then occur. + * The next edge of PCLK will make CNT = 2 and ARR will match. + * LPTIM1 will then be reset (synchronous with APB presumably). + * LPTIM1_OUT will clear briefly prior to setting again on reset CMP match. + * This will allow TIM1 to be re-triggered. + */ + //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 + */ + rccEnableTIM8(FALSE); + rccResetTIM8(); + + /* + * TIM1_IC1 is mapped to TRC which means we are in trigger input mode. + * TIM1 is set in slave reset mode with input from ITR2. + * 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 about the count. + * We simply use the DMA initiated by the trigger input. + */ + //TIM8->SMCR = TIM_SMCR_TS_1; + //TIM8->SMCR |= TIM_SMCR_SMS_2; + TIM8->CCMR1 |= (TIM_CCMR1_CC1S_0/* | TIM_CCMR1_CC1S_1*/); + TIM8->CCER = TIM_CCER_CC1E; + + capture_finished = false; + capture_error = 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(1000); + } while(!capture_finished && !capture_error); + + if (capture_error) { + TRACE_ERROR("CAM > Error capturing image"); + return false; + } + + 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_INPUT); + 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); + + palSetPadMode(GPIOB, 15, PAL_MODE_INPUT_PULLUP); + palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(3)); +} + +void OV5640_TransmitConfig(void) +{ + chThdSleepMilliseconds(1000); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3103, 0x11); + I2C_write8_16bitreg(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(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(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(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(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(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(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(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val); + } + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3406, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3400, 0x04); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3401, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3402, 0x04); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3403, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3404, 0x04); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3405, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // lanuch group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 ); + + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5381, 0x1c); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5382, 0x5a); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5383, 0x06); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5384, 0x1a); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5385, 0x66); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5386, 0x80); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5387, 0x82); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5388, 0x80); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5389, 0x02); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x538b, 0x98); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x538a, 0x01); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5587, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5588, 0x01); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5586, 0x20); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5585, 0x00); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5580, 0x06); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5583, 0x40); // sat U + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5584, 0x10); // sat V + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5003, 0x08); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group + + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a0f, 0x38); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a10, 0x30); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a11, 0x61); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1b, 0x38); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1e, 0x30); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1f, 0x10); +} + +void OV5640_init(ssdv_conf_t *config) { + ov5640_conf = config; + + // 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); +} + +bool OV5640_isAvailable(void) +{ + // 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(OV5640_I2C_ADR, 0x300A, &val) && I2C_read8_16bitreg(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 + + return ret; +} + diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index 670037b..e65084d 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -16,7 +16,7 @@ #include "watchdog.h" #include "flash.h" -static uint8_t gimage_id = 10; // Global image ID (for all image threads) +static uint8_t gimage_id; // 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) From 685d4c9165b5ffdb153b53de4bce43bf1e4c8f00 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 21 Aug 2017 05:00:23 +0200 Subject: [PATCH 08/37] Added JPEG validation filter, changed method of last packet detection --- tracker/software/drivers/ov5640.c | 48 +++++++++++++++++++++++++++++-- tracker/software/modules/image.c | 33 +++------------------ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 696a286..25c5b86 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -10,6 +10,7 @@ #include "board.h" #include "defines.h" #include "debug.h" +#include "ssdv.h" #include #define OV5640_I2C_ADR 0x3C @@ -733,14 +734,55 @@ static bool vsync; static bool dma_fault; static bool dma_overrun; +/** + * Analyzes the image for JPEG errors. Returns true if the image is error free. + */ +static bool analyze_image(uint8_t *image, uint32_t image_len) +{ + ssdv_t ssdv; + uint8_t pkt[SSDV_PKT_SIZE]; + uint8_t *b; + uint32_t bi = 0; + uint8_t c = SSDV_OK; + uint16_t packet_count = 0; + + ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, "", 0); + ssdv_enc_set_buffer(&ssdv, pkt); + + while(true) + { + while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME) + { + b = &image[bi]; + uint8_t r = bi < image_len-128 ? 128 : image_len - bi; + bi += r; + if(r <= 0) + break; + ssdv_enc_feed(&ssdv, b, r); + } + + if(c == SSDV_EOI) // End of image + return true; + if(c != SSDV_OK) // Error in JPEG image + return false; + + packet_count++; + } + + TRACE_INFO("SSDV > %i packets", packet_count); +} + /** * Captures an image from the camera. */ bool OV5640_Snapshot2RAM(void) { - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV5640_Capture(); + // Capture image until we get a good image (max 5 tries) + uint8_t cntr = 5; + do { + TRACE_INFO("CAM > Capture image"); + OV5640_Capture(); + } while(!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) && cntr--); return true; } diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index e65084d..7061bfc 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -27,33 +27,8 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ uint8_t *b; uint32_t bi = 0; uint8_t c = SSDV_OK; - uint16_t packet_count = 0; uint16_t i = 0; - // Count packets - ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id); - ssdv_enc_set_buffer(&ssdv, pkt); - - while(true) - { - while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME) - { - b = &image[bi]; - uint8_t r = bi < image_len-128 ? 128 : image_len - bi; - bi += r; - if(r <= 0) - break; - ssdv_enc_feed(&ssdv, b, r); - } - - if(c == SSDV_EOI || c != SSDV_OK) - break; - - packet_count++; - } - - TRACE_INFO("SSDV > %i packets", packet_count); - // Init SSDV (FEC at 2FSK, non FEC at APRS) bi = 0; ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id); @@ -106,8 +81,8 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ msg.bin_len = aprs_encode_experimental('I', msg.msg, msg.mod, &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91)); // Transmit on radio (keep transmitter switched on if packet spacing=0ms and it isnt the last packet being sent) - if(redudantTx) transmitOnRadio(&msg, false); - transmitOnRadio(&msg, conf->packet_spacing != 0 || i == packet_count-1); + if(redudantTx) transmitOnRadio(&msg, false); // Redundant transmission + transmitOnRadio(&msg, conf->packet_spacing != 0 || c == SSDV_EOI || c != SSDV_OK); break; case PROT_SSDV_2FSK: @@ -117,8 +92,8 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ memcpy(msg.msg, pkt, sizeof(pkt)); msg.bin_len = 8*sizeof(pkt); - if(redudantTx) transmitOnRadio(&msg, false); - transmitOnRadio(&msg, conf->packet_spacing != 0 || i == packet_count-1); + if(redudantTx) transmitOnRadio(&msg, false); // Redundant transmission + transmitOnRadio(&msg, conf->packet_spacing != 0 || c == SSDV_EOI || c != SSDV_OK); break; default: From 61defb05aae855126e5b8489cb33a51cc2df4dde Mon Sep 17 00:00:00 2001 From: CInsights Date: Mon, 21 Aug 2017 21:23:55 +1000 Subject: [PATCH 09/37] Add DMA DBM as multi-buffer handling for OV5640 --- tracker/software/Makefile | 519 +++++++++++++++--------------- tracker/software/config.c | 2 +- tracker/software/drivers/ov5640.c | 287 ++++++++++++----- tracker/software/drivers/ov5640.h | 2 + 4 files changed, 468 insertions(+), 342 deletions(-) diff --git a/tracker/software/Makefile b/tracker/software/Makefile index 531864b..f4026bb 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -1,259 +1,260 @@ -############################################################################## -# Build global options -# NOTE: Can be overridden externally. -# - -# Compiler options here. -ifeq ($(USE_OPT),) - USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -endif - -# C specific options here (added to USE_OPT). -ifeq ($(USE_COPT),) - USE_COPT = -endif - -# C++ specific options here (added to USE_OPT). -ifeq ($(USE_CPPOPT),) - USE_CPPOPT = -fno-rtti -endif - -# Enable this if you want the linker to remove unused code and data -ifeq ($(USE_LINK_GC),) - USE_LINK_GC = yes -endif - -# Linker extra options here. -ifeq ($(USE_LDOPT),) - USE_LDOPT = -endif - -# Enable this if you want link time optimizations (LTO) -ifeq ($(USE_LTO),) - USE_LTO = yes -endif - -# If enabled, this option allows to compile the application in THUMB mode. -ifeq ($(USE_THUMB),) - USE_THUMB = yes -endif - -# Enable this if you want to see the full log while compiling. -ifeq ($(USE_VERBOSE_COMPILE),) - USE_VERBOSE_COMPILE = no -endif - -# If enabled, this option makes the build process faster by not compiling -# modules not used in the current configuration. -ifeq ($(USE_SMART_BUILD),) - USE_SMART_BUILD = yes -endif - -# -# Build global options -############################################################################## - -############################################################################## -# Architecture or project specific options -# - -# Stack size to be allocated to the Cortex-M process stack. This stack is -# the stack used by the main() thread. -ifeq ($(USE_PROCESS_STACKSIZE),) - USE_PROCESS_STACKSIZE = 0x400 -endif - -# Stack size to the allocated to the Cortex-M main/exceptions stack. This -# stack is used for processing interrupts and exceptions. -ifeq ($(USE_EXCEPTIONS_STACKSIZE),) - USE_EXCEPTIONS_STACKSIZE = 0x400 -endif - -# Enables the use of FPU (no, softfp, hard). -ifeq ($(USE_FPU),) - USE_FPU = no -endif - -# -# Architecture or project specific options -############################################################################## - -############################################################################## -# Project, sources and paths -# - -# Define project name here -PROJECT = ch - -# Imported source files and paths -CHIBIOS = ChibiOS -# Startup files. -include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f4xx.mk -# HAL-OSAL files (optional). -include $(CHIBIOS)/os/hal/hal.mk -include $(CHIBIOS)/os/hal/ports/STM32/STM32F4xx/platform.mk -include board/board.mk -include $(CHIBIOS)/os/hal/osal/rt/osal.mk -# RTOS files (optional). -include $(CHIBIOS)/os/rt/rt.mk -include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk -# Other files (optional). -include $(CHIBIOS)/test/rt/test.mk -include $(CHIBIOS)/os/hal/lib/streams/streams.mk -include $(CHIBIOS)/os/various/shell/shell.mk - -# Define linker script file here -LDSCRIPT= board/STM32F413xH.ld - -# C sources that can be compiled in ARM or THUMB mode depending on the global -# setting. -CSRC = $(STARTUPSRC) \ - $(KERNSRC) \ - $(PORTSRC) \ - $(OSALSRC) \ - $(HALSRC) \ - $(PLATFORMSRC) \ - $(BOARDSRC) \ - $(TESTSRC) \ - $(SHELLSRC) \ - $(CHIBIOS)/os/hal/lib/streams/memstreams.c \ - $(CHIBIOS)/os/hal/lib/streams/chprintf.c \ - modules/tracking.c \ - modules/position.c \ - modules/image.c \ - modules/log.c \ - protocols/ssdv/ssdv.c \ - protocols/ssdv/rs8.c \ - protocols/aprs/aprs.c \ - protocols/aprs/ax25.c \ - protocols/morse/morse.c \ - drivers/wrapper/pi2c.c \ - drivers/wrapper/padc.c \ - drivers/wrapper/ptime.c \ - drivers/ublox.c \ - drivers/si4464.c \ - drivers/bme280.c \ - drivers/pac1720.c \ - drivers/ov2640.c \ - drivers/ov5640.c \ - drivers/flash/flash.c \ - drivers/flash/helper.c \ - drivers/flash/ihex.c \ - debug.c \ - radio.c \ - sleep.c \ - modules.c \ - math/base.c \ - math/sgp4.c \ - math/geofence.c \ - config.c \ - watchdog.c \ - usbcfg.c \ - main.c - -# C++ sources that can be compiled in ARM or THUMB mode depending on the global -# setting. -CPPSRC = - -# C sources to be compiled in ARM mode regardless of the global setting. -# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler -# option that results in lower performance and larger code size. -ACSRC = - -# C++ sources to be compiled in ARM mode regardless of the global setting. -# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler -# option that results in lower performance and larger code size. -ACPPSRC = - -# C sources to be compiled in THUMB mode regardless of the global setting. -# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler -# option that results in lower performance and larger code size. -TCSRC = - -# C sources to be compiled in THUMB mode regardless of the global setting. -# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler -# option that results in lower performance and larger code size. -TCPPSRC = - -# List ASM source files here -ASMSRC = -ASMXSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) - -INCDIR = $(CHIBIOS)/os/license \ - $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ - $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ - $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \ - $(SHELLINC) - -# -# Project, sources and paths -############################################################################## - -############################################################################## -# Compiler settings -# - -MCU = cortex-m4 - -#TRGT = arm-elf- -TRGT = arm-none-eabi- -CC = $(TRGT)gcc -CPPC = $(TRGT)g++ -# Enable loading with g++ only if you need C++ runtime support. -# NOTE: You can use C++ even without C++ support if you are careful. C++ -# runtime support makes code size explode. -LD = $(TRGT)gcc -#LD = $(TRGT)g++ -CP = $(TRGT)objcopy -AS = $(TRGT)gcc -x assembler-with-cpp -AR = $(TRGT)ar -OD = $(TRGT)objdump -SZ = $(TRGT)size -HEX = $(CP) -O ihex -BIN = $(CP) -O binary - -# ARM-specific options here -AOPT = - -# THUMB-specific options here -TOPT = -mthumb -DTHUMB - -# Define C warning options here -CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes - -# Define C++ warning options here -CPPWARN = -Wall -Wextra -Wundef - -# -# Compiler settings -############################################################################## - -############################################################################## -# Start of user section -# - -# List all user C define here, like -D_DEBUG=1 -UDEFS = - -# Define ASM defines here -UADEFS = - -# List all user directories here -UINCDIR = modules/ drivers/ drivers/wrapper/ protocols/aprs \ - protocols/ssdv protocols/morse math/ drivers/flash/ - -# List the user directory to look for the libraries here -ULIBDIR = - -# List all user libraries here -ULIBS = -lm - -# -# End of user defines -############################################################################## - -RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC -include $(RULESPATH)/rules.mk - -burn: - st-flash write build/$(PROJECT).bin 0x08000000 +############################################################################## +# Build global options +# NOTE: Can be overridden externally. +# + +# Compiler options here. +ifeq ($(USE_OPT),) + USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 +endif + +# C specific options here (added to USE_OPT). +ifeq ($(USE_COPT),) + USE_COPT = -std=c11 +endif + +# C++ specific options here (added to USE_OPT). +ifeq ($(USE_CPPOPT),) + USE_CPPOPT = -fno-rtti +endif + +# Enable this if you want the linker to remove unused code and data +ifeq ($(USE_LINK_GC),) + USE_LINK_GC = yes +endif + +# Linker extra options here. +ifeq ($(USE_LDOPT),) + USE_LDOPT = +endif + +# Enable this if you want link time optimizations (LTO) +ifeq ($(USE_LTO),) + USE_LTO = yes +endif + +# If enabled, this option allows to compile the application in THUMB mode. +ifeq ($(USE_THUMB),) + USE_THUMB = yes +endif + +# Enable this if you want to see the full log while compiling. +ifeq ($(USE_VERBOSE_COMPILE),) + USE_VERBOSE_COMPILE = no +endif + +# If enabled, this option makes the build process faster by not compiling +# modules not used in the current configuration. +ifeq ($(USE_SMART_BUILD),) + USE_SMART_BUILD = yes +endif + +# +# Build global options +############################################################################## + +############################################################################## +# Architecture or project specific options +# + +# Stack size to be allocated to the Cortex-M process stack. This stack is +# the stack used by the main() thread. +ifeq ($(USE_PROCESS_STACKSIZE),) + USE_PROCESS_STACKSIZE = 0x400 +endif + +# Stack size to the allocated to the Cortex-M main/exceptions stack. This +# stack is used for processing interrupts and exceptions. +ifeq ($(USE_EXCEPTIONS_STACKSIZE),) + USE_EXCEPTIONS_STACKSIZE = 0x400 +endif + +# Enables the use of FPU (no, softfp, hard). +ifeq ($(USE_FPU),) + USE_FPU = no +endif + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + +# Define project name here +PROJECT = ch + +# Imported source files and paths +CHIBIOS = ChibiOS +#CHIBIOS = C:\ChibiStudio\chibios_trunk +# Startup files. +include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f4xx.mk +# HAL-OSAL files (optional). +include $(CHIBIOS)/os/hal/hal.mk +include $(CHIBIOS)/os/hal/ports/STM32/STM32F4xx/platform.mk +include board/board.mk +include $(CHIBIOS)/os/hal/osal/rt/osal.mk +# RTOS files (optional). +include $(CHIBIOS)/os/rt/rt.mk +include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk +# Other files (optional). +include $(CHIBIOS)/test/rt/test.mk +include $(CHIBIOS)/os/hal/lib/streams/streams.mk +include $(CHIBIOS)/os/various/shell/shell.mk + +# Define linker script file here +LDSCRIPT= board/STM32F413xH.ld + +# C sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CSRC = $(STARTUPSRC) \ + $(KERNSRC) \ + $(PORTSRC) \ + $(OSALSRC) \ + $(HALSRC) \ + $(PLATFORMSRC) \ + $(BOARDSRC) \ + $(TESTSRC) \ + $(SHELLSRC) \ + $(CHIBIOS)/os/hal/lib/streams/memstreams.c \ + $(CHIBIOS)/os/hal/lib/streams/chprintf.c \ + modules/tracking.c \ + modules/position.c \ + modules/image.c \ + modules/log.c \ + protocols/ssdv/ssdv.c \ + protocols/ssdv/rs8.c \ + protocols/aprs/aprs.c \ + protocols/aprs/ax25.c \ + protocols/morse/morse.c \ + drivers/wrapper/pi2c.c \ + drivers/wrapper/padc.c \ + drivers/wrapper/ptime.c \ + drivers/ublox.c \ + drivers/si4464.c \ + drivers/bme280.c \ + drivers/pac1720.c \ + drivers/ov2640.c \ + drivers/ov5640.c \ + drivers/flash/flash.c \ + drivers/flash/helper.c \ + drivers/flash/ihex.c \ + debug.c \ + radio.c \ + sleep.c \ + modules.c \ + math/base.c \ + math/sgp4.c \ + math/geofence.c \ + config.c \ + watchdog.c \ + usbcfg.c \ + main.c + +# C++ sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CPPSRC = + +# C sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACSRC = + +# C++ sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACPPSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCPPSRC = + +# List ASM source files here +ASMSRC = +ASMXSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) + +INCDIR = $(CHIBIOS)/os/license \ + $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ + $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ + $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \ + $(SHELLINC) + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Compiler settings +# + +MCU = cortex-m4 + +#TRGT = arm-elf- +TRGT = arm-none-eabi- +CC = $(TRGT)gcc +CPPC = $(TRGT)g++ +# Enable loading with g++ only if you need C++ runtime support. +# NOTE: You can use C++ even without C++ support if you are careful. C++ +# runtime support makes code size explode. +LD = $(TRGT)gcc +#LD = $(TRGT)g++ +CP = $(TRGT)objcopy +AS = $(TRGT)gcc -x assembler-with-cpp +AR = $(TRGT)ar +OD = $(TRGT)objdump +SZ = $(TRGT)size +HEX = $(CP) -O ihex +BIN = $(CP) -O binary + +# ARM-specific options here +AOPT = + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB + +# Define C warning options here +CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes + +# Define C++ warning options here +CPPWARN = -Wall -Wextra -Wundef + +# +# Compiler settings +############################################################################## + +############################################################################## +# Start of user section +# + +# List all user C define here, like -D_DEBUG=1 +UDEFS = + +# Define ASM defines here +UADEFS = + +# List all user directories here +UINCDIR = modules/ drivers/ drivers/wrapper/ protocols/aprs \ + protocols/ssdv protocols/morse math/ drivers/flash/ + +# List the user directory to look for the libraries here +ULIBDIR = + +# List all user libraries here +ULIBS = -lm + +# +# End of user defines +############################################################################## + +RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC +include $(RULESPATH)/rules.mk + +burn: + st-flash write build/$(PROJECT).bin 0x08000000 diff --git a/tracker/software/config.c b/tracker/software/config.c index b5e6f6b..62a1c50 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -3,7 +3,7 @@ #include "debug.h" module_conf_t config[9]; -uint8_t ssdv_buffer[65535] __attribute__((aligned(1024))); +uint8_t ssdv_buffer[65535] __attribute__((aligned(32))); /* * Position module configuration description diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 25c5b86..8d1d9fd 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -799,6 +799,31 @@ uint32_t OV5640_getBuffer(uint8_t** buffer) { const stm32_dma_stream_t *dmastp; +#if OV5640_USE_DMA_DBM == TRUE +uint16_t dma_index; +uint16_t dma_buffers; +#define DMA_SEGMENT_SIZE 1024 +#define DMA_FIFO_BURST_ALIGN 32 + + +#if !defined(dmaStreamGetCurrentTarget) +/** + * @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)) + +#endif /* !defined(dmaStreamGetCurrentTarget) */ +#endif /* OV5640_USE_DMA_DBM == TRUE */ + inline int32_t dma_start(void) { /* Clear any pending interrupts. */ dmaStreamClearInterrupt(dmastp); @@ -818,50 +843,126 @@ inline uint16_t dma_stop(void) { return transfer; } +#if OV5640_USE_DMA_DBM == TRUE + static void dma_interrupt(void *p, uint32_t flags) { - (void)p; + /* No parameter passed. */ + (void)p; - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* - * Nothing really to do at half way point for now. - * Implementing DBM will use HTIF. - */ - return; - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Disable VYSNC edge interrupts. */ - //nvicDisableVector(EXTI1_IRQn); - //capture_finished = true; + if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF)) { + /* + * DMA transfer error or FIFO error. + * See 9.34.19 of RM0430. + */ + dmaStreamClearInterrupt(dmastp); + TIM1->DIER &= ~TIM_DIER_TDE; + dma_fault = true; + capture_error = true; + return; + } - /* - * If DMA has run to end within a frame then this is an error. - * In single buffer mode DMA should always be terminated by VSYNC. - * - * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. - * Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC. - */ - TIM1->DIER &= ~TIM_DIER_TDE; - LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; - dma_overrun = true; - capture_error = true; - return; - } - /* - * TODO: Anything else is an error. - * Maybe set an error flag? - */ + 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. + * The DBM switch is done in h/w. + * DMA could write beyond total buffer if not stopped. + * + * Because we have run to last DMA buffer this is treated as an error. + * The DMA should normally be terminated by VSYNC before last buffer. + * Stop DMA and TIM DMA trigger and flag error. + */ + + dmaStreamClearInterrupt(dmastp); + TIM1->DIER &= ~TIM_DIER_TDE; + dma_overrun = true; + capture_error = true; + return; + } + /* + * Else Safe to allow buffer to fill. + * DMA DBM will switch buffers in h/w when this one is full. + * Just clear the interrupt and wait for TCIF. + */ + dmaStreamClearInterrupt(dmastp); + return; + } + if (flags & STM32_DMA_ISR_TCIF) { + /* + * Full buffer 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, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]); + } else { + dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]); + } + return; + } } +#else + +static void dma_interrupt(void *p, uint32_t flags) { + (void)p; + + if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF)) { + /* + * DMA transfer error or FIFO error. + * See 9.34.19 of RM0430. + */ + dmaStreamClearInterrupt(dmastp); + TIM1->DIER &= ~TIM_DIER_TDE; + dma_fault = true; + capture_error = true; + return; + } + + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* + * Nothing really to do at half way point for now. + * Implementing DBM will use HTIF. + */ + return; + } + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Disable VYSNC edge interrupts. */ + //nvicDisableVector(EXTI1_IRQn); + //capture_finished = true; + + /* + * If DMA has run to end within a frame then this is an error. + * In single buffer mode DMA should always be terminated by VSYNC. + * + * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. + * Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC. + */ + TIM1->DIER &= ~TIM_DIER_TDE; + LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; + dma_overrun = true; + capture_error = true; + return; + } + +} + +#endif /* USE_OV5640_DMA_DBM */ /* * 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. + * LPTIM1 is vector 97. + * Check CORTEX_NUM_PARAMS in cmparams.h >= 106. + * Vector table is expanded in increments of 8. */ OSAL_IRQ_PROLOGUE(); /* Reset interrupt flag for ARR. */ @@ -885,9 +986,9 @@ OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) { } /* - * Note: VSYNC is a pulse at the start of each frame. - * This is unlike the OV2640 where VSYNC is active for the entire frame. -*/ + * VSYNC is asserted during a frame. + * See OV5640 datasheet for details. + */ CH_IRQ_HANDLER(Vector5C) { CH_IRQ_PROLOGUE(); @@ -953,70 +1054,92 @@ bool OV5640_Capture(void) STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | +#if OV5640_USE_DMA_DBM == TRUE + STM32_DMA_CR_DBM | +#endif 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 +#if OV5640_USE_DMA_DBM == TRUE + /* + * Buffer address must be word aligned. + * Also note requirement for burst transfers from FIFO. + * Bursts from FIFO to memory must not cross a 1K address boundary. + * See RM0430 9.3.12 + * + * TODO: To use DMA_FIFO_BURST_ALIGN in setting of ssdv buffer alignment. + * Currently this is set to 32 manually in config.c. + */ - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); - dmaStreamClearInterrupt(dmastp); + if (((uint32_t)ov5640_conf->ram_buffer % DMA_FIFO_BURST_ALIGN) != 0) + return false; - dma_overrun = false; - dma_fault = false; + /* + * Set the initial buffer addresses. + * The updating of DMA:MxAR is done in the the DMA interrupt function. + */ + dmaStreamSetMemory0(dmastp, &ov5640_conf->ram_buffer[0]); + dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[DMA_SEGMENT_SIZE]); + + /* + * Calculate the number of whole buffers. + * TODO: Make this include remainder memory as partial buffer? + */ + dma_buffers = (ov5640_conf->ram_size / DMA_SEGMENT_SIZE); + if (dma_buffers == 0) + return false; + + /* Start with buffer index 0. */ + dma_index = 0; +#else + dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer); // Thats the buffer address + dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size); // Thats the buffer size + +#endif + dmaStreamSetMode(dmastp, dmamode); // Setup DMA + dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL \ + | STM32_DMA_FCR_FEIE); + dmaStreamClearInterrupt(dmastp); + + dma_overrun = false; + dma_fault = false; // Setup timer for PCLK rccResetLPTIM1(); rccEnableLPTIM1(FALSE); - /* - * LPTIM1 is run in external count mode (CKSEL = 0, COUNTMODE = 1). - * CKPOL is set so leading and trailing edge of PCLK increment the counter. - * The internal clocking (checking edges of LPTIM1_IN) is set to use APB. - * The internal clock must be >4 times the frequency of the input (PCLK). - * NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK. - * Depending on PCLK state when LPTIM1 is enabled, LPMTIM1_OUT be inverted. - * - * Possible fix... - * Using CKSEL = 1 where PCLK is the actual clock may still be possible. - * This would ensure coincidence between LPTIM1_OUT and PCLK. - * If using CKSEL = 1 LPTIM1 needs 5 external clocks to reach kernel ready. - * Using CKSEL = 1 only allows for leading or trailing edge counting. - * Thus we would be sure which edge of PCLK incremented the LPTIM1 counter. - * Have to test to see if CMP and ARR interrupts work when CKSEL = 1. + /* + * LPTIM1 is run in external count mode (CKSEL = 0, COUNTMODE = 1). + * CKPOL is set so leading and trailing edge of PCLK increment the counter. + * The internal clocking (checking edges of LPTIM1_IN) is set to use APB. + * The internal clock must be >4 times the frequency of the input (PCLK). + * NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK. + * Depending on PCLK state when LPTIM1 is enabled OUT may get inverted. * - * Continuing... - * LPTIM1 is enabled on the leading edge of VSYNC. - * After enabling LPTIM1 wait for the first interrupt (ARRIF). - * Waiting for ARRIF indicates that LPTIM1 kernel is ready. - * Note that waiting for interrupt when using COUNTMODE is redundant. - * The ST RM says a delay of only 2 counter (APB) clocks are required. - * But leave the interrupt check in place for now as it does no harm. - * - * The interrupt must be disabled on the first interrupt (else flood). - * - * LPTIM1_OUT is gated to TIM1 internal trigger input 2. - */ + * LPTIM1 is enabled on the VSYNC edge interrupt. + * After enabling LPTIM1 wait for the first interrupt (ARRIF). + * The interrupt must be disabled on the first interrupt (else flood). + * + * LPTIM1_OUT is gated to TIM1 internal trigger input 2. + */ LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL); LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; LPTIM1->CR |= LPTIM_CR_ENABLE; LPTIM1->IER |= LPTIM_IER_ARRMIE; - /* - * TODO: When using COUNTMODE CMP and ARR should be 1 & 2? - * It is intended that after counter start CNT = 0. - * Then CNT reaches 1 on first PCLK edge and 2 on the second edge. - * Using 0 and 1 means LPTIM1_OUT gets CMP match as soon as LPMTIM1 is ready. - * This means LPTIM1_OUT will be set and TIM1 will be triggered immediately. - * A DMA transfer will then occur. - * The next edge of PCLK will make CNT = 2 and ARR will match. - * LPTIM1 will then be reset (synchronous with APB presumably). - * LPTIM1_OUT will clear briefly prior to setting again on reset CMP match. - * This will allow TIM1 to be re-triggered. - */ + /* + * When LPTIM1 is enabled and ready LPTIM1_OUT will be not set. + * WAVPOL inverts LPTIM1_OUT so it is not set. + * On the next PCLK edge LPTIM1 will count and match ARR. + * LPTIM1_OUT will set briefly and then clear again due ARR match. + * This triggers TIM1 with the short pulse from LPTIM1_OUT. + * TODO: + * This use of LPTIM1 works probably by good luck for now. + * Switch to direct triggering of TIM using Capture input is better. + * Requires a PCB change. + */ LPTIM1->CMP = 0; LPTIM1->ARR = 1; diff --git a/tracker/software/drivers/ov5640.h b/tracker/software/drivers/ov5640.h index 3b4ba99..4b60e97 100644 --- a/tracker/software/drivers/ov5640.h +++ b/tracker/software/drivers/ov5640.h @@ -9,6 +9,8 @@ #include "hal.h" #include "types.h" +#define OV5640_USE_DMA_DBM TRUE + bool OV5640_Snapshot2RAM(void); bool OV5640_Capture(void); void OV5640_InitGPIO(void); From 032baa63041fb2fb1ba368b803e84ee6ca1a9a75 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 22 Aug 2017 02:39:01 +0200 Subject: [PATCH 10/37] Fixed DMA error, implemented image size detection --- tracker/software/drivers/ov5640.c | 29 +++++++++++++++++------------ tracker/software/modules/image.c | 1 - tracker/software/types.h | 4 ++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 8d1d9fd..58412c9 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -744,12 +744,11 @@ static bool analyze_image(uint8_t *image, uint32_t image_len) uint8_t *b; uint32_t bi = 0; uint8_t c = SSDV_OK; - uint16_t packet_count = 0; ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, "", 0); ssdv_enc_set_buffer(&ssdv, pkt); - while(true) + while(true) // FIXME: I get caught in these loops occasionally and never return { while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME) { @@ -765,11 +764,12 @@ static bool analyze_image(uint8_t *image, uint32_t image_len) return true; if(c != SSDV_OK) // Error in JPEG image return false; - - packet_count++; } +} - TRACE_INFO("SSDV > %i packets", packet_count); +bool OV5640_BufferOverflow(void) +{ + return ov5640_conf->size_sampled == ov5640_conf->ram_size - 1; // If SRAM was used completly its most likley that and overflow has occured (TODO: This is not 100% accurate) } /** @@ -780,18 +780,22 @@ bool OV5640_Snapshot2RAM(void) // Capture image until we get a good image (max 5 tries) uint8_t cntr = 5; do { + TRACE_INFO("CAM > Capture image"); OV5640_Capture(); + TRACE_INFO("CAM > Capture finished"); + + ov5640_conf->size_sampled = ov5640_conf->ram_size - 1; + while(!ov5640_conf->ram_buffer[ov5640_conf->size_sampled] && ov5640_conf->size_sampled > 0) + ov5640_conf->size_sampled--; + + TRACE_INFO("CAM > Image size: %d bytes", ov5640_conf->size_sampled); + } while(!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) && cntr--); 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; @@ -1056,6 +1060,8 @@ bool OV5640_Capture(void) STM32_DMA_CR_TEIE | #if OV5640_USE_DMA_DBM == TRUE STM32_DMA_CR_DBM | + STM32_DMA_CR_HTIE | + #endif STM32_DMA_CR_TCIE; @@ -1188,8 +1194,6 @@ bool OV5640_Capture(void) return false; } - TRACE_INFO("CAM > Capture finished"); - return true; } @@ -1261,6 +1265,7 @@ void OV5640_TransmitConfig(void) I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val); } + //I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4404, 0x27); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index 7061bfc..92841c8 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -179,7 +179,6 @@ THD_FUNCTION(imgThread, arg) { // Get image image_len = OV2640_getBuffer(&image); - TRACE_INFO("IMG > Image size: %d bytes", image_len); } else if(OV5640_isAvailable()) { // OV5640 available diff --git a/tracker/software/types.h b/tracker/software/types.h index 090800b..6781693 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -112,8 +112,8 @@ typedef struct { resolution_t res; // Camera resolution uint8_t quality; // JPEG quality 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) + uint32_t ram_size; // Size of buffer + uint32_t size_sampled; // Actual image data size (do not set in config) bool redundantTx; // Redundand packet transmission (APRS only) } ssdv_conf_t; From 9eaaf814eb6bce00f8048464ba0323957a76e9bd Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 22 Aug 2017 05:19:02 +0200 Subject: [PATCH 11/37] Increased I2C speed --- tracker/software/drivers/wrapper/pi2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracker/software/drivers/wrapper/pi2c.c b/tracker/software/drivers/wrapper/pi2c.c index 2475cae..8e5a62c 100644 --- a/tracker/software/drivers/wrapper/pi2c.c +++ b/tracker/software/drivers/wrapper/pi2c.c @@ -10,7 +10,7 @@ const I2CConfig _i2cfg = { OPMODE_I2C, - 50000, + 400000, STD_DUTY_CYCLE, }; From e114808cb35b2705e82201aa49835509d2025ddc Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 22 Aug 2017 05:26:04 +0200 Subject: [PATCH 12/37] Added DMA transaction size --- tracker/software/drivers/ov5640.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 58412c9..2f91975 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -1088,6 +1088,7 @@ bool OV5640_Capture(void) */ dmaStreamSetMemory0(dmastp, &ov5640_conf->ram_buffer[0]); dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[DMA_SEGMENT_SIZE]); + dmaStreamSetTransactionSize(dmastp, DMA_SEGMENT_SIZE); /* * Calculate the number of whole buffers. From 3f1a049d8e4868f3ea7c136f527779b83f9b5359 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 22 Aug 2017 06:10:55 +0200 Subject: [PATCH 13/37] Added more debugging --- tracker/software/drivers/ov5640.c | 36 ++++++++++++------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 2f91975..8c4f3cc 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -1226,20 +1226,22 @@ void OV5640_InitGPIO(void) void OV5640_TransmitConfig(void) { - chThdSleepMilliseconds(1000); + TRACE_INFO("CAM > ... Software reset"); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3103, 0x11); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3008, 0x82); chThdSleepMilliseconds(100); + TRACE_INFO("CAM > ... Initialization"); for(uint32_t i=0; (OV5640YUV_Sensor_Dvp_Init[i].reg != 0xffff) || (OV5640YUV_Sensor_Dvp_Init[i].val != 0xff); i++) I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640YUV_Sensor_Dvp_Init[i].reg, OV5640YUV_Sensor_Dvp_Init[i].val); chThdSleepMilliseconds(500); + TRACE_INFO("CAM > ... Configure JPEG"); for(uint32_t i=0; (OV5640_JPEG_QSXGA[i].reg != 0xffff) || (OV5640_JPEG_QSXGA[i].val != 0xff); i++) I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_JPEG_QSXGA[i].reg, OV5640_JPEG_QSXGA[i].val); - + TRACE_INFO("CAM > ... Configure Resolution"); switch(ov5640_conf->res) { case RES_QVGA: for(uint32_t i=0; (OV5640_QSXGA2QVGA[i].reg != 0xffff) || (OV5640_QSXGA2QVGA[i].val != 0xff); i++) @@ -1267,8 +1269,9 @@ void OV5640_TransmitConfig(void) } //I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4404, 0x27); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale + //I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale + TRACE_INFO("CAM > ... Light Mode: Auto"); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3406, 0x00); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3400, 0x04); @@ -1279,9 +1282,9 @@ void OV5640_TransmitConfig(void) I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3405, 0x00); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // lanuch group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 ); - + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 ); + TRACE_INFO("CAM > ... Saturation: 0"); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5381, 0x1c); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5382, 0x5a); @@ -1297,33 +1300,20 @@ void OV5640_TransmitConfig(void) I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + TRACE_INFO("CAM > ... Brightness: 0"); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5587, 0x00); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5588, 0x01); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 + TRACE_INFO("CAM > ... Contrast: 0"); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5586, 0x20); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5585, 0x00); I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5580, 0x06); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5583, 0x40); // sat U - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5584, 0x10); // sat V - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5003, 0x08); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a0f, 0x38); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a10, 0x30); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a11, 0x61); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1b, 0x38); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1e, 0x30); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1f, 0x10); + I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 } void OV5640_init(ssdv_conf_t *config) { @@ -1342,11 +1332,13 @@ void OV5640_init(ssdv_conf_t *config) { palSetLine(LINE_CAM_EN); // Switch on camera palSetLine(LINE_CAM_RESET); // Toggle reset + chThdSleepMilliseconds(1000); + // Send settings to OV5640 TRACE_INFO("CAM > Transmit config to camera"); OV5640_TransmitConfig(); - chThdSleepMilliseconds(3000); + chThdSleepMilliseconds(1000); } void OV5640_deinit(void) { From fe5c08da7e81929af40d5dd7a4c400ca61e1b0f5 Mon Sep 17 00:00:00 2001 From: CInsights Date: Wed, 23 Aug 2017 18:10:29 +1000 Subject: [PATCH 14/37] Add saving of DMA flags so TRACE can output specific errors. Add TRACE messages for DMA error conditions. Consolidate DMA error condition flags. Add clear for DMA transfer full interrupt. Tidy up some comments. --- tracker/software/drivers/ov5640.c | 101 +++++++++++++++--------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 8c4f3cc..4f72bef 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -729,10 +729,9 @@ static ssdv_conf_t *ov5640_conf; /* TODO: Implement a state machine instead of multiple flags. */ static bool LptimRdy; static bool capture_finished; -static bool capture_error; static bool vsync; -static bool dma_fault; -static bool dma_overrun; +static bool dma_error; +static uint32_t dma_flags; /** * Analyzes the image for JPEG errors. Returns true if the image is error free. @@ -853,15 +852,17 @@ static void dma_interrupt(void *p, uint32_t flags) { /* No parameter passed. */ (void)p; + dma_flags = flags; if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF)) { /* * DMA transfer error or FIFO error. * See 9.34.19 of RM0430. + * + * Disable timer DMA request and flag fault. */ - dmaStreamClearInterrupt(dmastp); TIM1->DIER &= ~TIM_DIER_TDE; - dma_fault = true; - capture_error = true; + dma_error = true; + dmaStreamClearInterrupt(dmastp); return; } @@ -876,15 +877,14 @@ static void dma_interrupt(void *p, uint32_t flags) { * The DBM switch is done in h/w. * DMA could write beyond total buffer if not stopped. * - * Because we have run to last DMA buffer this is treated as an error. + * Since this is the last DMA buffer this is treated as an error. * The DMA should normally be terminated by VSYNC before last buffer. * Stop DMA and TIM DMA trigger and flag error. */ - dmaStreamClearInterrupt(dmastp); TIM1->DIER &= ~TIM_DIER_TDE; - dma_overrun = true; - capture_error = true; + dma_error = true; + dmaStreamClearInterrupt(dmastp); return; } /* @@ -901,13 +901,14 @@ static void dma_interrupt(void *p, uint32_t flags) { * Update non-active memory address register. * DMA will use new address at h/w DBM switch. */ - dmaStreamClearInterrupt(dmastp); + if (dmaStreamGetCurrentTarget(dmastp) == 1) { dmaStreamSetMemory0(dmastp, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]); } else { dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]); } + dmaStreamClearInterrupt(dmastp); return; } } @@ -917,29 +918,20 @@ static void dma_interrupt(void *p, uint32_t flags) { static void dma_interrupt(void *p, uint32_t flags) { (void)p; - if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF)) { + dma_flags = flags; + dmaStreamClearInterrupt(dmastp); + if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF } STM32_DMA_ISR_DMEIF)) { /* - * DMA transfer error or FIFO error. + * DMA transfer error, FIFO error or Direct mode error. * See 9.34.19 of RM0430. */ dmaStreamClearInterrupt(dmastp); TIM1->DIER &= ~TIM_DIER_TDE; - dma_fault = true; - capture_error = true; + dma_error = true; return; } - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* - * Nothing really to do at half way point for now. - * Implementing DBM will use HTIF. - */ - return; - } if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Disable VYSNC edge interrupts. */ - //nvicDisableVector(EXTI1_IRQn); - //capture_finished = true; /* * If DMA has run to end within a frame then this is an error. @@ -950,16 +942,17 @@ static void dma_interrupt(void *p, uint32_t flags) { */ TIM1->DIER &= ~TIM_DIER_TDE; LPTIM1->CR &= ~LPTIM_CR_CNTSTRT; - dma_overrun = true; - capture_error = true; + dma_error = true; return; } - } #endif /* USE_OV5640_DMA_DBM */ /* * The LPTIM interrupt handler. + * TODO: Remove LPTIM1 interrupt. + * Not needed in COUNTMODE. + * LPTIM clock kernel initialises in 2 internal clock cycles */ OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) { @@ -1040,12 +1033,12 @@ CH_IRQ_HANDLER(Vector5C) { 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 - */ + /* + * 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)); @@ -1079,9 +1072,10 @@ bool OV5640_Capture(void) * Currently this is set to 32 manually in config.c. */ - if (((uint32_t)ov5640_conf->ram_buffer % DMA_FIFO_BURST_ALIGN) != 0) + if (((uint32_t)ov5640_conf->ram_buffer % DMA_FIFO_BURST_ALIGN) != 0) { + TRACE_ERROR("CAM > Buffer not allocated on DMA burst boundary"); return false; - + } /* * Set the initial buffer addresses. * The updating of DMA:MxAR is done in the the DMA interrupt function. @@ -1095,14 +1089,15 @@ bool OV5640_Capture(void) * TODO: Make this include remainder memory as partial buffer? */ dma_buffers = (ov5640_conf->ram_size / DMA_SEGMENT_SIZE); - if (dma_buffers == 0) + if (dma_buffers == 0) { + TRACE_ERROR("CAM > Capture buffer less than minimum DMA segment size"); return false; - + } /* Start with buffer index 0. */ dma_index = 0; #else - dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size); // Thats the buffer size + dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer); + dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size); #endif dmaStreamSetMode(dmastp, dmamode); // Setup DMA @@ -1110,8 +1105,8 @@ bool OV5640_Capture(void) | STM32_DMA_FCR_FEIE); dmaStreamClearInterrupt(dmastp); - dma_overrun = false; - dma_fault = false; + dma_error = false; + dma_flags = 0; // Setup timer for PCLK rccResetLPTIM1(); @@ -1156,9 +1151,7 @@ bool OV5640_Capture(void) /* * 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 + * We use TIM1 because it can be triggered from LPTIM1. */ rccResetTIM1(); rccEnableTIM1(FALSE); @@ -1176,7 +1169,6 @@ bool OV5640_Capture(void) TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); capture_finished = false; - capture_error = false; vsync = false; // Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF) @@ -1188,13 +1180,22 @@ bool OV5640_Capture(void) do { // Have a look for some bytes in memory for testing if capturing works TRACE_INFO("CAM > Capturing"); chThdSleepMilliseconds(100); - } while(!capture_finished && !capture_error); + } while(!capture_finished && !dma_error); - if (capture_error) { - TRACE_ERROR("CAM > Error capturing image"); - return false; + if (dma_error) { + if (dma_flags & STM32_DMA_ISR_HTIF) + TRACE_ERROR("CAM > DMA abort - last buffer segment") + if (dma_flags & STM32_DMA_ISR_FEIF) + TRACE_ERROR("CAM > DMA FIFO error") + if (dma_flags & STM32_DMA_ISR_TEIF) + TRACE_ERROR("CAM > DMA stream transfer error") + if (dma_flags & STM32_DMA_ISR_DMEIF) + TRACE_ERROR("CAM > DMA direct mode error") + TRACE_ERROR("CAM > Error capturing image"); + return false; } + TRACE_INFO("CAM > Capture success"); return true; } From b5d7bed0ffd127ef29858787c303488a9182a028 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sun, 27 Aug 2017 20:45:59 +0200 Subject: [PATCH 15/37] Increased I2C speed --- tracker/software/drivers/wrapper/pi2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracker/software/drivers/wrapper/pi2c.c b/tracker/software/drivers/wrapper/pi2c.c index 8e5a62c..c3e72b0 100644 --- a/tracker/software/drivers/wrapper/pi2c.c +++ b/tracker/software/drivers/wrapper/pi2c.c @@ -10,8 +10,8 @@ const I2CConfig _i2cfg = { OPMODE_I2C, - 400000, - STD_DUTY_CYCLE, + 200000, + FAST_DUTY_CYCLE_2, }; static bool I2C_transmit(I2CDriver *driver, uint8_t addr, uint8_t *txbuf, uint32_t txbytes, uint8_t *rxbuf, uint32_t rxbytes, systime_t timeout) { From 70ff480b43cb9fb90226c803d18c32be5f455e3d Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sun, 27 Aug 2017 21:14:30 +0200 Subject: [PATCH 16/37] Fixed radio kept swiched on When a picture was sent, the radio was kept switched on. The detection of the image EOI did not work for the packet transmission because the EOI detection returned the method earlier. --- tracker/software/drivers/ov5640.c | 4 ++-- tracker/software/drivers/si4464.c | 4 ++-- tracker/software/drivers/si4464.h | 2 +- tracker/software/modules/image.c | 6 ++++-- tracker/software/radio.c | 9 +++++++-- tracker/software/radio.h | 1 + 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 4f72bef..cc292ef 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -1178,8 +1178,8 @@ bool OV5640_Capture(void) 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); + TRACE_INFO("CAM > ... capturing"); + chThdSleepMilliseconds(200); } while(!capture_finished && !dma_error); if (dma_error) { diff --git a/tracker/software/drivers/si4464.c b/tracker/software/drivers/si4464.c index 896f330..c44b291 100644 --- a/tracker/software/drivers/si4464.c +++ b/tracker/software/drivers/si4464.c @@ -29,7 +29,7 @@ bool initialized = false; */ void Si4464_Init(void) { // Reset radio) - radioShutdown(); + Si4464_shutdown(); chThdSleepMilliseconds(10); // Initialize SPI @@ -295,7 +295,7 @@ void stopTx(void) { Si4464_write(change_state_command, 2); } -void radioShutdown(void) { +void Si4464_shutdown(void) { palSetLine(LINE_RADIO_SDN); // Power down chip palSetLine(LINE_IO_LED1); // Set indication LED RADIO_MOD_GPIO(false); // Set GPIO1 low diff --git a/tracker/software/drivers/si4464.h b/tracker/software/drivers/si4464.h index c1f3083..d9cc835 100644 --- a/tracker/software/drivers/si4464.h +++ b/tracker/software/drivers/si4464.h @@ -21,7 +21,7 @@ void setDeviation(uint32_t deviation); void setPowerLevel(int8_t level); void startTx(uint16_t size); void stopTx(void); -void radioShutdown(void); +void Si4464_shutdown(void); bool radioTune(uint32_t frequency, uint16_t shift, int8_t level, uint16_t size); void Si4464_writeFIFO(uint8_t *msg, uint8_t size); uint8_t Si4464_freeFIFO(void); diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index 92841c8..d184baa 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -54,9 +54,11 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ if(c == SSDV_EOI) { + shutdownRadio(); TRACE_INFO("SSDV > ssdv_enc_get_packet said EOI"); break; } else if(c != SSDV_OK) { + shutdownRadio(); TRACE_ERROR("SSDV > ssdv_enc_get_packet failed: %i", c); return; } @@ -82,7 +84,7 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ // Transmit on radio (keep transmitter switched on if packet spacing=0ms and it isnt the last packet being sent) if(redudantTx) transmitOnRadio(&msg, false); // Redundant transmission - transmitOnRadio(&msg, conf->packet_spacing != 0 || c == SSDV_EOI || c != SSDV_OK); + transmitOnRadio(&msg, conf->packet_spacing != 0); // Keep transmitter switched on if next packet will be sent right away break; case PROT_SSDV_2FSK: @@ -93,7 +95,7 @@ void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_ msg.bin_len = 8*sizeof(pkt); if(redudantTx) transmitOnRadio(&msg, false); // Redundant transmission - transmitOnRadio(&msg, conf->packet_spacing != 0 || c == SSDV_EOI || c != SSDV_OK); + transmitOnRadio(&msg, conf->packet_spacing != 0); // Keep transmitter switched on if next packet will be sent right away break; default: diff --git a/tracker/software/radio.c b/tracker/software/radio.c index 12b28d3..fd1cd66 100644 --- a/tracker/software/radio.c +++ b/tracker/software/radio.c @@ -260,6 +260,12 @@ void send2FSK(radioMSG_t *msg) { chThdSleepMilliseconds(1); // Wait for routine to finish } +void shutdownRadio(void) +{ + Si4464_shutdown(); + active_mod = MOD_NOT_SET; +} + /** * Returns APRS region specific frequency determined by GPS location. It will * use the APRS default frequency set in the config file if no GPS fix has @@ -354,8 +360,7 @@ bool transmitOnRadio(radioMSG_t *msg, bool shutdown) { if(shutdown) { - radioShutdown(); // Shutdown radio for reinitialization - active_mod = MOD_NOT_SET; + shutdownRadio(); // Shutdown radio for reinitialization } else { active_mod = msg->mod; } diff --git a/tracker/software/radio.h b/tracker/software/radio.h index 21c7cbc..5f4f075 100644 --- a/tracker/software/radio.h +++ b/tracker/software/radio.h @@ -22,6 +22,7 @@ extern mutex_t radio_mtx; bool transmitOnRadio(radioMSG_t *msg, bool shutdown); +void shutdownRadio(void); uint32_t getFrequency(freq_conf_t *config); THD_FUNCTION(moduleRADIO, arg); From 934fd1ec3ac55fc4ee568860962766a9732f20a4 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 28 Aug 2017 04:14:56 +0200 Subject: [PATCH 17/37] Adjusted clocks Flipped image in OV5640 driver Fixed spelling --- tracker/software/config.c | 34 +++++++++++++++---------------- tracker/software/config.h | 6 +++--- tracker/software/drivers/ov5640.c | 10 ++++----- tracker/software/mcuconf.h | 4 ++-- tracker/software/sleep.c | 2 +- tracker/software/types.h | 2 +- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index 62a1c50..57e256f 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -3,7 +3,7 @@ #include "debug.h" module_conf_t config[9]; -uint8_t ssdv_buffer[65535] __attribute__((aligned(32))); +uint8_t ssdv_buffer[256*1024] __attribute__((aligned(32))); /* * Position module configuration description @@ -50,7 +50,7 @@ uint8_t ssdv_buffer[65535] __attribute__((aligned(32))); * - TRIG_NEW_POINT Triggered when new track point available * - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec) * this option requires trigger.timeout to be set - * - TRIG_CONTINOUSLY Continue continously (e.g. send new image once old image sent completely) + * - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely) * * ============================== The following options are needed if protocol == PROT_APRS_AFSK or protocol == PROT_APRS_2GFSK =============================== * @@ -173,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_STATIC; // Dynamic frequency allocation - config[0].frequency.hz = 144390000; // Default frequency 144.800 MHz + config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation + config[0].frequency.hz = 144800000; // 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 = 11; // APRS SSID + config[0].aprs_conf.ssid = 12; // 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 @@ -190,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 @@ -234,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_STATIC; // Dynamic frequency allocation - config[3].frequency.hz = 144390000; // Transmission frequency 144.800 MHz + config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation + config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz config[3].init_delay = 0; // Module startup delay in msec - //config[3].packet_spacing = 30000; // 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.type = TRIG_CONTINUOUSLY; // Continuous Trigger config[3].trigger.timeout = 10; // Timeout 10 sec chsnprintf(config[3].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign - config[3].aprs_conf.ssid = 11; // APRS SSID + config[3].aprs_conf.ssid = 12; // 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 @@ -266,15 +266,15 @@ void start_user_modules(void) start_position_thread(&config[4]);*/ // Module IMAGE, APRS 2m 2GFSK - config[5].power = 127; // Power 20 dBm + config[5].power = 50; // Power 20 dBm config[5].protocol = PROT_APRS_2GFSK; // Protocol APRS SSDV, modulation 2GFSK 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].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) + //config[5].sleep_conf.type = SLEEP_WHEN_VBAT_BELOW_THRES; + //config[5].sleep_conf.vbat_thres = 4000; + config[5].trigger.type = TRIG_CONTINUOUSLY; // Trigger transmission on timeout (Periodic cycling) config[5].trigger.timeout = 30; // Timeout 10 sec chsnprintf(config[5].aprs_conf.callsign, 6, "DL7AD"); // APRS Callsign config[5].aprs_conf.ssid = 12; // APRS SSID @@ -284,7 +284,7 @@ void start_user_modules(void) config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size config[5].ssdv_conf.res = RES_VGA; // Resolution XGA //config[5].ssdv_conf.redundantTx = true; // Transmit packets twice - start_image_thread(&config[5]); + //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 327972c..75a3b31 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -15,13 +15,13 @@ #define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */ #define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */ -#define GPS_ON_VBAT 0 /* Battery voltage threshold at which GPS is switched on */ -#define GPS_OFF_VBAT 0 /* Battery voltage threshold at which GPS is switched off */ +#define GPS_ON_VBAT 4000 /* Battery voltage threshold at which GPS is switched on */ +#define GPS_OFF_VBAT 3500 /* Battery voltage threshold at which GPS is switched off */ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE TRUE /* Enables file and line tracing on debugging port */ -#define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. +#define RUN_3V FALSE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. * With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in * order to work. However 3V takes a lot of power in idle. You can save energy using 1.8V. */ diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index cc292ef..dfec8a1 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -70,7 +70,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3c0a, 0x9c }, { 0x3c0b, 0x40 }, - { 0x3820, 0x41 }, + { 0x3820, 0x46 }, { 0x3821, 0x01 }, //07 //windows setup @@ -96,7 +96,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3034, 0x1a }, { 0x3035, 0x11 }, //15fps { 0x3036, 0x46 }, - { 0x3037, 0x13 }, + { 0x3037, 0x14 }, { 0x3038, 0x00 }, { 0x3039, 0x00 }, @@ -331,7 +331,7 @@ static const struct regval_list ov5640_vga_preview[] = { 0x3035, 0x11 }, // PLL { 0x3036, 0x46 }, // PLL { 0x3c07, 0x08 }, // light meter 1 threshold [7:0] - { 0x3820, 0x41 }, // Sensor flip off, ISP flip on + { 0x3820, 0x46 }, // 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 @@ -397,8 +397,8 @@ static const struct regval_list OV5640_RGB_QVGA[] = //2592x1944 QSXGA static const struct regval_list OV5640_JPEG_QSXGA[] = { - {0x3820 ,0x41}, - {0x3821 ,0x26}, + {0x3820 ,0x46}, + {0x3821 ,0x20}, {0x3814 ,0x11}, {0x3815 ,0x11}, {0x3803 ,0x00}, diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index 885ed41..63e57e5 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -48,9 +48,9 @@ #define STM32_PLLN_VALUE 192 #define STM32_PLLP_VALUE 4 #define STM32_PLLQ_VALUE 4 -#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_HPRE STM32_HPRE_DIV2 #define STM32_PPRE1 STM32_PPRE1_DIV2 -#define STM32_PPRE2 STM32_PPRE2_DIV1 +#define STM32_PPRE2 STM32_PPRE2_DIV4 #define STM32_RTCSEL STM32_RTCSEL_LSI #define STM32_RTCPRE_VALUE 8 #define STM32_MCO1SEL STM32_MCO1SEL_HSE diff --git a/tracker/software/sleep.c b/tracker/software/sleep.c index 6c3c370..87eb410 100644 --- a/tracker/software/sleep.c +++ b/tracker/software/sleep.c @@ -48,7 +48,7 @@ systime_t waitForTrigger(systime_t prev, trigger_conf_t *config) case TRIG_TIMEOUT: // Wait for specified timeout return chThdSleepUntilWindowed(prev, prev + S2ST(config->timeout)); - case TRIG_CONTINOUSLY: // Immediate trigger + case TRIG_CONTINUOUSLY: // Immediate trigger return chVTGetSystemTimeX(); case TRIG_ONCE: // No trigger defined diff --git a/tracker/software/types.h b/tracker/software/types.h index 6781693..521e35b 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -131,7 +131,7 @@ typedef enum { TRIG_ONCE, // Trigger once and never again (e.g. transmit specific position packet only at startup) TRIG_NEW_POINT, // Triggered when new track point available TRIG_TIMEOUT, // Triggered by timeout (e.g. trasmit position every 120sec) - TRIG_CONTINOUSLY // Continue continously (e.g. send new image once old image sent completely) + TRIG_CONTINUOUSLY // Continue continuously (e.g. send new image once old image sent completely) } trigger_type_t; typedef struct { From 23c42f83c39389bc5b3a6c6818e9ae69bfc73372 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 29 Aug 2017 00:35:16 +0200 Subject: [PATCH 18/37] Fixed faulty image transmission, implemented server exception handling in decoder --- decoder/decoder.py | 6 +++++- tracker/software/config.c | 2 +- tracker/software/config.h | 2 +- tracker/software/drivers/ov5640.c | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/decoder/decoder.py b/decoder/decoder.py index cbaef71..459f8b0 100755 --- a/decoder/decoder.py +++ b/decoder/decoder.py @@ -108,7 +108,11 @@ def received_data(data): print 'Send to SSDV data server: OK' error = False except urllib2.URLError, error: - print 'Send to SSDV data server: failed (connection error :( trying again...)' + if error.code == 400: + print 'The SSDV server indicated a faulty packet: ' + error.read() + error = False + else: + print 'Send to SSDV data server: failed (connection error :( trying again...)' except urllib2.HTTPError, error: # The server did not like our packets :( print 'Send to SSDV data server: failed (the server did not like our packets :( )' diff --git a/tracker/software/config.c b/tracker/software/config.c index 57e256f..906350f 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -237,7 +237,7 @@ void start_user_modules(void) config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz config[3].init_delay = 0; // Module startup delay in msec - config[3].packet_spacing = 30000; // Packet spacing in ms + config[3].packet_spacing = 10000; // 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_CONTINUOUSLY; // Continuous Trigger diff --git a/tracker/software/config.h b/tracker/software/config.h index 75a3b31..8ffbbb1 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -21,7 +21,7 @@ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE TRUE /* Enables file and line tracing on debugging port */ -#define RUN_3V FALSE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. +#define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. * With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in * order to work. However 3V takes a lot of power in idle. You can save energy using 1.8V. */ diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index dfec8a1..473ca16 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -96,7 +96,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3034, 0x1a }, { 0x3035, 0x11 }, //15fps { 0x3036, 0x46 }, - { 0x3037, 0x14 }, + { 0x3037, 0x13 }, { 0x3038, 0x00 }, { 0x3039, 0x00 }, From 6851721d03fd42854dfec8c333db06d6898920b6 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Tue, 29 Aug 2017 01:21:32 +0200 Subject: [PATCH 19/37] Removed duplicate GPS printout, added more debugging at PVT Polling --- tracker/software/config.c | 2 +- tracker/software/config.h | 8 ++++---- tracker/software/debug.h | 15 --------------- tracker/software/drivers/ublox.c | 8 ++++++-- tracker/software/modules/image.c | 2 +- tracker/software/modules/tracking.c | 10 ++++++---- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index 906350f..fdc9b48 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -237,7 +237,7 @@ void start_user_modules(void) config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz config[3].init_delay = 0; // Module startup delay in msec - config[3].packet_spacing = 10000; // Packet spacing in ms + config[3].packet_spacing = 20000; // 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_CONTINUOUSLY; // Continuous Trigger diff --git a/tracker/software/config.h b/tracker/software/config.h index 8ffbbb1..2c34167 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -8,18 +8,18 @@ #include "radio.h" #include "sleep.h" -#define TRACK_CYCLE_TIME 60 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */ +#define TRACK_CYCLE_TIME 120 /* 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 */ #define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */ #define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */ -#define GPS_ON_VBAT 4000 /* Battery voltage threshold at which GPS is switched on */ -#define GPS_OFF_VBAT 3500 /* Battery voltage threshold at which GPS is switched off */ +#define GPS_ON_VBAT 3500 /* Battery voltage threshold at which GPS is switched on */ +#define GPS_OFF_VBAT 3000 /* Battery voltage threshold at which GPS is switched off */ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ -#define TRACE_FILE TRUE /* Enables file and line tracing on debugging port */ +#define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */ #define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. * With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in diff --git a/tracker/software/debug.h b/tracker/software/debug.h index 456e5e9..daf7693 100644 --- a/tracker/software/debug.h +++ b/tracker/software/debug.h @@ -73,21 +73,6 @@ extern bool debug_on_usb; #define TRACE_TAB " " #endif -#define TRACE_GPSFIX(fix) { \ - TRACE_INFO("GPS > New GPS Fix\r\n"\ - "%s GPS Time: %04d-%02d-%02d %02d:%02d:%02d\r\n" \ - "%s Sats: %d (used for solution)\r\n" \ - "%s Latitude: %d.%07ddeg\r\n" \ - "%s Longitude: %d.%07ddeg\r\n" \ - "%s Altitude: %d Meter", \ - TRACE_TAB, (fix)->time.year, (fix)->time.month, (fix)->time.day, (fix)->time.hour, (fix)->time.minute, (fix)->time.second, \ - TRACE_TAB, (fix)->num_svs, \ - TRACE_TAB, (fix)->lat/10000000, ((fix)->lat > 0 ? 1:-1)*(fix)->lat%10000000, \ - TRACE_TAB, (fix)->lon/10000000, ((fix)->lon > 0 ? 1:-1)*(fix)->lon%10000000, \ - TRACE_TAB, (fix)->alt \ - ); \ -} - #define TRACE_BIN(data, len) { \ chMtxLock(&trace_mtx); \ chprintf((BaseSequentialStream*)&SD3, "[%8d.%03d][DEBUG] ", chVTGetSystemTimeX()/CH_CFG_ST_FREQUENCY, (chVTGetSystemTimeX()*1000/CH_CFG_ST_FREQUENCY)%1000); \ diff --git a/tracker/software/drivers/ublox.c b/tracker/software/drivers/ublox.c index f70129e..774c91e 100644 --- a/tracker/software/drivers/ublox.c +++ b/tracker/software/drivers/ublox.c @@ -188,8 +188,6 @@ bool gps_get_fix(gpsFix_t *fix) { return false; } - TRACE_INFO("GPS > PVT Polling OK"); - fix->num_svs = response[23]; fix->type = response[20]; @@ -217,6 +215,12 @@ bool gps_get_fix(gpsFix_t *fix) { fix->alt = (uint16_t)alt_tmp; } + TRACE_INFO("GPS > PVT Polling OK time=%04d-%02d-%02d %02d:%02d:%02d lat=%d.%05d lon=%d.%05d alt=%dm sats=%d", + fix->time.year, fix->time.month, fix->time.day, fix->time.hour, fix->time.minute, fix->time.day, + fix->lat/10000000, (fix->lat > 0 ? 1:-1)*(fix->lat/100)%100000, fix->lon/10000000, (fix->lon > 0 ? 1:-1)*(fix->lon/100)%100000, + fix->alt, fix->num_svs + ); + return true; } diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index d184baa..dbdc777 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -16,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) diff --git a/tracker/software/modules/tracking.c b/tracker/software/modules/tracking.c index 6aa82d4..96b76ee 100644 --- a/tracker/software/modules/tracking.c +++ b/tracker/software/modules/tracking.c @@ -252,13 +252,15 @@ THD_FUNCTION(trackingThread, arg) { if(isGPSLocked(&gpsFix)) { // GPS locked // Switch off GPS (if cycle time is more than 60 seconds) - #if TRACK_CYCLE_TIME > 60 + #if TRACK_CYCLE_TIME >= 60 + TRACE_INFO("TRAC > Switch off GPS"); GPS_Deinit(); + #else + TRACE_INFO("TRAC > Keep GPS switched of because cycle < 60sec"); #endif // Debug TRACE_INFO("TRAC > GPS sampling finished GPS LOCK"); - TRACE_GPSFIX(&gpsFix); // Calibrate RTC setTime(gpsFix.time); @@ -332,13 +334,13 @@ THD_FUNCTION(trackingThread, arg) { // Trace data TRACE_INFO( "TRAC > New tracking point available (ID=%d)\r\n" "%s Time %04d-%02d-%02d %02d:%02d:%02d\r\n" - "%s Pos %d.%07d %d.%07d Alt %dm\r\n" + "%s Pos %d.%05d %d.%05d Alt %dm\r\n" "%s Sats %d TTFF %dsec\r\n" "%s ADC Vbat=%d.%03dV Vsol=%d.%03dV VUSB=%d.%03dV Pbat=%dmW Isol=%dmA\r\n" "%s AIR p=%6d.%01dPa T=%2d.%02ddegC phi=%2d.%01d%%", tp->id, TRACE_TAB, tp->time.year, tp->time.month, tp->time.day, tp->time.hour, tp->time.minute, tp->time.day, - TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*tp->gps_lat%10000000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*tp->gps_lon%10000000, tp->gps_alt, + TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*(tp->gps_lat/100)%100000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*(tp->gps_lon/100)%100000, tp->gps_alt, TRACE_TAB, tp->gps_sats, tp->gps_ttff, TRACE_TAB, tp->adc_vbat/1000, (tp->adc_vbat%1000), tp->adc_vsol/1000, (tp->adc_vsol%1000), tp->adc_vusb/1000, (tp->adc_vusb%1000), tp->adc_pbat, tp->adc_isol, TRACE_TAB, tp->air_press/10, tp->air_press%10, tp->air_temp/100, tp->air_temp%100, tp->air_hum/10, tp->air_hum%10 From 20b70a5680d21cfd3ff841bddb26696548fd85fd Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Wed, 30 Aug 2017 05:29:59 +0200 Subject: [PATCH 20/37] Adjusted clocks for low power operation (camera fix no implemented yet) --- tracker/software/config.c | 2 +- tracker/software/config.h | 6 +++--- tracker/software/drivers/ov5640.c | 24 ++++++++++++++++++------ tracker/software/drivers/ov5640.h | 1 + tracker/software/mcuconf.h | 6 +++--- tracker/software/modules/image.c | 2 +- tracker/software/radio.c | 5 +++-- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index fdc9b48..d241cee 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -282,7 +282,7 @@ void start_user_modules(void) chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign 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_VGA; // Resolution XGA + config[5].ssdv_conf.res = RES_QVGA; // Resolution XGA //config[5].ssdv_conf.redundantTx = true; // Transmit packets twice //start_image_thread(&config[5]); diff --git a/tracker/software/config.h b/tracker/software/config.h index 2c34167..d087aaa 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -8,15 +8,15 @@ #include "radio.h" #include "sleep.h" -#define TRACK_CYCLE_TIME 120 /* 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 */ #define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */ #define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */ -#define GPS_ON_VBAT 3500 /* Battery voltage threshold at which GPS is switched on */ -#define GPS_OFF_VBAT 3000 /* Battery voltage threshold at which GPS is switched off */ +#define GPS_ON_VBAT 7000 /* Battery voltage threshold at which GPS is switched on */ +#define GPS_OFF_VBAT 7000 /* Battery voltage threshold at which GPS is switched off */ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */ diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 473ca16..c95c31a 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -96,7 +96,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3034, 0x1a }, { 0x3035, 0x11 }, //15fps { 0x3036, 0x46 }, - { 0x3037, 0x13 }, + { 0x3037, 0x14 }, { 0x3038, 0x00 }, { 0x3039, 0x00 }, @@ -755,7 +755,7 @@ static bool analyze_image(uint8_t *image, uint32_t image_len) uint8_t r = bi < image_len-128 ? 128 : image_len - bi; bi += r; if(r <= 0) - break; + return false; ssdv_enc_feed(&ssdv, b, r); } @@ -776,12 +776,13 @@ bool OV5640_BufferOverflow(void) */ bool OV5640_Snapshot2RAM(void) { - // Capture image until we get a good image (max 5 tries) - uint8_t cntr = 5; + // Capture image until we get a good image (max 10 tries) + uint8_t cntr = 10; + bool status; do { TRACE_INFO("CAM > Capture image"); - OV5640_Capture(); + status = OV5640_Capture(); TRACE_INFO("CAM > Capture finished"); ov5640_conf->size_sampled = ov5640_conf->ram_size - 1; @@ -790,7 +791,7 @@ bool OV5640_Snapshot2RAM(void) TRACE_INFO("CAM > Image size: %d bytes", ov5640_conf->size_sampled); - } while(!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) && cntr--); + } while((!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) || !status) && cntr--); return true; } @@ -1373,6 +1374,8 @@ bool OV5640_isAvailable(void) palSetLine(LINE_CAM_EN); // Switch on camera palSetLine(LINE_CAM_RESET); // Toggle reset + OV5640_getLightIntensity(); + chThdSleepMilliseconds(100); uint8_t val, val2; @@ -1390,3 +1393,12 @@ bool OV5640_isAvailable(void) return ret; } +uint32_t OV5640_getLightIntensity(void) +{ + uint8_t val1,val2,val3; + I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1B, &val1); + I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1C, &val2); + I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1D, &val3); + return (val1 << 16) | (val2 << 8) | val3; +} + diff --git a/tracker/software/drivers/ov5640.h b/tracker/software/drivers/ov5640.h index 4b60e97..2cdaaea 100644 --- a/tracker/software/drivers/ov5640.h +++ b/tracker/software/drivers/ov5640.h @@ -20,5 +20,6 @@ void OV5640_TransmitConfig(void); void OV5640_init(ssdv_conf_t *config); void OV5640_deinit(void); bool OV5640_isAvailable(void); +uint32_t OV5640_getLightIntensity(void); #endif diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index 63e57e5..9ac5e9b 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -48,8 +48,8 @@ #define STM32_PLLN_VALUE 192 #define STM32_PLLP_VALUE 4 #define STM32_PLLQ_VALUE 4 -#define STM32_HPRE STM32_HPRE_DIV2 -#define STM32_PPRE1 STM32_PPRE1_DIV2 +#define STM32_HPRE STM32_HPRE_DIV8 +#define STM32_PPRE1 STM32_PPRE1_DIV1 #define STM32_PPRE2 STM32_PPRE2_DIV4 #define STM32_RTCSEL STM32_RTCSEL_LSI #define STM32_RTCPRE_VALUE 8 @@ -67,7 +67,7 @@ /* * ADC driver system settings. */ -#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV4 +#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV2 #define STM32_ADC_USE_ADC1 TRUE #define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(2, 4) #define STM32_ADC_ADC1_DMA_PRIORITY 2 diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index dbdc777..d184baa 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -16,7 +16,7 @@ #include "watchdog.h" #include "flash.h" -static uint8_t gimage_id = 2; // Global image ID (for all image threads) +static uint8_t gimage_id; // 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) diff --git a/tracker/software/radio.c b/tracker/software/radio.c index fd1cd66..2a39b35 100644 --- a/tracker/software/radio.c +++ b/tracker/software/radio.c @@ -9,7 +9,7 @@ #include "padc.h" #include -#define PLAYBACK_RATE ((STM32_PCLK1) / 250) /* Samples per second (48Mhz / 250 = 192kHz) */ +#define PLAYBACK_RATE ((STM32_PCLK1) / 500) /* Samples per second (48Mhz / 250 = 192kHz) */ #define BAUD_RATE 1200 /* APRS AFSK baudrate */ #define SAMPLES_PER_BAUD (PLAYBACK_RATE / BAUD_RATE) /* Samples per baud (192kHz / 1200baud = 160samp/baud) */ #define PHASE_DELTA_1200 (((2 * 1200) << 16) / PLAYBACK_RATE) /* Delta-phase per sample for 1200Hz tone */ @@ -86,7 +86,7 @@ void send2GFSK(radioMSG_t *msg) { current_byte = 0; // Initialize variables for timer - uint32_t initial_interval = STM32_PCLK1 / msg->gfsk_conf->speed; + uint32_t initial_interval = STM32_PCLK1 / msg->gfsk_conf->speed / 2; RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; nvicEnableVector(TIM7_IRQn, 1); TIM7->ARR = initial_interval; @@ -136,6 +136,7 @@ CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER) current_sample_in_baud = 0; packet_pos++; } + palToggleLine(LINE_IO_LED1); } else if(tim_msg->mod == MOD_2GFSK) { From 1cea4b15737fe7efd209bfa1e1f85c7f1da9c6b2 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Thu, 31 Aug 2017 01:27:09 +0200 Subject: [PATCH 21/37] Finalized clockling (so far) for power savings. All dividers for all clocks are static except for HPRE. HPRE is dynamically changed between /1 and /8. It is usually set to /8. If the camera is capturing the image, it is set to /1. This is necessary while the sampling method needs a to sample the fast data from the camera. After capture, the prescaler is switched back to /8. HPRE will affect the speed of AHB1 and AHB2. Both have the same speed. At image capture, both are running at 48Mhz and 6Mhz at all other times. The clock change has a drastic effect of the power consumption. -------------------------------------------------- Running at 1.8V Position packet transmission, APRS AFSK, one packet every 1min (GPS off) Image transmission, APRS AFSK, one packet every 20sec, redundant TX => average consumption 55mW -------------------------------------------------- Running at 1.8V Position packet transmission, APRS AFSK, one packet every 1min (GPS off) => average consumption 34mW --- tracker/software/config.c | 2 +- tracker/software/config.h | 4 ++-- tracker/software/drivers/ov5640.c | 34 +++++++++++++++++++++++-------- tracker/software/mcuconf.h | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index d241cee..2ad2ce0 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -250,7 +250,7 @@ void start_user_modules(void) 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 diff --git a/tracker/software/config.h b/tracker/software/config.h index d087aaa..a998cce 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -10,7 +10,7 @@ #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_CYCLE_TIME 60 /* Log cycle time in seconds */ #define LOG_FLASH_ADDR1 0x080C0000 /* Log flash memory address 1 */ #define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */ #define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */ @@ -21,7 +21,7 @@ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */ -#define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. +#define RUN_3V FALSE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. * With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in * order to work. However 3V takes a lot of power in idle. You can save energy using 1.8V. */ diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index c95c31a..e598d93 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -96,7 +96,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] = { 0x3034, 0x1a }, { 0x3035, 0x11 }, //15fps { 0x3036, 0x46 }, - { 0x3037, 0x14 }, + { 0x3037, 0x12 }, { 0x3038, 0x00 }, { 0x3039, 0x00 }, @@ -993,6 +993,15 @@ CH_IRQ_HANDLER(Vector5C) { if (LptimRdy) { // VSYNC handling if(!vsync) { + // Increase AHB clock to 6 MHz + uint32_t new = (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | FLASH_ACR_LATENCY_3WS; + FLASH->ACR = new; + while(FLASH->ACR != new); + + new = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV1; + RCC->CFGR = new; + while(RCC->CFGR != new); + /* * Rising edge of VSYNC after LPTIM1 has been initialised. * Start DMA channel. @@ -1002,6 +1011,15 @@ CH_IRQ_HANDLER(Vector5C) { TIM1->DIER |= TIM_DIER_TDE; vsync = true; } else { + // Reduce AHB clock to 6 MHz + uint32_t new = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV8; + RCC->CFGR = new; + while(RCC->CFGR != new); + + new = (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | FLASH_ACR_LATENCY_0WS; + FLASH->ACR = new; + while(FLASH->ACR != new); + /* VSYNC leading with vsync true. * This means end of capture for the frame. * Stop & release the DMA channel. @@ -1034,12 +1052,12 @@ CH_IRQ_HANDLER(Vector5C) { 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 - */ + /* + * 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)); @@ -1374,8 +1392,6 @@ bool OV5640_isAvailable(void) palSetLine(LINE_CAM_EN); // Switch on camera palSetLine(LINE_CAM_RESET); // Toggle reset - OV5640_getLightIntensity(); - chThdSleepMilliseconds(100); uint8_t val, val2; diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index 9ac5e9b..8303d1e 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -50,7 +50,7 @@ #define STM32_PLLQ_VALUE 4 #define STM32_HPRE STM32_HPRE_DIV8 #define STM32_PPRE1 STM32_PPRE1_DIV1 -#define STM32_PPRE2 STM32_PPRE2_DIV4 +#define STM32_PPRE2 STM32_PPRE2_DIV1 #define STM32_RTCSEL STM32_RTCSEL_LSI #define STM32_RTCPRE_VALUE 8 #define STM32_MCO1SEL STM32_MCO1SEL_HSE From fe68f49820f3a9837286ff4825874ae5d12ec55b Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Thu, 31 Aug 2017 03:55:28 +0200 Subject: [PATCH 22/37] Removed backup files --- tracker/software/drivers/ov2640-ss.c | 1090 --------------- .../software/drivers/ov2640_lptim_working.c | 1146 --------------- tracker/software/drivers/ov2640_old.c | 958 ------------- tracker/software/drivers/ov2640_old2.c | 1046 -------------- tracker/software/drivers/ov2640_old3.c | 1090 --------------- tracker/software/drivers/ov2640_old4.c | 1131 --------------- tracker/software/drivers/ov2640_old5.c | 1069 -------------- .../drivers/ov5640_working_patch_tim8.c | 1234 ----------------- 8 files changed, 8764 deletions(-) delete mode 100644 tracker/software/drivers/ov2640-ss.c delete mode 100644 tracker/software/drivers/ov2640_lptim_working.c delete mode 100644 tracker/software/drivers/ov2640_old.c delete mode 100644 tracker/software/drivers/ov2640_old2.c delete mode 100644 tracker/software/drivers/ov2640_old3.c delete mode 100644 tracker/software/drivers/ov2640_old4.c delete mode 100644 tracker/software/drivers/ov2640_old5.c delete mode 100644 tracker/software/drivers/ov5640_working_patch_tim8.c diff --git a/tracker/software/drivers/ov2640-ss.c b/tracker/software/drivers/ov2640-ss.c deleted file mode 100644 index 75221e3..0000000 --- a/tracker/software/drivers/ov2640-ss.c +++ /dev/null @@ -1,1090 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(1) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -const stm32_dma_stream_t *dmastp; - -inline int32_t dma_start(void) { - dmaStreamEnable(dmastp); - return 0; -} - - -inline int32_t dma_stop(void) { - dmaStreamDisable(dmastp); - return 0; -} - - -inline int32_t dma_release(void) { - dmaStreamRelease(dmastp); - return 0; -} - -static void dma_interrupt(void *p, uint32_t flags) { - (void)p; - - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Nothing really to do at half way point. */ - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* End of transfer. */ - palSetLine(LINE_IO_LED1); - /* Stop DMA initiation from TIM1 trigger input. */ - TIM1->DIER &= ~TIM_DIER_TDE; - /* - * Stop DMA for this HREF. - * We enable it again with existing settings for (if) next HREF. - * Else on VSYNC trailing edge frame capture terminates. - */ - dma_stop(); - } -} - -/* - * The TIM1 interrupt handler. - */ - - /* 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; - -/* - * 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; - - palToggleLine(LINE_IO_LED1); - - /* - * 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(); - -} - -// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c) -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or re-start dma. The transfer count already set will be used. - * The M0AR (DMA memory address) register is set for autoincrement. - * It will be pointing to the next buffer address when DMA is re-started. - */ - dma_start(); - /* Set TIM1 trigger to initate DMA. */ - TIM1->DIER |= TIM_DIER_TDE; - } - } - EXTI->PR |= EXTI_PR_PR2; - CH_IRQ_EPILOGUE(); -} - -// 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_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // VSYNC handling - if(gpioc & 0x2) { - // VSYNC rising edge - // Start capturing image even if HREF is still low. We do this - // because the timers need some clocks to initialize. Therefore - // we will capture some bytes additionally in the beginning which - // we will remove later - //dma_start(); - // TIM1->DIER |= TIM_DIER_TDE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - } else { - // VSYNC falling edge - end if JPEG frame. Stop the DMA, disable - // capture LED and signal the semaphore (data can be processed). - TIM1->DIER &= ~TIM_DIER_TDE; - dma_stop(); - /* Capture complete so release the stream. */ - dma_release(); - if(ov2640_conf->ram_buffer[0] == 0xFF) // Picture correctly sampled FIXME: That should actually not be neccessary (workaround) - { - nvicDisableVector(EXTI1_IRQn); // Disable interrupt - nvicDisableVector(EXTI2_IRQn); // Disable interrupt - - palSetLine(LINE_IO_LED1); // Indicate that picture is captured completly - image_finished = true; - } - } - } else { - /* - * LPTIM1 is not yet started. - * 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(); -} - -void 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 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(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_HTIE; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // 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_CKSEL | LPTIM_CFGR_CKPOL_1); - 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; - - LPTIM1->CMP = 1; - LPTIM1->ARR = 2; - - /* Set vector, clear flag and start the counter. */ - 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 from the trigger which is independant of counting. - */ - TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); - //TIM1->DIER |= TIM_DIER_TDE; // Enable DMA on trigger in. (=> Is done in VSYNC and HREF interrupts) - //TIM1->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - //TIM1->CR1 |= TIM_CR1_CEN; // Enable the timer - - nvicEnableVector(TIM1_TRG_COM_TIM11_IRQn, 7); // Enable interrupt - - //TIM1->CR1 |= TIM_CR1_CEN; /* Enable the timer. */ - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - TRACE_INFO("CAM > Capture finished"); -} - - - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_lptim_working.c b/tracker/software/drivers/ov2640_lptim_working.c deleted file mode 100644 index 1eb2d32..0000000 --- a/tracker/software/drivers/ov2640_lptim_working.c +++ /dev/null @@ -1,1146 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSYNC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { 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_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - //{ COM10, COM10_PCLK_RISE }, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - for(uint32_t i=0; i<320; i++) - TRACE_DEBUG("%02x %02x %02x %02x %02x %02x %02x %02x", -ov2640_conf->ram_buffer[i*8+0], -ov2640_conf->ram_buffer[i*8+1], -ov2640_conf->ram_buffer[i*8+2], -ov2640_conf->ram_buffer[i*8+3], -ov2640_conf->ram_buffer[i*8+4], -ov2640_conf->ram_buffer[i*8+5], -ov2640_conf->ram_buffer[i*8+6], -ov2640_conf->ram_buffer[i*8+7]); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_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. */ - palSetLine(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; - -/* - * 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; - - palToggleLine(LINE_IO_LED1); - - /* - * 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(); - -} - -// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c) -/* To be deprecated - not used. */ -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or re-start dma. The transfer count already set will be used. - * The M0AR (DMA memory address) register is set for autoincrement. - * It will be pointing to the next buffer address when DMA is re-started. - */ - //dma_start(); - /* Set TIM1 trigger to initate DMA. */ - TIM1->DIER |= TIM_DIER_TDE; - } else { - /* Set TIM1 trigger to initate DMA. */ - TIM1->DIER &= ~TIM_DIER_TDE; - } - - } - - 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_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - if (LptimRdy) { - // VSYNC handling - if(!vsync && palReadLine(LINE_CAM_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; - //TIM1->DIER |= TIM_DIER_TDE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - vsync = true; - } else if(vsync) { - /* 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); - //nvicDisableVector(EXTI2_IRQn); - /* Turn on capture LED and signal the semaphore (data can be processed). */ - palSetLine(LINE_IO_LED1); - 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; - } - - palToggleLine(LINE_IO_LED1); - - EXTI->PR |= EXTI_PR_PR1; - CH_IRQ_EPILOGUE(); -} - -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 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(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - /*STM32_DMA_CR_CIRC |*/ - STM32_DMA_CR_TCIE /*| - STM32_DMA_CR_HTIE*/; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // 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_CKSEL | LPTIM_CFGR_CKPOL_1); - LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL); - LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; - LPTIM1->CR |= LPTIM_CR_ENABLE; - LPTIM1->IER |= LPTIM_IER_ARRMIE; - - 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); - //TIM1->DIER |= TIM_DIER_TDE; // Enable DMA on trigger in. (=> Is done in VSYNC and HREF interrupts) - //TIM1->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - //TIM1->CR1 |= TIM_CR1_CEN; // Enable the timer - - //nvicEnableVector(TIM1_TRG_COM_TIM11_IRQn, 7); // Enable interrupt - - //TIM1->CR1 |= TIM_CR1_CEN; /* Enable the timer. */ - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_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->ram_buffer[soi] == 0xFF && ov2640_conf->ram_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->ram_buffer[i] = ov2640_conf->ram_buffer[i+soi]; - - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3], ov2640_conf->ram_buffer[4], ov2640_conf->ram_buffer[5]); - TRACE_INFO("CAM > Capture finished"); - - return true; -} - - - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_old.c b/tracker/software/drivers/ov2640_old.c deleted file mode 100644 index 01d2fad..0000000 --- a/tracker/software/drivers/ov2640_old.c +++ /dev/null @@ -1,958 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(2) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -const stm32_dma_stream_t *dmastp; - -int32_t dma_start(void) { - dmaStreamEnable(dmastp); - return 0; -} - - -int32_t dma_stop(void) { - dmaStreamDisable(dmastp); - return 0; -} - -static void dma_interrupt(void *p, uint32_t flags) { - (void)p; - - if ((flags & STM32_DMA_ISR_HTIF) != 0) { // I think we dont need this - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { // I think that stands for "memory filled completly" - palSetLine(LINE_IO_LED1); - dma_stop(); // Stop DMA - } -} - - - -// This is the vector for EXTI2 (stolen from hal_ext_lld_isr.c) -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - TIM5->DIER |= TIM_DIER_TDE; - } else { - // HREF falling edge, stop capturing - TIM5->DIER &= ~TIM_DIER_TDE; - } - - EXTI->PR |= EXTI_PR_PR2; - CH_IRQ_EPILOGUE(); -} - -// This is the vector for EXTI1 (stolen from hal_ext_lld_isr.c) -CH_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - - // VSYNC handling - if(gpioc & 0x2) { - // VSYNC rising edge - // Start capturing image even if HREF is still low. We do this - // because the timers need some clocks to initialize. Therefore - // we will capture some bytes additionally in the beginning which - // we will remove later - dma_start(); - TIM5->DIER |= TIM_DIER_TDE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - } else { - // VSYNC falling edge - end if JPEG frame. Stop the DMA, disable - // capture LED and signal the semaphore (data can be processed). - TIM5->DIER &= ~TIM_DIER_TDE; - dma_stop(); - palSetLine(LINE_IO_LED1); // Indicate that picture is captured completly - } - - EXTI->PR |= EXTI_PR_PR1; - CH_IRQ_EPILOGUE(); -} - - -void OV2640_Capture(void) -{ - // Setup DMA - dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(1, 2)); // TIM5_CH1 DMA1 Stream 2 - uint32_t dmamode = STM32_DMA_CR_CHSEL(6) | // Channel 6 - STM32_DMA_CR_PL(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_HTIE; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // Setup timer for PLCK - rccResetLPTIM1(); - rccEnableLPTIM1(FALSE); - - LPTIM1->CFGR = (LPTIM_CFGR_CKSEL | LPTIM_CFGR_CKPOL_1); - LPTIM1->OR |= LPTIM_OR_TIM5_ITR1_RMP; - - rccResetTIM5(); - rccEnableTIM5(FALSE); - - - TIM5->SMCR = TIM_SMCR_TS_0; // Select ITR1 as trigger - TIM5->SMCR |= TIM_SMCR_SMS_2; // Set timer in reset mode - TIM5->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); // IC1 is mapped to TRC - //TIM5->DIER |= TIM_DIER_TDE; // Enable DMA on trigger request. - //TIM5->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - TIM5->CR1 |= TIM_CR1_CEN; // Enable the timer - //nvicEnableVector(TIM5_IRQn, 7); // Enable interrupt - - LPTIM1->CR |= LPTIM_CR_ENABLE; - - LPTIM1->CMP = 1; - LPTIM1->ARR = 2; - - LPTIM1->CR |= LPTIM_CR_CNTSTRT; - - - // 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 - nvicEnableVector(EXTI1_IRQn, 1); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - while(true) { // Have a look for some bytes in memory for testing if capturing works - TRACE_DEBUG("CAM > BLA %d %d %d %d %d", TIM5->CNT, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1] -, ov2640_conf->ram_buffer[1024], ov2640_conf->ram_buffer[2048]); - chThdSleepMilliseconds(1); - } - TRACE_INFO("CAM > Stop capture"); -} - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_old2.c b/tracker/software/drivers/ov2640_old2.c deleted file mode 100644 index a8bb747..0000000 --- a/tracker/software/drivers/ov2640_old2.c +++ /dev/null @@ -1,1046 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(1) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -const stm32_dma_stream_t *dmastp; - -inline int32_t dma_start(void) { - dmaStreamEnable(dmastp); - return 0; -} - - -inline int32_t dma_stop(void) { - dmaStreamDisable(dmastp); - return 0; -} - -static void dma_interrupt(void *p, uint32_t flags) { - (void)p; - - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Nothing really to do at half way point. */ - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - palSetLine(LINE_IO_LED1); - dma_stop(); // Stop DMA - } -} - -/* - * The TIM1 interrupt handler. - */ - - /* Not defined buy 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 lptim_rdy; -static bool image_finished; - -/* - * 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 flags. */ - LPTIM1->ICR = LPTIM1->ISR; - - palToggleLine(LINE_IO_LED1); - - /* - * The first interrupt indicates LPTIM core has been initialized by PIXCLK. - * This flag can be used to synchronise capture start. - * If on trailing edge of VSYNC lptim_rdy is set then capture can start. - * If not wait for the next VSYNC. - */ - lptim_rdy = true; - OSAL_IRQ_EPILOGUE(); - -} - -// This is the vector for EXTI2 (stolen from hal_ext_lld_isr.c) -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - TIM1->DIER |= TIM_DIER_TDE; - } else { - // HREF falling edge, stop capturing - TIM1->DIER &= ~TIM_DIER_TDE; - } - - EXTI->PR |= EXTI_PR_PR2; - CH_IRQ_EPILOGUE(); -} - -// This is the vector for EXTI1 (stolen from hal_ext_lld_isr.c) -/* - * Note: VSYNC is a pulse sigfnifying start of frame. - * So the following code should start capture on falling edge. - * Then stop capture should be handled by the DMA end interrupt. -*/ -CH_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - - // VSYNC handling - if(gpioc & 0x2) { - // VSYNC rising edge - // Start capturing image even if HREF is still low. We do this - // because the timers need some clocks to initialize. Therefore - // we will capture some bytes additionally in the beginning which - // we will remove later - dma_start(); -// TIM1->DIER |= TIM_DIER_TDE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - } else { - // VSYNC falling edge - end if JPEG frame. Stop the DMA, disable - // capture LED and signal the semaphore (data can be processed). - TIM1->DIER &= ~TIM_DIER_TDE; - dma_stop(); - - if(ov2640_conf->ram_buffer[0] == 0xFF) // Picture correctly sampled FIXME: That should actually not be neccessary (workaround) - { - nvicDisableVector(EXTI1_IRQn); // Disable interrupt - nvicDisableVector(EXTI2_IRQn); // Disable interrupt - - palSetLine(LINE_IO_LED1); // Indicate that picture is captured completly - image_finished = true; - } - } - - EXTI->PR |= EXTI_PR_PR1; - CH_IRQ_EPILOGUE(); -} - - -void 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 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(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_HTIE; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // Setup timer for PCLCK - rccResetLPTIM1(); - rccEnableLPTIM1(FALSE); - - LPTIM1->CFGR = (LPTIM_CFGR_CKSEL | LPTIM_CFGR_CKPOL_1); - LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; - LPTIM1->CR |= LPTIM_CR_ENABLE; - //LPTIM1->IER |= LPTIM_IER_ARRMIE; - - LPTIM1->CMP = 1; - LPTIM1->ARR = 2; - - /* Start start the counter. */ - LPTIM1->CR |= LPTIM_CR_CNTSTRT; - - nvicEnableVector(LPTIM1_IRQn, 7); // Enable interrupt - - /* - * 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. - * 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 need the DMA from the trigger. - */ - TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); - //TIM1->DIER |= TIM_DIER_TDE; // Enable DMA on trigger in. (=> Is done in VSYNC and HREF interrupts) - //TIM1->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - TIM1->CR1 |= TIM_CR1_CEN; // Enable the timer - - nvicEnableVector(TIM1_TRG_COM_TIM11_IRQn, 7); // Enable interrupt - - TIM1->CR1 |= TIM_CR1_CEN; /* Enable the timer. */ - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", TIM1->CNT, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - TRACE_INFO("CAM > Capture finished"); -} - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_old3.c b/tracker/software/drivers/ov2640_old3.c deleted file mode 100644 index 75221e3..0000000 --- a/tracker/software/drivers/ov2640_old3.c +++ /dev/null @@ -1,1090 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(1) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -const stm32_dma_stream_t *dmastp; - -inline int32_t dma_start(void) { - dmaStreamEnable(dmastp); - return 0; -} - - -inline int32_t dma_stop(void) { - dmaStreamDisable(dmastp); - return 0; -} - - -inline int32_t dma_release(void) { - dmaStreamRelease(dmastp); - return 0; -} - -static void dma_interrupt(void *p, uint32_t flags) { - (void)p; - - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Nothing really to do at half way point. */ - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* End of transfer. */ - palSetLine(LINE_IO_LED1); - /* Stop DMA initiation from TIM1 trigger input. */ - TIM1->DIER &= ~TIM_DIER_TDE; - /* - * Stop DMA for this HREF. - * We enable it again with existing settings for (if) next HREF. - * Else on VSYNC trailing edge frame capture terminates. - */ - dma_stop(); - } -} - -/* - * The TIM1 interrupt handler. - */ - - /* 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; - -/* - * 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; - - palToggleLine(LINE_IO_LED1); - - /* - * 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(); - -} - -// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c) -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or re-start dma. The transfer count already set will be used. - * The M0AR (DMA memory address) register is set for autoincrement. - * It will be pointing to the next buffer address when DMA is re-started. - */ - dma_start(); - /* Set TIM1 trigger to initate DMA. */ - TIM1->DIER |= TIM_DIER_TDE; - } - } - EXTI->PR |= EXTI_PR_PR2; - CH_IRQ_EPILOGUE(); -} - -// 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_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // VSYNC handling - if(gpioc & 0x2) { - // VSYNC rising edge - // Start capturing image even if HREF is still low. We do this - // because the timers need some clocks to initialize. Therefore - // we will capture some bytes additionally in the beginning which - // we will remove later - //dma_start(); - // TIM1->DIER |= TIM_DIER_TDE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - } else { - // VSYNC falling edge - end if JPEG frame. Stop the DMA, disable - // capture LED and signal the semaphore (data can be processed). - TIM1->DIER &= ~TIM_DIER_TDE; - dma_stop(); - /* Capture complete so release the stream. */ - dma_release(); - if(ov2640_conf->ram_buffer[0] == 0xFF) // Picture correctly sampled FIXME: That should actually not be neccessary (workaround) - { - nvicDisableVector(EXTI1_IRQn); // Disable interrupt - nvicDisableVector(EXTI2_IRQn); // Disable interrupt - - palSetLine(LINE_IO_LED1); // Indicate that picture is captured completly - image_finished = true; - } - } - } else { - /* - * LPTIM1 is not yet started. - * 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(); -} - -void 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 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(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_HTIE; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // 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_CKSEL | LPTIM_CFGR_CKPOL_1); - 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; - - LPTIM1->CMP = 1; - LPTIM1->ARR = 2; - - /* Set vector, clear flag and start the counter. */ - 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 from the trigger which is independant of counting. - */ - TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); - //TIM1->DIER |= TIM_DIER_TDE; // Enable DMA on trigger in. (=> Is done in VSYNC and HREF interrupts) - //TIM1->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - //TIM1->CR1 |= TIM_CR1_CEN; // Enable the timer - - nvicEnableVector(TIM1_TRG_COM_TIM11_IRQn, 7); // Enable interrupt - - //TIM1->CR1 |= TIM_CR1_CEN; /* Enable the timer. */ - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - TRACE_INFO("CAM > Capture finished"); -} - - - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_old4.c b/tracker/software/drivers/ov2640_old4.c deleted file mode 100644 index 5fe0a31..0000000 --- a/tracker/software/drivers/ov2640_old4.c +++ /dev/null @@ -1,1131 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSYNC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(2) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_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. */ - palSetLine(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; - -/* - * 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; - - palToggleLine(LINE_IO_LED1); - - /* - * 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(); - -} - -// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c) -/* To be deprecated - not used. */ -CH_IRQ_HANDLER(Vector60) { - CH_IRQ_PROLOGUE(); - - uint8_t gpioc = GPIOC->IDR; - if (LptimRdy) { - // HREF handling - if(gpioc & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or re-start dma. The transfer count already set will be used. - * The M0AR (DMA memory address) register is set for autoincrement. - * It will be pointing to the next buffer address when DMA is re-started. - */ - //dma_start(); - /* Set TIM1 trigger to initate DMA. */ - //TIM1->DIER |= TIM_DIER_TDE; - } - } - - 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_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - if (LptimRdy) { - // VSYNC handling - if(!vsync && palReadLine(LINE_CAM_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; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - vsync = true; - } else if(vsync) { - /* 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); - //nvicDisableVector(EXTI2_IRQn); - /* Turn on capture LED and signal the semaphore (data can be processed). */ - palSetLine(LINE_IO_LED1); - 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; - } - - palToggleLine(LINE_IO_LED1); - - EXTI->PR |= EXTI_PR_PR1; - CH_IRQ_EPILOGUE(); -} - -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 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(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - /*STM32_DMA_CR_CIRC |*/ - STM32_DMA_CR_TCIE /*| - STM32_DMA_CR_HTIE*/; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - - // 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_CKSEL | LPTIM_CFGR_CKPOL_1); - 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; - - 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); - //TIM1->DIER |= TIM_DIER_TDE; // Enable DMA on trigger in. (=> Is done in VSYNC and HREF interrupts) - //TIM1->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request - - //TIM1->CR1 |= TIM_CR1_CEN; // Enable the timer - - //nvicEnableVector(TIM1_TRG_COM_TIM11_IRQn, 7); // Enable interrupt - - //TIM1->CR1 |= TIM_CR1_CEN; /* Enable the timer. */ - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - //nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_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->ram_buffer[soi] == 0xFF && ov2640_conf->ram_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->ram_buffer[i] = ov2640_conf->ram_buffer[i+soi]; - - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - TRACE_INFO("CAM > Capture finished"); - - return true; -} - - - -/** - * 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 | 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 | 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); - - palSetPadMode(GPIOB, 8, PAL_MODE_ALTERNATE(1)); - - palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL); - palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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/ov2640_old5.c b/tracker/software/drivers/ov2640_old5.c deleted file mode 100644 index fae5d8a..0000000 --- a/tracker/software/drivers/ov2640_old5.c +++ /dev/null @@ -1,1069 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSYNC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(3) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -#if 0 - -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. */ - palSetLine(LINE_IO_LED1); - - /* - * Stop TIM8 DMA trigger. - * Stop and release DMA channel. - * Either DMA count full or VSNC traling edge can terminate frame capture - */ - TIM8->DIER &= ~TIM_DIER_CC1DE; - dma_stop(); - return; - } - /* - * TODO: Anything else is an error. - * Maybe set an error flag? - */ -} - -static bool image_finished; - - -// 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 & 0x4) { - // HREF rising edge, start capturing data on pixel clock - /* - * Start or re-start dma. The transfer count already set will be used. - * The M0AR (DMA memory address) register is set for autoincrement. - * It will be pointing to the next buffer address when DMA is re-started. - */ - //dma_start(); - /* Set TIM8 trigger to initate DMA. */ - TIM8->DIER |= TIM_DIER_CC1DE; - } else { - /* Remove TIM8 trigger to initate 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 after LPTIM1 has been initiualized. - * Start DMA channel. - * Enable TIM8 trigger of DMA. - */ - dma_start(); - TIM8->DIER |= TIM_DIER_CC1DE; - palClearLine(LINE_IO_LED1); // Indicate that picture will be captured - vsync = true; - } else if(vsync) { - /* VSYNC falling edge - end of JPEG frame. - * Stop & release the DMA channel. - * Disable TIM8 trigger of DMA and stop PCLK via LPTIM1 - * These should have already been disabled in DMA interrupt if was filled. - */ - dma_stop(); - TIM8->DIER &= ~TIM_DIER_CC1DE; - - /* Disable 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(); -} - -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 */ - dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 2)); - uint32_t dmamode = STM32_DMA_CR_CHSEL(7) | - STM32_DMA_CR_PL(2) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_PSIZE_BYTE | - STM32_DMA_CR_MINC | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_MBURST_INCR16; - - dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL); - - dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here - dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address - dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size - - dmaStreamSetMode(dmastp, dmamode); // Setup DMA - dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); - - /* - * 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 - - image_finished = false; - - // 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 - - nvicEnableVector(EXTI1_IRQn, 2); // Enable interrupt - nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt - - do { // Have a look for some bytes in memory for testing if capturing works - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - //TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - chThdSleepMilliseconds(100); - } while(!image_finished); - - int32_t size=65535; - while(ov2640_conf->ram_buffer[size] == 0 && size >= 0) - size--; - TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_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->ram_buffer[soi] == 0xFF && ov2640_conf->ram_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->ram_buffer[i] = ov2640_conf->ram_buffer[i+soi]; - - TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1], ov2640_conf->ram_buffer[2], ov2640_conf->ram_buffer[3]); - TRACE_INFO("CAM > Capture finished"); - - return true; -} -#else - -bool OV2640_Capture(void) -{ - TRACE_INFO("CAM > Start capture"); - while(1) - { - while(palReadLine(LINE_CAM_VSYNC)); - while(!palReadLine(LINE_CAM_VSYNC)); - - uint8_t gpioc; - uint8_t gpioa; - ov2640_conf->size_sampled = 0; - while(true) - { - do { - gpioc = GPIOC->IDR & 0x7; - } while((gpioc & 0x1) != 0x1); // Wait for PCLK to rise - - gpioa = GPIOA->IDR; - - switch(gpioc) { - case 0x3: - break; - case 0x7: - ov2640_conf->ram_buffer[ov2640_conf->size_sampled++] = gpioa; - break; - default: - return true; - } - - // Wait for falling edge - while(GPIOC->IDR & 0x1); - } - } -} - - - -#endif - -/** - * Initializes GPIO (for pseudo 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_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); - - palSetPadMode(GPIOC, 0, PAL_MODE_INPUT); - palSetPadMode(GPIOB, 15, PAL_MODE_INPUT); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_lock(); - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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 OV2640, it interferes if byte 0x30 transmitted on I2C bus) - I2C_unlock(); -} - -bool OV2640_isAvailable(void) -{ - I2C_lock(); - - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - 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_working_patch_tim8.c b/tracker/software/drivers/ov5640_working_patch_tim8.c deleted file mode 100644 index dc08908..0000000 --- a/tracker/software/drivers/ov5640_working_patch_tim8.c +++ /dev/null @@ -1,1234 +0,0 @@ -/* - * Registers by Arducam https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov5640_regs.h - * https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ArduCAM.cpp - */ - -#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, 0x04 }, - - { 0x4050, 0x6e }, - { 0x4051, 0x8f }, - - { 0x3008, 0x42 }, - { 0x3103, 0x03 }, - { 0x3017, 0x7f }, - { 0x3018, 0xff }, - { 0x302c, 0x02 }, - { 0x3108, 0x31 }, - { 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, 0x11 }, //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, 0x11 }, // 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, 0x11}, - {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 ,0x41}, - {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 ,0x11}, - {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; - -/* TODO: Implement a state machine instead of multiple flags. */ -//static bool LptimRdy; -static bool capture_finished; -static bool capture_error; -static bool vsync; -static bool dma_fault; -static bool dma_overrun; - -/** - * 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 interrupts. */ - dmaStreamClearInterrupt(dmastp); - dmaStreamEnable(dmastp); - return 0; -} - -/* - * Stop DMA release stream and return count remaining. - * Note that any DMA FIFO transfer will complete. - * The Chibios DMAV2 driver waits for EN to clear before proceeding. - */ -inline uint16_t dma_stop(void) { - dmaStreamDisable(dmastp); - uint16_t transfer = dmaStreamGetTransactionSize(dmastp); - dmaStreamRelease(dmastp); - return transfer; -} - -static void dma_interrupt(void *p, uint32_t flags) { - (void)p; - - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* - * Nothing really to do at half way point for now. - * Implementing DBM will use HTIF. - */ - return; - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Disable VYSNC edge interrupts. */ - //nvicDisableVector(EXTI1_IRQn); - //capture_finished = true; - - /* - * If DMA has run to end within a frame then this is an error. - * In single buffer mode DMA should always be terminated by VSYNC. - * - * Stop PCLK from LPTIM1 and disable TIM1 DMA trigger. - * Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC. - */ - TIM8->DIER &= ~TIM_DIER_CC1DE; - TIM8->CCER &= ~TIM_CCER_CC1E; - //LPTIM8->CR &= ~LPTIM_CR_CNTSTRT; - dma_overrun = true; - capture_error = true; - return; - } - /* - * TODO: Anything else is an error. - * Maybe set an error flag? - */ -} - -/* - * 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 at the start of each frame. - * This is unlike the OV2640 where VSYNC is active for the entire frame. -*/ -CH_IRQ_HANDLER(Vector5C) { - CH_IRQ_PROLOGUE(); - - //if (LptimRdy) { - // VSYNC handling - if(!vsync) { - /* - * Rising edge of VSYNC after LPTIM1 has been initialised. - * Start DMA channel. - * Enable TIM1 trigger of DMA. - */ - dma_start(); - TIM8->DIER |= TIM_DIER_CC1DE; - vsync = true; - } else { - /* VSYNC leading with vsync true. - * This means end of capture for the frame. - * Stop & release the DMA channel. - * Disable TIM1 trigger of DMA and stop PCLK counting on LPTIM1. - * If buffer was filled in DMA then that is an error. - * We check that here. - */ - dma_stop(); - TIM8->DIER &= ~TIM_DIER_CC1DE; - TIM8->CCER &= ~TIM_CCER_CC1E; - //LPTIM8->CR &= ~LPTIM_CR_CNTSTRT; - - /* - * Disable VSYNC edge interrupts. - * Flag image capture complete. - */ - nvicDisableVector(EXTI1_IRQn); - capture_finished = true; - } - //} else { - /* - * LPTIM1 is not yet initialised. - * So we enable LPTIM1 to start counting. - */ - //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, 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_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); - - dma_overrun = false; - dma_fault = false; - - // Setup timer for PCLK - //rccResetLPTIM1(); - //rccEnableLPTIM1(FALSE); - - /* - * LPTIM1 is run in external count mode (CKSEL = 0, COUNTMODE = 1). - * CKPOL is set so leading and trailing edge of PCLK increment the counter. - * The internal clocking (checking edges of LPTIM1_IN) is set to use APB. - * The internal clock must be >4 times the frequency of the input (PCLK). - * NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK. - * Depending on PCLK state when LPTIM1 is enabled, LPMTIM1_OUT be inverted. - * - * Possible fix... - * Using CKSEL = 1 where PCLK is the actual clock may still be possible. - * This would ensure coincidence between LPTIM1_OUT and PCLK. - * If using CKSEL = 1 LPTIM1 needs 5 external clocks to reach kernel ready. - * Using CKSEL = 1 only allows for leading or trailing edge counting. - * Thus we would be sure which edge of PCLK incremented the LPTIM1 counter. - * Have to test to see if CMP and ARR interrupts work when CKSEL = 1. - * - * Continuing... - * LPTIM1 is enabled on the leading edge of VSYNC. - * After enabling LPTIM1 wait for the first interrupt (ARRIF). - * Waiting for ARRIF indicates that LPTIM1 kernel is ready. - * Note that waiting for interrupt when using COUNTMODE is redundant. - * The ST RM says a delay of only 2 counter (APB) clocks are required. - * But leave the interrupt check in place for now as it does no harm. - * - * The interrupt must be disabled on the first interrupt (else flood). - * - * LPTIM1_OUT is gated to TIM1 internal trigger input 2. - */ - //LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL); - //LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP; - //LPTIM1->CR |= LPTIM_CR_ENABLE; - //LPTIM1->IER |= LPTIM_IER_ARRMIE; - - /* - * TODO: When using COUNTMODE CMP and ARR should be 1 & 2? - * It is intended that after counter start CNT = 0. - * Then CNT reaches 1 on first PCLK edge and 2 on the second edge. - * Using 0 and 1 means LPTIM1_OUT gets CMP match as soon as LPMTIM1 is ready. - * This means LPTIM1_OUT will be set and TIM1 will be triggered immediately. - * A DMA transfer will then occur. - * The next edge of PCLK will make CNT = 2 and ARR will match. - * LPTIM1 will then be reset (synchronous with APB presumably). - * LPTIM1_OUT will clear briefly prior to setting again on reset CMP match. - * This will allow TIM1 to be re-triggered. - */ - //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 - */ - rccEnableTIM8(FALSE); - rccResetTIM8(); - - /* - * TIM1_IC1 is mapped to TRC which means we are in trigger input mode. - * TIM1 is set in slave reset mode with input from ITR2. - * 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 about the count. - * We simply use the DMA initiated by the trigger input. - */ - //TIM8->SMCR = TIM_SMCR_TS_1; - //TIM8->SMCR |= TIM_SMCR_SMS_2; - TIM8->CCMR1 |= (TIM_CCMR1_CC1S_0/* | TIM_CCMR1_CC1S_1*/); - TIM8->CCER = TIM_CCER_CC1E; - - capture_finished = false; - capture_error = 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(1000); - } while(!capture_finished && !capture_error); - - if (capture_error) { - TRACE_ERROR("CAM > Error capturing image"); - return false; - } - - 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_INPUT); - 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); - - palSetPadMode(GPIOB, 15, PAL_MODE_INPUT_PULLUP); - palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(3)); -} - -void OV5640_TransmitConfig(void) -{ - chThdSleepMilliseconds(1000); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3103, 0x11); - I2C_write8_16bitreg(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(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(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(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(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(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(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(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val); - } - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3406, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3400, 0x04); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3401, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3402, 0x04); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3403, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3404, 0x04); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3405, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // lanuch group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 ); - - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5381, 0x1c); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5382, 0x5a); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5383, 0x06); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5384, 0x1a); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5385, 0x66); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5386, 0x80); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5387, 0x82); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5388, 0x80); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5389, 0x02); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x538b, 0x98); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x538a, 0x01); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5587, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5588, 0x01); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5586, 0x20); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5585, 0x00); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3 - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5580, 0x06); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5583, 0x40); // sat U - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5584, 0x10); // sat V - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5003, 0x08); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3 - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group - - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a0f, 0x38); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a10, 0x30); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a11, 0x61); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1b, 0x38); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1e, 0x30); - I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1f, 0x10); -} - -void OV5640_init(ssdv_conf_t *config) { - ov5640_conf = config; - - // 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); -} - -bool OV5640_isAvailable(void) -{ - // 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(OV5640_I2C_ADR, 0x300A, &val) && I2C_read8_16bitreg(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 - - return ret; -} - From 94aaa434cf99372b833c7730a47c1d91a6a3d949 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Fri, 1 Sep 2017 02:02:30 +0200 Subject: [PATCH 23/37] Implemented RBAT (Battery impedance measurement) Removed ISOL Added LOWBATT message in APRS position packet --- tracker/software/config.c | 20 +++--- tracker/software/config.h | 2 +- tracker/software/drivers/ov5640.c | 2 +- tracker/software/drivers/pac1720.c | 83 ++++++++++++++++--------- tracker/software/drivers/pac1720.h | 11 ++-- tracker/software/drivers/wrapper/padc.c | 17 +++-- tracker/software/drivers/wrapper/padc.h | 1 + tracker/software/modules/position.c | 4 +- tracker/software/modules/tracking.c | 21 ++++--- tracker/software/modules/tracking.h | 8 ++- tracker/software/protocols/aprs/aprs.c | 27 ++++---- tracker/software/sleep.c | 8 +-- tracker/software/types.h | 8 +-- 13 files changed, 130 insertions(+), 82 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index 2ad2ce0..74ac450 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -73,13 +73,13 @@ uint8_t ssdv_buffer[256*1024] __attribute__((aligned(32))); * (required) There are possible options: * - TEL_SATS GPS Satellites * - TEL_TTFF Time to first fix (amount of seconds which it needed to aquire a GPS fix) - * - TEL_VBAT Battery voltage - * - TEL_VSOL Solar voltage + * - TEL_VBAT Battery voltage (in mV) + * - TEL_VSOL Solar voltage (in mV) * - TEL_PBAT Battery power (positive charge, negative discharge) - * - TEL_ISOL Solar short current (works only if USB is unattached) - * - TEL_PRESS Air pressure (by BME280) - * - TEL_TEMP Air temperature (by BME280) - * - TEL_HUM Air humidity (by BME280) + * - TEL_RBAT Battery impedance (in mOhm) + * - TEL_PRESS Air pressure (in Pa) + * - TEL_TEMP Air temperature (in degC*100) + * - TEL_HUM Air humidity (in %*10) * * aprs_conf.tel_enc bool The telemetry in the position packets do only contain the raw values. Receivers (like aprs.fi) dont know what these * (default false) values stands for. So we must tell them (e.g. that value 1 is air pressure measured in Pascal). If set to true, the @@ -184,8 +184,8 @@ void start_user_modules(void) config[0].aprs_conf.preamble = 300; // APRS Preamble config[0].aprs_conf.tel[0] = TEL_VBAT; // APRS Telemetry parameter 1 config[0].aprs_conf.tel[1] = TEL_PBAT; // APRS Telemetry parameter 2 - config[0].aprs_conf.tel[2] = TEL_TEMP; // APRS Telemetry parameter 3 - config[0].aprs_conf.tel[3] = TEL_PRESS; // APRS Telemetry parameter 4 + config[0].aprs_conf.tel[2] = TEL_RBAT; // APRS Telemetry parameter 3 + config[0].aprs_conf.tel[3] = TEL_TEMP; // APRS Telemetry parameter 4 config[0].aprs_conf.tel[4] = TEL_HUM; // APRS Telemetry parameter 5 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 @@ -248,7 +248,7 @@ void start_user_modules(void) 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.res = RES_QVGA; // Resolution QVGA config[3].ssdv_conf.redundantTx = true; // Transmit packets twice //start_image_thread(&config[3]); @@ -282,7 +282,7 @@ void start_user_modules(void) chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign 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.res = RES_VGA; // Resolution XGA //config[5].ssdv_conf.redundantTx = true; // Transmit packets twice //start_image_thread(&config[5]); diff --git a/tracker/software/config.h b/tracker/software/config.h index a998cce..5db3fb6 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -21,7 +21,7 @@ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */ -#define RUN_3V FALSE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. +#define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power. * With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in * order to work. However 3V takes a lot of power in idle. You can save energy using 1.8V. */ diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index e598d93..1939a1f 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -993,7 +993,7 @@ CH_IRQ_HANDLER(Vector5C) { if (LptimRdy) { // VSYNC handling if(!vsync) { - // Increase AHB clock to 6 MHz + // Increase AHB clock to 48 MHz uint32_t new = (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | FLASH_ACR_LATENCY_3WS; FLASH->ACR = new; while(FLASH->ACR != new); diff --git a/tracker/software/drivers/pac1720.c b/tracker/software/drivers/pac1720.c index ca65a20..e0e4622 100644 --- a/tracker/software/drivers/pac1720.c +++ b/tracker/software/drivers/pac1720.c @@ -4,6 +4,7 @@ #include "pi2c.h" #include "pac1720.h" #include "padc.h" +#include /* * FSP = FSC * FSV @@ -19,6 +20,8 @@ static int32_t pac1720_pbat; static int32_t pac1720_pbat_counter; +static int32_t pac1720_rbat; +static int32_t pac1720_rbat_counter; int16_t pac1720_getPbat(void) { int32_t fsp = FSV * FSC; @@ -27,40 +30,18 @@ int16_t pac1720_getPbat(void) { if(I2C_read16(PAC1720_ADDRESS, PAC1720_CH2_PWR_RAT_HIGH, (uint16_t*)&val)) { I2C_read8(PAC1720_ADDRESS, PAC1720_CH2_VSENSE_HIGH, &sign); + TRACE_DEBUG("%016x %02x", val, sign >> 7); return (sign >> 7 ? -1 : 1) * (val * fsp / 65535); } else { + TRACE_DEBUG("bla"); return 0; // PAC1720 not available (maybe Vcc too low) } } -int16_t pac1720_getIsol(void) { - if(isUsbConnected()) // USB not connected - { - // Short solar cells - palClearLine(LINE_SOL_SHORT_EN); - chThdSleepMilliseconds(300); // Wait a little bit to measure a correct value - - int16_t val; - uint8_t ret = I2C_read16(PAC1720_ADDRESS, PAC1720_CH1_VSENSE_HIGH, (uint16_t*)&val); - - // Unshort solar cells - palSetLine(LINE_SOL_SHORT_EN); - - // Calculate solar current - if(ret) { - val = val < 0 ? 0 : val >> 4; // Current can only flow in one direction (negative values are inaccurate readings) - return val * FSC / DENO; - } else { - return 0; // PAC1720 not available (maybe Vcc too low) - } - } - return 0; // USB connected (we dont want to short USB) -} - int16_t pac1720_getAvgPbat(void) { // Return current value if time interval too short if(!pac1720_pbat_counter) - pac1720_getPbat(); + return pac1720_getPbat(); // Calculate average power int16_t ret = pac1720_pbat / pac1720_pbat_counter; @@ -72,7 +53,7 @@ int16_t pac1720_getAvgPbat(void) { return ret; } -uint16_t pac1720_getBatteryVoltage(void) { +uint16_t pac1720_getVbat(void) { uint16_t val; if(!I2C_read16(PAC1720_ADDRESS, PAC1720_CH2_VSOURCE_HIGH, &val)) return 0; // PAC1720 not available (maybe Vcc too low) @@ -80,7 +61,22 @@ uint16_t pac1720_getBatteryVoltage(void) { return (val >> 5) * 20000 / 0x400; } -uint16_t pac1720_getSolarVoltage(void) { +int16_t pac1720_getAvgRbat(void) { + // Return current value if time interval too short + if(!pac1720_rbat_counter) + return 0; + + // Calculate average power + int16_t ret = pac1720_rbat / pac1720_rbat_counter; + + // Reset current measurement + pac1720_rbat = 0; + pac1720_rbat_counter = 0; + + return ret; +} + +uint16_t pac1720_getVsol(void) { uint16_t val; if(!I2C_read16(PAC1720_ADDRESS, PAC1720_CH1_VSOURCE_HIGH, &val)) return 0; // PAC1720 not available (maybe Vcc too low) @@ -101,12 +97,42 @@ THD_FUNCTION(pac1720_thd, arg) { (void)arg; + uint32_t counter = 0; + int32_t u1 = 999999; + int32_t p1 = 999999; + int32_t u2 = -999999; + int32_t p2 = -999999; while(true) { + // Sample data + int32_t v = pac1720_getVbat(); + int32_t p = pac1720_getPbat(); + // Measure battery power - pac1720_pbat += pac1720_getPbat(); + pac1720_pbat += p; pac1720_pbat_counter++; + // Measure battery impedance + if(p < p1) { + u1 = v; + p1 = p; + } + if(p > p2) { + u2 = v; + p2 = p; + } + if(++counter%10 == 0 && abs(p1-p2) > 100 && abs(u1-u2) > 0 && p1*u2 != p2*u1) + { + int32_t rbat = abs((u1*u2*(u1-u2)) / (p1*u2 - p2*u1)); + pac1720_rbat += rbat; + pac1720_rbat_counter++; + + u1 = 999999; + p1 = 999999; + u2 = -999999; + p2 = -999999; + } + chThdSleepMilliseconds(50); } } @@ -131,3 +157,4 @@ void pac1720_init(void) TRACE_INFO("PAC > Init PAC1720 continuous measurement"); chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(256), "PAC1720", NORMALPRIO, pac1720_thd, NULL); } + diff --git a/tracker/software/drivers/pac1720.h b/tracker/software/drivers/pac1720.h index 6707cc5..dd42e84 100644 --- a/tracker/software/drivers/pac1720.h +++ b/tracker/software/drivers/pac1720.h @@ -40,9 +40,9 @@ #define PAC1720_CH2_VSENSE_LIMIT_LOW 0x1C #define PAC1720_CH1_VSOURCE_LIMIT_HIGH 0x1D -#define PAC1720_CH2_VSOURCE_LIMIT_HIGH 0x1E -#define PAC1720_CH1_VSOURCE_LIMIT_LOW 0x1F -#define PAC1720_CH2_VSOURCE_LIMIT_LOW 0x20 +#define PAC1720_CH2_VSOURCE_LIMIT_HIGH 0x1E +#define PAC1720_CH1_VSOURCE_LIMIT_LOW 0x1F +#define PAC1720_CH2_VSOURCE_LIMIT_LOW 0x20 #define PAC1720_PRODUCT_ID 0xFD #define PAC1720_MANUFACTURER_ID 0xFE @@ -53,8 +53,9 @@ void pac1720_init(void); int16_t pac1720_getIsol(void); int16_t pac1720_getPbat(void); int16_t pac1720_getAvgPbat(void); -uint16_t pac1720_getBatteryVoltage(void); -uint16_t pac1720_getSolarVoltage(void); +uint16_t pac1720_getVbat(void); +int16_t pac1720_getAvgRbat(void); +uint16_t pac1720_getVsol(void); bool pac1720_isAvailable(void); #endif diff --git a/tracker/software/drivers/wrapper/padc.c b/tracker/software/drivers/wrapper/padc.c index e03b4f2..20ccdbf 100644 --- a/tracker/software/drivers/wrapper/padc.c +++ b/tracker/software/drivers/wrapper/padc.c @@ -68,15 +68,20 @@ void doConversion(void) deinitADC(); } -uint16_t getBatteryVoltageMV(void) +uint16_t getBatteryVoltageMV_STM32(void) { doConversion(); - uint16_t vbat = samples[2] * vcc_ref * DIVIDER_VBAT / 4096; + return samples[2] * vcc_ref * DIVIDER_VBAT / 4096; +} + +uint16_t getBatteryVoltageMV(void) +{ + uint16_t vbat = getBatteryVoltageMV_STM32(); // Get voltage from PAC1720 (PAC1720 returns false redings below 2.35V) if(vbat >= 2500) { - uint16_t vbat_pac = pac1720_getBatteryVoltage(); // Get value from PAC1720 + uint16_t vbat_pac = pac1720_getVbat(); // Get value from PAC1720 if(vbat_pac) // Apply it if valid vbat = vbat_pac; } @@ -86,10 +91,12 @@ uint16_t getBatteryVoltageMV(void) uint16_t getSolarVoltageMV(void) { + uint16_t vbat = getBatteryVoltageMV_STM32(); + // Get voltage from PAC1720 (PAC1720 returns false redings below 2.35V) - if(getBatteryVoltageMV() >= 2500) + if(vbat >= 2500) { - uint16_t vsol_pac = pac1720_getSolarVoltage(); // Get value from PAC1720 + uint16_t vsol_pac = pac1720_getVsol(); // Get value from PAC1720 if(vsol_pac) return vsol_pac; } diff --git a/tracker/software/drivers/wrapper/padc.h b/tracker/software/drivers/wrapper/padc.h index 448e68b..e957d54 100644 --- a/tracker/software/drivers/wrapper/padc.h +++ b/tracker/software/drivers/wrapper/padc.h @@ -8,6 +8,7 @@ void initADC(void); void deinitADC(void); +uint16_t getBatteryVoltageMV_STM32(void); uint16_t getBatteryVoltageMV(void); uint16_t getSolarVoltageMV(void); uint16_t getUSBVoltageMV(void); diff --git a/tracker/software/modules/position.c b/tracker/software/modules/position.c index 7ea210a..4ac1365 100644 --- a/tracker/software/modules/position.c +++ b/tracker/software/modules/position.c @@ -93,8 +93,8 @@ void replace_placeholders(char* fskmsg, uint16_t size, trackPoint_t *tp) { str_replace(fskmsg, size, "", buf); chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_pbat/1000, (tp->adc_pbat >= 0 ? 1 : -1) * (tp->adc_pbat%1000)); str_replace(fskmsg, size, "", buf); - chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_isol/1000, (tp->adc_isol >= 0 ? 1 : -1) * (tp->adc_isol%1000)); - str_replace(fskmsg, size, "", buf); + chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_rbat/1000, (tp->adc_rbat >= 0 ? 1 : -1) * (tp->adc_rbat%1000)); + str_replace(fskmsg, size, "", buf); chsnprintf(buf, sizeof(buf), "%d", tp->air_press/10); str_replace(fskmsg, size, "", buf); chsnprintf(buf, sizeof(buf), "%d.%d", tp->air_temp/100, (tp->air_temp%100)/10); diff --git a/tracker/software/modules/tracking.c b/tracker/software/modules/tracking.c index 96b76ee..db1c85c 100644 --- a/tracker/software/modules/tracking.c +++ b/tracker/software/modules/tracking.c @@ -179,7 +179,7 @@ THD_FUNCTION(trackingThread, arg) { lastTrackPoint->gps_alt = lastLogPoint.gps_alt; } - lastTrackPoint->gps_lock = 0; // But tell the user that there is no current lock nor any GPS sats locked + lastTrackPoint->gps_lock = GPS_LOSS; // But tell the user that there is no current lock nor any GPS sats locked lastTrackPoint->gps_sats = 0; lastTrackPoint->gps_ttff = 0; @@ -202,7 +202,6 @@ THD_FUNCTION(trackingThread, arg) { lastTrackPoint->adc_vsol = getSolarVoltageMV(); lastTrackPoint->adc_vbat = getBatteryVoltageMV(); lastTrackPoint->adc_vusb = getUSBVoltageMV(); - lastTrackPoint->adc_isol = pac1720_getIsol(); lastTrackPoint->adc_pbat = pac1720_getPbat(); bme280_t bme280; @@ -278,13 +277,17 @@ THD_FUNCTION(trackingThread, arg) { tp->gps_lon = gpsFix.lon; tp->gps_alt = gpsFix.alt; - tp->gps_lock = isGPSLocked(&gpsFix); + tp->gps_lock = GPS_LOCKED; tp->gps_sats = gpsFix.num_svs; } else { // GPS lost (keep GPS switched on) // Debug - TRACE_WARN("TRAC > GPS sampling finished GPS LOSS"); + if(batt < GPS_OFF_VBAT) { + TRACE_WARN("TRAC > GPS sampling finished GPS LOW BATT"); + } else { + TRACE_WARN("TRAC > GPS sampling finished GPS LOSS"); + } // Take time from internal RTC getTime(&rtc); @@ -300,8 +303,8 @@ THD_FUNCTION(trackingThread, arg) { tp->gps_lon = ltp->gps_lon; tp->gps_alt = ltp->gps_alt; - // Mark gpsloss - tp->gps_lock = false; + // Mark GPS loss (or low batt) + tp->gps_lock = batt < GPS_OFF_VBAT ? GPS_LOWBATT : GPS_LOSS; tp->gps_sats = 0; } @@ -313,8 +316,8 @@ THD_FUNCTION(trackingThread, arg) { tp->adc_vsol = getSolarVoltageMV(); tp->adc_vbat = getBatteryVoltageMV(); tp->adc_vusb = getUSBVoltageMV(); - tp->adc_isol = pac1720_getIsol(); tp->adc_pbat = pac1720_getAvgPbat(); + tp->adc_rbat = pac1720_getAvgRbat(); bme280_t bme280; @@ -336,13 +339,13 @@ THD_FUNCTION(trackingThread, arg) { "%s Time %04d-%02d-%02d %02d:%02d:%02d\r\n" "%s Pos %d.%05d %d.%05d Alt %dm\r\n" "%s Sats %d TTFF %dsec\r\n" - "%s ADC Vbat=%d.%03dV Vsol=%d.%03dV VUSB=%d.%03dV Pbat=%dmW Isol=%dmA\r\n" + "%s ADC Vbat=%d.%03dV Vsol=%d.%03dV VUSB=%d.%03dV Pbat=%dmW Rbat=%dmOhm\r\n" "%s AIR p=%6d.%01dPa T=%2d.%02ddegC phi=%2d.%01d%%", tp->id, TRACE_TAB, tp->time.year, tp->time.month, tp->time.day, tp->time.hour, tp->time.minute, tp->time.day, TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*(tp->gps_lat/100)%100000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*(tp->gps_lon/100)%100000, tp->gps_alt, TRACE_TAB, tp->gps_sats, tp->gps_ttff, - TRACE_TAB, tp->adc_vbat/1000, (tp->adc_vbat%1000), tp->adc_vsol/1000, (tp->adc_vsol%1000), tp->adc_vusb/1000, (tp->adc_vusb%1000), tp->adc_pbat, tp->adc_isol, + TRACE_TAB, tp->adc_vbat/1000, (tp->adc_vbat%1000), tp->adc_vsol/1000, (tp->adc_vsol%1000), tp->adc_vusb/1000, (tp->adc_vusb%1000), tp->adc_pbat, tp->adc_rbat, TRACE_TAB, tp->air_press/10, tp->air_press%10, tp->air_temp/100, tp->air_temp%100, tp->air_hum/10, tp->air_hum%10 ); diff --git a/tracker/software/modules/tracking.h b/tracker/software/modules/tracking.h index ea84155..c9b6c42 100644 --- a/tracker/software/modules/tracking.h +++ b/tracker/software/modules/tracking.h @@ -5,12 +5,16 @@ #include "hal.h" #include "ptime.h" +#define GPS_LOCKED 0 /* GPS is locked and could aquire a fix */ +#define GPS_LOSS 1 /* GPS was switched on all time but it couln't aquire a fix */ +#define GPS_LOWBATT 2 /* GPS was switched on but had to be switched off prematurely while the battery is almost empty (or is too cold) */ + typedef struct { uint32_t id; // Serial ID ptime_t time; // GPS time // GPS - bool gps_lock; // True = on last try GPS has been locked + uint8_t gps_lock; // 0: locked, 1: GPS loss, 2: low power (switched off) int32_t gps_lat; // Latitude in °*10^7 int32_t gps_lon; // Longitude in °*10^7 int32_t gps_alt; // Altitude in meter @@ -22,7 +26,7 @@ typedef struct { uint16_t adc_vbat; // Current battery voltage in mV uint16_t adc_vusb; // Current USB voltage in mV int16_t adc_pbat; // Average battery current (since last track point) - int16_t adc_isol; // Current solar shot current + int16_t adc_rbat; // Battery impedance // BME280 (on board) uint32_t air_press; // Airpressure in Pa*10 (in 0.1Pa) diff --git a/tracker/software/protocols/aprs/aprs.c b/tracker/software/protocols/aprs/aprs.c index 7cae199..09923eb 100644 --- a/tracker/software/protocols/aprs/aprs.c +++ b/tracker/software/protocols/aprs/aprs.c @@ -77,7 +77,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co uint32_t a1 = a / 91; uint32_t a1r = a % 91; - uint8_t gpsFix = trackPoint->gps_lock ? GSP_FIX_CURRENT : GSP_FIX_OLD; + uint8_t gpsFix = trackPoint->gps_lock ? GSP_FIX_OLD : GSP_FIX_CURRENT; uint8_t src = NMEA_SRC_GGA; uint8_t origin = ORIGIN_PICO; @@ -103,7 +103,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co chsnprintf(temp, sizeof(temp), "%d", trackPoint->gps_sats); ax25_send_string(&packet, temp); - if(trackPoint->gps_lock) + if(trackPoint->gps_lock == GPS_LOCKED) // GPS is locked { // TTFF (Time to first fix) ax25_send_string(&packet, " TTFF "); @@ -112,12 +112,18 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co ax25_send_string(&packet, "sec"); } - // GPS Loss counter - if(!trackPoint->gps_lock) - { + if(trackPoint->gps_lock == GPS_LOSS) { // No GPS lock + ax25_send_string(&packet, " GPS LOSS "); chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); ax25_send_string(&packet, temp); + + } else if(trackPoint->gps_lock == GPS_LOWBATT) { // GPS switched off prematurely + + ax25_send_string(&packet, " GPS LOWBATT "); + chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); + ax25_send_string(&packet, temp); + } else { loss_of_gps_counter = 0; } @@ -140,7 +146,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co case TEL_VBAT: t = trackPoint->adc_vbat; break; case TEL_VSOL: t = trackPoint->adc_vsol; break; case TEL_PBAT: t = trackPoint->adc_pbat+4096; break; - case TEL_ISOL: t = trackPoint->adc_isol; break; + case TEL_RBAT: t = trackPoint->adc_rbat; break; case TEL_HUM: t = trackPoint->air_hum; break; case TEL_PRESS: t = trackPoint->air_press/125 - 40; break; case TEL_TEMP: t = trackPoint->air_temp/10 + 1000; break; @@ -259,7 +265,7 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const case TEL_VBAT: ax25_send_string(&packet, "Vbat"); break; case TEL_VSOL: ax25_send_string(&packet, "Vsol"); break; case TEL_PBAT: ax25_send_string(&packet, "Pbat"); break; - case TEL_ISOL: ax25_send_string(&packet, "Isol"); break; + case TEL_RBAT: ax25_send_string(&packet, "Rbat"); break; case TEL_HUM: ax25_send_string(&packet, "Humidity"); break; case TEL_PRESS: ax25_send_string(&packet, "Airpressure"); break; case TEL_TEMP: ax25_send_string(&packet, "Temperature"); break; @@ -292,8 +298,8 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const ax25_send_string(&packet, "W"); break; - case TEL_ISOL: - ax25_send_string(&packet, "A"); + case TEL_RBAT: + ax25_send_string(&packet, "Ohm"); break; case TEL_HUM: @@ -325,10 +331,9 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const ax25_send_string(&packet, "0,1,0"); break; - - case TEL_ISOL: case TEL_VBAT: case TEL_VSOL: + case TEL_RBAT: ax25_send_string(&packet, "0,.001,0"); break; diff --git a/tracker/software/sleep.c b/tracker/software/sleep.c index 87eb410..2db9897 100644 --- a/tracker/software/sleep.c +++ b/tracker/software/sleep.c @@ -17,14 +17,14 @@ bool p_sleep(const sleep_conf_t *config) case SLEEP_WHEN_VBAT_BELOW_THRES: return getBatteryVoltageMV() < config->vbat_thres; - case SLEEP_WHEN_ISOL_BELOW_THRES: - return pac1720_getIsol() < config->isol_thres; + case SLEEP_WHEN_RBAT_BELOW_THRES: + return 0 < config->rbat_thres; // FIXME case SLEEP_WHEN_VBAT_ABOVE_THRES: return getBatteryVoltageMV() > config->vbat_thres; - case SLEEP_WHEN_ISOL_ABOVE_THRES: - return pac1720_getIsol() > config->isol_thres; + case SLEEP_WHEN_RBAT_ABOVE_THRES: + return 0 > config->rbat_thres; // FIXME case SLEEP_WHEN_DISCHARGING: case SLEEP_WHEN_CHARGING: diff --git a/tracker/software/types.h b/tracker/software/types.h index 521e35b..9331c5f 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -31,7 +31,7 @@ typedef enum { TEL_VBAT, TEL_VSOL, TEL_PBAT, - TEL_ISOL, + TEL_RBAT, TEL_PRESS, TEL_TEMP, TEL_HUM @@ -52,9 +52,9 @@ typedef struct { typedef enum { SLEEP_DISABLED, SLEEP_WHEN_VBAT_BELOW_THRES, - SLEEP_WHEN_ISOL_BELOW_THRES, + SLEEP_WHEN_RBAT_BELOW_THRES, SLEEP_WHEN_VBAT_ABOVE_THRES, - SLEEP_WHEN_ISOL_ABOVE_THRES, + SLEEP_WHEN_RBAT_ABOVE_THRES, SLEEP_WHEN_DISCHARGING, SLEEP_WHEN_CHARGING } sleep_type_t; @@ -62,7 +62,7 @@ typedef enum { typedef struct { sleep_type_t type; uint16_t vbat_thres; - uint16_t isol_thres; + uint16_t rbat_thres; } sleep_conf_t; typedef struct { From 136540414b5071628cb3e0f120b12b3689e8a849 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Fri, 1 Sep 2017 03:48:15 +0200 Subject: [PATCH 24/37] Removed OV2640, implemented testimage --- tracker/software/Makefile | 1 - tracker/software/config.c | 4 +- tracker/software/config.h | 4 +- tracker/software/drivers/ov2640.c | 836 ------------------------- tracker/software/drivers/ov2640.h | 22 - tracker/software/drivers/pac1720.c | 2 - tracker/software/modules/image.c | 323 ++++++++-- tracker/software/protocols/ssdv/ssdv.c | 2 +- tracker/software/protocols/ssdv/ssdv.h | 4 +- 9 files changed, 279 insertions(+), 919 deletions(-) delete mode 100644 tracker/software/drivers/ov2640.c delete mode 100644 tracker/software/drivers/ov2640.h diff --git a/tracker/software/Makefile b/tracker/software/Makefile index f4026bb..b52408a 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -135,7 +135,6 @@ CSRC = $(STARTUPSRC) \ drivers/si4464.c \ drivers/bme280.c \ drivers/pac1720.c \ - drivers/ov2640.c \ drivers/ov5640.c \ drivers/flash/flash.c \ drivers/flash/helper.c \ diff --git a/tracker/software/config.c b/tracker/software/config.c index 74ac450..d91c433 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -282,9 +282,9 @@ void start_user_modules(void) chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign 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_VGA; // Resolution XGA + config[5].ssdv_conf.res = RES_QVGA; // Resolution XGA //config[5].ssdv_conf.redundantTx = true; // Transmit packets twice - //start_image_thread(&config[5]); + 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 5db3fb6..9d8c283 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -15,8 +15,8 @@ #define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */ #define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */ -#define GPS_ON_VBAT 7000 /* Battery voltage threshold at which GPS is switched on */ -#define GPS_OFF_VBAT 7000 /* Battery voltage threshold at which GPS is switched off */ +#define GPS_ON_VBAT 3000 /* Battery voltage threshold at which GPS is switched on */ +#define GPS_OFF_VBAT 2500 /* Battery voltage threshold at which GPS is switched off */ #define TRACE_TIME TRUE /* Enables time tracing on debugging port */ #define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */ diff --git a/tracker/software/drivers/ov2640.c b/tracker/software/drivers/ov2640.c deleted file mode 100644 index 01ca139..0000000 --- a/tracker/software/drivers/ov2640.c +++ /dev/null @@ -1,836 +0,0 @@ -/** - * This is the OV2640 driver - * I2C configuring concept has been taken from - * https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c - */ - -/* - * ov2640 Camera Driver - * - * Copyright (C) 2010 Alberto Panizzo - * - * Based on ov772x, ov9640 drivers and previous non merged implementations. - * - * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2006, OmniVision - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "ch.h" -#include "hal.h" -#include "ov2640.h" -#include "pi2c.h" -#include "board.h" -#include "defines.h" -#include "debug.h" -#include - -#define OV2640_I2C_ADR 0x30 - - -#define VAL_SET(x, mask, rshift, lshift) \ - ((((x) >> rshift) & mask) << lshift) -/* - * DSP registers - * register offset for BANK_SEL == BANK_SEL_DSP - */ -#define R_BYPASS 0x05 /* Bypass DSP */ -#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ -#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ -#define QS 0x44 /* Quantization Scale Factor */ -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 -#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) -#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) -#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ -#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ -#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define XOFFL 0x53 /* OFFSET_X[7:0] */ -#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define YOFFL 0x54 /* OFFSET_Y[7:0] */ -#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) -#define VHYX 0x55 /* Offset and size completion */ -#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) -#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) -#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) -#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) -#define DPRP 0x56 -#define TEST 0x57 /* Horizontal size completion */ -#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) -#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ -#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ -#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) -#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ -#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) -#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) -#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) -#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ -#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ -#define CTRL2 0x86 /* DSP Module enable 2 */ -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 -#define CTRL3 0x87 /* DSP Module enable 3 */ -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define SIZEL 0x8C /* Image Size Completion */ -#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) -#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) -#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) -#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ -#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ -#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) -#define CTRL0 0xC2 /* DSP Module enable 0 */ -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 -#define CTRL1 0xC3 /* DSP Module enable 1 */ -#define CTRL1_CIP 0x80 -#define CTRL1_DMY 0x40 -#define CTRL1_RAW_GMA 0x20 -#define CTRL1_DG 0x10 -#define CTRL1_AWB 0x08 -#define CTRL1_AWB_GAIN 0x04 -#define CTRL1_LENC 0x02 -#define CTRL1_PRE 0x01 -#define R_DVP_SP 0xD3 /* DVP output speed control */ -#define R_DVP_SP_AUTO_MODE 0x80 -#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); - * = sysclk (48)/(2*[6:0]) (RAW);*/ -#define IMAGE_MODE 0xDA /* Image Output Format Select */ -#define IMAGE_MODE_Y8_DVP_EN 0x40 -#define IMAGE_MODE_JPEG_EN 0x10 -#define IMAGE_MODE_YUV422 0x00 -#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ -#define IMAGE_MODE_RGB565 0x08 -#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output - * mode (0 for HREF is same as sensor) */ -#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP - * 1: Low byte first UYVY (C2[4] =0) - * VYUY (C2[4] =1) - * 0: High byte first YUYV (C2[4]=0) - * YVYU (C2[4] = 1) */ -#define RESET 0xE0 /* Reset */ -#define RESET_MICROC 0x40 -#define RESET_SCCB 0x20 -#define RESET_JPEG 0x10 -#define RESET_DVP 0x04 -#define RESET_IPU 0x02 -#define RESET_CIF 0x01 -#define REGED 0xED /* Register ED */ -#define REGED_CLK_OUT_DIS 0x10 -#define MS_SP 0xF0 /* SCCB Master Speed */ -#define SS_ID 0xF7 /* SCCB Slave ID */ -#define SS_CTRL 0xF8 /* SCCB Slave Control */ -#define SS_CTRL_ADD_AUTO_INC 0x20 -#define SS_CTRL_EN 0x08 -#define SS_CTRL_DELAY_CLK 0x04 -#define SS_CTRL_ACC_EN 0x02 -#define SS_CTRL_SEN_PASS_THR 0x01 -#define MC_BIST 0xF9 /* Microcontroller misc register */ -#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ -#define MC_BIST_BOOT_ROM_SEL 0x40 -#define MC_BIST_12KB_SEL 0x20 -#define MC_BIST_12KB_MASK 0x30 -#define MC_BIST_512KB_SEL 0x08 -#define MC_BIST_512KB_MASK 0x0C -#define MC_BIST_BUSY_BIT_R 0x02 -#define MC_BIST_MC_RES_ONE_SH_W 0x02 -#define MC_BIST_LAUNCH 0x01 -#define BANK_SEL 0xFF /* Register Bank Select */ -#define BANK_SEL_DSP 0x00 -#define BANK_SEL_SENS 0x01 - -/* - * Sensor registers - * register offset for BANK_SEL == BANK_SEL_SENS - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define COM1 0x03 /* Common control 1 */ -#define COM1_1_DUMMY_FR 0x40 -#define COM1_3_DUMMY_FR 0x80 -#define COM1_7_DUMMY_FR 0xC0 -#define COM1_VWIN_LSB_UXGA 0x0F -#define COM1_VWIN_LSB_SVGA 0x0A -#define COM1_VWIN_LSB_CIF 0x06 -#define REG04 0x04 /* Register 04 */ -#define REG04_DEF 0x20 /* Always set */ -#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ -#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ -#define REG04_VREF_EN 0x10 -#define REG04_HREF_EN 0x08 -#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) -#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ -#define COM2 0x09 /* Common control 2 */ -#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ -#define COM3_BAND_AUTO 0x02 /* Auto Banding */ -#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the - * snapshot sequence*/ -#define AEC 0x10 /* AEC[9:2] Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define CLKRC_EN 0x80 -#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ -#define COM7 0x12 /* Common control 7 */ -#define COM7_SRST 0x80 /* Initiates system reset. All registers are - * set to factory default values after which - * the chip resumes normal operation */ -#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ -#define COM7_RES_SVGA 0x40 /* SVGA */ -#define COM7_RES_CIF 0x20 /* CIF */ -#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ -#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ -#define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ -#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ -#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ -#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ -#define COM9 0x14 /* Common control 9 - * Automatic gain ceiling - maximum AGC value [7:5]*/ -#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ -#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ -#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ -#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ -#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ -#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ -#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ -#define COM10 0x15 /* Common control 10 */ -#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ -#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of - * PCLK (user can latch data at the next - * falling edge of PCLK). - * 0 otherwise. */ -#define COM10_HREF_INV 0x08 /* Invert HREF polarity: - * HREF negative for valid data*/ -#define COM10_VSYNC_INV 0x02 /* Invert VSYNC polarity */ -#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ -#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ -#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ -#define VEND 0x1A /* Vertical Window end MSB 8 bit */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VV 0x26 /* AGC/AEC Fast mode operating region */ -#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) -#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) -#define REG2A 0x2A /* Dummy pixel insert MSB */ -#define FRARL 0x2B /* Dummy pixel insert LSB */ -#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ -#define YAVG 0x2F /* Y/G Channel Average value */ -#define REG32 0x32 /* Common Control 32 */ -#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ -#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ -#define ARCOM2 0x34 /* Zoom: Horizontal start point */ -#define REG45 0x45 /* Register 45 */ -#define FLL 0x46 /* Frame Length Adjustment LSBs */ -#define FLH 0x47 /* Frame Length Adjustment MSBs */ -#define COM19 0x48 /* Zoom: Vertical start point */ -#define ZOOMS 0x49 /* Zoom: Vertical start point */ -#define COM22 0x4B /* Flash light control */ -#define COM25 0x4E /* For Banding operations */ -#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ -#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ -#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ -#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ -#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ -#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ -#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ -#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ - -#define MANUFACTURER_ID 0x7FA2 -#define PID_OV2640 0x2626 -#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) - -struct regval_list { - uint8_t reg; - uint8_t val; -}; - -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - -struct ov2640_win_size { - char *name; - enum ov2640_width width; - enum ov2640_height height; - const struct regval_list *regs; -}; - - -/* - * Registers settings. Most of them are undocumented. Some documentation is - * is available in the OV2640 datasheet, the OV2640 hardware app notes and - * the OV2640 software app notes documents. - */ - -#define ENDMARKER { 0xff, 0xff } - -static const struct regval_list ov2640_init_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0x2c, 0xff }, - { 0x2e, 0xdf }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x3c, 0x32 }, - { 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_BNDF_EN }, - //~ { AEC, 0x00 }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, - //{ COM10, COM10_PCLK_RISE }, - { 0x2c, 0x0c }, - { 0x33, 0x78 }, - { 0x3a, 0x33 }, - { 0x3b, 0xfb }, - { 0x3e, 0x00 }, - { 0x43, 0x11 }, - { 0x16, 0x10 }, - { 0x39, 0x02 }, - { 0x35, 0x88 }, - { 0x22, 0x0a }, - { 0x37, 0x40 }, - { 0x23, 0x00 }, - { ARCOM2, 0xa0 }, - { 0x06, 0x02 }, - { 0x06, 0x88 }, - { 0x07, 0xc0 }, - { 0x0d, 0xb7 }, - { 0x0e, 0x01 }, - { 0x4c, 0x00 }, - { 0x4a, 0x81 }, - { 0x21, 0x99 }, - { AEW, 0x40 }, - { AEB, 0x38 }, - { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, - { 0x5c, 0x00 }, - { 0x63, 0x00 }, - { FLL, 0x22 }, - { COM3, 0x38 | COM3_BAND_AUTO }, - { REG5D, 0x55 }, - { REG5E, 0x7d }, - { REG5F, 0x7d }, - { REG60, 0x55 }, - { HISTO_LOW, 0x70 }, - { HISTO_HIGH, 0x80 }, - { 0x7c, 0x05 }, - { 0x20, 0x80 }, - { 0x28, 0x30 }, - { 0x6c, 0x00 }, - { 0x6d, 0x80 }, - { 0x6e, 0x00 }, - { 0x70, 0x02 }, - { 0x71, 0x94 }, - { 0x73, 0xc1 }, - { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, - { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, - { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, - { 0x76, 0xff }, - { 0x33, 0xa0 }, - { 0x42, 0x20 }, - { 0x43, 0x18 }, - { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, - { 0x88, 0x3f }, - { 0xd7, 0x03 }, - { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, - { 0xc8, 0x08 }, - { 0xc9, 0x80 }, - { BPADDR, 0x00 }, - { BPDATA, 0x00 }, - { BPADDR, 0x03 }, - { BPDATA, 0x48 }, - { BPDATA, 0x48 }, - { BPADDR, 0x08 }, - { BPDATA, 0x20 }, - { BPDATA, 0x10 }, - { BPDATA, 0x0e }, - { 0x90, 0x00 }, - { 0x91, 0x0e }, - { 0x91, 0x1a }, - { 0x91, 0x31 }, - { 0x91, 0x5a }, - { 0x91, 0x69 }, - { 0x91, 0x75 }, - { 0x91, 0x7e }, - { 0x91, 0x88 }, - { 0x91, 0x8f }, - { 0x91, 0x96 }, - { 0x91, 0xa3 }, - { 0x91, 0xaf }, - { 0x91, 0xc4 }, - { 0x91, 0xd7 }, - { 0x91, 0xe8 }, - { 0x91, 0x20 }, - { 0x92, 0x00 }, - { 0x93, 0x06 }, - { 0x93, 0xe3 }, - { 0x93, 0x03 }, - { 0x93, 0x03 }, - { 0x93, 0x00 }, - { 0x93, 0x02 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x93, 0x00 }, - { 0x96, 0x00 }, - { 0x97, 0x08 }, - { 0x97, 0x19 }, - { 0x97, 0x02 }, - { 0x97, 0x0c }, - { 0x97, 0x24 }, - { 0x97, 0x30 }, - { 0x97, 0x28 }, - { 0x97, 0x26 }, - { 0x97, 0x02 }, - { 0x97, 0x98 }, - { 0x97, 0x80 }, - { 0x97, 0x00 }, - { 0x97, 0x00 }, - { 0xa4, 0x00 }, - { 0xa8, 0x00 }, - { 0xc5, 0x11 }, - { 0xc6, 0x51 }, - { 0xbf, 0x80 }, - { 0xc7, 0x10 }, /* white balance */ - { 0xb6, 0x66 }, - { 0xb8, 0xA5 }, - { 0xb7, 0x64 }, - { 0xb9, 0x7C }, - { 0xb3, 0xaf }, - { 0xb4, 0x97 }, - { 0xb5, 0xFF }, - { 0xb0, 0xC5 }, - { 0xb1, 0x94 }, - { 0xb2, 0x0f }, - { 0xc4, 0x5c }, - { 0xa6, 0x00 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x1b }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0xa7, 0x20 }, - { 0xa7, 0xd8 }, - { 0xa7, 0x19 }, - { 0xa7, 0x31 }, - { 0xa7, 0x00 }, - { 0xa7, 0x18 }, - { 0x7f, 0x00 }, - { 0xe5, 0x1f }, - { 0xe1, 0x77 }, - { 0xdd, 0x7f }, - { QS, 0x0C }, - { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN }, - ENDMARKER, -}; - -/* - * Register settings for window size - * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. - * Then the different zooming configurations will setup the output image size. - */ -static const struct regval_list ov2640_size_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, - { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | - CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, - { XOFFL, XOFFL_SET(0) }, - { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | - VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, - ENDMARKER, -}; - -#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ - { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ - CTRLI_H_DIV_SET(h_div)}, \ - { ZMOW, ZMOW_OUTW_SET(x) }, \ - { ZMOH, ZMOH_OUTH_SET(y) }, \ - { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ - { R_DVP_SP, pclk_div }, \ - { RESET, 0x00} - -static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), - ENDMARKER, -}; - -static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), - ENDMARKER, -}; - -static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), - ENDMARKER, -}; - -static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), - { CTRLI, 0x00}, - ENDMARKER, -}; - -static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), - { CTRLI, 0x00}, - { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), - { CTRLI, 0x00}, - { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, - ENDMARKER, -}; - -#define OV2640_SIZE(n, w, h, r) \ - {.name = n, .width = w , .height = h, .regs = r } - -static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), -}; - -/* - * Register settings for pixel formats - */ -static const struct regval_list ov2640_format_change_preamble_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_yuyv_regs[] = { - { IMAGE_MODE, IMAGE_MODE_YUV422 }, - { 0xd7, 0x03 }, - { 0x33, 0xa0 }, - { 0xe5, 0x1f }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_uyvy_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, - { 0xd7, 0x01 }, - { 0x33, 0xa0 }, - { 0xe1, 0x67 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_be_regs[] = { - { IMAGE_MODE, IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_rgb565_le_regs[] = { - { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, - { 0xd7, 0x03 }, - { RESET, 0x00 }, - { R_BYPASS, R_BYPASS_USE_DSP }, - ENDMARKER, -}; - -static const struct regval_list ov2640_jpeg_regs[] = { - { BANK_SEL, BANK_SEL_DSP }, - { 0xe0, 0x14 }, - { 0xe1, 0x77 }, - { 0xe5, 0x1f }, - { 0xd7, 0x03 }, - { IMAGE_MODE, IMAGE_MODE_JPEG_EN }, - { 0xe0, 0x00 }, - { BANK_SEL, BANK_SEL_SENS }, - { 0x04, 0x08 }, - ENDMARKER, -}; - -ssdv_conf_t *ov2640_conf; -uint32_t size; - -/** - * Captures an image from the camera. - */ -bool OV2640_Snapshot2RAM(void) -{ - // Capture enable - TRACE_INFO("CAM > Capture image"); - OV2640_Capture(); - - return true; -} - -bool OV2640_BufferOverflow(void) -{ - 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; - return ov2640_conf->size_sampled; -} - - -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; - while(true) - { - do { - gpioc = GPIOC->IDR & 0x7; - } while((gpioc & 0x1) != 0x1); // Wait for PCLK to rise - gpioa = GPIOA->IDR; - switch(gpioc) { - case 0x3: - break; - case 0x7: - ov2640_conf->ram_buffer[ov2640_conf->size_sampled++] = gpioa; - break; - default: - return true; - } - // Wait for falling edge - while(GPIOC->IDR & 0x1); - } -} - -/** - * 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 | 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 | 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); -} - -void OV2640_TransmitConfig(void) -{ - // Set to page 1 - I2C_write8(OV2640_I2C_ADR, 0xff, 0x01); - I2C_write8(OV2640_I2C_ADR, 0x12, 0x80); - chThdSleepMilliseconds(50); - - /* Write selected arrays to the camera to initialize it and set the - * desired output format. */ - for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val); - - for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val); - - switch(ov2640_conf->res) { - case RES_QCIF: - for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val); - break; - - case RES_QVGA: - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - break; - - case RES_VGA: - for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val); - break; - - case RES_XGA: - for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val); - break; - - case RES_UXGA: - for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val); - break; - - default: // Default QVGA - for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val); - } - - for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val); - for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val); - - for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++) - I2C_write8(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val); -} - -void OV2640_init(ssdv_conf_t *config) { - ov2640_conf = config; - - // Clearing buffer - uint32_t i; - for(i=0; iram_size; i++) - ov2640_conf->ram_buffer[i] = 0; - - TRACE_INFO("CAM > Init pins"); - OV2640_InitGPIO(); - - // Power on OV2640 - TRACE_INFO("CAM > Switch on"); - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - // Send settings to OV2640 - TRACE_INFO("CAM > Transmit config to camera"); - OV2640_TransmitConfig(); - - chThdSleepMilliseconds(3000); -} - -void OV2640_deinit(void) { - // Power off OV2640 - 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); -} - -bool OV2640_isAvailable(void) -{ - // Configure pins - OV2640_InitGPIO(); - - // Switch on camera - palSetLine(LINE_CAM_EN); // Switch on camera - palSetLine(LINE_CAM_RESET); // Toggle reset - - chThdSleepMilliseconds(100); - - uint16_t val; - bool ret; - if(I2C_read16(OV2640_I2C_ADR, 0x0A, &val)) - ret = val == PID_OV2640; - else - ret = false; - - palClearLine(LINE_CAM_EN); // Switch off camera - palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT); // CAM_RESET - - return ret; -} - - diff --git a/tracker/software/drivers/ov2640.h b/tracker/software/drivers/ov2640.h deleted file mode 100644 index 1f89af7..0000000 --- a/tracker/software/drivers/ov2640.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * This is the OV2640 driver - */ - -#ifndef __OV2640_H__ -#define __OV2640_H__ - -#include "ch.h" -#include "hal.h" -#include "types.h" - -bool OV2640_Snapshot2RAM(void); -bool OV2640_Capture(void); -void OV2640_InitGPIO(void); -uint32_t OV2640_getBuffer(uint8_t** buffer); -bool OV2640_BufferOverflow(void); -void OV2640_TransmitConfig(void); -void OV2640_init(ssdv_conf_t *config); -void OV2640_deinit(void); -bool OV2640_isAvailable(void); - -#endif diff --git a/tracker/software/drivers/pac1720.c b/tracker/software/drivers/pac1720.c index e0e4622..e3eea11 100644 --- a/tracker/software/drivers/pac1720.c +++ b/tracker/software/drivers/pac1720.c @@ -30,10 +30,8 @@ int16_t pac1720_getPbat(void) { if(I2C_read16(PAC1720_ADDRESS, PAC1720_CH2_PWR_RAT_HIGH, (uint16_t*)&val)) { I2C_read8(PAC1720_ADDRESS, PAC1720_CH2_VSENSE_HIGH, &sign); - TRACE_DEBUG("%016x %02x", val, sign >> 7); return (sign >> 7 ? -1 : 1) * (val * fsp / 65535); } else { - TRACE_DEBUG("bla"); return 0; // PAC1720 not available (maybe Vcc too low) } } diff --git a/tracker/software/modules/image.c b/tracker/software/modules/image.c index d184baa..b52a000 100644 --- a/tracker/software/modules/image.c +++ b/tracker/software/modules/image.c @@ -3,7 +3,6 @@ #include "debug.h" #include "modules.h" -#include "ov2640.h" #include "ov5640.h" #include "pi2c.h" #include "ssdv.h" @@ -16,15 +15,273 @@ #include "watchdog.h" #include "flash.h" +const uint8_t noCameraFound[] = { + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48, + 0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, + 0x0E, 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, + 0x25, 0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, + 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, + 0x71, 0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x11, 0x12, + 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A, 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xFF, 0xC0, + 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0xA0, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, + 0x01, 0xFF, 0xC4, 0x00, 0x1B, 0x00, 0x00, 0x02, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x04, 0x05, 0x07, 0x06, 0x01, 0xFF, 0xC4, + 0x00, 0x47, 0x10, 0x00, 0x00, 0x05, 0x02, 0x02, 0x05, 0x05, 0x0C, 0x06, 0x09, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11, 0x12, 0x21, 0x06, 0x13, 0x31, 0x41, + 0x81, 0x14, 0x15, 0x16, 0x42, 0x51, 0x22, 0x32, 0x53, 0x54, 0x61, 0x71, 0x91, 0x92, 0xA1, 0xC1, + 0xE1, 0xE2, 0x36, 0x55, 0x63, 0x93, 0xA3, 0xB3, 0x23, 0x24, 0x52, 0x62, 0x64, 0x65, 0xA2, 0xB1, + 0xD1, 0x07, 0x35, 0x43, 0x74, 0x82, 0xB2, 0xFF, 0xC4, 0x00, 0x1A, 0x01, 0x01, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x03, + 0x04, 0x06, 0x01, 0xFF, 0xC4, 0x00, 0x39, 0x11, 0x01, 0x00, 0x00, 0x03, 0x04, 0x06, 0x07, 0x05, + 0x08, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x11, 0x05, 0x12, 0x51, 0xC1, + 0x03, 0x04, 0x13, 0x31, 0x42, 0x81, 0x14, 0x15, 0x21, 0x43, 0x52, 0xA1, 0xE1, 0x06, 0x32, 0x33, + 0x61, 0x62, 0x16, 0x22, 0x41, 0x63, 0x71, 0x82, 0xA2, 0xE2, 0x23, 0x24, 0xD1, 0x91, 0xFF, 0xDA, + 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xF4, 0x00, 0x00, 0x0A, + 0x7F, 0xAB, 0xC4, 0x49, 0xB4, 0xF8, 0x39, 0xE4, 0xD9, 0x21, 0x22, 0x43, 0x60, 0x01, 0x5E, 0x66, + 0xC4, 0x71, 0xF7, 0x0B, 0xD6, 0x2F, 0x79, 0xCB, 0x34, 0x5B, 0x5F, 0x83, 0x9E, 0x4A, 0xC2, 0xF2, + 0x20, 0x00, 0x97, 0xFA, 0xBC, 0x44, 0x9B, 0x4F, 0x74, 0x9C, 0xF2, 0x67, 0x21, 0x22, 0x43, 0x60, + 0x00, 0x89, 0x7D, 0xEA, 0x3C, 0xE6, 0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xDE, 0x9B, 0xD9, 0xFE, 0xF7, + 0xF6, 0xE6, 0xAA, 0x2F, 0x3D, 0x30, 0x00, 0xA7, 0xFA, 0xBC, 0x44, 0x9B, 0x4F, 0x83, 0x9E, 0x4C, + 0xE4, 0x24, 0x48, 0x6C, 0x00, 0x2B, 0xCC, 0xD8, 0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66, + 0x8B, 0x6B, 0xF0, 0x73, 0xC9, 0x58, 0x5E, 0x44, 0x00, 0x3A, 0xA8, 0xF2, 0x4E, 0x80, 0x01, 0x4F, + 0xF5, 0x78, 0x89, 0x36, 0x9F, 0x07, 0x3C, 0x9B, 0x24, 0x24, 0x48, 0x6C, 0x00, 0x2B, 0xCC, 0xD8, + 0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66, 0x8B, 0x6B, 0xF0, 0x73, 0xC9, 0x58, 0x5E, 0x44, + 0x00, 0x12, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xEE, 0x93, 0x9E, 0x4C, 0xE4, 0x24, 0x48, 0x6C, + 0x00, 0x11, 0x2F, 0xBD, 0x47, 0x9C, 0xC5, 0xEB, 0x17, 0xBC, 0xE5, 0x9B, 0xD3, 0x7B, 0x3F, 0xDE, + 0xFE, 0xDC, 0xD5, 0x45, 0xE7, 0xA6, 0x00, 0x14, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xF0, 0x73, + 0xC9, 0x9C, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79, 0x9B, 0x11, 0xC7, 0xDC, 0x2F, 0x58, 0xBD, 0xE7, + 0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B, 0xC8, 0x80, 0x07, 0x55, 0x1E, 0x49, 0xD0, 0x00, + 0x29, 0xFE, 0xAF, 0x11, 0x26, 0xD3, 0xE0, 0xE7, 0x93, 0x64, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79, + 0x9B, 0x11, 0xC7, 0xDC, 0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B, + 0xC8, 0x80, 0x02, 0x5F, 0xEA, 0xF1, 0x12, 0x6D, 0x3D, 0xD2, 0x73, 0xC9, 0x9C, 0x84, 0x89, 0x0D, + 0x80, 0x02, 0x25, 0xF7, 0xA8, 0xF3, 0x98, 0xBD, 0x62, 0xF7, 0x9C, 0xB3, 0x7A, 0x6F, 0x67, 0xFB, + 0xDF, 0xDB, 0x9A, 0xA8, 0xBC, 0xF4, 0xC0, 0x02, 0x9F, 0xEA, 0xF1, 0x12, 0x6D, 0x3E, 0x0E, 0x79, + 0x33, 0x90, 0x91, 0x21, 0xB0, 0x00, 0xAF, 0x33, 0x62, 0x38, 0xFB, 0x85, 0xEB, 0x17, 0xBC, 0xE5, + 0x9A, 0x2D, 0xAF, 0xC1, 0xCF, 0x25, 0x61, 0x79, 0x10, 0x00, 0xEA, 0xA3, 0xC9, 0x3A, 0x00, 0x05, + 0x3F, 0xD5, 0xE2, 0x24, 0xDA, 0x7C, 0x1C, 0xF2, 0x6C, 0x90, 0x91, 0x21, 0xB0, 0x00, 0xAF, 0x33, + 0x62, 0x38, 0xFB, 0x85, 0xEB, 0x17, 0xBC, 0xE5, 0x9A, 0x2D, 0xAF, 0xC1, 0xCF, 0x25, 0x61, 0x79, + 0x10, 0x00, 0x4B, 0xFD, 0x5E, 0x22, 0x4D, 0xA7, 0xBA, 0x4E, 0x79, 0x33, 0x90, 0x91, 0x21, 0xB0, + 0x00, 0x44, 0xBE, 0xF5, 0x1E, 0x73, 0x17, 0xAC, 0x5E, 0xF3, 0x96, 0x6F, 0x4D, 0xEC, 0xFF, 0x00, + 0x7B, 0xFB, 0x73, 0x55, 0x17, 0x9E, 0x98, 0x00, 0x53, 0xFD, 0x5E, 0x22, 0x4D, 0xA7, 0xC1, 0xCF, + 0x26, 0x72, 0x12, 0x24, 0x36, 0x00, 0x15, 0xE6, 0x6C, 0x47, 0x1F, 0x70, 0xBD, 0x62, 0xF7, 0x9C, + 0xB3, 0x45, 0xB5, 0xF8, 0x39, 0xE4, 0xAC, 0x2F, 0x22, 0x00, 0x1D, 0x54, 0x79, 0x27, 0x43, 0x0A, + 0xAF, 0xA4, 0xCC, 0xD2, 0xA6, 0x9C, 0x67, 0x23, 0xB8, 0xE2, 0x89, 0x24, 0xAC, 0x49, 0x32, 0x22, + 0xCC, 0x06, 0x7B, 0x9A, 0x6B, 0x1D, 0x76, 0xB4, 0x47, 0x4A, 0xDF, 0xBC, 0x43, 0x8F, 0x5B, 0xD5, + 0xA3, 0xA7, 0xBB, 0x48, 0xD2, 0x95, 0x65, 0x2C, 0xD4, 0x43, 0xA6, 0x31, 0xFC, 0x51, 0xDF, 0x58, + 0x87, 0x17, 0x56, 0x4F, 0xE2, 0x65, 0x7C, 0x74, 0xC6, 0x3F, 0x8A, 0x3B, 0xEB, 0x10, 0x75, 0x64, + 0xFE, 0x22, 0xF9, 0x4F, 0xE9, 0x6B, 0x0E, 0x92, 0x6D, 0x15, 0xC2, 0xB7, 0xEF, 0x10, 0xA3, 0xA8, + 0x68, 0x63, 0xAA, 0xDE, 0xAC, 0x6B, 0x5A, 0x79, 0x55, 0xC3, 0xAE, 0xEA, 0xF1, 0xD6, 0x2E, 0xD2, + 0x34, 0xA5, 0x72, 0x2B, 0xA4, 0xEC, 0xF8, 0xB3, 0x9E, 0xB1, 0x0A, 0x1B, 0x68, 0x60, 0xE0, 0xEA, + 0xC9, 0xFC, 0x43, 0xA4, 0xEC, 0xF8, 0xB3, 0x9E, 0xB1, 0x06, 0xDA, 0x18, 0x1D, 0x59, 0x3F, 0x89, + 0x07, 0x34, 0x91, 0x95, 0xDA, 0xD1, 0xD6, 0x56, 0xF2, 0x90, 0xE3, 0xD6, 0xE4, 0xDB, 0xDD, 0xA7, + 0x65, 0x2A, 0xCA, 0x5B, 0x3A, 0x68, 0x71, 0x21, 0xD2, 0x16, 0xBC, 0x02, 0xFD, 0x24, 0x38, 0xBA, + 0x1C, 0xD8, 0xB2, 0xE8, 0x13, 0x62, 0x3A, 0x42, 0xD7, 0x80, 0x5F, 0xA4, 0x83, 0xA1, 0xCD, 0x89, + 0xD0, 0x26, 0xC4, 0xB7, 0xAB, 0xAD, 0x3A, 0x49, 0x22, 0x65, 0x65, 0x6F, 0x29, 0x0A, 0x3A, 0x87, + 0xFA, 0xB7, 0xAB, 0xDB, 0x5A, 0x79, 0x55, 0x56, 0xCD, 0xFF, 0x00, 0x4E, 0xFD, 0xEE, 0xDA, 0xD3, + 0xCA, 0xBF, 0xF4, 0x9E, 0x77, 0x6F, 0xC1, 0x2B, 0xD2, 0x28, 0xF4, 0xC9, 0x70, 0x55, 0xE9, 0xF2, + 0xE0, 0x39, 0xDD, 0xBF, 0x04, 0xAF, 0x48, 0x74, 0xC9, 0x70, 0x3A, 0x7C, 0xB8, 0x20, 0xE5, 0x51, + 0x0B, 0xB5, 0x9B, 0x51, 0x5B, 0xCA, 0x38, 0xF5, 0xB9, 0xF6, 0xF7, 0x69, 0xD9, 0x4A, 0xB2, 0x96, + 0xD1, 0x96, 0x1C, 0x28, 0x73, 0x8A, 0x3C, 0x1A, 0xBD, 0x23, 0x8B, 0x63, 0x1C, 0x59, 0x75, 0x9C, + 0x9E, 0x11, 0xCE, 0x28, 0xF0, 0x6A, 0xF4, 0x86, 0xC6, 0x38, 0x9D, 0x67, 0x27, 0x84, 0xA7, 0xE6, + 0x25, 0xD2, 0x4D, 0x90, 0x65, 0x61, 0x47, 0x50, 0xD3, 0x43, 0x55, 0xBD, 0x58, 0x56, 0xB4, 0xF2, + 0xAB, 0x87, 0x5D, 0xD6, 0x21, 0xAC, 0x5D, 0xA4, 0x29, 0x4A, 0xE4, 0x5A, 0x5D, 0x25, 0x28, 0x8A, + 0xDB, 0x45, 0x4D, 0x16, 0xBF, 0x2E, 0x92, 0x78, 0x49, 0x08, 0x6F, 0x4F, 0x8C, 0x94, 0x30, 0x50, + 0x60, 0xEA, 0xA3, 0xC9, 0x3A, 0x18, 0x6C, 0x9B, 0x85, 0xA5, 0x55, 0x63, 0x65, 0xC4, 0xB4, 0xE9, + 0x52, 0x97, 0x81, 0xC5, 0x2B, 0x09, 0x20, 0xEE, 0x9B, 0x19, 0x99, 0xEC, 0xB7, 0x68, 0x08, 0xD3, + 0xA4, 0xB4, 0xFE, 0x93, 0xE8, 0xFA, 0x1C, 0x92, 0xC4, 0xBA, 0x8B, 0x68, 0x7C, 0xA5, 0x3E, 0xC9, + 0x92, 0x89, 0x57, 0x4A, 0xB0, 0x16, 0x22, 0xC8, 0xCC, 0x8A, 0xE0, 0x30, 0xE5, 0x4B, 0x4D, 0x5A, + 0x4C, 0x08, 0x4E, 0xD7, 0x64, 0xCE, 0x69, 0xD9, 0x48, 0x4A, 0xD0, 0xEB, 0x1A, 0xB2, 0x41, 0x19, + 0xDB, 0x15, 0xEE, 0x79, 0xE6, 0x60, 0x17, 0xA4, 0x95, 0x99, 0xBC, 0xB6, 0xA1, 0x4A, 0x49, 0xA5, + 0xA8, 0x08, 0x70, 0xDA, 0x44, 0x74, 0xA0, 0x89, 0x29, 0x24, 0x2B, 0x23, 0x2C, 0xB6, 0xE5, 0x7E, + 0x20, 0x3D, 0x0D, 0x6E, 0x94, 0xFA, 0x34, 0x35, 0x50, 0x8D, 0x85, 0xA5, 0x10, 0x18, 0x61, 0xF4, + 0x9D, 0xB6, 0xB8, 0x66, 0xAD, 0x6E, 0x7D, 0x84, 0x4A, 0xB8, 0x06, 0x9B, 0x69, 0x98, 0x5A, 0x38, + 0xDA, 0x52, 0x5A, 0xF8, 0x4C, 0x44, 0x90, 0x5B, 0x6E, 0xA6, 0xD6, 0x78, 0x17, 0x97, 0x90, 0xC9, + 0x07, 0xC4, 0xC0, 0x78, 0x5A, 0xE7, 0xFB, 0xED, 0x43, 0xFE, 0xCB, 0x9F, 0xFD, 0x18, 0x0A, 0x20, + 0x3D, 0x66, 0x88, 0x2A, 0x42, 0x28, 0x55, 0xD5, 0x43, 0x92, 0xDC, 0x57, 0xCB, 0x93, 0xE1, 0x75, + 0xC7, 0x09, 0x09, 0x4F, 0x74, 0xAB, 0xDC, 0xCF, 0x22, 0xCA, 0xE5, 0xC4, 0x05, 0x6D, 0x23, 0xA9, + 0x13, 0x55, 0x98, 0x72, 0xA9, 0xF2, 0x9B, 0x54, 0xC6, 0xA2, 0xA1, 0x2F, 0xC8, 0x8E, 0x45, 0x65, + 0x3B, 0x63, 0x25, 0x1D, 0xED, 0x63, 0xB9, 0x1E, 0xD0, 0x1A, 0xF5, 0xA2, 0x3A, 0xA7, 0xFA, 0x90, + 0xCD, 0x3A, 0x6B, 0xA6, 0xA8, 0x8D, 0x38, 0x93, 0x43, 0x6A, 0xD8, 0x57, 0x6D, 0x2A, 0x32, 0xE2, + 0x65, 0x6E, 0x20, 0x31, 0xF9, 0xDA, 0x65, 0x66, 0xB5, 0x1A, 0x9F, 0x38, 0xCB, 0x91, 0xAE, 0x6A, + 0x0B, 0x93, 0xE1, 0x24, 0x93, 0x65, 0x8B, 0x0E, 0x12, 0xB6, 0x65, 0x91, 0x99, 0x00, 0xD3, 0xA9, + 0xC8, 0x72, 0xA1, 0x07, 0x49, 0x99, 0x95, 0x85, 0x6D, 0xD3, 0xDF, 0x41, 0x45, 0x2C, 0x04, 0x5A, + 0x92, 0xD6, 0x1A, 0x6C, 0x56, 0x2D, 0x96, 0x22, 0x20, 0x1E, 0x28, 0x00, 0x03, 0xDA, 0x68, 0xCB, + 0x8E, 0x15, 0x1A, 0x22, 0x29, 0x8F, 0xC7, 0x69, 0xF3, 0x9B, 0xFA, 0xE9, 0x38, 0xB4, 0xA5, 0x4A, + 0x6F, 0x2B, 0x77, 0xDB, 0x53, 0x6D, 0xC5, 0xBC, 0x05, 0xEA, 0x04, 0x15, 0x40, 0xAF, 0xD7, 0x25, + 0xC2, 0x8E, 0x6B, 0x26, 0x1F, 0x4B, 0x0D, 0xB6, 0x82, 0xCB, 0x0A, 0x9C, 0x23, 0x59, 0x17, 0x99, + 0x24, 0x03, 0xC8, 0xD7, 0xA1, 0x73, 0x7E, 0x91, 0x4C, 0x8A, 0x45, 0x64, 0xA1, 0xD3, 0x34, 0x97, + 0x62, 0x4F, 0x32, 0xF6, 0x19, 0x0E, 0x8D, 0x53, 0xE3, 0x4A, 0xF9, 0x36, 0xE5, 0x61, 0xE9, 0x5A, + 0x1D, 0x54, 0x79, 0x27, 0x43, 0xC4, 0xE9, 0x1D, 0x49, 0xEA, 0x75, 0x7A, 0x66, 0xA5, 0x2D, 0xAB, + 0x94, 0xC3, 0xE4, 0xEB, 0xC6, 0x46, 0x76, 0x4A, 0xB6, 0x99, 0x58, 0xF6, 0xE4, 0x03, 0x06, 0x93, + 0x52, 0x7A, 0x91, 0x52, 0x6A, 0x74, 0x74, 0xB6, 0xA7, 0x5A, 0xBE, 0x12, 0x70, 0x8C, 0xD3, 0x99, + 0x19, 0x67, 0x63, 0x2E, 0xD0, 0x0C, 0x9B, 0x53, 0x4C, 0xB6, 0x92, 0x86, 0xE9, 0xD0, 0xA2, 0xA9, + 0x2A, 0x25, 0x6B, 0x23, 0xA5, 0x49, 0x57, 0x9A, 0xE6, 0xA3, 0xCB, 0xFC, 0x00, 0xB3, 0x23, 0x49, + 0x1F, 0x94, 0x93, 0x39, 0x30, 0xA0, 0xBA, 0xF2, 0xB0, 0x6B, 0x1F, 0x36, 0x8C, 0x9C, 0x70, 0x92, + 0x64, 0x76, 0x33, 0x23, 0xDF, 0x62, 0x23, 0xB1, 0x16, 0x40, 0x23, 0xD2, 0x29, 0x87, 0x53, 0x9B, + 0x3D, 0x6D, 0xB2, 0xB7, 0x26, 0xB4, 0xA6, 0x5D, 0x42, 0x92, 0x78, 0x70, 0x99, 0x11, 0x58, 0xB3, + 0xBE, 0xE2, 0xDE, 0x02, 0x71, 0xF4, 0x9E, 0x7C, 0x7A, 0x84, 0x39, 0x8D, 0xA5, 0x92, 0x72, 0x24, + 0x62, 0x8A, 0x94, 0xD9, 0x58, 0x56, 0x82, 0x23, 0xB6, 0x2C, 0xF3, 0x3C, 0xEF, 0xBB, 0x61, 0x00, + 0xCA, 0x97, 0x21, 0x72, 0xE5, 0xBD, 0x25, 0xC2, 0x49, 0x2D, 0xE7, 0x14, 0xE2, 0x89, 0x3B, 0x08, + 0xCC, 0xEF, 0x90, 0x05, 0x00, 0xB9, 0x1A, 0xA4, 0xF4, 0x6A, 0x6C, 0xD8, 0x28, 0x4B, 0x66, 0xD4, + 0xCC, 0x1A, 0xC3, 0x51, 0x1E, 0x22, 0xC0, 0x77, 0x2B, 0x66, 0x01, 0x31, 0x1F, 0x28, 0xD2, 0x50, + 0xF2, 0x98, 0x69, 0xF2, 0x4D, 0xFF, 0x00, 0x46, 0xF1, 0x19, 0xA5, 0x59, 0x5B, 0x3B, 0x19, 0x18, + 0x0B, 0xF5, 0x5A, 0xFC, 0x8A, 0x9C, 0xF6, 0xA7, 0x29, 0x88, 0xF1, 0xE5, 0x36, 0xA2, 0x5E, 0xB5, + 0x84, 0x99, 0x1A, 0x8C, 0xAD, 0x63, 0x3B, 0x99, 0xEC, 0xC2, 0x56, 0x00, 0x4E, 0xAF, 0x3F, 0x34, + 0x89, 0x47, 0x16, 0x23, 0x12, 0x35, 0x84, 0xEA, 0xA4, 0x30, 0xDE, 0x07, 0x14, 0xA2, 0xDE, 0x67, + 0x7E, 0xD3, 0xBE, 0x56, 0xCC, 0x04, 0xAA, 0x1A, 0x45, 0x2E, 0x7C, 0x57, 0x58, 0x53, 0x31, 0x98, + 0x27, 0xD4, 0x4B, 0x7D, 0x6C, 0x37, 0x85, 0x4F, 0x99, 0x6C, 0x35, 0x67, 0x9E, 0x79, 0xEE, 0xCC, + 0x06, 0x40, 0x00, 0x06, 0x95, 0x32, 0xB2, 0xBA, 0x6B, 0x64, 0x4D, 0x43, 0x86, 0xEB, 0x89, 0x5E, + 0xB1, 0x0F, 0x3A, 0xD6, 0x25, 0xB6, 0x76, 0x2D, 0x87, 0x7D, 0xD6, 0xB9, 0x5C, 0x8F, 0x30, 0x10, + 0x7A, 0xB1, 0x25, 0xFA, 0x62, 0xA0, 0xB9, 0x80, 0xD0, 0xB9, 0x27, 0x25, 0x6E, 0x67, 0x8D, 0x6B, + 0x32, 0xB6, 0x67, 0x7B, 0x7B, 0x00, 0x15, 0x1A, 0x9B, 0xD5, 0x69, 0x8D, 0xC8, 0x90, 0x86, 0xD2, + 0xEA, 0x5B, 0x4B, 0x66, 0x68, 0x23, 0x2C, 0x76, 0xDE, 0x77, 0x33, 0xCC, 0x74, 0x6A, 0x9F, 0x1A, + 0x57, 0xC9, 0xB7, 0x16, 0x3D, 0x2B, 0x43, 0xAA, 0x8F, 0x24, 0xE8, 0x79, 0xEE, 0x4D, 0x06, 0x4E, + 0x97, 0x4F, 0x3A, 0x93, 0x24, 0xEC, 0x76, 0x69, 0xE6, 0xE9, 0x91, 0xEE, 0xB1, 0xA7, 0x32, 0xF2, + 0xDA, 0xE0, 0x15, 0x0F, 0x46, 0x22, 0x25, 0xB8, 0xCC, 0x4C, 0x67, 0x12, 0x9A, 0x96, 0xF9, 0x3A, + 0xB4, 0x9D, 0x8D, 0xC4, 0x21, 0x37, 0x49, 0x5F, 0xB0, 0xF2, 0xE0, 0x60, 0x30, 0xEA, 0x4C, 0xC5, + 0x99, 0xA3, 0x6D, 0x55, 0x98, 0x88, 0xDC, 0x47, 0x53, 0x2C, 0xE3, 0x2D, 0x0D, 0x19, 0xE1, 0x51, + 0x61, 0xC4, 0x47, 0x63, 0x33, 0xB1, 0xEE, 0x01, 0x82, 0x03, 0xD2, 0x56, 0xCA, 0x05, 0x11, 0xE5, + 0xD2, 0x13, 0x4E, 0x65, 0xF7, 0x50, 0xC1, 0x13, 0xD2, 0x16, 0xA5, 0x63, 0xD6, 0xA9, 0x37, 0xBA, + 0x73, 0xB1, 0x11, 0x5C, 0xB2, 0xB6, 0xE0, 0x16, 0xE1, 0x51, 0xA2, 0x39, 0xA2, 0x26, 0x4A, 0x65, + 0x27, 0x3D, 0xF8, 0xEF, 0x4C, 0x43, 0xA7, 0xDF, 0x21, 0x2D, 0xA9, 0x25, 0x84, 0x8B, 0xCA, 0x57, + 0x01, 0xA7, 0x40, 0xD1, 0xEA, 0x74, 0xEA, 0x35, 0x16, 0x4B, 0xB1, 0x9B, 0x52, 0xCD, 0xC5, 0x9C, + 0x83, 0x33, 0xB6, 0x34, 0xF7, 0x64, 0x57, 0xED, 0xEE, 0xB0, 0x17, 0x10, 0x15, 0x91, 0x4E, 0x88, + 0xCC, 0x0A, 0x93, 0xAD, 0x44, 0xA6, 0x1B, 0x8D, 0xD5, 0x9D, 0x61, 0x27, 0x39, 0x78, 0x10, 0x96, + 0xC8, 0xAE, 0x49, 0x23, 0xB9, 0x67, 0xF1, 0x00, 0x8A, 0x5C, 0x02, 0x90, 0x8A, 0xE3, 0xAD, 0xD3, + 0x29, 0xF3, 0x25, 0xB1, 0xC9, 0xF5, 0x2C, 0xB0, 0x66, 0xB6, 0x73, 0xBE, 0x2C, 0x36, 0x57, 0x66, + 0x67, 0x9E, 0xD2, 0x01, 0x17, 0x68, 0xB0, 0xDD, 0xD3, 0x1A, 0x64, 0x24, 0xB2, 0xDB, 0x04, 0xB6, + 0x10, 0xF4, 0xC6, 0x09, 0x46, 0xA4, 0xB6, 0xB2, 0x23, 0x52, 0x93, 0xB4, 0xEC, 0x56, 0x22, 0x2D, + 0xBB, 0xC0, 0x64, 0xE9, 0x4C, 0x56, 0x19, 0x9B, 0x1A, 0x4C, 0x46, 0x92, 0xCC, 0x79, 0xB1, 0x9B, + 0x7D, 0x2D, 0xA7, 0x63, 0x66, 0x65, 0x63, 0x4D, 0xF7, 0xE6, 0x5E, 0xD0, 0x18, 0xA0, 0x3D, 0xAE, + 0x95, 0x45, 0x8F, 0x4E, 0x7A, 0x6B, 0x51, 0xA1, 0x51, 0x52, 0xCA, 0x52, 0x44, 0x92, 0x37, 0x3F, + 0x58, 0x2C, 0x49, 0x2C, 0xC9, 0x38, 0xB6, 0xDC, 0xEE, 0x59, 0x6C, 0x00, 0x4B, 0xA5, 0x53, 0x8A, + 0x75, 0x4A, 0x88, 0x88, 0x48, 0x4A, 0xA1, 0x42, 0xD7, 0x22, 0x51, 0x29, 0x5A, 0xC5, 0x38, 0x49, + 0x4A, 0x8E, 0xF9, 0xDA, 0xC7, 0x7B, 0x5A, 0xC0, 0x28, 0xBD, 0x4F, 0x88, 0x9A, 0x8E, 0x8C, 0x36, + 0x4C, 0x20, 0x91, 0x29, 0xB6, 0x4D, 0xE2, 0xFD, 0xB3, 0x35, 0xD8, 0xEF, 0xC0, 0x06, 0x8D, 0x56, + 0x89, 0x01, 0x54, 0x29, 0xBC, 0x9E, 0x3A, 0x1B, 0x96, 0xDC, 0x99, 0x0B, 0x6D, 0x49, 0xCA, 0xE8, + 0x6D, 0xC3, 0x23, 0x4F, 0x04, 0x9D, 0xFF, 0x00, 0xF2, 0x01, 0x95, 0xB8, 0x74, 0xEA, 0x52, 0x6A, + 0xEF, 0x35, 0x4C, 0x8A, 0xEE, 0xA6, 0x4B, 0x2D, 0xA1, 0x0E, 0x11, 0xD9, 0x29, 0x53, 0x69, 0x33, + 0xB5, 0x8C, 0xB7, 0x80, 0xF3, 0x9A, 0x41, 0x02, 0x3C, 0x2A, 0x84, 0x65, 0xC3, 0x4A, 0x91, 0x1E, + 0x5C, 0x66, 0xE4, 0xA1, 0xB5, 0x2A, 0xE6, 0x82, 0x51, 0x1F, 0x73, 0x7D, 0xFB, 0x07, 0x46, 0xA9, + 0xF1, 0xA5, 0x7C, 0x9B, 0x72, 0x88, 0xF4, 0xAD, 0x0E, 0xAA, 0x3C, 0x93, 0xA1, 0xE4, 0xEA, 0xF2, + 0xD7, 0x06, 0xB9, 0x51, 0x57, 0x26, 0x79, 0xE4, 0xCA, 0xA7, 0xAA, 0x32, 0x4D, 0x09, 0xC8, 0x8D, + 0x56, 0xCF, 0xD8, 0x01, 0x68, 0xD2, 0x89, 0x4D, 0xC4, 0xA5, 0xA4, 0xE9, 0xCE, 0xAD, 0xE8, 0x86, + 0x64, 0xE9, 0xA8, 0x8C, 0x89, 0xE4, 0xE1, 0xC1, 0xD9, 0x7B, 0xE1, 0xB6, 0x7E, 0x41, 0xF2, 0x33, + 0x42, 0x1B, 0xE2, 0x32, 0xEA, 0x52, 0xC9, 0xCA, 0x63, 0x54, 0xDA, 0x74, 0x09, 0x2C, 0xC5, 0x43, + 0xA6, 0xFA, 0x8D, 0xD3, 0xC4, 0xA5, 0xAC, 0xCA, 0xDB, 0x88, 0xAC, 0x44, 0x59, 0x0C, 0x76, 0x92, + 0x62, 0x51, 0x91, 0xC9, 0x64, 0x78, 0x07, 0x7D, 0x43, 0x0D, 0xA4, 0x98, 0x94, 0x6E, 0x4D, 0x9F, + 0x1E, 0xA2, 0xC9, 0x3D, 0x3E, 0x97, 0x28, 0xEA, 0x09, 0x63, 0x55, 0xAC, 0x42, 0xEC, 0x87, 0x0C, + 0x8A, 0xC9, 0x5A, 0x8A, 0xD7, 0xB9, 0x65, 0xB0, 0xF3, 0xB0, 0xCA, 0x58, 0xC2, 0x6D, 0xDD, 0xAF, + 0x91, 0x9A, 0x12, 0xEF, 0x68, 0x33, 0xA5, 0x92, 0xA3, 0xD4, 0x21, 0x6A, 0x63, 0xC9, 0xE6, 0xD8, + 0xEC, 0x25, 0x95, 0x46, 0x3F, 0xF9, 0x2C, 0x93, 0x2B, 0x9E, 0x5B, 0x73, 0x2F, 0x40, 0xCA, 0x91, + 0x63, 0xB4, 0x93, 0x15, 0x04, 0xD5, 0xD4, 0xDA, 0x28, 0xAD, 0xB5, 0x1A, 0x49, 0x22, 0x9C, 0xEA, + 0x96, 0xBF, 0xB4, 0x49, 0xB8, 0x4B, 0x22, 0xF6, 0x05, 0x22, 0x6D, 0x24, 0xC4, 0xE7, 0x2A, 0xD0, + 0xE5, 0xC2, 0x99, 0x16, 0x74, 0x19, 0xD8, 0x1F, 0xA8, 0x2E, 0x6A, 0x4D, 0x93, 0x24, 0x99, 0x5C, + 0xAC, 0x44, 0x77, 0x23, 0xED, 0x31, 0xF2, 0x31, 0xA6, 0xF2, 0xFC, 0xB8, 0xA9, 0xA2, 0x6B, 0x31, + 0xA9, 0xD5, 0x68, 0x31, 0x22, 0xCA, 0xD5, 0x4D, 0xD4, 0xEA, 0xCD, 0xD3, 0x23, 0x52, 0x30, 0x1D, + 0xCE, 0xF6, 0x22, 0xBD, 0xFC, 0x83, 0x1B, 0xD2, 0xE2, 0xFB, 0x7E, 0x5C, 0x5A, 0x48, 0xD2, 0x6C, + 0x3F, 0xAE, 0x14, 0x39, 0x25, 0x53, 0x38, 0x27, 0x11, 0x4F, 0x15, 0xAC, 0xA3, 0xCA, 0xCE, 0x1E, + 0x57, 0xC5, 0x90, 0x5E, 0x97, 0x12, 0xFC, 0xB8, 0xB3, 0x6A, 0xF5, 0x77, 0xEA, 0xD4, 0xA8, 0x2C, + 0xCB, 0x6D, 0xF5, 0xCB, 0x8C, 0xA5, 0xDD, 0xE5, 0x16, 0x4B, 0x4A, 0x8E, 0xFE, 0x92, 0xB1, 0x10, + 0xCA, 0x5F, 0xBD, 0xBB, 0xB5, 0x94, 0xBF, 0x7B, 0xDD, 0xED, 0x62, 0xEA, 0x9C, 0xFD, 0x85, 0x7A, + 0x06, 0x57, 0x66, 0xC1, 0x95, 0xC9, 0xB0, 0x69, 0x69, 0x14, 0xE3, 0xAC, 0x56, 0xE4, 0x4F, 0x6D, + 0x87, 0x1B, 0x4B, 0xB8, 0x6C, 0x95, 0x15, 0xCC, 0xAC, 0x92, 0x2F, 0x70, 0x5D, 0x9B, 0x02, 0xE4, + 0xD8, 0x34, 0xE4, 0x69, 0x1B, 0x6E, 0x72, 0xA9, 0x69, 0x80, 0xEA, 0x6A, 0x72, 0xE3, 0x72, 0x67, + 0x5C, 0x35, 0xFE, 0x8E, 0xD6, 0x22, 0x35, 0x12, 0x6D, 0x7B, 0x99, 0x11, 0x6F, 0xCB, 0xFB, 0xFC, + 0x8C, 0x29, 0xBD, 0xF6, 0xE4, 0xD8, 0x21, 0x12, 0xB9, 0x10, 0x9B, 0xA7, 0x39, 0x3A, 0x9F, 0x21, + 0xE9, 0x54, 0xD2, 0xB3, 0x2A, 0x6D, 0xDC, 0x29, 0x59, 0x11, 0xDD, 0x24, 0xA2, 0xB1, 0xEC, 0xF2, + 0x0C, 0x6B, 0x03, 0x67, 0x3E, 0x00, 0xB4, 0x95, 0xC3, 0x28, 0x2B, 0x54, 0x75, 0x1B, 0xCC, 0x4A, + 0x75, 0xF7, 0x72, 0xEE, 0x5C, 0x27, 0x0F, 0xBA, 0x49, 0x17, 0x94, 0x8C, 0xCB, 0x88, 0x56, 0x06, + 0xCE, 0x7C, 0x16, 0x6A, 0x1A, 0x49, 0x02, 0xA2, 0x75, 0x04, 0xCB, 0x85, 0x2C, 0x9A, 0x94, 0xF3, + 0x6E, 0xA4, 0x9B, 0x5A, 0x52, 0x65, 0x85, 0x04, 0x9B, 0x19, 0x99, 0x1F, 0x60, 0xCA, 0x58, 0x46, + 0x6D, 0xDD, 0xAC, 0x63, 0x2C, 0x65, 0xDF, 0x06, 0x25, 0x56, 0xA4, 0xBA, 0xAD, 0x41, 0x0F, 0x6A, + 0x49, 0x96, 0x9B, 0x42, 0x5A, 0x69, 0xA2, 0x3B, 0x93, 0x68, 0x49, 0x64, 0x57, 0xDE, 0x3A, 0xB5, + 0x59, 0x26, 0x86, 0x9A, 0x58, 0xC6, 0x0C, 0x26, 0x8F, 0x61, 0x03, 0xD1, 0x34, 0xBA, 0xA8, 0xF2, + 0x4E, 0x80, 0x01, 0x4F, 0xF5, 0x78, 0x89, 0x36, 0x9F, 0x07, 0x3C, 0x9B, 0x24, 0x24, 0x48, 0x6C, + 0x00, 0x2B, 0xCC, 0xD8, 0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66, 0x8B, 0x6B, 0xF0, 0x73, + 0xC9, 0x58, 0x5E, 0x44, 0x00, 0x12, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xEE, 0x93, 0x9E, 0x4C, + 0xE4, 0x24, 0x48, 0x6C, 0x00, 0x11, 0x2F, 0xBD, 0x47, 0x9C, 0xC5, 0xEB, 0x17, 0xBC, 0xE5, 0x9B, + 0xD3, 0x7B, 0x3F, 0xDE, 0xFE, 0xDC, 0xD5, 0x45, 0xE7, 0xA6, 0x00, 0x14, 0xFF, 0x00, 0x57, 0x88, + 0x93, 0x69, 0xF0, 0x73, 0xC9, 0x9C, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79, 0x9B, 0x11, 0xC7, 0xDC, + 0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B, 0xC8, 0x80, 0x07, 0xAC, + 0xE9, 0xA2, 0x7E, 0xAF, 0x3F, 0xBF, 0xF9, 0x44, 0x8E, 0xAC, 0x8F, 0x8F, 0xCB, 0xD5, 0xB2, 0xF8, + 0xE9, 0xA2, 0x7E, 0xAF, 0x3F, 0xBF, 0xF9, 0x43, 0xAB, 0x23, 0xE3, 0xF2, 0xF5, 0x2F, 0x96, 0xEE, + 0x98, 0x92, 0xAD, 0x6A, 0x7E, 0xCF, 0xB7, 0xF9, 0x44, 0x9B, 0x4E, 0xCC, 0xF7, 0x3E, 0xFE, 0x3F, + 0x87, 0xE9, 0xF3, 0x6C, 0x92, 0x72, 0xFA, 0x5D, 0xFC, 0xBF, 0xF1, 0xFE, 0x51, 0x23, 0xAA, 0xFE, + 0xBF, 0x2F, 0x56, 0xCB, 0xE3, 0xA5, 0xDF, 0xCB, 0xFF, 0x00, 0x1F, 0xE5, 0x0E, 0xAB, 0xFA, 0xFC, + 0xBD, 0x4B, 0xE6, 0x35, 0xA4, 0x05, 0x34, 0x8E, 0xF1, 0x4D, 0xBC, 0x1F, 0x6B, 0x7B, 0xDF, 0x81, + 0x76, 0x0A, 0xF6, 0x66, 0xA5, 0xB2, 0xBF, 0xF7, 0xAB, 0x5A, 0x7E, 0x1F, 0xAF, 0xCD, 0xAE, 0x7B, + 0x3F, 0xA7, 0xF1, 0x5D, 0xBB, 0xF2, 0xAE, 0xFE, 0x70, 0xC0, 0xCE, 0x71, 0x4F, 0x82, 0x3F, 0x5B, + 0xE0, 0x2B, 0x6C, 0x63, 0x8B, 0x5F, 0xD9, 0xBF, 0xCD, 0xFE, 0x3F, 0xD8, 0x73, 0x8A, 0x7C, 0x11, + 0xFA, 0xDF, 0x00, 0xD8, 0xC7, 0x13, 0xEC, 0xDF, 0xE6, 0xFF, 0x00, 0x1F, 0xEC, 0xA9, 0x3A, 0xAE, + 0x4D, 0x6A, 0xED, 0x1F, 0x15, 0xEF, 0xD7, 0xB7, 0x67, 0x90, 0x4D, 0xB4, 0x35, 0x5B, 0xF7, 0x7B, + 0x71, 0xC9, 0xA3, 0x4F, 0x62, 0xF4, 0x7A, 0x7F, 0x92, 0xB5, 0xF9, 0x7A, 0xAA, 0x73, 0xEF, 0xF0, + 0xBF, 0x89, 0xF0, 0x13, 0x3A, 0x17, 0xD5, 0xE5, 0xEA, 0xE7, 0xEA, 0xFF, 0x00, 0xAB, 0xCB, 0xD4, + 0x73, 0xEF, 0xF0, 0xBF, 0x89, 0xF0, 0x0E, 0x85, 0xF5, 0x79, 0x7A, 0x9D, 0x5F, 0xF5, 0x79, 0x7A, + 0xAD, 0xC1, 0x7C, 0xAA, 0xA4, 0xE1, 0x1A, 0x4D, 0x9D, 0x55, 0xB7, 0xE2, 0xBD, 0xEF, 0xE6, 0xEC, + 0x14, 0xAC, 0xF9, 0x3A, 0x3D, 0xEE, 0xDA, 0xD6, 0x99, 0xBA, 0x34, 0x3A, 0xD7, 0x55, 0xD7, 0xB2, + 0xF5, 0xEE, 0x54, 0xA7, 0xFE, 0xE2, 0xB7, 0xCD, 0xC9, 0xF0, 0xA7, 0xEA, 0xFC, 0x45, 0x3D, 0xB4, + 0x70, 0x6F, 0xFB, 0x49, 0xF9, 0x5F, 0xCB, 0xFA, 0x8E, 0x6E, 0x4F, 0x85, 0x3F, 0x57, 0xE2, 0x1B, + 0x68, 0xE0, 0x7D, 0xA4, 0xFC, 0xAF, 0xE5, 0xFD, 0x59, 0xB5, 0x84, 0x14, 0x2D, 0x4D, 0x8C, 0xDC, + 0xC7, 0x8B, 0xC9, 0x6B, 0x5B, 0xFC, 0x8E, 0x1D, 0x77, 0xFC, 0xB7, 0x7F, 0x0A, 0x57, 0x27, 0x6E, + 0xA9, 0x6E, 0x6D, 0xAF, 0x7F, 0x8E, 0x94, 0xA7, 0xE3, 0xFA, 0xFC, 0x99, 0xBC, 0xB3, 0xEC, 0xFF, + 0x00, 0xAB, 0xE0, 0x27, 0xEC, 0x3E, 0x6E, 0xEE, 0xB4, 0xFA, 0x3C, 0xFD, 0x07, 0x2C, 0xFB, 0x3F, + 0xEA, 0xF8, 0x06, 0xC3, 0xE6, 0x75, 0xA7, 0xD1, 0xE7, 0xE8, 0xD2, 0xA3, 0xD2, 0xCA, 0xBA, 0x4F, + 0x5D, 0xE3, 0x8F, 0xA8, 0xC3, 0xD4, 0xC7, 0x8B, 0x15, 0xFC, 0xA5, 0x6E, 0xF7, 0xDA, 0x3B, 0xF5, + 0x2D, 0x3F, 0x45, 0xBD, 0xD9, 0x5A, 0xD3, 0xE5, 0xBA, 0xBF, 0xAE, 0x2E, 0x2D, 0x6F, 0x58, 0xE9, + 0x37, 0x7B, 0x29, 0x4A, 0xFC, 0xFF, 0x00, 0xE3, 0x4B, 0xA1, 0x69, 0xFA, 0xC0, 0xFE, 0xE3, 0xE6, + 0x1D, 0xFD, 0x67, 0x1F, 0x07, 0x9F, 0xA3, 0x86, 0xE0, 0xE8, 0x5A, 0x7E, 0xB0, 0x3F, 0xB8, 0xF9, + 0x83, 0xAC, 0xE3, 0xE0, 0xF3, 0xF4, 0x2E, 0x3C, 0xA5, 0x8C, 0x51, 0xE9, 0x1A, 0x2F, 0x13, 0x58, + 0xB1, 0x87, 0x48, 0xD1, 0x78, 0x87, 0xC3, 0x23, 0xEC, 0x13, 0x6D, 0x09, 0xA1, 0xA4, 0xBB, 0x73, + 0xB6, 0x95, 0xC9, 0x9C, 0x91, 0x84, 0x17, 0xE2, 0xD0, 0xAA, 0x93, 0x23, 0xA5, 0xF8, 0xD0, 0x9D, + 0x75, 0xA5, 0xDF, 0x0A, 0xD2, 0x59, 0x1D, 0x8E, 0xC7, 0xED, 0x21, 0x2E, 0x30, 0xA6, 0xF6, 0xD3, + 0x7A, 0x33, 0x5A, 0xFA, 0xB5, 0xFF, 0x00, 0x40, 0xF8, 0x2E, 0xD3, 0xB4, 0x7A, 0xAE, 0xDE, 0xB3, + 0x1C, 0x07, 0x93, 0x7B, 0x5A, 0xE5, 0xE7, 0x1D, 0x9A, 0xA6, 0x92, 0x59, 0x2F, 0x5E, 0x8E, 0x19, + 0xBB, 0xF5, 0x2D, 0x24, 0x92, 0x5E, 0xBD, 0x1A, 0x6E, 0xCD, 0x73, 0x98, 0xAA, 0x9E, 0x24, 0xEF, + 0xA0, 0x76, 0x6D, 0xF4, 0x78, 0xA8, 0x74, 0x9D, 0x17, 0x89, 0x95, 0xCA, 0x59, 0xF0, 0x84, 0x36, + 0x5E, 0x83, 0xEE, 0xDF, 0x47, 0x8A, 0x9D, 0x41, 0xC4, 0x3B, 0xAB, 0xC0, 0xA2, 0x55, 0xAF, 0x7B, + 0x70, 0x1C, 0x7A, 0xDC, 0x23, 0x3D, 0xDB, 0xBF, 0x3C, 0x9C, 0x1A, 0xEC, 0xF2, 0xCF, 0x76, 0xEC, + 0x6B, 0xBF, 0x25, 0x2B, 0x1F, 0x60, 0xE3, 0xD9, 0x4F, 0x82, 0x7D, 0xD8, 0x8B, 0x18, 0xF9, 0x1D, + 0x1C, 0xD0, 0x85, 0x63, 0x02, 0x91, 0x83, 0x5F, 0x47, 0xE5, 0x33, 0x1B, 0x94, 0x6B, 0x9C, 0x4A, + 0x31, 0x60, 0xB5, 0xF7, 0xDB, 0x17, 0xF9, 0x19, 0xE8, 0xA6, 0x84, 0x2B, 0x54, 0xCB, 0x43, 0x45, + 0x3E, 0x92, 0xED, 0xC8, 0x56, 0x95, 0xC9, 0xB1, 0xCE, 0x70, 0xBC, 0x61, 0x03, 0x76, 0xD2, 0x5C, + 0x53, 0x3A, 0x2E, 0x9B, 0xC2, 0x39, 0xCE, 0x17, 0x8C, 0x20, 0x36, 0x92, 0xE2, 0x74, 0x5D, 0x37, + 0x85, 0x8F, 0xA4, 0x12, 0x98, 0x91, 0xC9, 0xB5, 0x2E, 0xA5, 0x78, 0x71, 0xDE, 0xDB, 0xBB, 0xD1, + 0xA7, 0x4B, 0x34, 0x23, 0x4A, 0x29, 0xD9, 0xFA, 0x29, 0xF4, 0x77, 0xAF, 0xC2, 0x95, 0xA6, 0x6C, + 0x72, 0x32, 0x3D, 0x83, 0x4A, 0x93, 0xE9, 0x11, 0x9E, 0xC0, 0x1E, 0x8F, 0x44, 0x66, 0xC6, 0x83, + 0xCB, 0x39, 0x53, 0xC9, 0x6B, 0x1E, 0xAF, 0x0E, 0x2D, 0xF6, 0xC5, 0x7F, 0xEE, 0x40, 0x3D, 0x1F, + 0x3E, 0x53, 0x3C, 0x75, 0xAF, 0x48, 0x03, 0x9F, 0x29, 0x9E, 0x3A, 0xD7, 0xA4, 0x07, 0x3D, 0x1D, + 0xAE, 0x60, 0x00, 0x01, 0xD5, 0x34, 0x23, 0xE8, 0xA4, 0x2F, 0x3B, 0x9F, 0x98, 0xA1, 0xCB, 0xA4, + 0xF7, 0xA2, 0xDF, 0x2E, 0xE6, 0xF0, 0xC1, 0x90, 0x00, 0x00, 0xE1, 0x85, 0xB0, 0x85, 0x56, 0xF8, + 0x00, 0x7D, 0x00, 0x05, 0x77, 0xA6, 0x35, 0x69, 0xFE, 0x1C, 0x58, 0xCD, 0xB8, 0xB1, 0x3D, 0xA4, + 0x00, 0x00, 0x45, 0x7B, 0x80, 0x08, 0xDE, 0x01, 0xA8, 0xDE, 0x00, 0x5E, 0xE0, 0x10, 0x00, 0x00, + 0x68, 0xED, 0x73, 0x00, 0x00, 0x0E, 0xA9, 0xA1, 0x1F, 0x45, 0x21, 0x79, 0xDC, 0xFC, 0xC5, 0x0E, + 0x5D, 0x27, 0xBD, 0x16, 0xF9, 0x77, 0x37, 0x86, 0x0C, 0x80, 0x00, 0x07, 0x0C, 0x2D, 0x84, 0x2A, + 0xB7, 0xC0, 0x03, 0xE8, 0x00, 0x2B, 0xBD, 0x31, 0xAB, 0x4F, 0xF0, 0xE2, 0xC6, 0x6D, 0xC5, 0x89, + 0xED, 0x20, 0x00, 0x02, 0x2B, 0xDC, 0x00, 0x46, 0xF0, 0x0D, 0x46, 0xF0, 0x02, 0xF7, 0x00, 0x80, + 0x00, 0x03, 0x47, 0x6B, 0x98, 0x00, 0x00, 0x75, 0x4D, 0x08, 0xFA, 0x29, 0x0B, 0xCE, 0xE7, 0xE6, + 0x28, 0x72, 0xE9, 0x3D, 0xE8, 0xB7, 0xCB, 0xB9, 0xBC, 0x30, 0x64, 0x00, 0x00, 0x38, 0x61, 0x6C, + 0x21, 0x55, 0xBE, 0x00, 0x1F, 0x40, 0x01, 0x5D, 0xE9, 0x8D, 0x5A, 0x7F, 0x87, 0x16, 0x33, 0x6E, + 0x2C, 0x4F, 0x69, 0x00, 0x00, 0x11, 0x5E, 0xE0, 0x02, 0x37, 0x80, 0x6A, 0x37, 0x80, 0x17, 0xB8, + 0x04, 0x00, 0x00, 0x1A, 0x3B, 0x5C, 0xC0, 0x00, 0x03, 0xAA, 0x68, 0x47, 0xD1, 0x48, 0x5E, 0x77, + 0x3F, 0x31, 0x43, 0x97, 0x49, 0xEF, 0x45, 0xBE, 0x5D, 0xCD, 0xE1, 0x83, 0x20, 0x00, 0x01, 0xC3, + 0x0B, 0x61, 0x0A, 0xAD, 0xF0, 0x00, 0xFA, 0x00, 0x0A, 0xEF, 0x4C, 0x6A, 0xD3, 0xFC, 0x38, 0xB1, + 0x9B, 0x71, 0x62, 0x7B, 0x48, 0x00, 0x00, 0x8A, 0xF7, 0x00, 0x11, 0xBC, 0x03, 0x51, 0xBC, 0x00, + 0xBD, 0xC0, 0x20, 0x00, 0x01, 0xFF, 0xD9 +}; + static uint8_t gimage_id; // 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) +void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx) { ssdv_t ssdv; uint8_t pkt[SSDV_PKT_SIZE]; uint8_t pkt_base91[BASE91LEN(SSDV_PKT_SIZE-37)]; - uint8_t *b; + const uint8_t *b; uint32_t bi = 0; uint8_t c = SSDV_OK; uint16_t i = 0; @@ -136,55 +393,13 @@ THD_FUNCTION(imgThread, arg) { uint8_t tries; bool status = false; + bool camera_found = false; // Detect camera - if(OV2640_isAvailable()) // OV2640 available - { - TRACE_INFO("IMG > OV2640 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 - 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 - - // 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); - - } - - // Switch off camera - OV2640_deinit(); - - // Get image - image_len = OV2640_getBuffer(&image); - - } else if(OV5640_isAvailable()) { // OV5640 available + if(OV5640_isAvailable()) { // OV5640 available TRACE_INFO("IMG > OV5640 found"); + camera_found = true; if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory) { @@ -244,13 +459,19 @@ THD_FUNCTION(imgThread, arg) { TRACE_INFO("IMG > Unlocked camera"); // Encode/Transmit SSDV if image sampled successfully - if(status) - { + 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 if(!camera_found) { // No camera found + + gimage_id++; + TRACE_INFO("IMG > Encode/Transmit SSDV (no cam found) ID=%d", gimage_id-1); + encode_ssdv(noCameraFound, sizeof(noCameraFound), conf, gimage_id-1, conf->ssdv_conf.redundantTx); + + } } time = waitForTrigger(time, &conf->trigger); diff --git a/tracker/software/protocols/ssdv/ssdv.c b/tracker/software/protocols/ssdv/ssdv.c index b0eea35..4c7720a 100644 --- a/tracker/software/protocols/ssdv/ssdv.c +++ b/tracker/software/protocols/ssdv/ssdv.c @@ -1020,7 +1020,7 @@ char ssdv_enc_get_packet(ssdv_t *s) return(SSDV_FEED_ME); } -char ssdv_enc_feed(ssdv_t *s, uint8_t *buffer, size_t length) +char ssdv_enc_feed(ssdv_t *s, const uint8_t *buffer, size_t length) { s->inp = buffer; s->in_len = length; diff --git a/tracker/software/protocols/ssdv/ssdv.h b/tracker/software/protocols/ssdv/ssdv.h index abaea38..19a4394 100644 --- a/tracker/software/protocols/ssdv/ssdv.h +++ b/tracker/software/protocols/ssdv/ssdv.h @@ -66,7 +66,7 @@ typedef struct uint8_t packet_mcu_offset; /* Source buffer */ - uint8_t *inp; /* Pointer to next input byte */ + const uint8_t *inp; /* Pointer to next input byte */ size_t in_len; /* Number of input bytes remaining */ size_t in_skip; /* Number of input bytes to skip */ @@ -144,7 +144,7 @@ typedef struct { extern char ssdv_enc_init(ssdv_t *s, uint8_t type, char *callsign, uint8_t image_id); extern char ssdv_enc_set_buffer(ssdv_t *s, uint8_t *buffer); extern char ssdv_enc_get_packet(ssdv_t *s); -extern char ssdv_enc_feed(ssdv_t *s, uint8_t *buffer, size_t length); +extern char ssdv_enc_feed(ssdv_t *s, const uint8_t *buffer, size_t length); /* Decoding */ extern char ssdv_dec_init(ssdv_t *s); From 8b35d701d181be8a026258e250ff9ec56c8d9c36 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Sat, 2 Sep 2017 00:36:45 +0200 Subject: [PATCH 25/37] Tidy up --- tracker/software/Makefile | 12 +- tracker/software/README.md | 307 ------------------ tracker/software/config.c | 3 +- tracker/software/drivers/ov5640.c | 40 ++- tracker/software/drivers/si4464.c | 1 - tracker/software/main.c | 2 +- tracker/software/radio.h | 2 +- tracker/software/{modules => threads}/error.c | 2 +- tracker/software/{modules => threads}/error.h | 0 tracker/software/{modules => threads}/image.c | 4 +- tracker/software/{modules => threads}/image.h | 0 tracker/software/{modules => threads}/log.c | 2 +- tracker/software/{modules => threads}/log.h | 0 .../software/{modules => threads}/position.c | 2 +- .../software/{modules => threads}/position.h | 0 .../software/{modules.c => threads/threads.c} | 2 +- .../software/{modules.h => threads/threads.h} | 0 .../software/{modules => threads}/tracking.c | 0 .../software/{modules => threads}/tracking.h | 0 tracker/software/{ => threads}/watchdog.c | 0 tracker/software/{ => threads}/watchdog.h | 0 21 files changed, 41 insertions(+), 338 deletions(-) delete mode 100644 tracker/software/README.md rename tracker/software/{modules => threads}/error.c (99%) rename tracker/software/{modules => threads}/error.h (100%) rename tracker/software/{modules => threads}/image.c (99%) rename tracker/software/{modules => threads}/image.h (100%) rename tracker/software/{modules => threads}/log.c (99%) rename tracker/software/{modules => threads}/log.h (100%) rename tracker/software/{modules => threads}/position.c (99%) rename tracker/software/{modules => threads}/position.h (100%) rename tracker/software/{modules.c => threads/threads.c} (95%) rename tracker/software/{modules.h => threads/threads.h} (100%) rename tracker/software/{modules => threads}/tracking.c (100%) rename tracker/software/{modules => threads}/tracking.h (100%) rename tracker/software/{ => threads}/watchdog.c (100%) rename tracker/software/{ => threads}/watchdog.h (100%) diff --git a/tracker/software/Makefile b/tracker/software/Makefile index b52408a..79c5272 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -119,10 +119,10 @@ CSRC = $(STARTUPSRC) \ $(SHELLSRC) \ $(CHIBIOS)/os/hal/lib/streams/memstreams.c \ $(CHIBIOS)/os/hal/lib/streams/chprintf.c \ - modules/tracking.c \ - modules/position.c \ - modules/image.c \ - modules/log.c \ + threads/tracking.c \ + threads/position.c \ + threads/image.c \ + threads/log.c \ protocols/ssdv/ssdv.c \ protocols/ssdv/rs8.c \ protocols/aprs/aprs.c \ @@ -142,7 +142,7 @@ CSRC = $(STARTUPSRC) \ debug.c \ radio.c \ sleep.c \ - modules.c \ + threads/threads.c \ math/base.c \ math/sgp4.c \ math/geofence.c \ @@ -239,7 +239,7 @@ UDEFS = UADEFS = # List all user directories here -UINCDIR = modules/ drivers/ drivers/wrapper/ protocols/aprs \ +UINCDIR = threads/ drivers/ drivers/wrapper/ protocols/aprs \ protocols/ssdv protocols/morse math/ drivers/flash/ # List the user directory to look for the libraries here diff --git a/tracker/software/README.md b/tracker/software/README.md deleted file mode 100644 index fd91a8f..0000000 --- a/tracker/software/README.md +++ /dev/null @@ -1,307 +0,0 @@ -# Software for Pecan Balloon Trackers - -This is the software, that runs the [Pecan Pico 7 balloon trackers](https://github.com/DL7AD/pecanpico7). Since the new trackers can be used in many ways, this software is designed for multiple purpose. It can be used for: -- Image transmissions - - [SSDV](https://ukhas.org.uk/guides:ssdv) as 2FSK modulation (RTTY) - - [APRS](http://aprs.org)-encoded [SSDV](https://ukhas.org.uk/guides:ssdv) as AFSK or 2GFSK modulation -- Position/Telemetry transmission (GPS) - - by [APRS](http://aprs.org) AFSK or 2GFSK - - 2FSK (RTTY) - - CW (morse) - -This software may also feature -- Amateur satellite communication links -- Full duplex repeater for digital modulations (up 70cm, down 2m) -- Ground control access via radio link - -###### Transmitted Test Images -Here are some images which were transmitted by [SSDV](https://ukhas.org.uk/guides:ssdv). These QVGA-images were captured by an [Omnivision OV9655](http://www.waveshare.com/wiki/OV9655_Camera_Board). - -![Test image 1](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/sample_pictures/test2.jpg) -![Test image 2](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/sample_pictures/test3.jpg)
- -###### Targets -Currently there is only one target supported by the Software. Pecan Pico 7a will be succeeded by Pecan Pico 7b due to some mayor errors ([See known PCB errors](https://github.com/DL7AD/pecanpico7/tree/7a)). - -Pecan Pico 7a
[Pecan Pico 7a](https://github.com/DL7AD/pecanpico7/tree/7a) - -###### Features -All features can be combined as required for the user. Currently the software is incomplete but basic features can be used: - -- Image capturing (just QVGA at the moment) and transmission by SSDV -- Reception of positions (by ublox MAX8 GNSS module) -- Measurement of temperature/air-pressure/humidity (by onboard BME280) -- Measurement of temperature/air-pressure/humidity (by external BME280) -- Measurement of the power consumption (battery charging and discharging) -- Position and telemetry transmissions by 2FSK (or RTTY) -- High altitude mode (up to 50km altitude) -- Debugging (serial connection) -- APRS encoding (AFSK, 2GFSK) -- APRS SSDV-encoded Image Transmission - -What currently doesn't work (but planned): -- High resolution image capture (just QVGA at the moment) -- Logging and log transmissions -- Amateur satellite orbit calculations and transmissions -- Power save modes -- Storage of telemetry and image data on the microSD card - -It is currently not planned to implement video capture. The STM32F4 doesn't have sufficient computing power for compression encoding. However the new pin-compabile [STM32F7x7](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1858) supports JPEG encoding. So video capture may be possible in future. - -###### Dependency installations/ Compiling/ Flashing -In order to get the hardware working, you'll need a programmer. There are several devices on the market but I personally use the [STM32F429I-DISCOVERY](http://www.st.com/st-web-ui/static/active/en/resource/technical/document/user_manual/DM00093903.pdf) demo board. The discovery board is basically a programmer with a demo target (microcontroller) attached to it. It's designed in a way so you can either flash the demo target or the Pecan Pico 7 by switching specific jumpers on the board. You may get the programmer at [Mouser](http://www2.mouser.com/search/ProductDetail.aspx?R=0virtualkey0virtualkeySTM32F429I-DISC1), [Farnell](http://www.newark.com/stmicroelectronics/stm32f429i-disc1/dev-board-stm32f429zi-advanced/dp/72Y1169), [Digikey](http://www.digikey.de/product-detail/de/stmicroelectronics/STM32F429I-DISCO/497-13898-ND/4310131) or on Ebay for roughly $30. You'll need to connect the programmer as follows. Don't forget to remove the jumpers which connect the programmer with the demo target. - -You'll also need some software dependencies. -- _GCC_ to build binary files -- _ST-Flash_ to flash the binaries to your target -There's a [great guide](http://www.wolinlabs.com/blog/linux.stm32.discovery.gcc.html) on the Web which I've followed myself to set up everything. They are using a different demo board but it works similarily. - -Then you'll need the source code for the tracker: -``` -git clone --recursive https://github.com/DL7AD/pecan-stm32f429.git -cd pecan-stm32f429 -make -make flash -``` -A _build_ folder will be created automatically. The software can be configured/customized in the [config.h](config.h). - -###### How to connect the programmer to the Pecan Pico 7 PCB -Programmer connected to Pecan Pico 7 - - - - -# Technical Description -Technical description below... - -# Modules -This section describes the different function blocks in the software and how they interact which each other. The software provides multiple modules, which supply the main functionality of the tracker like transmitting positions or images. Modules are basically [threads in the ChibiOS RTOS](http://www.chibios.org/dokuwiki/doku.php?id=chibios:howtos:createthread). Modules can be: - -- POSITION - for transmining position and telemetry -- IMAGE - for transmitting images -- LOG - for transmitting logs - -There are two more specific modules which are started automatically be the software at run time. They are necessary to handle input/output jobs. -- TRACKING (or tracking manager) - collects all the data from the periphery (BME280, GPS, etc.) and provides them (as struct) to other modules in the software. -- RADIO - provides the two radios which are available. This module receives internal messages from other modules which intend to send packets via VHF or UHF. - -The following diagram shows how the modules interact with each other. - -``` - DRIVER | DATA PROCESSING MODULES | DRIVER -------------+--------------------------------------------------------------+------------------ - - MPU9250 ---+ +---> SATELLITE ---+ - | | | - BME280 ----+---> TRACKING Manager -----+---> POSITION ----+----> RADIO ---+---> RADIO1 (2m) - | | | | - MAX -------+ +-----> LOG -------+ +---> RADIO2 (70cm) - | - OV9655 or ----------------------------------> IMAGE ------+ - OV2640 -``` - -## Module configuration -In order to use these modules, they can be configured in the MOULDES() macro in the [configuration file](config.h). All modules can be configured except of TRACKING and RADIO. They have no configurable parameters because they get their parameters from other modules in run time. The following example shows how an image module and a position module can be configured. -``` -MODULES() { \ - MODULE_IMAGE (TX_CONTINUOSLY, SLEEP_WHEN_BATT_BELOW_3V5, CUSTOM_FREQ, 10, PROT_SSDV_2FSK ); \ - MODULE_POSITION(WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_3V0, CUSTOM_FREQ, 10, PROT_UKHAS_2FSK); \ -} -``` -As you can see, there are several parameters which can be used to configure a specific module: - -**Parameter 1** specifies in which cycle the modules shall send their data. This can be a time in seconds. It's also allowed to indicate *TX_CONTINUOSLY* or *WAIT_FOR_TRACKING_POINT* in this field.
-*TX_CONTINUOSLY* - Transmit new data as soon as the previous frame is compled
-*WAIT_FOR_TRACKING_POINT* - Transmit when the tracking manager generates a new track point
-**Parameter 2** specifies the sleep mode. Note this function is currently not implemented. The modules will disregard this field.
-**Parameter 3** specifies a frequency method. Currently *CUSTOM_FREQ* or *APRS_REGION_FREQ* can be used. The frequencies are dynamic and set in [radio.c](radio.c). *CUSTOM_FREQ* is set to 434.500MHz and *APRS_REGION_FREQ* is set to 144.800MHz as the default. A geofencing algorithm is implemented for *APRS_REGION_FREQ* in order to find out the specific APRS region frequency by the actual location.
-**Parameter 4** specifies the transmission power in dBm. If the value is set to a point which the radio is unable to reach (or would damage the PCB) the actual value may be limited by the RADIO module.
-**Parameter 5** specifies the protocol and modulation. While you can use *PROT_UKHAS_2FSK*, *PROT_APRS_AFSK* or *PROT_RAW_CW* for the POSITION module, the IMAGE module can accept *PROT_SSDV_2FSK*, *PROT_SSDV_APRS_AFSK* and *PROT_SSDV_APRS_2GFSK* at the moment. - -Note not all of the parameters can be used for every individual module because some configurations may not make sense e.g. CW modulation for transmitting images. You can find an exact documentation in the [configuration file](config.h) which specific parameters can be applied to the the modules. - -## Concurrent use of modules -Modules can be merged in various combinations. However modules can be also understood as classes whose instances can be used multiple times. So if you want to transmit telemetry and positions by RTTY and APRS at the same time, you may configure modules multiple times. The following example shows, how the position/telemetry is sent on a custon frequency while it's also transmitted by APRS on the regional APRS frequency and CW. -``` -MODULES() { \ - MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_2V5, CUSTOM_FREQ, 10, PROT_UKHAS_2FSK); \ - MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_2V5, APRS_REGION_FREQ, 10, PROT_APRS_AFSK ); \ - MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_3V0,CUSTOM_FREQ, 10, PROT_RAW_CW ); \ -} -``` - -## Module TRACKING (or tracking manager) -The tracking manager keeps an eye on all peripheral components that provide data. With exception of the camera, the tracking manager collects the data of: -- BME280 (airpressure, humidity, temperature) -- MAX7/8 (GPS/GLONASS receiver) -- PAC1720 (Power consumption tracking) -The data of these components will be collected at a specific cycle time and will be provided to other modules in a struct [trackPoint_t](modules/tracking.h). The most recently sampled data can be accessed by other modules by [getLastTrackPoint()](modules/tracking.h). - -## Module RADIO -The module RADIO handles the two radios which are available on the Pecan Pico 7. It initializes a mailbox in which messages (or data packets) can be written by other functions in order to use the radios. If a module wants to send a packet on HF, it may generate a [radioMSG_t](modules/radio.h) struct and pass is over to [transmitOnRadio()](modules/radio.h). The radio will determine a suitable radio by the frequency which is set in the struct. Once the radio is free, the module will send the message. If the radio is already in use, the RADIO module may buffer up to 3 more messages in a message queue. The queue is cleared by the FIFO concept. While only 3 messages can be stored in the queue, the maximum generation-transmission delay is quite small (<15sec usually). [transmitOnRadio()](modules/radio.h) may return *true* if the message is sent to the queue. *False* indicates the queue is full. If a module wants to send the message, it has to try again by calling transmitOnRadio() again. While there is no priority management at the moment, the modules have to try more or less aggressivly to insert its messages to the radio queue. This strategy more a workaround than a solution. Priority management would be better. - -###### 2FSK modulation -2FSK is the name for RTTY in the software because this modulation type also transmits start and stop bit (8n2). The modulation is managed by a software timer because the timing dont have to be that accurate (compared to AFSK and GFSK). The modulation is passed over to the Si446x by a GPIO pin. As the pin is toggled, the transmitter will change its frequency. - -###### CW modulation -CW is moduled like 2FSK. The transmitter is just set into the mode OOK (On Off Keying). - -###### APRS AFSK modulation -The APRS modulation is a Si446x-specific hack because the Si Chip is unable to do FM natively. But Si446x chips are able to modulate 2GFSK. GFSK has a much smoother frequency change than its the case at FSK. The modulation is afftected on a GPIO pin at the transmitter which is connected to the Microcontroller. As this GPIO pin is switched the transmitter also changes its frequency. This characteristic knowlegde can be used to implement FM. If the pin is toggled 4400 times in a second, a 2200Hz tone can be modulated at FM. The 1200Hz tone can be modulated by toggling the pin 2400 times in a second. The modulation is additionally filtered in the Si446x chips with a FIR filter, so harmonics above 2200Hz are filtered. -Unfortunately the modulation requires an accurate timing. If the timing is not managed correctly at the microcontroller, the modulation gets distorted. To ensure accurate timings, all threads and interrupts are disabled at the microcontroller so the pin is toggled at the right time. - -###### APRS 2GFSK modulation -The 2GFSK modulation is quite simple because the Si446x supports 2 and 4GFSK natively. Like it`s done at AFSK, the modulation is generated by the microcontroller which requires timing. So the microcontroller cant do anything else at the actual transmission. - -## Module POSITION -This module handles the radio transmission encoding for all position/telemetry packets. It is able to encode CW, APRS and 2FSK (or RTTY). A cycle can be continuosly, time-specific or be triggered by the TRACKING manager when a new track point is published. This modules can be configured in the MODULES() marco: `MODULE_POSITION(trigger, sleep, frequency, power, protocol)`. - -| Field | Meaning | Possible options -| --------- | ------- | ---------------- -| trigger | Cycle time or trigger type for transmission | `TX_CONTINUOSLY` or `WAIT_FOR_TRACKING_POINT` or Integer (in seconds) -| sleep | Sleep method | `SLEEP_WHEN_BATT_BELOW_2V9` or `SLEEP_WHEN_BATT_BELOW_3V0` ... `SLEEP_WHEN_BATT_BELOW_4V1` or `SLEEP_WHEN_ISS_NOT_VISIBLE` -| frequency | Frequency method | `CUSTOM_FREQ` or `APRS_REGION_FREQ` or `APRS_ISS_FREQ` -| power | Radio power (in dBm) | Integer -| protocol | Protocol | `PROT_APRS_2GFSK` or `PROT_APRS_AFSK` or `PROT_UKHAS_2FSK` or `PROT_RAW_CW` or `PROT_APRSCONFIG_AFSK` or `PROT_APRSCONFIG_2GFSK` - -For a detailed explanation about the option macros see [config.h](config.h). - -###### APRS Encoding -This software makes use of the amateur radio [APRS system (Automatic Packet Reporting system)](http://aprs.org), which has been invented by Bob Bruninga in 1992. Since APRS has a [great coverage](http://aprs.fi/#!mt=roadmap&z=3) in the US and Europe, is it very useful for balloon tracking. At the moment its also the most convenient system which can be used. - -The software itself implements the compressed format. This software is actually not recommended but supported by all devices. Since there has been no replacement and the raw format is quiet big, this is still the best encoding option. The format also supports 5 different 13bit-telemetry transmissons which are used for: -- Battery voltage -- Solar voltage -- Temperature (in Celcius) -- Battery charge (in mW) -- Battery discharge (in mW) - -The software also might display the GPS-satellite count, the TTFF (time to first fix) and GPS-LOSS counter in the comment field. Note the telemetry fields are mathematically encoded and the receives have to know, how they can decode this telemetry. In order to transmit the configuration by the tracker itself, place an additional POSITION modules in the MODULES() marco in the [config.h](config.h) with the protocol `PROT_APRSCONFIG_AFSK`. It will transmit all the necessary decoding information for the receiver. - -`MODULE_POSITION (3600, SLEEP_WHEN_BATT_BELOW_3V0, APRS_REGION_FREQ, 10, PROT_APRSCONFIG_AFSK);` - -All APRS messages will be encoded by the POSITION module for the RADIO module in a bitstream (1 bit = 1 Symbol). The packets already contain the AX.25 specific bitstuffing, NRZ-I encoding (for AX.25 AFSK) and scrambling (for AX.25 2GFSK). - -![APRS Packet](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/aprs.jpg)
-Transmitted APRS packet displayed by [APRS.fi](http://aprs.fi) - -###### 2FSK Encoding -2FSK messages will be encoded especially for the [UKHAS system](https://ukhas.org.uk/guides:tracking_guide). Unlike AFSK, this modulation can be only received with a SSB receiver. See on the [UKHAS Tracking Guide](https://ukhas.org.uk/guides:tracking_guide) which receivers can be used. While UKHAS is very flexible, the packet format can be configured in the configuration file by the UKHAS_FORMAT marco. The format contains a set of variables which can be changed user specific. - -`#define UKHAS_FORMAT ",,