From ceefd722644480fefd019a5e65c4ac1338f1a657 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 28 Mar 2016 16:30:35 +0200 Subject: [PATCH] Improved QVGA sampling time --- ChibiOS | 2 +- Makefile | 12 +- main.c | 417 +++++++++++++++++++++++++------------------------------ 3 files changed, 197 insertions(+), 234 deletions(-) diff --git a/ChibiOS b/ChibiOS index 2698f6f..f5e06da 160000 --- a/ChibiOS +++ b/ChibiOS @@ -1 +1 @@ -Subproject commit 2698f6f13ee3efda9348ef9761a445465900f7ae +Subproject commit f5e06da07e1e515ad481d087c44ca3cd6deb5fa9 diff --git a/Makefile b/Makefile index 964c448..8f7da43 100644 --- a/Makefile +++ b/Makefile @@ -182,21 +182,15 @@ AOPT = TOPT = -mthumb -DTHUMB # Define C warning options here -CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes +CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -std=c99 # Define C++ warning options here -CPPWARN = -Wall -Wextra -Wundef +CPPWARN = -Wall -Wextra -Wundef -std=c99 # # Compiler settings ############################################################################## -# -# Programmer settings -############################################################################## - -STLINK=/src/stlink - ############################################################################## # Start of user section # @@ -224,5 +218,5 @@ RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC include $(RULESPATH)/rules.mk flash: - $(STLINK)/st-flash write build/$(PROJECT).bin 0x8000000 + st-flash write build/$(PROJECT).bin 0x8000000 diff --git a/main.c b/main.c index 5a91a89..d447edf 100644 --- a/main.c +++ b/main.c @@ -57,7 +57,7 @@ #include -//#define QVGA +#define QVGA #ifdef QVGA #define OV9655_MAXX 320 @@ -67,7 +67,7 @@ #define OV9655_MAXY 480 #endif -#define OV9655_BUFFER_SIZE OV9655_MAXX*16 +#define OV9655_BUFFER_SIZE OV9655_MAXX*OV9655_MAXY #define OV9655_DCMI_BASE_ADR ((uint32_t)0x50050000) #define OV9655_DCMI_REG_DR_OFFSET 0x28 @@ -76,18 +76,19 @@ #define OV9655_I2C_ADR 0x30 /* Slave-address vom OV9655 */ -uint16_t ov9655_ram_buffer[2][OV9655_BUFFER_SIZE]; +uint16_t ov9655_ram_buffer[OV9655_BUFFER_SIZE]; volatile uint32_t buffNum = 0; -CACHE_ALIGN BGR RGB16x16[16][16]; -CACHE_ALIGN color_t R[16][16], G[16][16], B[16][16]; -CACHE_ALIGN conv Y8x8[2][2][8][8]; // four 8x8 blocks - 16x16 -CACHE_ALIGN conv Cb8x8[8][8]; -CACHE_ALIGN conv Cr8x8[8][8]; +BGR RGB16x16[16][16] __attribute__((section(".ram4"))); +color_t R[16][16], G[16][16], B[16][16] __attribute__((section(".ram4"))); +conv Y8x8[2][2][8][8] __attribute__((section(".ram4"))); // four 8x8 blocks - 16x16 +conv Cb8x8[8][8]; conv Cb8x8[8][8] __attribute__((section(".ram4"))); +conv Cr8x8[8][8]; conv Cr8x8[8][8] __attribute__((section(".ram4"))); uint64_t subsclk = 0; uint64_t readclk = 0; -static uint8_t jpeg[50*1024]; -static uint32_t jpeg_pos = 0; +uint8_t jpeg[30*1024]; // JPEG output buffer +uint32_t jpeg_pos = 0; +bool samplingFinished; uint8_t rxbuf[1]; @@ -98,67 +99,53 @@ static const I2CConfig i2cfg2 = { FAST_DUTY_CYCLE_2, }; -static void txend1(UARTDriver *uartp) { - (void)uartp; -} -static void txend2(UARTDriver *uartp) { - - (void)uartp; -} -static void rxerr(UARTDriver *uartp, uartflags_t e) { - (void)uartp; - (void)e; -} -static void rxchar(UARTDriver *uartp, uint16_t c) { - (void)uartp; - (void)c; -} -static void rxend(UARTDriver *uartp) { - (void)uartp; -} - // UART interface static UARTConfig uart_cfg_1 = { - txend1, - txend2, - rxend, - rxchar, - rxerr, - 2000000, + NULL, + NULL, + NULL, + NULL, + NULL, + 115200, 0, USART_CR2_LINEN, 0 }; -// I2C camera configuration for VGA/QVGA resolution -static uint8_t OV9655_CONFIG[]= +// I2C camera configuration for QVGA resolution +static const uint8_t OV9655_CONFIG[] = { - 0x00,0x00,0x01,0x80,0x02,0x80,0x03,0x02,0x04,0x03,0x09,0x01, - 0x0b,0x57,0x0e,0x61,0x0f,0x40,0x11,0x01,0x13,0xc7,0x14,0x3a, - 0x16,0x24,0x17,0x18,0x18,0x04,0x19,0x01,0x1a,0x81,0x1e,0x00, - 0x24,0x3c,0x25,0x36,0x26,0x72,0x27,0x08,0x28,0x08,0x29,0x15, - 0x2a,0x00,0x2b,0x00,0x2c,0x08,0x33,0x00,0x34,0x3f,0x35,0x00, - 0x36,0x3a,0x38,0x72,0x39,0x57,0x3a,0xcc,0x3b,0x04,0x3d,0x99, - 0x3e,0x02,0x3f,0xc1,0x40,0xc0,0x41,0x41,0x42,0xc0,0x43,0x0a, - 0x44,0xf0,0x45,0x46,0x46,0x62,0x47,0x2a,0x48,0x3c,0x4a,0xfc, - 0x4b,0xfc,0x4c,0x7f,0x4d,0x7f,0x4e,0x7f,0x4f,0x98,0x50,0x98, - 0x51,0x00,0x52,0x28,0x53,0x70,0x54,0x98,0x58,0x1a,0x59,0x85, - 0x5a,0xa9,0x5b,0x64,0x5c,0x84,0x5d,0x53,0x5e,0x0e,0x5f,0xf0, - 0x60,0xf0,0x61,0xf0,0x62,0x00,0x63,0x00,0x64,0x02,0x65,0x20, - 0x66,0x00,0x69,0x0a,0x6b,0x5a,0x6c,0x04,0x6d,0x55,0x6e,0x00, - 0x6f,0x9d,0x70,0x21,0x71,0x78,0x74,0x10,0x75,0x10,0x76,0x01, - 0x77,0x02,0x7A,0x12,0x7B,0x08,0x7C,0x16,0x7D,0x30,0x7E,0x5e, - 0x7F,0x72,0x80,0x82,0x81,0x8e,0x82,0x9a,0x83,0xa4,0x84,0xac, - 0x85,0xb8,0x86,0xc3,0x87,0xd6,0x88,0xe6,0x89,0xf2,0x8a,0x24, - 0x8c,0x80,0x90,0x7d,0x91,0x7b,0x9d,0x02,0x9e,0x02,0x9f,0x7a, - 0xa0,0x79,0xa1,0x40,0xa4,0x50,0xa5,0x68,0xa6,0x4a,0xa8,0xc1, - 0xa9,0xef,0xaa,0x92,0xab,0x04,0xac,0x80,0xad,0x80,0xae,0x80, - 0xaf,0x80,0xb2,0xf2,0xb3,0x20,0xb4,0x20,0xb5,0x00,0xb6,0xaf, - 0xb6,0xaf,0xbb,0xae,0xbc,0x7f,0xbd,0x7f,0xbe,0x7f,0xbf,0x7f, - 0xbf,0x7f,0xc0,0xaa,0xc1,0xc0,0xc2,0x01,0xc3,0x4e,0xc6,0x05, - 0xc9,0xe0,0xca,0xe8,0xcb,0xf0,0xcc,0xd8,0xcd,0x93,0x12,0x63, - 0x40,0x10, + 0x0b,0x57,0x0e,0x61,0x0f,0x40,0x13,0xc7,0x14,0x3a,0x16,0x24, + 0x17,0x18,0x18,0x04,0x19,0x01,0x1a,0x81,0x1e,0x00,0x24,0x3c, + 0x25,0x36,0x26,0x72,0x27,0x08,0x28,0x08,0x29,0x15,0x2a,0x00, + 0x2b,0x00,0x2c,0x08,0x33,0x00,0x34,0x3f,0x35,0x00,0x36,0x3a, + 0x38,0x72,0x39,0x57,0x3a,0xcc,0x3b,0x04,0x3d,0x99,0x3e,0x02, + 0x3f,0xc1,0x40,0xc0,0x41,0x41,0x42,0xc0,0x43,0x0a,0x44,0xf0, + 0x45,0x46,0x46,0x62,0x47,0x2a,0x48,0x3c,0x4a,0xfc,0x4b,0xfc, + 0x4c,0x7f,0x4d,0x7f,0x4e,0x7f,0x4f,0x98,0x50,0x98,0x51,0x00, + 0x52,0x28,0x53,0x70,0x54,0x98,0x58,0x1a,0x59,0x85,0x5a,0xa9, + 0x5b,0x64,0x5c,0x84,0x5d,0x53,0x5e,0x0e,0x5f,0xf0,0x60,0xf0, + 0x61,0xf0,0x62,0x00,0x63,0x00,0x64,0x02,0x65,0x20,0x66,0x00, + 0x69,0x0a, + #ifdef SEC1_80 + 0x6b,0xda, + #else + 0x6b,0x5a, + #endif + 0x6c,0x04,0x6d,0x55,0x6e,0x00,0x6f,0x9d, + 0x70,0x21,0x71,0x78,0x74,0x10,0x75,0x10,0x76,0x01,0x77,0x02, + 0x7A,0x12,0x7B,0x08,0x7C,0x16,0x7D,0x30,0x7E,0x5e,0x7F,0x72, + 0x80,0x82,0x81,0x8e,0x82,0x9a,0x83,0xa4,0x84,0xac,0x85,0xb8, + 0x86,0xc3,0x87,0xd6,0x88,0xe6,0x89,0xf2,0x8a,0x24,0x8c,0x80, + 0x90,0x7d,0x91,0x7b,0x9d,0x02,0x9e,0x02,0x9f,0x7a,0xa0,0x79, + 0xa1,0x40,0xa4,0x50,0xa5,0x68,0xa6,0x4a,0xa8,0xc1,0xa9,0xef, + 0xaa,0x92,0xab,0x04,0xac,0x80,0xad,0x80,0xae,0x80,0xaf,0x80, + 0xb2,0xf2,0xb3,0x20,0xb4,0x20,0xb5,0x00,0xb6,0xaf,0xb6,0xaf, + 0xbb,0xae,0xbc,0x7f,0xbd,0x7f,0xbe,0x7f,0xbf,0x7f,0xbf,0x7f, + 0xc0,0xaa,0xc1,0xc0,0xc2,0x01,0xc3,0x4e,0xc6,0x05,0xc9,0xe0, + 0xca,0xe8,0xcb,0xf0,0xcc,0xd8,0xcd,0x93,0x12,0x63,0x40,0x10, + 0x11,0x00, #ifdef QVGA 0x15,0x08,0x32,0x12,0x72,0x11,0x73,0x01,0xc7,0x81 #else @@ -168,10 +155,10 @@ static uint8_t OV9655_CONFIG[]= void OV9655_RAM2SD(void); -void OV9655_Snapshot2RAM(void); +bool OV9655_Snapshot2RAM(void); void OV9655_InitDMA(void); void OV9655_InitDCMI(void); -void OV9655_InitGPIO(bool fast); +void OV9655_InitGPIO(void); void dma_avail(uint32_t flags); @@ -192,29 +179,6 @@ __inline color RGB2Cr(const color r, const color g, const color b) return (8421376 + 32767*r - 27438*g - 5329*b) >> 16; } -// chroma subsampling, i.e. converting a 16x16 RGB block into 8x8 Cb and Cr -void subsample(const BGR rgb[16][16], conv cb[8][8], conv cr[8][8]) -{ - unsigned r,c; - for (r = 0; r < 8; r++) - for (c = 0; c < 8; c++) - { - unsigned rr = (r<<1); - unsigned cc = (c<<1); - - // calculating an average values - color_t R = (rgb[rr][cc].Red + rgb[rr][cc+1].Red - + rgb[rr+1][cc].Red + rgb[rr+1][cc+1].Red) >> 2; - color_t G = (rgb[rr][cc].Green + rgb[rr][cc+1].Green - + rgb[rr+1][cc].Green + rgb[rr+1][cc+1].Green) >> 2; - color_t B = (rgb[rr][cc].Blue + rgb[rr][cc+1].Blue - + rgb[rr+1][cc].Blue + rgb[rr+1][cc+1].Blue) >> 2; - - cb[r][c] = (conv)RGB2Cb(R, G, B)-128; - cr[r][c] = (conv)RGB2Cr(R, G, B)-128; - } -} - void subsample2(BGR rgb[16][16], conv Y[2][2][8][8], conv cb[8][8], conv cr[8][8]) { unsigned r, c; @@ -262,54 +226,6 @@ void subsample2(BGR rgb[16][16], conv Y[2][2][8][8], conv cb[8][8], conv cr[8][8 } } -void subsample3(const color_t R[16][16], const color_t G[16][16], const color_t B[16][16], - conv Y[2][2][8][8], conv cb[8][8], conv cr[8][8]) -{ - unsigned r, c; - - for (r = 0; r < 16; r += 2) - for (c = 0; c < 16; c += 2) - { - //unsigned rr, cc; - unsigned i, j, k, l; - unsigned sR, sG, sB; - unsigned R1, G1, B1; - - i = r >> 3, j = c >> 3, - k = r & 7, l = c & 7; - - sR = R1 = R[r][c], - sG = G1 = G[r][c], - sB = B1 = B[r][c]; - Y[i][j][k][l] = RGB2Y(R1, G1, B1)-128; - - sR += R1 = R[r][c+1], - sG += G1 = G[r][c+1], - sB += B1 = B[r][c+1]; - Y[i][j][k][l+1] = RGB2Y(R1, G1, B1)-128; - - sR += R1 = R[r+1][c], - sG += G1 = G[r+1][c], - sB += B1 = B[r+1][c]; - Y[i][j][k+1][l] = RGB2Y(R1, G1, B1)-128; - - sR += R1 = R[r+1][c+1], - sG += G1 = G[r+1][c+1], - sB += B1 = B[r+1][c+1]; - Y[i][j][k+1][l+1] = RGB2Y(R1, G1, B1)-128; - - // calculating an average values - R1 = sR >> 2, - G1 = sG >> 2, - B1 = sB >> 2; - - //rr = r >> 1, cc = c >> 1; - - cb[r>>1][c>>1] = (conv)RGB2Cb(R1, G1, B1)-128; - cr[r>>1][c>>1] = (conv)RGB2Cr(R1, G1, B1)-128; - } -} - void write_jpeg(const unsigned char buff[], unsigned size) { if(jpeg_pos+size > sizeof(jpeg)) // Buffer overflow protection @@ -319,68 +235,80 @@ void write_jpeg(const unsigned char buff[], unsigned size) jpeg_pos += size; } -void OV9655_Snapshot2RAM(void) +/** + * Captures an image from the camera. + * Returns false in case of an error. + */ +bool OV9655_Snapshot2RAM(void) { + palSetPad(GPIOG, 14); + + // Reset output pointer + jpeg_pos = 0; + buffNum = 0; + // Add JPEG header to buffer huffman_start(OV9655_MAXY & -16, OV9655_MAXX & -16); // DCMI init OV9655_InitDCMI(); - // DCMI enable - DCMI->CR |= (uint32_t)DCMI_CR_ENABLE; - // Capture enable - DCMI->CR |= (uint32_t)DCMI_CR_CAPTURE; // Encode JPEG data - uint32_t lines = 0; - while(lines < OV9655_MAXY / 16) - { - unsigned x,xb,yb; - uint16_t color; + samplingFinished = false; + systime_t timeout = chVTGetSystemTimeX() + MS2ST(1000); // Timeout 1sec + while(!samplingFinished && chVTGetSystemTimeX() < timeout) + chThdSleepMilliseconds(1); + + if(chVTGetSystemTimeX() >= timeout) { // Timeout has occurred - while(buffNum <= lines) // Wait for data by DMA - (void)0; + return false; - palSetPad(GPIOG, 14); - for (x = 0; x < OV9655_MAXX-15; x += 16) + } else { // Data successfully sampled + + unsigned y,x,xb,yb; + uint16_t color; + + for(y = 0; y < OV9655_MAXY-15; y += 16) { - // Create 16x16 block - for(yb=0;yb<16;yb++) { - for(xb=x;xb>3); // 6bit green - RGB16x16[yb][xb-x].Red = ((color&0xF800)>>8); // 5bit red + for(x = 0; x < OV9655_MAXX-15; x += 16) + { + // Create 16x16 block + for(yb=0; yb<16; yb++) { + for(xb=0; xb<16; xb++) { + color=ov9655_ram_buffer[(yb+y)*OV9655_MAXX + (xb+x)]; + RGB16x16[yb][xb].Blue = ((color&0x001F)<<3); // 5bit blue + RGB16x16[yb][xb].Green = ((color&0x07E0)>>3); // 6bit green + RGB16x16[yb][xb].Red = ((color&0xF800)>>8); // 5bit red + } } - } - // Encode JPEG - // getting subsampled Cb and Cr - subsample2(RGB16x16, Y8x8, Cb8x8, Cr8x8); - //subsample3(R, G, B, Y8x8, Cb8x8, Cr8x8); - - dct(Y8x8[0][0], Y8x8[0][0]); // 1 Y-transform - dct(Y8x8[0][1], Y8x8[0][1]); // 2 Y-transform - dct(Y8x8[1][0], Y8x8[1][0]); // 3 Y-transform - dct(Y8x8[1][1], Y8x8[1][1]); // 4 Y-transform - dct(Cb8x8, Cb8x8); // Cb-transform - dct(Cr8x8, Cr8x8); // Cr-transform - - huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[0][0]); - huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[0][1]); - huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[1][0]); - huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[1][1]); - huffman_encode(HUFFMAN_CTX_Cb, (short*)Cb8x8); - huffman_encode(HUFFMAN_CTX_Cr, (short*)Cr8x8); + // Encode JPEG + // getting subsampled Cb and Cr + subsample2(RGB16x16, Y8x8, Cb8x8, Cr8x8); + + dct(Y8x8[0][0], Y8x8[0][0]); // 1 Y-transform + dct(Y8x8[0][1], Y8x8[0][1]); // 2 Y-transform + dct(Y8x8[1][0], Y8x8[1][0]); // 3 Y-transform + dct(Y8x8[1][1], Y8x8[1][1]); // 4 Y-transform + dct(Cb8x8, Cb8x8); // Cb-transform + dct(Cr8x8, Cr8x8); // Cr-transform + + huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[0][0]); + huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[0][1]); + huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[1][0]); + huffman_encode(HUFFMAN_CTX_Y, (short*)Y8x8[1][1]); + huffman_encode(HUFFMAN_CTX_Cb, (short*)Cb8x8); + huffman_encode(HUFFMAN_CTX_Cr, (short*)Cr8x8); + } } - palClearPad(GPIOG, 14); - - lines++; } // Add JPEG footer to buffer - chThdSleepMilliseconds(100); huffman_stop(); + + palClearPad(GPIOG, 14); + + return true; } /** @@ -399,7 +327,8 @@ void OV9655_RAM2SD(void) void dma_avail(uint32_t flags) { (void)flags; - buffNum++; + samplingFinished = true; + dmaStreamDisable(STM32_DMA2_STREAM1); } /** @@ -410,18 +339,23 @@ void OV9655_InitDMA(void) const stm32_dma_stream_t *stream = STM32_DMA2_STREAM1; dmaStreamAllocate(stream, 10, (stm32_dmaisr_t)dma_avail, NULL); dmaStreamSetPeripheral(stream, ((uint32_t*)OV9655_DCMI_REG_DR_ADDRESS)); - dmaStreamSetMemory0(stream, (uint32_t)&ov9655_ram_buffer[0]); - dmaStreamSetMemory1(stream, (uint32_t)&ov9655_ram_buffer[1]); + dmaStreamSetMemory0(stream, (uint32_t)ov9655_ram_buffer); dmaStreamSetTransactionSize(stream, OV9655_BUFFER_SIZE/sizeof(uint16_t)); dmaStreamSetMode(stream, STM32_DMA_CR_CHSEL(1) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MINC | STM32_DMA_CR_PSIZE_WORD | - STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_DBM | - STM32_DMA_CR_MBURST_SINGLE | STM32_DMA_CR_PBURST_SINGLE | - STM32_DMA_CR_TCIE); + STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_MBURST_SINGLE | + STM32_DMA_CR_PBURST_SINGLE | STM32_DMA_CR_TCIE); dmaStreamSetFIFO(stream, STM32_DMA_FCR_FTH_FULL); dmaStreamEnable(stream); } +void OV9655_DeinitDMA(void) +{ + const stm32_dma_stream_t *stream = STM32_DMA2_STREAM1; + dmaStreamDisable(stream); +} + + /** * Initializes DCMI */ @@ -436,26 +370,25 @@ void OV9655_InitDCMI(void) DCMI_CR_FCRC_1 | DCMI_CR_EDM_0 | DCMI_CR_EDM_1); DCMI->CR = DCMI_CaptureMode_SnapShot | DCMI_SynchroMode_Hardware | DCMI_PCKPolarity_Falling | DCMI_VSPolarity_High | DCMI_HSPolarity_High | DCMI_CaptureRate_All_Frame | DCMI_ExtendedDataMode_8b; + + // DCMI enable + DCMI->CR |= (uint32_t)DCMI_CR_ENABLE; + // Capture enable + DCMI->CR |= (uint32_t)DCMI_CR_CAPTURE; +} + +void OV9655_DeinitDCMI(void) +{ + // Clock enable + RCC->AHB2ENR &= ~RCC_AHB2Periph_DCMI; } /** - * Initializes GPIO (for DCMI) and setup a CLOCKOUT pin (PA8) - * which is needed by the camera (XCLK pin) - * The high speed clock supports communication by I2C (XCLK = 8MHz) - * The slow clock allows sampling of VGA resolution in realtime (XCLK = 2.7MHz) - * @param Activate fast clock + * Initializes GPIO (for DCMI) + * The high speed clock supports communication by I2C (XCLK = 16MHz) */ -void OV9655_InitGPIO(bool fast) +void OV9655_InitGPIO(void) { - palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(0)); // PA8 -> XCLK - if(fast) - { - RCC->CFGR = (RCC->CFGR & (uint32_t)0xF8FFFFFF) | (uint32_t)0x04000000; - } else { - RCC->CFGR = (RCC->CFGR & (uint32_t)0xF8FFFFFF) | (uint32_t)0x06000000; // Speed - RCC->CFGR = (RCC->CFGR & (uint32_t)0xFF9FFFFF) | (uint32_t)0x00400000; - } - palSetPadMode(GPIOA, 4, PAL_MODE_ALTERNATE(13)); // HSYNC -> PA4 palSetPadMode(GPIOA, 6, PAL_MODE_ALTERNATE(13)); // PCLK -> PA6 palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(13)); // VSYNC -> PB7 @@ -469,6 +402,46 @@ void OV9655_InitGPIO(bool fast) palSetPadMode(GPIOE, 6, PAL_MODE_ALTERNATE(13)); // D7 -> PE6 } +/** + * Setup a CLOCKOUT pin (PA8) which is needed by the camera (XCLK pin) + */ +void OV9655_InitClockout(void) +{ + palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(0)); // PA8 -> XCLK +} + + +void OV9655_TransmitConfig(void) { + for(uint32_t i=0; i