kopia lustrzana https://github.com/DL7AD/pecanpico9
Implemented OV5640
rodzic
44aa301d5f
commit
7c99afef6a
|
@ -135,6 +135,7 @@ CSRC = $(STARTUPSRC) \
|
|||
drivers/bme280.c \
|
||||
drivers/pac1720.c \
|
||||
drivers/ov2640.c \
|
||||
drivers/ov5640.c \
|
||||
drivers/flash/flash.c \
|
||||
drivers/flash/helper.c \
|
||||
drivers/flash/ihex.c \
|
||||
|
|
|
@ -272,7 +272,7 @@
|
|||
|
||||
// Camera pins
|
||||
#define LINE_CAM_XCLK PAL_LINE(GPIOA, 8U)
|
||||
#define LINE_CAM_PCLK PAL_LINE(GPIOC, 6U)
|
||||
#define LINE_CAM_PCLK PAL_LINE(GPIOC, 0U)
|
||||
#define LINE_CAM_HREF PAL_LINE(GPIOC, 2U)
|
||||
#define LINE_CAM_VSYNC PAL_LINE(GPIOC, 1U)
|
||||
#define LINE_CAM_D2 PAL_LINE(GPIOA, 0U)
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
#include "debug.h"
|
||||
|
||||
module_conf_t config[9];
|
||||
uint8_t ssdv_buffer[65535];
|
||||
uint8_t ssdv_buffer2[65535];
|
||||
uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
|
||||
|
||||
/*
|
||||
* Position module configuration description
|
||||
|
@ -174,12 +173,12 @@ void start_user_modules(void)
|
|||
// Module POSITION, APRS 2m AFSK
|
||||
config[0].power = 127; // Power 20 dBm
|
||||
config[0].protocol = PROT_APRS_AFSK; // Protocol APRS, modulation AFSK
|
||||
config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[0].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[0].frequency.type = FREQ_STATIC; // Dynamic frequency allocation
|
||||
config[0].frequency.hz = 144390000; // Default frequency 144.800 MHz
|
||||
config[0].init_delay = 0; // Module startup delay in msec
|
||||
config[0].trigger.type = TRIG_NEW_POINT; // Trigger when new track point released
|
||||
chsnprintf(config[0].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign
|
||||
config[0].aprs_conf.ssid = 12; // APRS SSID
|
||||
config[0].aprs_conf.ssid = 11; // APRS SSID
|
||||
config[0].aprs_conf.symbol = SYM_BALLOON; // APRS Symbol
|
||||
chsnprintf(config[0].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
config[0].aprs_conf.preamble = 300; // APRS Preamble
|
||||
|
@ -191,7 +190,7 @@ void start_user_modules(void)
|
|||
config[0].aprs_conf.tel_enc = TRUE; // Transmit Telemetry encoding information activated
|
||||
config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
|
||||
chsnprintf(config[0].aprs_conf.tel_comment, 30, "http://ssdv.habhub.org/DL7AD");// Telemetry comment
|
||||
start_position_thread(&config[0]);
|
||||
//start_position_thread(&config[0]);
|
||||
|
||||
// Module POSITION, APRS 2m 2GFSK
|
||||
/*config[1].power = 127; // Power 10 dBm
|
||||
|
@ -235,23 +234,23 @@ void start_user_modules(void)
|
|||
// Module IMAGE, APRS 2m AFSK low-duty cycle
|
||||
config[3].power = 127; // Power 20 dBm
|
||||
config[3].protocol = PROT_APRS_AFSK; // Protocol APRS SSDV, modulation AFSK
|
||||
config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz
|
||||
config[3].frequency.type = FREQ_STATIC; // Dynamic frequency allocation
|
||||
config[3].frequency.hz = 144390000; // Transmission frequency 144.800 MHz
|
||||
config[3].init_delay = 0; // Module startup delay in msec
|
||||
config[3].packet_spacing = 15000; // Packet spacing in ms
|
||||
//config[3].packet_spacing = 30000; // Packet spacing in ms
|
||||
//config[3].sleep_conf.type = SLEEP_WHEN_ISOL_BELOW_THRES;
|
||||
//config[3].sleep_conf.isol_thres = 3;
|
||||
config[3].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
|
||||
config[3].trigger.timeout = 10; // Timeout 10 sec
|
||||
chsnprintf(config[3].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign
|
||||
config[3].aprs_conf.ssid = 12; // APRS SSID
|
||||
config[3].aprs_conf.ssid = 11; // APRS SSID
|
||||
config[3].aprs_conf.preamble = 300; // APRS Preamble
|
||||
chsnprintf(config[3].ssdv_conf.callsign, 7, "DL7AD"); // SSDV Callsign
|
||||
config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[3].ssdv_conf.res = RES_QVGA; // Resolution VGA
|
||||
config[3].ssdv_conf.redundantTx = true; // Transmit packets twice
|
||||
start_image_thread(&config[3]);
|
||||
//start_image_thread(&config[3]);
|
||||
|
||||
// Module POSITION, Morse 2m OOK
|
||||
/*config[4].power = 127; // Power 10 dBm
|
||||
|
@ -272,7 +271,7 @@ void start_user_modules(void)
|
|||
config[5].gfsk_conf.speed = 9600; // 2GFSK Speed
|
||||
config[5].frequency.type = FREQ_STATIC; // Static frequency allocation
|
||||
config[5].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
|
||||
config[5].init_delay = 60000; // Module startup delay in msec
|
||||
//config[5].init_delay = 60000; // Module startup delay in msec
|
||||
config[5].sleep_conf.type = SLEEP_WHEN_VBAT_BELOW_THRES;
|
||||
config[5].sleep_conf.vbat_thres = 4000;
|
||||
config[5].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
|
||||
|
@ -281,11 +280,11 @@ void start_user_modules(void)
|
|||
config[5].aprs_conf.ssid = 12; // APRS SSID
|
||||
config[5].aprs_conf.preamble = 100; // APRS Preamble
|
||||
chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign
|
||||
config[5].ssdv_conf.ram_buffer = ssdv_buffer2; // Camera buffer
|
||||
config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer2); // Buffer size
|
||||
config[5].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[5].ssdv_conf.res = RES_QVGA; // Resolution XGA
|
||||
config[5].ssdv_conf.redundantTx = true; // Transmit packets twice
|
||||
//start_image_thread(&config[5]);
|
||||
//config[5].ssdv_conf.redundantTx = true; // Transmit packets twice
|
||||
start_image_thread(&config[5]);
|
||||
|
||||
// Module IMAGE, SSDV 2m 2FSK
|
||||
/*config[6].power = 127; // Power 20 dBm
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "radio.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define TRACK_CYCLE_TIME 30 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */
|
||||
#define TRACK_CYCLE_TIME 60 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */
|
||||
|
||||
#define LOG_CYCLE_TIME 30 /* Log cycle time in seconds */
|
||||
#define LOG_FLASH_ADDR1 0x080C0000 /* Log flash memory address 1 */
|
||||
|
|
|
@ -312,12 +312,13 @@ static const struct regval_list ov2640_init_regs[] = {
|
|||
{ 0x2e, 0xdf },
|
||||
{ BANK_SEL, BANK_SEL_SENS },
|
||||
{ 0x3c, 0x32 },
|
||||
{ CLKRC, CLKRC_DIV_SET(4) },
|
||||
{ CLKRC, CLKRC_DIV_SET(8) },
|
||||
{ COM2, COM2_OCAP_Nx_SET(3) },
|
||||
{ REG04, REG04_DEF | REG04_HREF_EN },
|
||||
{ COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN },
|
||||
{ COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN },
|
||||
//~ { AEC, 0x00 },
|
||||
{ COM9, COM9_AGC_GAIN_8x | 0x08},
|
||||
//{ COM10, COM10_PCLK_RISE },
|
||||
{ 0x2c, 0x0c },
|
||||
{ 0x33, 0x78 },
|
||||
{ 0x3a, 0x33 },
|
||||
|
@ -648,313 +649,20 @@ bool OV2640_Snapshot2RAM(void)
|
|||
|
||||
bool OV2640_BufferOverflow(void)
|
||||
{
|
||||
return ov2640_conf->image_buffer[0] != 0xFF || ov2640_conf->image_buffer[1] != 0xD8; // Check for JPEG SOI header
|
||||
return ov2640_conf->ram_buffer[0] != 0xFF || ov2640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header
|
||||
}
|
||||
|
||||
uint32_t OV2640_getBuffer(uint8_t** buffer) {
|
||||
*buffer = ov2640_conf->ram_buffer[0];
|
||||
*buffer = ov2640_conf->ram_buffer;
|
||||
return ov2640_conf->size_sampled;
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
const stm32_dma_stream_t *dmastp;
|
||||
static bool image_finished;
|
||||
uint8_t dma_index;
|
||||
|
||||
inline int32_t dma_start(void) {
|
||||
/* Clear any pending inerrupts. */
|
||||
dmaStreamClearInterrupt(dmastp);
|
||||
dmaStreamEnable(dmastp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int32_t dma_stop(void) {
|
||||
dmaStreamDisable(dmastp);
|
||||
dmaStreamRelease(dmastp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get DMA stream current target.
|
||||
* @note This function can be invoked in both ISR or thread context.
|
||||
* @pre The stream must have been allocated using @p dmaStreamAllocate().
|
||||
* @post After use the stream can be released using @p dmaStreamRelease().
|
||||
*
|
||||
* @param[in] dmastp pointer to a stm32_dma_stream_t structure
|
||||
* @return Current target index
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
#define dmaStreamGetCurrentTarget(dmastp) \
|
||||
((uint8_t)(((dmastp)->stream->CR >> DMA_SxCR_CT_Pos) & 1U))
|
||||
|
||||
static void dma_interrupt(void *p, uint32_t flags) {
|
||||
/* No parameter passed. */
|
||||
(void)p;
|
||||
|
||||
if (flags & STM32_DMA_ISR_HTIF) {
|
||||
/*
|
||||
* Half transfer complete.
|
||||
* Check if DMA is writing to the last buffer.
|
||||
*/
|
||||
if (dma_index == (DMA_BUFFERS - 1)) {
|
||||
/*
|
||||
* This is the last buffer so we have to terminate DMA.
|
||||
* Because the DBM switch is done in h/w.
|
||||
* Stopping DMA in interrupt would be too late.
|
||||
* DMA would write beyond buffer or overwrite prior buffer (if MxAR not updated).
|
||||
*
|
||||
* So stop DMA and TIM DMA trigger.
|
||||
*/
|
||||
dmaStreamClearInterrupt(dmastp);
|
||||
chSysLockFromISR();
|
||||
dma_stop();
|
||||
TIM1->DIER &= ~TIM_DIER_TDE;
|
||||
image_finished = true;
|
||||
/* Disable HSYNC and VYSNC edge interrupts. */
|
||||
nvicDisableVector(EXTI1_IRQn);
|
||||
nvicDisableVector(EXTI2_IRQn);
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Else Safe to allow buffer to fill.
|
||||
* DMA DBM will switch buffers in h/w this one is full.
|
||||
* Just clear the interrupt and wait for TCIF.
|
||||
*/
|
||||
dmaStreamClearInterrupt(dmastp);
|
||||
return;
|
||||
}
|
||||
if (flags & STM32_DMA_ISR_TCIF) {
|
||||
/*
|
||||
* Full transfer complete.
|
||||
* Update non-active memory address register.
|
||||
* DMA will use new address at h/w DBM switch.
|
||||
*/
|
||||
dmaStreamClearInterrupt(dmastp);
|
||||
|
||||
if (dmaStreamGetCurrentTarget(dmastp) == 1) {
|
||||
dmaStreamSetMemory0(dmastp, ram_buffer[++dma_index]);
|
||||
} else {
|
||||
dmaStreamSetMemory1(dmastp, ram_buffer[++dma_index]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the vector for HREF (EXTI2) (stolen from hal_ext_lld_isr.c)
|
||||
|
||||
CH_FAST_IRQ_HANDLER(Vector60) {
|
||||
//CH_IRQ_PROLOGUE();
|
||||
|
||||
//uint8_t gpioc = GPIOC->IDR;
|
||||
// HREF handling
|
||||
if(GPIOC->IDR & 0x4) {
|
||||
// HREF rising edge, start capturing data on pixel clock
|
||||
/*
|
||||
* Start or continuation of dma.
|
||||
* Set TIM8_CH1 capture input rising edge as DMA trigger.
|
||||
*/
|
||||
TIM8->DIER |= TIM_DIER_CC1DE;
|
||||
} else {
|
||||
/* On falling edge remove TIM8 trigger for DMA. */
|
||||
TIM8->DIER &= ~TIM_DIER_CC1DE;
|
||||
}
|
||||
|
||||
EXTI->PR |= EXTI_PR_PR2;
|
||||
//CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
bool vsync = false;
|
||||
|
||||
// This is the vector for EXTI1 (stolen from hal_ext_lld_isr.c)
|
||||
/*
|
||||
* Note: VSYNC is a pulse the full length of a frame.
|
||||
* This is contrary to the OV2640 datasheet which shows VSYNC as pulses.
|
||||
*/
|
||||
CH_FAST_IRQ_HANDLER(Vector5C) {
|
||||
//CH_IRQ_PROLOGUE();
|
||||
|
||||
// VSYNC handling
|
||||
if(!vsync && palReadLine(LINE_CAM_VSYNC)) {
|
||||
/*
|
||||
* Rising edge of VSYNC .
|
||||
*/
|
||||
|
||||
palClearLine(LINE_IO_LED1); // Indicate that picture will be captured
|
||||
vsync = true;
|
||||
} else if(vsync && !palReadLine(LINE_CAM_VSYNC)) {
|
||||
/* VSYNC falling edge - end of JPEG frame.
|
||||
* Stop & release the DMA channel.
|
||||
* Disable TIM8 trigger of DMA
|
||||
* These should have already been disabled in DMA interrupt if was filled.
|
||||
*/
|
||||
dma_stop();
|
||||
TIM8->DIER &= ~TIM_DIER_CC1DE;
|
||||
|
||||
/* Disable HSYNC & VYSNC edge interrupts. */
|
||||
nvicDisableVector(EXTI1_IRQn);
|
||||
nvicDisableVector(EXTI2_IRQn);
|
||||
/* Turn on capture LED and signal the semaphore (data can be processed). */
|
||||
palSetLine(LINE_IO_LED1);
|
||||
image_finished = true;
|
||||
vsync = false;
|
||||
}
|
||||
|
||||
palToggleLine(LINE_IO_LED1);
|
||||
|
||||
EXTI->PR |= EXTI_PR_PR1;
|
||||
//CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
uint8_t ram_buffer[DMA_BUFFERS][DMA_SIZE] __attribute__((aligned(DMA_ALIGNMENT)));
|
||||
|
||||
bool OV2640_Capture(void)
|
||||
{
|
||||
/*
|
||||
* Note:
|
||||
* If there are no Chibios devices enabled that use DMA then...
|
||||
* In makefile add entry to UDEFS:
|
||||
* UDEFS = -DSTM32_DMA_REQUIRED
|
||||
*/
|
||||
|
||||
/*
|
||||
* Setup DMA for transfer on TIM8_CH1 - DMA2 stream 2, channel 7.
|
||||
* Direction is peripheral (GPIO) to memory.
|
||||
* Set highest priority (3).
|
||||
* Further down we set DMA FIFO to write to memory on FIFO full (4 words).
|
||||
* GPIO is 8 bit, memory is 32 bit, memory burst write using 4 beats.
|
||||
*/
|
||||
dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 2));
|
||||
uint32_t dmamode = STM32_DMA_CR_CHSEL(7) |
|
||||
STM32_DMA_CR_PL(3) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_MSIZE_WORD |
|
||||
STM32_DMA_CR_PSIZE_BYTE |
|
||||
STM32_DMA_CR_MINC |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE |
|
||||
STM32_DMA_CR_HTIE |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DBM |
|
||||
STM32_DMA_CR_MBURST_INCR4;
|
||||
|
||||
chSysLock();
|
||||
|
||||
dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL);
|
||||
|
||||
dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here
|
||||
/*
|
||||
* Buffer address must be word aligned.
|
||||
* Also note requirement for burst transfers.
|
||||
* Bursts must not cross a 1K address boundary.
|
||||
* See RM0430 9.3.12
|
||||
* DMA buffers for ov2640_conf should be defined like this...
|
||||
* #define DMA_ALIGNMENT 1024
|
||||
* #define DMA_SIZE DMA_ALIGNMENT
|
||||
* #define DMA_BUFFERS 6
|
||||
* uint8_t ram_buffer[DMA_BUFFERS][DMA_SIZE] __attribute__((aligned(DMA_ALIGNMENT)));
|
||||
*
|
||||
*/
|
||||
dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer[0]);
|
||||
dmaStreamSetMemory1(dmastp, ov2640_conf->ram_buffer[1]);
|
||||
/*
|
||||
* Transfer size (NDT) must be a multiple of 4 bytes.
|
||||
* See RM0430 9.3.11
|
||||
*/
|
||||
dmaStreamSetTransactionSize(dmastp, DMA_SIZE);
|
||||
//dmaStreamSetTransactionSize(dmastp, (ov2640_conf->ram_size));
|
||||
|
||||
/* Setup DMA...
|
||||
* Disable direct mode (enable FIFO).
|
||||
* Set FIFO to transfer at full.
|
||||
* Clear any outstanding interrupt.
|
||||
*/
|
||||
dmaStreamSetMode(dmastp, dmamode);
|
||||
dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL);
|
||||
dmaStreamClearInterrupt(dmastp);
|
||||
/*
|
||||
* Setup timer to trigger DMA.
|
||||
* We have to use TIM8 because...
|
||||
* - TIM8_CH1 is in DMA2 and we need DMA2 for peripheral -> memory transfer
|
||||
*/
|
||||
rccResetTIM8();
|
||||
rccEnableTIM8(FALSE);
|
||||
|
||||
TIM8->CCMR1 = TIM_CCMR1_CC1S_0; // Select channel 1 as input
|
||||
TIM8->CCER |= TIM_CCER_CC1E; // Enable capture channel
|
||||
TIM8->CCER |= TIM_CCER_CC1P; // Select trailing edge
|
||||
|
||||
|
||||
|
||||
// Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF)
|
||||
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC | SYSCFG_EXTICR1_EXTI2_PC;
|
||||
|
||||
EXTI->IMR = EXTI_IMR_MR1 | EXTI_IMR_MR2; // Activate interrupt for chan1 (=>PC1) and chan2 (=>PC2)
|
||||
EXTI->RTSR = EXTI_RTSR_TR1 | EXTI_RTSR_TR2; // Listen on rising edge
|
||||
EXTI->FTSR = EXTI_FTSR_TR1 | EXTI_FTSR_TR2; // Listen on falling edge too
|
||||
/* Reset any pending interrupt. */
|
||||
EXTI->PR |= EXTI_PR_PR1;
|
||||
EXTI->PR |= EXTI_PR_PR2;
|
||||
/* Enable interrupt vectors. */
|
||||
nvicEnableVector(EXTI1_IRQn, 2);
|
||||
nvicEnableVector(EXTI2_IRQn, 1);
|
||||
/* Start DMA and timer DMA. */
|
||||
dma_start();
|
||||
TIM8->DIER |= TIM_DIER_CC1DE;
|
||||
|
||||
image_finished = false;
|
||||
dma_index = 0;
|
||||
chSysUnlock();
|
||||
|
||||
do {
|
||||
osalThdSleepMilliseconds(100);
|
||||
} while(!image_finished);
|
||||
|
||||
unit8_t *image_buffer = ov2640_conf->ram_buffer[0];
|
||||
int32_t size=65535;
|
||||
while(ov2640_conf->image_buffer[size] == 0 && size >= 0)
|
||||
size--;
|
||||
TRACE_DEBUG("CAM > Image %d %02x %02x %02x %02x", size, ov2640_conf->image_buffer[0], ov2640_conf->image_buffer[1], ov2640_conf->image_buffer[2], ov2640_conf->image_buffer[3]);
|
||||
|
||||
TRACE_DEBUG("CAM > Have a look for SOI");
|
||||
uint32_t soi; // Start of Image
|
||||
for(soi=0; soi<65533; soi++)
|
||||
{
|
||||
if(ov2640_conf->image_buffer[soi] == 0xFF && ov2640_conf->image_buffer[soi+1] == 0xD8)
|
||||
break;
|
||||
}
|
||||
|
||||
if(soi == 65533) {
|
||||
TRACE_ERROR("CAM > Could not find SOI flag");
|
||||
return false; // We failed to sample the picture correctly because we didn't find the JPEG SOI flag
|
||||
}
|
||||
|
||||
TRACE_DEBUG("SOI=%d", soi)
|
||||
|
||||
// Found SOI, move bytes
|
||||
for(uint32_t i=0; i<65535; i++)
|
||||
ov2640_conf->image_buffer[i] = ov2640_conf->image_buffer[i+soi];
|
||||
|
||||
TRACE_DEBUG("CAM > Image %02x %02x %02x %02x", ov2640_conf->image_buffer[0], ov2640_conf->image_buffer[1], ov2640_conf->image_buffer[2], ov2640_conf->image_buffer[3]);
|
||||
TRACE_INFO("CAM > Capture finished");
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
|
||||
bool image_finished;
|
||||
bool sample_byte;
|
||||
|
||||
bool OV2640_Capture(void)
|
||||
{
|
||||
TRACE_INFO("CAM > Start capture");
|
||||
|
||||
while(palReadLine(LINE_CAM_VSYNC));
|
||||
while(!palReadLine(LINE_CAM_VSYNC));
|
||||
|
||||
uint8_t gpioc;
|
||||
uint8_t gpioa;
|
||||
ov2640_conf->size_sampled = 0;
|
||||
|
@ -963,9 +671,7 @@ bool OV2640_Capture(void)
|
|||
do {
|
||||
gpioc = GPIOC->IDR & 0x7;
|
||||
} while((gpioc & 0x1) != 0x1); // Wait for PCLK to rise
|
||||
|
||||
gpioa = GPIOA->IDR;
|
||||
|
||||
switch(gpioc) {
|
||||
case 0x3:
|
||||
break;
|
||||
|
@ -975,41 +681,32 @@ bool OV2640_Capture(void)
|
|||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait for falling edge
|
||||
while(GPIOC->IDR & 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes GPIO (for pseudo DCMI)
|
||||
* Initializes GPIO (for DCMI)
|
||||
* The high speed clock supports communication by I2C (XCLK = 16MHz)
|
||||
*/
|
||||
void OV2640_InitGPIO(void)
|
||||
{
|
||||
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
/* PC6 AF3 = TIM8_CH1. */
|
||||
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(3));
|
||||
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(1));
|
||||
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_XCLK, PAL_MODE_ALTERNATE(0));
|
||||
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT_PULLUP | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
|
||||
|
||||
palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
|
||||
palSetPadMode(GPIOC, 0, PAL_MODE_INPUT);
|
||||
palSetPadMode(GPIOB, 15, PAL_MODE_INPUT);
|
||||
}
|
||||
|
||||
void OV2640_TransmitConfig(void)
|
||||
|
@ -1076,7 +773,7 @@ void OV2640_init(ssdv_conf_t *config) {
|
|||
// Clearing buffer
|
||||
uint32_t i;
|
||||
for(i=0; i<ov2640_conf->ram_size; i++)
|
||||
ov2640_conf->ram_buffer[0][i] = 0;
|
||||
ov2640_conf->ram_buffer[i] = 0;
|
||||
|
||||
TRACE_INFO("CAM > Init pins");
|
||||
OV2640_InitGPIO();
|
||||
|
@ -1145,3 +842,4 @@ bool OV2640_isAvailable(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* This is the OV2640 driver
|
||||
*/
|
||||
|
||||
#ifndef __OV5640_H__
|
||||
#define __OV5640_H__
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "types.h"
|
||||
|
||||
bool OV5640_Snapshot2RAM(void);
|
||||
bool OV5640_Capture(void);
|
||||
void OV5640_InitGPIO(void);
|
||||
uint32_t OV5640_getBuffer(uint8_t** buffer);
|
||||
bool OV5640_BufferOverflow(void);
|
||||
void OV5640_TransmitConfig(void);
|
||||
void OV5640_init(ssdv_conf_t *config);
|
||||
void OV5640_deinit(void);
|
||||
bool OV5640_isAvailable(void);
|
||||
|
||||
#endif
|
|
@ -52,6 +52,12 @@ bool I2C_write8_locked(uint8_t address, uint8_t reg, uint8_t value)
|
|||
return I2C_send(address, txbuf, 2, NULL, 0, MS2ST(100));
|
||||
}
|
||||
|
||||
bool I2C_write8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t value) // 16bit register (for OV5640)
|
||||
{
|
||||
uint8_t txbuf[] = {reg >> 8, reg & 0xFF, value};
|
||||
return I2C_send(address, txbuf, 3, NULL, 0, MS2ST(100));
|
||||
}
|
||||
|
||||
bool I2C_writeN_locked(uint8_t address, uint8_t *txbuf, uint32_t length)
|
||||
{
|
||||
return I2C_send(address, txbuf, length, NULL, 0, MS2ST(100));
|
||||
|
@ -66,6 +72,15 @@ bool I2C_read8_locked(uint8_t address, uint8_t reg, uint8_t *val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool I2C_read8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t *val) // 16bit register (for OV5640)
|
||||
{
|
||||
uint8_t txbuf[] = {reg >> 8, reg & 0xFF};
|
||||
uint8_t rxbuf[1];
|
||||
bool ret = I2C_send(address, txbuf, 2, rxbuf, 1, MS2ST(100));
|
||||
*val = rxbuf[0];
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool I2C_read16_locked(uint8_t address, uint8_t reg, uint16_t *val)
|
||||
{
|
||||
uint8_t txbuf[] = {reg};
|
||||
|
|
|
@ -19,6 +19,9 @@ bool I2C_writeN_locked(uint8_t address, uint8_t *txbuf, uint32_t length);
|
|||
bool I2C_read8_locked(uint8_t address, uint8_t reg, uint8_t *val);
|
||||
bool I2C_read16_locked(uint8_t address, uint8_t reg, uint16_t *val);
|
||||
|
||||
bool I2C_write8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t value); // 16bit register (for OV5640)
|
||||
bool I2C_read8_16bitreg_locked(uint8_t address, uint16_t reg, uint8_t *val); // 16bit register (for OV5640)
|
||||
|
||||
// I2C Mutex unlocked access functions
|
||||
bool I2C_write8(uint8_t address, uint8_t reg, uint8_t value);
|
||||
bool I2C_writeN(uint8_t address, uint8_t *txbuf, uint32_t length);
|
||||
|
|
|
@ -49,8 +49,8 @@
|
|||
#define STM32_PLLP_VALUE 2
|
||||
#define STM32_PLLQ_VALUE 4
|
||||
#define STM32_HPRE STM32_HPRE_DIV1
|
||||
#define STM32_PPRE1 STM32_PPRE1_DIV4
|
||||
#define STM32_PPRE2 STM32_PPRE2_DIV4
|
||||
#define STM32_PPRE1 STM32_PPRE1_DIV2
|
||||
#define STM32_PPRE2 STM32_PPRE2_DIV1
|
||||
#define STM32_RTCSEL STM32_RTCSEL_LSI
|
||||
#define STM32_RTCPRE_VALUE 8
|
||||
#define STM32_MCO1SEL STM32_MCO1SEL_HSE
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "debug.h"
|
||||
#include "modules.h"
|
||||
#include "ov2640.h"
|
||||
#include "ov5640.h"
|
||||
#include "pi2c.h"
|
||||
#include "ssdv.h"
|
||||
#include "aprs.h"
|
||||
|
@ -15,7 +16,7 @@
|
|||
#include "watchdog.h"
|
||||
#include "flash.h"
|
||||
|
||||
static uint8_t gimage_id; // Global image ID (for all image threads)
|
||||
static uint8_t gimage_id = 2; // Global image ID (for all image threads)
|
||||
mutex_t camera_mtx;
|
||||
|
||||
void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx)
|
||||
|
@ -146,49 +147,29 @@ THD_FUNCTION(imgThread, arg) {
|
|||
uint32_t image_len = 0;
|
||||
uint8_t *image;
|
||||
|
||||
// Take photo if camera activated (if camera disabled, camera buffer is probably shared in config file)
|
||||
if(!conf->ssdv_conf.no_camera)
|
||||
// Lock camera
|
||||
TRACE_INFO("IMG > Lock camera");
|
||||
chMtxLock(&camera_mtx);
|
||||
TRACE_INFO("IMG > Locked camera");
|
||||
|
||||
// Lock RADIO from producing interferences
|
||||
TRACE_INFO("IMG > Lock radio");
|
||||
chMtxLock(&interference_mtx);
|
||||
TRACE_INFO("IMG > Locked radio");
|
||||
|
||||
uint8_t tries;
|
||||
bool status = false;
|
||||
|
||||
// Detect camera
|
||||
if(OV2640_isAvailable()) // OV2640 available
|
||||
{
|
||||
// Lock camera
|
||||
TRACE_INFO("IMG > Lock camera");
|
||||
chMtxLock(&camera_mtx);
|
||||
TRACE_INFO("IMG > Locked camera");
|
||||
TRACE_INFO("IMG > OV2640 found");
|
||||
|
||||
// Lock RADIO from producing interferences
|
||||
TRACE_INFO("IMG > Lock radio");
|
||||
chMtxLock(&interference_mtx);
|
||||
TRACE_INFO("IMG > Locked radio");
|
||||
|
||||
uint8_t tries;
|
||||
bool status = false;
|
||||
|
||||
// Detect camera
|
||||
if(OV2640_isAvailable()) // OV2640 available
|
||||
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
|
||||
{
|
||||
TRACE_INFO("IMG > OV2640 found");
|
||||
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
|
||||
|
||||
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
|
||||
{
|
||||
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
|
||||
|
||||
do {
|
||||
|
||||
// Init camera
|
||||
OV2640_init(&conf->ssdv_conf);
|
||||
|
||||
// Sample data from DCMI through DMA into RAM
|
||||
tries = 5; // Try 5 times at maximum
|
||||
do { // Try capturing image until capture successful
|
||||
status = OV2640_Snapshot2RAM();
|
||||
} while(!status && --tries);
|
||||
|
||||
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
|
||||
|
||||
} while(OV2640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
|
||||
|
||||
conf->ssdv_conf.res = RES_MAX; // Revert register
|
||||
|
||||
} else { // Static resolution
|
||||
do {
|
||||
|
||||
// Init camera
|
||||
OV2640_init(&conf->ssdv_conf);
|
||||
|
@ -199,49 +180,101 @@ THD_FUNCTION(imgThread, arg) {
|
|||
status = OV2640_Snapshot2RAM();
|
||||
} while(!status && --tries);
|
||||
|
||||
}
|
||||
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
|
||||
|
||||
// Switch off camera
|
||||
OV2640_deinit();
|
||||
} while(OV2640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
|
||||
|
||||
// Get image
|
||||
image_len = OV2640_getBuffer(&image);
|
||||
TRACE_INFO("IMG > Image size: %d bytes", image_len);
|
||||
conf->ssdv_conf.res = RES_MAX; // Revert register
|
||||
|
||||
} else { // Camera error
|
||||
} else { // Static resolution
|
||||
|
||||
TRACE_ERROR("IMG > No camera found");
|
||||
// Init camera
|
||||
OV2640_init(&conf->ssdv_conf);
|
||||
|
||||
// Sample data from DCMI through DMA into RAM
|
||||
tries = 5; // Try 5 times at maximum
|
||||
do { // Try capturing image until capture successful
|
||||
status = OV2640_Snapshot2RAM();
|
||||
} while(!status && --tries);
|
||||
|
||||
}
|
||||
|
||||
// Unlock radio
|
||||
TRACE_INFO("IMG > Unlock radio");
|
||||
chMtxUnlock(&interference_mtx);
|
||||
TRACE_INFO("IMG > Unlocked radio");
|
||||
|
||||
// Unlock camera
|
||||
TRACE_INFO("IMG > Unlock camera");
|
||||
chMtxUnlock(&camera_mtx);
|
||||
TRACE_INFO("IMG > Unlocked camera");
|
||||
|
||||
// Encode/Transmit SSDV if image sampled successfully
|
||||
if(status)
|
||||
{
|
||||
gimage_id++;
|
||||
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
|
||||
encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Switch off camera
|
||||
OV2640_deinit();
|
||||
|
||||
// Get image
|
||||
image_len = OV2640_getBuffer(&image);
|
||||
TRACE_INFO("IMG > Image size: %d bytes", image_len);
|
||||
|
||||
TRACE_INFO("IMG > Camera disabled");
|
||||
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id);
|
||||
encode_ssdv(image, image_len, conf, gimage_id, conf->ssdv_conf.redundantTx);
|
||||
} else if(OV5640_isAvailable()) { // OV5640 available
|
||||
|
||||
TRACE_INFO("IMG > OV5640 found");
|
||||
|
||||
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
|
||||
{
|
||||
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
|
||||
|
||||
do {
|
||||
|
||||
// Init camera
|
||||
OV5640_init(&conf->ssdv_conf);
|
||||
|
||||
// Sample data from DCMI through DMA into RAM
|
||||
tries = 5; // Try 5 times at maximum
|
||||
do { // Try capturing image until capture successful
|
||||
status = OV5640_Snapshot2RAM();
|
||||
} while(!status && --tries);
|
||||
|
||||
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
|
||||
|
||||
} while(OV5640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
|
||||
|
||||
conf->ssdv_conf.res = RES_MAX; // Revert register
|
||||
|
||||
} else { // Static resolution
|
||||
|
||||
// Init camera
|
||||
OV5640_init(&conf->ssdv_conf);
|
||||
|
||||
// Sample data from DCMI through DMA into RAM
|
||||
tries = 5; // Try 5 times at maximum
|
||||
do { // Try capturing image until capture successful
|
||||
status = OV5640_Snapshot2RAM();
|
||||
} while(!status && --tries);
|
||||
|
||||
}
|
||||
|
||||
// Switch off camera
|
||||
OV5640_deinit();
|
||||
|
||||
// Get image
|
||||
image_len = OV5640_getBuffer(&image);
|
||||
TRACE_INFO("IMG > Image size: %d bytes", image_len);
|
||||
|
||||
} else { // Camera error
|
||||
|
||||
TRACE_ERROR("IMG > No camera found");
|
||||
|
||||
}
|
||||
|
||||
// Unlock radio
|
||||
TRACE_INFO("IMG > Unlock radio");
|
||||
chMtxUnlock(&interference_mtx);
|
||||
TRACE_INFO("IMG > Unlocked radio");
|
||||
|
||||
// Unlock camera
|
||||
TRACE_INFO("IMG > Unlock camera");
|
||||
chMtxUnlock(&camera_mtx);
|
||||
TRACE_INFO("IMG > Unlocked camera");
|
||||
|
||||
// Encode/Transmit SSDV if image sampled successfully
|
||||
if(status)
|
||||
{
|
||||
gimage_id++;
|
||||
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
|
||||
encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
time = waitForTrigger(time, &conf->trigger);
|
||||
|
|
|
@ -114,7 +114,6 @@ typedef struct {
|
|||
uint8_t *ram_buffer; // Camera Buffer
|
||||
uint16_t ram_size; // Size of buffer
|
||||
uint16_t size_sampled; // Actual image data size (do not set in config)
|
||||
bool no_camera; // Camera disabled
|
||||
bool redundantTx; // Redundand packet transmission (APRS only)
|
||||
} ssdv_conf_t;
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue