/**
  ******************************************************************************
  * @file    dcmi_ov9655.c
  * @author  MCD Application Team
  * @version V1.0.1
  * @date    06-September-2012
  * @brief   This file includes the driver for OV9655 Camera module mounted on 
  *          STM322xG-EVAL board RevA and RevB.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "global_includes.h"
#include "dcmi_ov9655.h"

/** @addtogroup DCMI_OV9655_Camera
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define  TIMEOUT  2

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* QQVGA 160x120 */
static uint8_t OV9655_QQVGA[][2]=
{
 { 0x00, 0x00}, 
 { 0x01,  0x80}, 
 { 0x02,  0x80}, 
 { 0x03,  0x02}, 
 { 0x04,  0x03}, 
 { 0x09,  0x01}, 
 { 0x0b,  0x57}, 
 { 0x0e,  0x61}, 
 { 0x0f,  0x40}, 
 { 0x11,  0x01}, 
 { 0x12,  0x62}, 
 { 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}, 
 { 0x32,  0xa4}, 
 { 0x33,  0x00}, 
 { 0x34,  0x3f}, 
 { 0x35,  0x00}, 
 { 0x36,  0x3a}, 
 { 0x38,  0x72}, 
 { 0x39,  0x57}, 
 { 0x3a,  0xcc}, 
 { 0x3b,  0x04}, 
 { 0x3d,  0x99}, 
 { 0x3e,  0x0e}, 
 { 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}, 
 { 0x72,  0x22}, 
 { 0x73,  0x02}, 
 { 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}, 
 { 0xc7,  0x82}, 
 { 0xc9,  0xe0}, 
 { 0xca,  0xe8}, 
 { 0xcb,  0xf0}, 
 { 0xcc,  0xd8}, 
 { 0xcd,  0x93}, 

 { 0x12,  0x63}, 
 { 0x40,  0x10}, 
 { 0x15,  0x08}, 

 { 0x55,  0x30}, 
};

/* QVGA 360x240 */
static uint8_t OV9655_QVGA[][2]=
{
 { 0x00,  0x00}, 
 { 0x01,  0x80}, 
 { 0x02,  0x80}, 
 { 0x03,  0x02}, 
 { 0x04,  0x03}, 
 { 0x09,  0x01}, 
 { 0x0b,  0x57}, 
 { 0x0e,  0x61}, 
 { 0x0f,  0x40}, 
 { 0x11,  0x01}, 
 { 0x12,  0x62}, 
 { 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}, 
 { 0x32,  0x12}, 
 { 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}, 
 { 0x72,  0x11}, 
 { 0x73,  0x01}, 
 { 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}, 
 { 0xc7,  0x81}, 
 { 0xc9,  0xe0}, 
 { 0xca,  0xe8}, 
 { 0xcb,  0xf0}, 
 { 0xcc,  0xd8}, 
 { 0xcd,  0x93}, 

 { 0x12,  0x63}, 
 { 0x40,  0x10}, 
 { 0x15,  0x08}, 

 { 0x55,  0x30}, 
};

/* VGA 640x480 */
const uint8_t OV9655_VGA[][2] = 
{
 { 0x12,  0x80}, 
 { 0x00,  0x00}, 
 { 0x01,  0x80}, 
 { 0x02,  0x80}, 
 { 0x03,  0x12}, 
 { 0x04,  0x03}, 
 { 0x0b,  0x57}, 
 { 0x0e,  0x61}, 
 { 0x0f,  0x40}, 
 { 0x11,  0x01}, 
 { 0x12,  0x62}, 
 { 0x13,  0xc7}, 
 { 0x14,  0x3a}, 
 { 0x16,  0x24}, 
 { 0x17,  0x16}, 
 { 0x18,  0x02}, 
 { 0x19,  0x01}, 
 { 0x1a,  0x3d}, 
 { 0x1e,  0x04}, 
 { 0x24,  0x3c}, 
 { 0x25,  0x36}, 
 { 0x26,  0x72}, 
 { 0x27,  0x08}, 
 { 0x28,  0x08}, 
 { 0x29,  0x15}, 
 { 0x2a,  0x00}, 
 { 0x2b,  0x00}, 
 { 0x2c,  0x08}, 
 { 0x32,  0xff}, 
 { 0x33,  0x00}, 
 { 0x34,  0x3F}, 
 { 0x35,  0x00}, 
 { 0x36,  0xfa}, 
 { 0x38,  0x72}, 
 { 0x39,  0x57}, 
 { 0x3a,  0xcc}, 
 { 0x3b,  0x04}, 
 { 0x3d,  0x99}, 
 { 0x3e,  0x0c}, 
 { 0x3f,  0xc1}, 
 { 0x40,  0xc0}, 
 { 0x41,  0x00}, 
 { 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}, 
 { 0x72,  0x00}, 
 { 0x73,  0x02}, //0x00
 { 0x74,  0x3a}, 
 { 0x75,  0x35}, 
 { 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,  0x8d}, 
 { 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}, 
 { 0xbb,  0xae}, 
 { 0xbc,  0x7f}, 
 { 0xbd,  0x7f}, 
 { 0xbe,  0x7f}, 
 { 0xbf,  0x7f}, 
 { 0xc0,  0xaa}, 
 { 0xc1,  0xc0}, 
 { 0xc2,  0x01}, 
 { 0xc3,  0x4e}, 
 { 0xc6,  0x05}, 
 { 0xc7,  0x80}, 
 { 0xc9,  0xe0}, 
 { 0xca,  0xe8}, 
 { 0xcb,  0xf0}, 
 { 0xcc,  0xd8}, 
 { 0xcd,  0x93}, 

 { 0x12,  0x63}, 
 { 0x40,  0x10}, 
 { 0x15,  0x08},
};

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
static uint8_t OV9655_TimeoutUserCallback(void);
/**
  * @brief  Initializes the hardware resources (I2C and GPIO) used to configure 
  *         the OV9655 camera.
  * @param  None
  * @retval None
  */
void OV9655_HW_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  I2C_InitTypeDef  I2C_InitStruct;

  /*** Configures the DCMI GPIOs to interface with the OV9655 camera module ***/
  /* Enable DCMI GPIOs clocks */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOH |
                         RCC_AHB1Periph_GPIOI, ENABLE);

  /* Connect DCMI pins to AF13 */
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI);

  GPIO_PinAFConfig(GPIOH, GPIO_PinSource8, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource9, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource10, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource11, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource12, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource14, GPIO_AF_DCMI);

  GPIO_PinAFConfig(GPIOI, GPIO_PinSource5, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOI, GPIO_PinSource6, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOI, GPIO_PinSource7, GPIO_AF_DCMI);
  GPIO_PinAFConfig(GPIOI, GPIO_PinSource4, GPIO_AF_DCMI);

  /* DCMI GPIO configuration */
  /* D0..D4(PH9/10/11/12/14), HSYNC(PH8) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |
                                GPIO_Pin_12 | GPIO_Pin_14| GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOH, &GPIO_InitStructure);

  /* D5..D7(PI4/6/7), VSYNC(PI5) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_5;
  GPIO_Init(GPIOI, &GPIO_InitStructure);

  /* PCLK(PA6) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /****** Configures the I2C1 used for OV9655 camera module configuration *****/
 /* I2C1 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  /* GPIOB clock enable */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

  /* Connect I2C1 pins to AF4 */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);

  /* Configure I2C1 GPIOs */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  /* Configure I2C1 */
  /* I2C DeInit */
  I2C_DeInit(I2C1);

  /* Enable the I2C peripheral */
  I2C_Cmd(I2C1, ENABLE);

  /* Set the I2C structure parameters */
  I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStruct.I2C_OwnAddress1 = 0xFE;
  I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStruct.I2C_ClockSpeed = 30000;

  /* Initialize the I2C peripheral w/ selected parameters */
  I2C_Init(I2C1, &I2C_InitStruct);
}

/**
  * @brief  Resets the OV9655 camera.
  * @param  None
  * @retval None
  */
void OV9655_Reset(void)
{
  OV9655_WriteReg(OV9655_COM7, 0x80);
}

/**
  * @brief  Reads the OV9655 Manufacturer identifier.
  * @param  OV9655ID: pointer to the OV9655 Manufacturer identifier.
  * @retval None
  */
void OV9655_ReadID(OV9655_IDTypeDef* OV9655ID)
{
  OV9655ID->Manufacturer_ID1 = OV9655_ReadReg(OV9655_MIDH);
  OV9655ID->Manufacturer_ID2 = OV9655_ReadReg(OV9655_MIDL);
  OV9655ID->Version = OV9655_ReadReg(OV9655_VER);
  OV9655ID->PID = OV9655_ReadReg(OV9655_PID);
}

/**
  * @brief  Configures the DCMI/DMA to capture image from the OV9655 camera.
  * @param  ImageFormat: Image format BMP or JPEG
  * @param  BMPImageSize: BMP Image size  
  * @retval None
  */
void OV9655_Init(ImageFormat_TypeDef ImageFormat)
{
  DCMI_InitTypeDef DCMI_InitStructure;
  DMA_InitTypeDef  DMA_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;

  /*** Configures the DCMI to interface with the OV9655 camera module ***/
  /* Enable DCMI clock */
  RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);

  /* DCMI configuration */
  DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_SnapShot;
  DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;
  DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Falling;
  DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High;
  DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High;
  DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;
  DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;

  /* Configures the DMA2 to transfer Data from DCMI */
  /* Enable DMA2 clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  
  /* DMA2 Stream1 Configuration */
  DMA_DeInit(DMA2_Stream1);

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;
  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;	
  DMA_InitStructure.DMA_Memory0BaseAddr = FSMC_SRAM_ADDRESS;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 0xFFFF;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  switch(ImageFormat)
  {
    case BMP_QQVGA:
    {
      /* DCMI configuration */
      DCMI_Init(&DCMI_InitStructure);

      /* DMA2 IRQ channel Configuration */
      DMA_InitStructure.DMA_BufferSize = 0x2580;
      DMA_Init(DMA2_Stream1, &DMA_InitStructure);
      DMA_ITConfig(DMA2_Stream1, DMA_IT_TC | DMA_IT_TE, ENABLE);

      /* DMA2 IRQ channel Configuration */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn ;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStructure);
      break;
    }
    case BMP_QVGA:
    {
      /* DCMI configuration */ 
      DCMI_Init(&DCMI_InitStructure);

      /* DMA2 IRQ channel Configuration */
      DMA_InitStructure.DMA_BufferSize = 0x9600;
      DMA_Init(DMA2_Stream1, &DMA_InitStructure);
      DMA_ITConfig(DMA2_Stream1, DMA_IT_TC | DMA_IT_TE, ENABLE);

      /* DMA2 IRQ channel Configuration */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn ;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStructure);
      break;
    }
    default:
    {
      /* DCMI configuration */ 
      DCMI_Init(&DCMI_InitStructure);

      /* DMA2 IRQ channel Configuration */
      DMA_InitStructure.DMA_BufferSize = 0x2580;
      DMA_Init(DMA2_Stream1, &DMA_InitStructure);
      DMA_ITConfig(DMA2_Stream1, DMA_IT_TC | DMA_IT_TE, ENABLE);

      /* DMA2 IRQ channel Configuration */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
      NVIC_Init(&NVIC_InitStructure);
      break;
    }
  }
}

/**
  * @brief  Configures the OV9655 camera in VGA mode.
  * @param  None
  * @retval None
  */
void OV9655_VGAConfig(void)
{
  uint32_t i;

  OV9655_Reset();
  vTaskDelay(200);

  /* Initialize OV9655 */
  for(i=0; i<(sizeof(OV9655_VGA)/2); i++)
  {
    OV9655_WriteReg(OV9655_VGA[i][0], OV9655_VGA[i][1]);
    vTaskDelay(4);
  }
}

/**
  * @brief  Configures the OV9655 camera in QQVGA mode.
  * @param  None
  * @retval None
  */
void OV9655_QQVGAConfig(void)
{
  uint32_t i;

  OV9655_Reset();
  vTaskDelay(200);

  /* Initialize OV9655 */
  for(i=0; i<(sizeof(OV9655_QQVGA)/2); i++)
  {
    OV9655_WriteReg(OV9655_QQVGA[i][0], OV9655_QQVGA[i][1]);
    vTaskDelay(10);
  }
}

/**
  * @brief  SConfigures the OV9655 camera in QVGA mode.
  * @param  None
  * @retval None
  */
void OV9655_QVGAConfig(void)
{
  uint32_t i;

  OV9655_Reset();
  vTaskDelay(200);

  /* Initialize OV9655 */
  for(i=0; i<(sizeof(OV9655_QVGA)/2); i++)
  {
    OV9655_WriteReg(OV9655_QVGA[i][0], OV9655_QVGA[i][1]);
    vTaskDelay(4);
  }
}

/**
  * @brief  Configures the OV9655 camera brightness.
  * @param  Brightness: Brightness value, where Brightness can be: 
  *         positively (0x01 ~ 0x7F) and negatively (0x80 ~ 0xFF)
  * @retval None
  */
void OV9655_BrightnessConfig(uint8_t Brightness)
{
  OV9655_WriteReg(OV9655_BRTN, Brightness);
}

/**
  * @brief  Writes a byte at a specific Camera register
  * @param  Addr: OV9655 register address
  * @param  Data: data to be written to the specific register
  * @retval 0x00 if write operation is OK
  *        { 0xFF if timeout condition occured (device not connected or bus error).
  */
uint8_t OV9655_WriteReg(uint16_t Addr, uint8_t Data)
{
  uint32_t timeout = DCMI_TIMEOUT_MAX;

  __disable_irq();

  /* Generate the Start Condition */
  I2C_GenerateSTART(I2C1, ENABLE);

  /* Test on I2C1 EV5 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Send DCMI selcted device slave Address for write */
  I2C_Send7bitAddress(I2C1, OV9655_DEVICE_WRITE_ADDRESS, I2C_Direction_Transmitter);

  /* Test on I2C1 EV6 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }
 
  /* Send I2C1 location address LSB */
  I2C_SendData(I2C1, (uint8_t)(Addr));

  /* Test on I2C1 EV8 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Send Data */
  I2C_SendData(I2C1, Data);

  /* Test on I2C1 EV8 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }  
 
  /* Send I2C1 STOP Condition */
  I2C_GenerateSTOP(I2C1, ENABLE);

  __enable_irq ();
  /* If operation is OK, return 0 */
  return 0;
}

/**
  * @brief  Reads a byte from a specific Camera register
  * @param  Addr: OV9655 register address.
  * @retval data read from the specific register or 0xFF if timeout condition
  *         occured.
  */
uint8_t OV9655_ReadReg(uint16_t Addr)
{
  uint32_t timeout = DCMI_TIMEOUT_MAX;
  uint8_t Data = 0;

  __disable_irq();
  /* Generate the Start Condition */
  I2C_GenerateSTART(I2C1, ENABLE);

  /* Test on I2C1 EV5 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Send DCMI selcted device slave Address for write */
  I2C_Send7bitAddress(I2C1, OV9655_DEVICE_READ_ADDRESS, I2C_Direction_Transmitter);

  /* Test on I2C1 EV6 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Send I2C1 location address LSB */
  I2C_SendData(I2C1, (uint8_t)(Addr));

  /* Test on I2C1 EV8 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Clear AF flag if arised */
  I2C1->SR1 |= (uint16_t)0x0400;

  /* Generate the Start Condition */
  I2C_GenerateSTART(I2C1, ENABLE);

  /* Test on I2C1 EV6 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Send DCMI selcted device slave Address for write */
  I2C_Send7bitAddress(I2C1, OV9655_DEVICE_READ_ADDRESS, I2C_Direction_Receiver);

  /* Test on I2C1 EV6 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Prepare an NACK for the next data received */
  I2C_AcknowledgeConfig(I2C1, DISABLE);

  /* Test on I2C1 EV7 and clear it */
  timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
  {
    /* If the timeout vTaskDelay is exeeded, exit with error code */
    if ((timeout--) == 0)
    {
      __enable_irq ();
      return OV9655_TimeoutUserCallback();
    }
  }

  /* Prepare Stop after receiving data */
  I2C_GenerateSTOP(I2C1, ENABLE);

  /* Receive the Data */
  Data = I2C_ReceiveData(I2C1);

  __enable_irq ();
  /* return the read data */
  return Data;
}

/**
  * @brief  IOE_TIMEOUT_UserCallback
  * @param  None
  * @retval 0
  */
static uint8_t OV9655_TimeoutUserCallback(void)
{

  I2C_GenerateSTOP(I2C1, ENABLE);
  I2C_SoftwareResetCmd(I2C1, ENABLE);
  I2C_SoftwareResetCmd(I2C1, DISABLE);

  OV9655_HW_Init();

  return 0;
}
/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
