prettyprint

2021年3月27日 星期六

Digital Camera with STM32 and OV2640 || STM32CubeIDE || OV2640 || ILI9341 16 bits parallel

本實驗使用STM32F407VE、OV2640與ILI9341 16 bits parallel(含XPT2046 touch)製作一組數位相機。

主要功能:

  1. Still photo capture: JPEG format(1280x960)
  2. Movie record: RIFF-AVI Motion JPEG(320x240 at least 3fps)
  3. Playback: 在TFT 上顯示 JPEG與AVI
  4. Media: 影像儲存在SD card
  5. Button: Touch

主要組件:

  1. STM32F407VE: STM32_F4VE開發版
  2. OV2640鏡頭
  3. SD Card
  4. ILI9341 TFT: 16bit parallel mode and XPT2046 touch
  5. 轉接版

開發轉體環境:
STM32CubeIDE and HAL library。

STM32F407VET6使用介面

  1. SDIO
  2. FSMC
  3. DCMI

Middleware:

  1. FATFS
  2. LIBJPEG
本篇著重在DCMI介面的應用,其他有關FSCM(TFT display)與SD card等Device應用請參閱前兩篇文章的詳細就紹:
OV2640 鏡頭:
    本實驗使用的OV2640模組為DVP 8bit(D0~D7)輸出,含VSYNC, HREF, RST, PWDN, SCCB(I2C,SDA,SCL), VCC與GND等接腳。最大輸出為2M,格式可為RGB565或JPEG格式。詳細內容請參考Datasheet。
OV2640 的 Device control registers分為DSP與Sensor兩種模式(0xFF為0 or 1),幾個主要設定resister說明:
  1. 0xDA(when 0xFF=00):Image Mode; 本實驗分別設定為RGB565(0x09)輸出給TFT,Live顯示;  JPEG(0x10)輸出,儲存照片與影片。
  2. 0x5A(when 0xFF=00):Output width; 實際寬度除以4,輸出320給TFT,值為0x50。1280給JPEG照片ˊ值為0x40。
  3. 0x5B(when 0xFF=00):Output Height; 實際寬度除以4,輸出240給TFT,值為0x3C。1280給JPEG照片ˊ值為0xf0。
  4. 0x5C(when 0xFF=00):Bit[1:0]  OUTW, Bit[2] OUTH。輸出1280x960JPEG,值為0x01

STM32F407VE DCMI參數設定:




  • OV2640 0x15 register default value is 00,DCMI mode如上圖設定。
  • DMA: mode: circular peripheral to memory, increment Address(memory)
  • NVIC: DCMI global interrupt
  • GPIO: maximum speed: very height

使用HAL指令

  • HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_CONTINUOUS, pData, Length); or
  • HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, pData, Length);
將OV2640 data output透過DMA2 Stream1 輸入至相對應的memory(pData)。
當沒有拍照或錄影時,設備處於Live Mode。影像以RGB565直接輸出至TFT Display,先將OV2640 control register 設成320x240 RGB565 output mode,再透過HAL_DCMI_Start_DMA指定直接寫到TFT GRAM即可。
如以下程式碼即可完成。

HAL_DCMI_Stop(&hdcmi);
ov2640_Init(0x60, CAMERA_Monitor); //set control register: 320x240, RGB565
lcdSetWindow(0, 0, lcdGetWidth()-1, lcdGetHeight()-13); // ILI9341 display window
status=HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, ((uint32_t)(LCD_BASE1)), lcdGetWidth()*(lcdGetHeight()-12)/2);
LCD_BASE1為 ILI9341 data address  ((uint32_t)0x60080000)

STILL PHOTO Capture:
    STM32F407VET6 RAM分為CCMRAM 64K, RAM 128K。程式部分只能使用到128K,因此當我們擷取靜態照片時設定OV2640 output 為JPEG格式,因此1280x960解析度的照片只要使用90K的RAM即夠用。

DCMI CR

HAL_DCMI_Stop(&hdcmi);
HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, ((uint32_t)dmabuff), MAX_AVI_BUFF/4);
因為DMA transfer為4 bytes,所以上述指令為MAX_AVI_BUFF/4。因為擷取JPEG照片每張大小未知,因此檢查DCMI_CR 的CM 與Capture bit決定是否已擷取完畢。
timeout=0;
while((hdcmi->Instance->CR & 0x03) == 3)
{
    if (timeout > 5000)
    {
	break; //max timeout: 5 seconds
    }
    HAL_Delay(1);
    timeout++;
}
而JPEG stream 以0xFFD8為開始,0xFFD9為結束。因此我們檢查
for (idx = 0; idx<MAX_AVI_BUFF;idx++)
{
    if (headerFinder == 0 && dmabuff[idx] == 0xff && dmabuff[idx+1]==0xD8)
    {
	headerFinder=1;
	f_begin=idx;
    }
    if (headerFinder==1 && dmabuff[idx]== 0xFF && dmabuff[idx+1] == 0xD9 )
    {
	frame_ok=1;
	headerFinder=0;
	f_end = idx+1;
    .
    .
    .

將dmabuff[f_begin]~dmabuff[f_end]寫入SD card即可儲存成JPEG file。

Movie Record:
    輸出大小為320x240,目前實驗的硬體環境只能做到3 fps。採用格式RIFF-AVI Header Motion JPEG格式。有關RIFF請參閱
RIFF(resource interchange file format),下圖為本實驗輸出AVI檔RIFF header。
本實驗輸出的檔案。

Playback:

本實驗利用STM32CubeIDE環境下整合提供的LIBJPEG,作為解碼JPEG到RGB565格式,以便展示靜態照片;
動態影片: 根據RIFF header 的dwMicroSecPerFrame 取得影片中每個影格輸出到TFT 影像的速度,而每個影格由LIST/move/00db取得JPEG影像大小與內容,以正確速度decode JPEG到TFT,完成播放avi 檔。

成果影片:



程式碼:
main.c
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "libjpeg.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ili9341.h"
#include "avi.h"
#include "ov2640.h"
#include "stdio.h"
#include "string.h"
#include "XPT2046_touch.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
DCMI_HandleTypeDef hdcmi;
DMA_HandleTypeDef hdma_dcmi;

I2C_HandleTypeDef hi2c1;

RTC_HandleTypeDef hrtc;

SD_HandleTypeDef hsd;
DMA_HandleTypeDef hdma_sdio_rx;
DMA_HandleTypeDef hdma_sdio_tx;

SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_rx;
DMA_HandleTypeDef hdma_spi2_tx;

SRAM_HandleTypeDef hsram1;

/* USER CODE BEGIN PV */
FATFS fs;
FRESULT res;
FIL file;
HAL_StatusTypeDef status;
uint8_t menuSelected=0;
uint8_t CAMERA_STATE=0;  // 0: live View, 1: Take a picture, 2: movie recording, 3: playback, 4: No SD Card
DIR dir;
long totalFiles=0;
long playingFileIndex=0;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_FSMC_Init(void);
static void MX_RTC_Init(void);
static void MX_DCMI_Init(void);
static void MX_SPI2_Init(void);
static void MX_I2C1_Init(void);
static void MX_SDIO_SD_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */


void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin)
{
	 if (GPIO_Pin == T_IRQ_Pin)
	 {
		  uint16_t cx=0,cy=0;
		  if(XPT2046_TouchPressed())
		  {
			  XPT2046_TouchGetCoordinates(&cx, &cy);
			  if (cy >= 180)
			  {
				  if (cx <= 90 ) menuSelected=1;
				  if (cx >  120 && cx < 200 )
				  {
					  menuSelected=2;
					  if (read_avi_output_status() == 1) set_avi_output_status(2); // stop recording
				  }
				  if (cx > 230  ) menuSelected=3;
			  }

		  }

	 }
}
void takeAPicture()
{
		take_A_Picture(&hdcmi);

}
void movie()
{
	char fn[64];
	memset(fn, 0, 64);

	sprintf(fn, "%smov_%05ld.avi",SDPath, get_fattime()&0xFFFF);
	res = f_open(&file, fn, FA_CREATE_ALWAYS|FA_WRITE);
	if (res==FR_OK)
	{
	  start_output_mjpeg_avi(&file, &hdcmi, (uint8_t)3, (uint8_t)1);
	}
}
void camera_monitor_state()
{
	HAL_DCMI_Stop(&hdcmi);
	ov2640_Init(0x60, CAMERA_Monitor);
	lcdFillCircle(lcdGetWidth()/3, lcdGetHeight()-11, 5, COLOR_BLACK);
	lcdSetWindow(0, 0, lcdGetWidth()-1, lcdGetHeight()-13);
	status=HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, ((uint32_t)(LCD_BASE1)), lcdGetWidth()*(lcdGetHeight()-12)/2);
	CAMERA_STATE=0;
}


void setPlaybackButton()
{
	lcdSetCursor(15, lcdGetHeight()-11);
	lcdPrintf("<");

	lcdSetCursor(lcdGetWidth()/3+14, lcdGetHeight()-11);
	lcdPrintf(">");

	if (playingFileIndex == 2)
	{
		lcdSetCursor(15, lcdGetHeight()-11);
		lcdPrintf(" ");
	}
	if (playingFileIndex == totalFiles)
	{
		lcdSetCursor(lcdGetWidth()/3+14, lcdGetHeight()-11);
		lcdPrintf(" ");
	}

}


void playPrev()
{
	FILINFO fno;
	FRESULT res;

	long i;

	if (playingFileIndex == 1) return;
	playingFileIndex--;
	if (playingFileIndex > 0)
	{
		res = f_opendir(&dir, "0:/");
		if (res == FR_OK)
		{
			for (i = 0; i < playingFileIndex; i++)
			{
				res = f_readdir(&dir, &fno);
				if (res != FR_OK || fno.fname[0] == 0) break;
			}
			if (i == playingFileIndex)
			{
				if (strstr(fno.fname, ".avi"))
				{
					lcdFillTriangle(280, lcdGetHeight()-11, 280, lcdGetHeight()-3, 290, lcdGetHeight()-7, COLOR_GREEN);
					play_avi_file(fno.fname);
					lcdFillTriangle(280, lcdGetHeight()-11, 280, lcdGetHeight()-3, 290, lcdGetHeight()-7, COLOR_BLACK);
				}
				if (strstr(fno.fname, ".jpg"))
					decode_jpeg_file_to_ftf(fno.fname);
			}
			f_closedir(&dir);
		}
	}

	setPlaybackButton();

}

void playNext()
{
	FILINFO fno;
	FRESULT res;
	long i;

	if (playingFileIndex == totalFiles) return;
	playingFileIndex++;

	if (playingFileIndex <= totalFiles)
	{
		res = f_opendir(&dir, "0:/");
		if (res == FR_OK)
		{
			for (i = 0; i < playingFileIndex; i++)
			{
				res = f_readdir(&dir, &fno);
				if (res != FR_OK || fno.fname[0] == 0) break;
			}
			if (i == playingFileIndex)
			{
				if (strstr(fno.fname, ".avi"))
				{
					lcdFillTriangle(280, lcdGetHeight()-11, 280, lcdGetHeight()-3, 290, lcdGetHeight()-7, COLOR_GREEN);
					play_avi_file(fno.fname);
					lcdFillTriangle(280, lcdGetHeight()-11, 280, lcdGetHeight()-3, 290, lcdGetHeight()-7, COLOR_BLACK);
				}
				if (strstr(fno.fname, ".jpg"))
					decode_jpeg_file_to_ftf(fno.fname);
			}
			f_closedir(&dir);

		}
	}

	setPlaybackButton();
}

void play_First_file()
{
	static FILINFO fno;
	FRESULT res;

	res = f_opendir(&dir, "0:/");
	if (res == FR_OK) {
		totalFiles=0;
		for (;;) {
			res = f_readdir(&dir, &fno);
			if (res != FR_OK || fno.fname[0] == 0) break;
			totalFiles++;
		}
		f_closedir(&dir);
		playingFileIndex=1;
		playNext();

	}

}



void setPlaybackMenu()
{
	lcdSetCursor(15, lcdGetHeight()-11);
	lcdPrintf("<      ");
	lcdSetCursor(lcdGetWidth()/3+14, lcdGetHeight()-11);
	lcdPrintf(">     ");
	lcdSetCursor(lcdGetWidth()*2/3+14, lcdGetHeight()-11);
	lcdPrintf("RETURN");
}

void setMainMenu()
{
	lcdSetCursor(15, lcdGetHeight()-11);
	lcdPrintf("PICTURE");
	lcdSetCursor(lcdGetWidth()/3+14, lcdGetHeight()-11);
	lcdPrintf("MOVIE");
	lcdSetCursor(lcdGetWidth()*2/3+14, lcdGetHeight()-11);
	lcdPrintf("PLAY   ");
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_FSMC_Init();
  MX_RTC_Init();
  MX_DCMI_Init();
  MX_SPI2_Init();
  MX_I2C1_Init();
  MX_FATFS_Init();
  MX_LIBJPEG_Init();
  MX_SDIO_SD_Init();
  /* USER CODE BEGIN 2 */


  lcdBacklightOn();
  lcdInit();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */


  lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE);
  lcdFillRGB(COLOR_BLACK);
  lcdSetTextColor(COLOR_WHITE, COLOR_BLACK);
  setMainMenu();

  res = f_mount(&fs, SDPath, 1);
  if (res != FR_OK)
  {
	  lcdSetCursor(lcdGetWidth()*2/-30, lcdGetHeight()/2);
	  lcdPrintf("NO SD Card!");
	  CAMERA_STATE=4;
  }
  camera_monitor_state();

  while (1)
  {
	  if (CAMERA_STATE < 3)
	  {
		  switch (menuSelected)
		  {
		  case 1:
			  HAL_DCMI_Suspend(&hdcmi);
			  lcdFillRect(0, 0, 319, 227, COLOR_BLACK);
			  takeAPicture();
			  menuSelected=0;
			  CAMERA_STATE=1;
			  camera_monitor_state();
			  break;
		  case 2:
			  if (read_avi_output_status() == 0)
			  {
				  HAL_DCMI_Stop(&hdcmi);
				  lcdSetCursor(lcdGetWidth()/3+14, lcdGetHeight()-11);
				  lcdFillCircle(lcdGetWidth()/3, lcdGetHeight()-13, 5, COLOR_RED);
				  movie();
			  }
			  if (read_avi_output_status() == 3) //output finished
			  {
				  //HAL_Delay(1000); // wait for final capture to finish
				  camera_monitor_state();
				  set_avi_output_status(0);
			  }
			  CAMERA_STATE=2;
			  menuSelected=0;
			  break;
		  case 3:
			  menuSelected=0;
			  CAMERA_STATE=3;
			  HAL_DCMI_Stop(&hdcmi);
			  setPlaybackMenu();
			  play_First_file();
			  break;
		  }
	  }
	  else
	  {
		  if (CAMERA_STATE == 3)
		  {
			  int sel =menuSelected;
			  menuSelected=0;
			  switch(sel)
			  {
			  case 1:
				  playPrev();
				  break;
			  case 2:
				  playNext();
				  break;
			  case 3:
				  setMainMenu();
				  camera_monitor_state();
				  break;
			  }
		  }
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief DCMI Initialization Function
  * @param None
  * @retval None
  */
static void MX_DCMI_Init(void)
{

  /* USER CODE BEGIN DCMI_Init 0 */

  /* USER CODE END DCMI_Init 0 */

  /* USER CODE BEGIN DCMI_Init 1 */

  /* USER CODE END DCMI_Init 1 */
  hdcmi.Instance = DCMI;
  hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
  hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
  hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
  hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
  hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
  hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
  hdcmi.Init.JPEGMode = DCMI_JPEG_ENABLE;
  if (HAL_DCMI_Init(&hdcmi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DCMI_Init 2 */

  /* USER CODE END DCMI_Init 2 */

}

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief RTC Initialization Function
  * @param None
  * @retval None
  */
static void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN Check_RTC_BKUP */
int set =1;
if (set)
{
  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date
  */
  sTime.Hours = 11;
  sTime.Minutes = 37;
  sTime.Seconds = 0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY;
  sDate.Month = RTC_MONTH_MARCH;
  sDate.Date = 24;
  sDate.Year = 21;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */
}
  /* USER CODE END RTC_Init 2 */

}

/**
  * @brief SDIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_SDIO_SD_Init(void)
{

  /* USER CODE BEGIN SDIO_Init 0 */

  /* USER CODE END SDIO_Init 0 */

  /* USER CODE BEGIN SDIO_Init 1 */

  /* USER CODE END SDIO_Init 1 */
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 0;
  /* USER CODE BEGIN SDIO_Init 2 */

  /* USER CODE END SDIO_Init 2 */

}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
  /* DMA1_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
  /* DMA2_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LCD_BL_Pin|T_CS_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, DC_RST_Pin|DC_PDWN_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : T_IRQ_Pin */
  GPIO_InitStruct.Pin = T_IRQ_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(T_IRQ_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : LCD_BL_Pin T_CS_Pin */
  GPIO_InitStruct.Pin = LCD_BL_Pin|T_CS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : DC_RST_Pin DC_PDWN_Pin */
  GPIO_InitStruct.Pin = DC_RST_Pin|DC_PDWN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

}

/* FSMC initialization function */
static void MX_FSMC_Init(void)
{

  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the SRAM1 memory initialization sequence
  */
  hsram1.Instance = FSMC_NORSRAM_DEVICE;
  hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  /* hsram1.Init */
  hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
  hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
  hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  hsram1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
  /* Timing */
  Timing.AddressSetupTime = 1;
  Timing.AddressHoldTime = 15;
  Timing.DataSetupTime = 5;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

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

ov2640,c

#include "ov2640.h"
#include "main.h"

extern I2C_HandleTypeDef hi2c1;

/* Initialization sequence for QVGA resolution (320x240, RGB565) */
const unsigned char OV2640_QVGA[][2]=
{
  {0xff, 0x00},  {0x2c, 0xff},  {0x2e, 0xdf},  {0xff, 0x01},  {0x3c, 0x32},  {0x11, 0x00},
  {0x09, 0x02},  {0x04, 0xF8}, //mirror
  {0x13, 0xe5},  {0x14, 0x48},  {0x2c, 0x0c},  {0x33, 0x78},  {0x3a, 0x33},
  {0x3b, 0xfB},  {0x3e, 0x00},  {0x43, 0x11},  {0x16, 0x10},  {0x4a, 0x81},
  {0x21, 0x99},  {0x24, 0x40},  {0x25, 0x38},  {0x26, 0x82},  {0x5c, 0x00},
  {0x63, 0x00},  {0x46, 0x3f},  {0x0c, 0x38},  {0x61, 0x70},  {0x62, 0x80},
  {0x7c, 0x05},  {0x20, 0x80},  {0x28, 0x30},  {0x6c, 0x00},  {0x6d, 0x80},
  {0x6e, 0x00},  {0x70, 0x02},  {0x71, 0x94},  {0x73, 0xc1},  {0x3d, 0x34},
  {0x5a, 0x57},  {0x11, 0x00},  {0x17, 0x11},  {0x18, 0x75},  {0x19, 0x01},
  {0x1a, 0x97},  {0x32, 0x36},  {0x03, 0x0f},  {0x37, 0x40},  {0x4f, 0xbb},
  {0x50, 0x9c},  {0x5a, 0x57},  {0x6d, 0x80},  {0x6d, 0x38},  {0x39, 0x02},
  {0x35, 0x88},  {0x22, 0x0a},  {0x37, 0x40},  {0x23, 0x00},  {0x34, 0xa0},
  {0x36, 0x1a},  {0x06, 0x02},  {0x07, 0xc0},  {0x0d, 0xb7},  {0x0e, 0x01},
  {0x4c, 0x00},  {0xff, 0x00},  {0xe5, 0x7f},  {0xf9, 0xc0},  {0x41, 0x24},
  {0xe0, 0x14},  {0x76, 0xff},  {0x33, 0xa0},  {0x42, 0x20},  {0x43, 0x18},
  {0x4c, 0x00},  {0x87, 0xd0},  {0x88, 0x3f},  {0xd7, 0x03},  {0xd9, 0x10},
  {0xd3, 0x82},  {0xc8, 0x08},  {0xc9, 0x80},  {0x7d, 0x00},  {0x7c, 0x03},
  {0x7d, 0x48},  {0x7c, 0x08},  {0x7d, 0x20},  {0x7d, 0x10},  {0x7d, 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, 0x02},  {0x93, 0x02},  {0x93, 0x00},  {0x93, 0x04},  {0x93, 0x00},
  {0x93, 0x03},  {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},  {0xc3, 0xef},  {0xff, 0x00},  {0xba, 0xdc},
  {0xbb, 0x08},  {0xb6, 0x24},  {0xb8, 0x33},  {0xb7, 0x20},  {0xb9, 0x30},
  {0xb3, 0xb4},  {0xb4, 0xca},  {0xb5, 0x43},  {0xb0, 0x5c},  {0xb1, 0x4f},
  {0xb2, 0x06},  {0xc7, 0x00},  {0xc6, 0x51},  {0xc5, 0x11},  {0xc4, 0x9c},
  {0xbf, 0x00},  {0xbc, 0x64},  {0xa6, 0x00},  {0xa7, 0x1e},  {0xa7, 0x6b},
  {0xa7, 0x47},  {0xa7, 0x33},  {0xa7, 0x00},  {0xa7, 0x23},  {0xa7, 0x2e},
  {0xa7, 0x85},  {0xa7, 0x42},  {0xa7, 0x33},  {0xa7, 0x00},  {0xa7, 0x23},
  {0xa7, 0x1b},  {0xa7, 0x74},  {0xa7, 0x42},  {0xa7, 0x33},  {0xa7, 0x00},
  {0xa7, 0x23},  {0xc0, 0xc8},  {0xc1, 0x96},  {0x8c, 0x00},  {0x86, 0x3d},
  {0x50, 0x92},  {0x51, 0x90},  {0x52, 0x2c},  {0x53, 0x00},  {0x54, 0x00},
  {0x55, 0x88},  {0x5a, 0x50},  {0x5b, 0x3c},  {0x5c, 0x00},  {0xd3, 0x04},
  {0x7f, 0x00},  {0xda, 0x09},  {0xe5, 0x1f},  {0xe1, 0x67},  {0xe0, 0x00},
  {0xdd, 0x7f},  {0x05, 0x00},  {0xff, 0x00},  {0xe0, 0x04},  {0xc0, 0xc8},
  {0xc1, 0x96},  {0x86, 0x3d},  {0x50, 0x92},  {0x51, 0x90},  {0x52, 0x2c},
  {0x53, 0x00},  {0x54, 0x00},  {0x55, 0x88},  {0x57, 0x00},  {0x5a, 0x50},
  {0x5b, 0x39},  {0x5c, 0x00},  {0xd3, 0x08},  {0xe0, 0x00},  {0xFF, 0x00},
  {0x05, 0x00},  {0xDA, 0x08},  {0xda, 0x09},  {0x98, 0x00},  {0x99, 0x00},
  {0x00, 0x00},
};


const unsigned char OV2640_JPEG_INIT[][2] = { { 0xff, 0x00 }, { 0x2c, 0xff }, {
		0x2e, 0xdf }, { 0xff, 0x01 }, { 0x3c, 0x32 }, { 0x11, 0x00 }, { 0x09,
		0x02 }, { 0x04, 0x28 }, { 0x13, 0xe5 }, { 0x14, 0x48 }, { 0x2c, 0x0c },
		{ 0x33, 0x78 }, { 0x3a, 0x33 }, { 0x3b, 0xfB }, { 0x3e, 0x00 }, { 0x43,
				0x11 }, { 0x16, 0x10 }, { 0x39, 0x92 }, { 0x35, 0xda }, { 0x22,
				0x1a }, { 0x37, 0xc3 }, { 0x23, 0x00 }, { 0x34, 0xc0 }, { 0x36,
				0x1a }, { 0x06, 0x88 }, { 0x07, 0xc0 }, { 0x0d, 0x87 }, { 0x0e,
				0x41 }, { 0x4c, 0x00 }, { 0x48, 0x00 }, { 0x5B, 0x00 }, { 0x42,
				0x03 }, { 0x4a, 0x81 }, { 0x21, 0x99 }, { 0x24, 0x40 }, { 0x25,
				0x38 }, { 0x26, 0x82 }, { 0x5c, 0x00 }, { 0x63, 0x00 }, { 0x61,
				0x70 }, { 0x62, 0x80 }, { 0x7c, 0x05 }, { 0x20, 0x80 }, { 0x28,
				0x30 }, { 0x6c, 0x00 }, { 0x6d, 0x80 }, { 0x6e, 0x00 }, { 0x70,
				0x02 }, { 0x71, 0x94 }, { 0x73, 0xc1 }, { 0x12, 0x40 }, { 0x17,
				0x11 }, { 0x18, 0x43 }, { 0x19, 0x00 }, { 0x1a, 0x4b }, { 0x32,
				0x09 }, { 0x37, 0xc0 }, { 0x4f, 0x60 }, { 0x50, 0xa8 }, { 0x6d,
				0x00 }, { 0x3d, 0x38 }, { 0x46, 0x3f }, { 0x4f, 0x60 }, { 0x0c,
				0x3c }, { 0xff, 0x00 }, { 0xe5, 0x7f }, { 0xf9, 0xc0 }, { 0x41,
				0x24 }, { 0xe0, 0x14 }, { 0x76, 0xff }, { 0x33, 0xa0 }, { 0x42,
				0x20 }, { 0x43, 0x18 }, { 0x4c, 0x00 }, { 0x87, 0xd5 }, { 0x88,
				0x3f }, { 0xd7, 0x03 }, { 0xd9, 0x10 }, { 0xd3, 0x82 }, { 0xc8,
				0x08 }, { 0xc9, 0x80 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x7c,
				0x03 }, { 0x7d, 0x48 }, { 0x7d, 0x48 }, { 0x7c, 0x08 }, { 0x7d,
				0x20 }, { 0x7d, 0x10 }, { 0x7d, 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, 0x05 }, { 0x93,
				0x05 }, { 0x93, 0x00 }, { 0x93, 0x04 }, { 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 }, { 0xc3,
				0xed }, { 0xa4, 0x00 }, { 0xa8, 0x00 }, { 0xc5, 0x11 }, { 0xc6,
				0x51 }, { 0xbf, 0x80 }, { 0xc7, 0x10 }, { 0xb6, 0x66 }, { 0xb8,
				0xA5 }, { 0xb7, 0x64 }, { 0xb9, 0x7C }, { 0xb3, 0xaf }, { 0xb4,
				0x97 }, { 0xb5, 0xFF }, { 0xb0, 0xC5 }, { 0xb1, 0x94 }, { 0xb2,
				0x0f }, { 0xc4, 0x5c }, { 0xc0, 0x64 }, { 0xc1, 0x4B }, { 0x8c,
				0x00 }, { 0x86, 0x3D }, { 0x50, 0x00 }, { 0x51, 0xC8 }, { 0x52,
				0x96 }, { 0x53, 0x00 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x5a,
				0xC8 }, { 0x5b, 0x96 }, { 0x5c, 0x00 }, { 0xd3, 0x00 }, { 0xc3,
				0xed }, { 0x7f, 0x00 }, { 0xda, 0x00 }, { 0xe5, 0x1f }, { 0xe1,
				0x67 }, { 0xe0, 0x00 }, { 0xdd, 0x7f }, { 0x05, 0x00 }, { 0x12,
				0x40 }, { 0xd3, 0x04 }, { 0xc0, 0x16 }, { 0xC1, 0x12 }, { 0x8c,
				0x00 }, { 0x86, 0x3d }, { 0x50, 0x00 }, { 0x51, 0x2C }, { 0x52,
				0x24 }, { 0x53, 0x00 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x5A,
				0x2c }, { 0x5b, 0x24 }, { 0x5c, 0x00 },  };

const unsigned char OV2640_YUV422[][2] = { { 0xFF, 0x00 }, { 0x05, 0x00 }, {
		0xDA, 0x10 }, { 0xD7, 0x03 }, { 0xDF, 0x00 }, { 0x33, 0x80 }, { 0x3C,
		0x40 }, { 0xe1, 0x77 }, { 0x00, 0x00 },  };

const unsigned char OV2640_JPEG[][2] = { { 0xe0, 0x14 }, { 0xe1, 0x77 }, { 0xe5,
		0x1f }, { 0xd7, 0x03 }, { 0xda, 0x10 }, { 0xe0, 0x00 }, { 0xFF, 0x01 },
		//{ 0x04, 0x08 }, { 0xff, 0xff }, };
		{ 0x04, 0xF8 },  };

const unsigned char OV2640_1280x960_JPEG[][2] = { { 0xFF, 0x01 },
		{ 0x11, 0x01 }, { 0x12, 0x00 }, { 0x17, 0x11 }, { 0x18, 0x75 }, { 0x32,
				0x36 }, { 0x19, 0x01 }, { 0x1a, 0x97 }, { 0x03, 0x0f }, { 0x37,
				0x40 }, { 0x4f, 0xbb }, { 0x50, 0x9c }, { 0x5a, 0x57 }, { 0x6d,
				0x80 }, { 0x3d, 0x34 }, { 0x39, 0x02 }, { 0x35, 0x88 }, { 0x22,
				0x0a }, { 0x37, 0x40 }, { 0x34, 0xa0 }, { 0x06, 0x02 }, { 0x0d,
				0xb7 }, { 0x0e, 0x01 }, { 0xFF, 0x00 }, { 0xe0, 0x04 }, { 0xc0,
				0xc8 }, { 0xc1, 0x96 }, { 0x86, 0x3d }, { 0x50, 0x00 }, { 0x51,
				0x90 }, { 0x52, 0x2c }, { 0x53, 0x00 }, { 0x54, 0x00 }, { 0x55,
				0x88 }, { 0x57, 0x00 }, { 0x5a, 0x40 }, { 0x5b, 0xf0 }, { 0x5c,
				0x01 }, { 0xd3, 0x02 }, { 0xe0, 0x00 },  };

void CAMERA_IO_Write(uint8_t deviceAddr, uint8_t reg, uint8_t value)
{
	HAL_I2C_Mem_Write(&hi2c1, deviceAddr, reg, I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
}
void CAMERA_Delay(uint32_t Delay)
{
	HAL_Delay(Delay);
}

void ov2640_Init(uint16_t DeviceAddr, uint8_t action)
{
  uint32_t index;
  
//hardware and software init
  HAL_GPIO_WritePin(DC_PDWN_GPIO_Port, DC_PDWN_Pin, GPIO_PIN_RESET); //power on
  HAL_Delay(1);
  HAL_GPIO_WritePin(DC_RST_GPIO_Port, DC_RST_Pin, GPIO_PIN_RESET);  //hardware reset
  HAL_Delay(1);
  HAL_GPIO_WritePin(DC_RST_GPIO_Port, DC_RST_Pin, GPIO_PIN_SET);
  HAL_Delay(1);
  
  /* Prepare the camera to be configured */
  CAMERA_IO_Write(DeviceAddr, OV2640_DSP_RA_DLMT, 0x01);
  CAMERA_IO_Write(DeviceAddr, OV2640_SENSOR_COM7, 0x80);
  CAMERA_Delay(20);
  
  /* Initialize OV2640 */
  switch (action)
  {
  case CAMERA_Movie:
	  for(index=0; index<(sizeof(OV2640_JPEG_INIT)/2); index++)
	  {
		  CAMERA_IO_Write(DeviceAddr, OV2640_JPEG_INIT[index][0], OV2640_JPEG_INIT[index][1]);
		  CAMERA_Delay(1);
	  }

	for(index=0; index<(sizeof(OV2640_YUV422)/2); index++)
	{
		CAMERA_IO_Write(DeviceAddr, OV2640_YUV422[index][0], OV2640_YUV422[index][1]);
	  	CAMERA_Delay(1);
	}

	for(index=0; index<(sizeof(OV2640_JPEG)/2); index++)
	{
		CAMERA_IO_Write(DeviceAddr, OV2640_JPEG[index][0], OV2640_JPEG[index][1]);
		CAMERA_Delay(1);
	}

	for(index=0; index<(sizeof(OV2640_320x240_JPEG)/2); index++)
	{
		CAMERA_IO_Write(DeviceAddr, OV2640_320x240_JPEG[index][0], OV2640_320x240_JPEG[index][1]);
		CAMERA_Delay(1);
	}
	break;
  case CAMERA_Monitor:
    {
      for(index=0; index<(sizeof(OV2640_QVGA)/2); index++)
      {
        CAMERA_IO_Write(DeviceAddr, OV2640_QVGA[index][0], OV2640_QVGA[index][1]);
        CAMERA_Delay(1);
      } 
      break;
    }
  case CAMERA_Picture:
  	  for(index=0; index<(sizeof(OV2640_JPEG_INIT)/2); index++)
  	  {
  		  CAMERA_IO_Write(DeviceAddr, OV2640_JPEG_INIT[index][0], OV2640_JPEG_INIT[index][1]);
  		  CAMERA_Delay(1);
  	  }

  	for(index=0; index<(sizeof(OV2640_YUV422)/2); index++)
  	{
  		CAMERA_IO_Write(DeviceAddr, OV2640_YUV422[index][0], OV2640_YUV422[index][1]);
  	  	CAMERA_Delay(1);
  	}

  	for(index=0; index<(sizeof(OV2640_JPEG)/2); index++)
  	{
  		CAMERA_IO_Write(DeviceAddr, OV2640_JPEG[index][0], OV2640_JPEG[index][1]);
  		CAMERA_Delay(1);
  	}

  	for(index=0; index<(sizeof(OV2640_1280x960_JPEG)/2); index++)
  	{
  		CAMERA_IO_Write(DeviceAddr, OV2640_1280x960_JPEG[index][0], OV2640_1280x960_JPEG[index][1]);
  		CAMERA_Delay(1);
  	}
	  break;
  default:
    {
      break;
    }
  }
}

ov2640.h

#ifndef __OV2640_H
#define __OV2640_H

#ifdef __cplusplus
 extern "C" {
#endif 

#include "stm32f4xx_hal.h"
#define CAMERA_Monitor	1
#define CAMERA_Movie	2
#define CAMERA_Picture	3
#define CAMERA_Play		4

void     ov2640_Init(uint16_t DeviceAddr, uint8_t action);
void     ov2640_Config(uint16_t DeviceAddr, uint32_t feature, uint32_t value, uint32_t BR_value);
uint16_t ov2640_ReadID(uint16_t DeviceAddr);

void     CAMERA_IO_Init(void);
void     CAMERA_IO_Write(uint8_t addr, uint8_t reg, uint8_t value);
uint8_t  CAMERA_IO_Read(uint8_t addr, uint8_t reg);
void     CAMERA_Delay(uint32_t delay);
#ifdef __cplusplus
}
#endif
#endif /* __OV2640_H */

avi.c


#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"
#include "ili9341.h"

#include "avi.h"
#include "stdlib.h"
#include "ov2640.h"
#include "string.h"
#include "unistd.h"

#define MAX_AVI_BUFF		20480//20k for motion jpeg
#define MAX_PICTURE_BUFF	92160 //90k for stikk jepg

extern I2C_HandleTypeDef hi2c1;

struct jpeg_decompress_struct cinfo;
JSAMPROW buffer[2] = {0};
uint8_t rowBuff[1024];

typedef struct RGB
{
  uint8_t B;
  uint8_t G;
  uint8_t R;
}RGB_typedef;
struct jpeg_error_mgr jerr;

RGB_typedef *RGB_matrix;

uint16_t RGB16PixelColor;

uint8_t bAviStartRecording=0; // 1: start, 2: pending, 3: closed output file , 0: ready for recording

DWORD totalLen=0;
DWORD frames=0;

DWORD LSBtoDWORD(char* lsb)
{
	return lsb[0] | lsb[1]<<8 | lsb[2]<<16 | lsb[3] << 24;
}
FRESULT fwrite_DWORD(FIL * file, DWORD word)
{
	unsigned char * p;
	UINT bw;

	p = (unsigned char *)&word;

	return (f_write(file, p, 4, &bw));

}

FRESULT fwrite_WORD(FIL * file, WORD word)
{
	unsigned char * p;
	UINT bw;

	p = (unsigned char*)&word;
	return (f_write(file, p, 2, &bw));

}
void set_avi_output_status(uint8_t stat)
{
	bAviStartRecording = stat;
}
uint8_t read_avi_output_status()
{
	return bAviStartRecording;
}

void play_avi_file(char* fname)
{
	DWORD dwMilliPerFrame; 	// offset=32
	DWORD dwTotalFramess;		//offset=48
	DWORD dwWidth, dwHeight;	//offset=64,68
	FRESULT res;
	FIL file;
	UINT br;
	uint32_t msdelay;
	uint32_t offset;
	uint32_t imgSize;
	uint8_t imgBuff[MAX_AVI_BUFF];
	uint16_t blocks, remain;
	uint8_t *ptrBuff;

	char dwBuff[5];
	memset(dwBuff,0,5);


	res = f_open(&file, fname, FA_READ);
	if (res != FR_OK) { f_close(&file); return; }
	if (f_size(&file)==0)
	{
		lcdSetCursor(10, 80);
		lcdPrintf("%s: file size = 0", fname);
		return;
	}
	f_lseek(&file, 32);
	res = f_read(&file, dwBuff, 4, &br);
	if (res != FR_OK) { f_close(&file); return; }
	dwMilliPerFrame = LSBtoDWORD(dwBuff)/1000;

	f_lseek(&file, 48);
	res = f_read(&file, dwBuff, 4, &br);
	if (res != FR_OK) { f_close(&file); return; }
	dwTotalFramess = LSBtoDWORD(dwBuff);

	f_lseek(&file, 64);
	res = f_read(&file, dwBuff, 4, &br);
	if (res != FR_OK) { f_close(&file); return; }
	dwWidth = LSBtoDWORD(dwBuff);

	f_lseek(&file, 68);
	res = f_read(&file, dwBuff, 4, &br);
	if (res != FR_OK) { f_close(&file); return; }
	dwHeight = LSBtoDWORD(dwBuff);



	offset=240; //movi start here
	for (DWORD i = 0; i < dwTotalFramess; i++)
	{
		msdelay=HAL_GetTick();
		memset(imgBuff,0,MAX_AVI_BUFF);
		ptrBuff = imgBuff;
		offset+=4;  // jpeg image size;
		f_lseek(&file, offset);
		res=f_read(&file, dwBuff, 4, &br);
		if (res!=FR_OK) { f_close(&file); return; }
		imgSize = LSBtoDWORD(dwBuff);
		offset += 4; // image data start here
		blocks = imgSize/512;
		remain = imgSize%512;
		for (int b = 0; b < blocks; b++)
		{
			f_lseek(&file, offset);
			f_read(&file, ptrBuff, 512, &br);
			offset += 512;
			ptrBuff += 512;
		}
		if (remain)
		{
			f_lseek(&file, offset);
			f_read(&file, ptrBuff, remain, &br);
			offset += remain;
		}
		if (imgSize%2) offset++;
		decode_jpeg_to_tft(imgBuff, imgSize);
		msdelay = HAL_GetTick() - msdelay;
		if (msdelay < dwMilliPerFrame) HAL_Delay(dwMilliPerFrame-msdelay);

	}
	f_close(&file);



}

void decode_jpeg_file_to_ftf(char* fname)
{
	FIL file;
	FRESULT res;
	uint32_t line_counter = 0;
	uint32_t i = 0;
	res = f_open(&file, fname, FA_READ);
	if (res != FR_OK)
		return;
	if (f_size(&file)==0)
	{
		lcdSetCursor(10, 80);
		lcdPrintf("%s: file size = 0", fname);
		return;
	}


	buffer[0] = rowBuff;

	cinfo.err = jpeg_std_error(&jerr);

	jpeg_create_decompress(&cinfo);

	jpeg_stdio_src(&cinfo, &file);
	jpeg_read_header(&cinfo, TRUE);
	cinfo.scale_num=1;
	cinfo.scale_denom=4;
	cinfo.dct_method = JDCT_IFAST;

	jpeg_start_decompress(&cinfo);
	//while (cinfo.output_scanline < cinfo.output_height && line_counter < 240)
	lcdSetWindow(0, 0, 319, 227);
	while (cinfo.output_scanline < cinfo.output_height && line_counter < 228) //240-12 (12 for menu)
	{
		(void)jpeg_read_scanlines(&cinfo, buffer, 1);

		RGB_matrix=(RGB_typedef*)buffer[0];
		for(i = 0; i < cinfo.output_width ; i++)
		{
			RGB16PixelColor = (uint16_t)
			(
				((RGB_matrix[i].R & 0x00F8) >> 3)|
				((RGB_matrix[i].G & 0x00FC) << 3)|
				((RGB_matrix[i].B & 0x00F8) << 8)
			);
			LCD_DataWrite(RGB16PixelColor);

		}

		line_counter++;

	}

	jpeg_finish_decompress(&cinfo);

	jpeg_destroy_decompress(&cinfo);
	f_close(&file);
}

void decode_jpeg_to_tft(uint8_t *buff, uint32_t len)
{
	uint32_t line_counter = 0;
	uint32_t i = 0;

	buffer[0] = rowBuff;

	if (!(buff[0] == 0xFF && buff[1] == 0xD8 && buff[len-2]==0xFF && buff[len-1] == 0xD9))
		return; //jpeg must begin 0xffd8 end oxffd9
	cinfo.err = jpeg_std_error(&jerr);

	jpeg_create_decompress(&cinfo);

	jpeg_mem_src(&cinfo, buff, len);
	jpeg_read_header(&cinfo, TRUE);
	//cinfo.scale_num=1;
	//cinfo.scale_denom=2;
	cinfo.dct_method = JDCT_IFAST;

	jpeg_start_decompress(&cinfo);
	//while (cinfo.output_scanline < cinfo.output_height && line_counter < 240)
	lcdSetWindow(0, 0, 319, 227);
	while (cinfo.output_scanline < cinfo.output_height && line_counter < 228) //240-12 (12 for menu)
	{
		(void)jpeg_read_scanlines(&cinfo, buffer, 1);

		RGB_matrix=(RGB_typedef*)buffer[0];
		for(i = 0; i < cinfo.output_width ; i++)
		{
			RGB16PixelColor = (uint16_t)
			(
				((RGB_matrix[i].R & 0x00F8) >> 3)|
				((RGB_matrix[i].G & 0x00FC) << 3)|
				((RGB_matrix[i].B & 0x00F8) << 8)
			);
			LCD_DataWrite(RGB16PixelColor);

		}

	  	line_counter++;

	}

	jpeg_finish_decompress(&cinfo);

	jpeg_destroy_decompress(&cinfo);
}

FRESULT output_avi_header(FIL *file, uint8_t fps, uint16_t width, uint16_t height)
{
	FRESULT res=FR_OK;
	UINT bw;
	RIFF RIFF_LIST;


	RIFF_LIST.dwRIFF = 'RIFF';
	res = f_write(file, "RIFF", 4, &bw); //offset=0

	//RIFF_LIST.dwSize =  150 + 12 + len + 8*frames + 8 + 4*4*frames;
	RIFF_LIST.dwSize =  0;
	// must rewrite when stop_output at file offset  4;
	res = fwrite_DWORD(file, RIFF_LIST.dwSize); //offset+4=4

	RIFF_LIST.dwFourCC = 'AVI ';
	res = f_write(file, "AVI ", 4, &bw); //offset+4=8
	// 	RIFF_LIST.data = WAIT WITH THIS

	LIST hdrl;
	hdrl.dwList = 'LIST';
	res = f_write(file, "LIST", 4, &bw); //offset+4=12

	hdrl.dwSize = 208;
	res = fwrite_DWORD(file, hdrl.dwSize);//offset+4=16

	hdrl.dwFourCC = 'hdrl';
	res = f_write(file, "hdrl", 4, &bw);//offset+4=20

	MainAVIHeader avih;

	avih.dwFourCC = 'avih';
	res = f_write(file, "avih", 4, &bw); //offset+4=24

	avih.dwSize = 56;
	res = fwrite_DWORD(file, avih.dwSize);//offset+4=28

	avih.dwMicroSecPerFrame = 1000000/fps;
	res = fwrite_DWORD(file, avih.dwMicroSecPerFrame);//offset+4=32

	avih.dwMaxBytesPerSec = 7000;
	res = fwrite_DWORD(file, avih.dwMaxBytesPerSec);//offset+4=36

	avih.dwPaddingGranularity = 0;
	res = fwrite_DWORD(file, avih.dwPaddingGranularity);//offset+4=40

	// dwFlags set to 16, do not know why!
	avih.dwFlags = 16;
	res = fwrite_DWORD(file, avih.dwFlags);//offset+4=44
///////////////////////////////
	avih.dwTotalFrames = 0;  // frames, offset ?, must rewrite

	res = fwrite_DWORD(file, avih.dwTotalFrames);//offset+4=48

	avih.dwInitialFrames = 0;
	res = fwrite_DWORD(file, avih.dwInitialFrames);//offset+4=52

	avih.dwStreams = 1;
	res = fwrite_DWORD(file, avih.dwStreams);//offset+4=56

	avih.dwSuggestedBufferSize = 0;
	res = fwrite_DWORD(file, avih.dwSuggestedBufferSize);//offset+4=60

	avih.dwWidth = width;
	res = fwrite_DWORD(file, avih.dwWidth);//offset+4=64

	avih.dwHeight = height;
	res = fwrite_DWORD(file, avih.dwHeight);//offset+4=68

	avih.dwReserved[0] = 0;
	res = fwrite_DWORD(file, avih.dwReserved[0]);//offset+4=72
	avih.dwReserved[1] = 0;
	res = fwrite_DWORD(file, avih.dwReserved[1]);//offset+4=76
	avih.dwReserved[2] = 0;
	res = fwrite_DWORD(file, avih.dwReserved[2]);//offset+4=80
	avih.dwReserved[3] = 0;
	res = fwrite_DWORD(file, avih.dwReserved[3]);//offset+4=84

	LIST strl;
	strl.dwList = 'LIST';
	res = f_write(file, "LIST", 4, &bw);//offset+4=88

	strl.dwSize = 132;
	res = fwrite_DWORD(file, strl.dwSize);//offset+4=92

	strl.dwFourCC = 'strl';
	res = f_write(file, "strl", 4, &bw);//offset+4=96

	AVIStreamHeader strh;
	strh.dwFourCC = 'strh';
	res = f_write(file, "strh", 4, &bw);//offset+4=100

	strh.dwSize = 48;
	res = fwrite_DWORD(file, strh.dwSize);//offset+4=104

	strh.fccType = 'vids';
	res = f_write(file, "vids", 4, &bw);//offset+4=108

	strh.fccHandler = 'MJPG';
	res = f_write(file, "MJPG", 4,&bw);//offset+4=112

	strh.dwFlags = 0;
	res = fwrite_DWORD(file, strh.dwFlags);//offset+4=116

	strh.wPriority = 0; // +2 = 14
	res = fwrite_WORD(file, strh.wPriority);//offset+4=120

	strh.wLanguage = 0; // +2 = 16
	res = fwrite_WORD(file, strh.wLanguage);//offset+2=122

	strh.dwInitialFrames = 0; // +4 = 20
	res = fwrite_DWORD(file, strh.dwInitialFrames);//offset+2=124

	strh.dwScale = 1; // +4 = 24
	res = fwrite_DWORD(file, strh.dwScale);//offset+4=128

	// insert FPS
	strh.dwRate = fps; // +4 = 28
	res = fwrite_DWORD(file, strh.dwRate);//offset+4=132

	strh.dwStart = 0; // +4 = 32
	res = fwrite_DWORD(file, strh.dwStart);//offset+4=136
	//////////////////////
	// insert nbr of jpegs, must rewrite, offset ?
	strh.dwLength = 0; //nbr_of_jpgs;  +4 = 36
	res = fwrite_DWORD(file, strh.dwLength);//offset+4=140

	strh.dwSuggestedBufferSize = 0; // +4 = 40
	res = fwrite_DWORD(file, strh.dwSuggestedBufferSize);//offset+4=144

	strh.dwQuality = 0; // +4 = 44
	res = fwrite_DWORD(file, strh.dwQuality);//offset+4=148
	// Specifies the size of a single sample of data.
	// This is set to zero if the samples can vary in size.
	// If this number is nonzero, then multiple samples of data
	// can be grouped into a single chunk within the file.
	// If it is zero, each sample of data (such as a video frame) must be in a separate chunk.
	// For video streams, this number is typically zero, although
	// it can be nonzero if all video frames are the same size.
	//
	strh.dwSampleSize = 0; // +4 = 48
	res = fwrite_DWORD(file, strh.dwSampleSize);//offset+4=152

	EXBMINFOHEADER strf;

	strf.dwFourCC = 'strf';
	res = f_write(file, "strf", 4, &bw);//offset+4=156

	strf.dwSize = 40;
	res = fwrite_DWORD(file, strf.dwSize);//offset+4=160

	strf.biSize = 40;
	res = fwrite_DWORD(file, strf.biSize);//offset+4=164

	strf.biWidth = width;
	res = fwrite_DWORD(file, strf.biWidth);//offset+4=168
	strf.biHeight = height;
	res = fwrite_DWORD(file, strf.biHeight);//offset+4=172
	strf.biPlanes = 1;
	res = fwrite_WORD(file, strf.biPlanes);//offset+4=176
	strf.biBitCount = 24;
	res = fwrite_WORD(file, strf.biBitCount);//offset+2=178
	strf.biCompression = 'MJPG';
	res = f_write(file, "MJPG", 4, &bw);//offset+2=180

	strf.biSizeImage = ((strf.biWidth*strf.biBitCount/8 + 3)&0xFFFFFFFC)*strf.biHeight;
	res = fwrite_DWORD(file, strf.biSizeImage);//offset+4=184
	strf.biXPelsPerMeter = 0;
	res = fwrite_DWORD(file, strf.biXPelsPerMeter);//offset+4=188
	strf.biYPelsPerMeter = 0;
	res = fwrite_DWORD(file, strf.biYPelsPerMeter);//offset+4=192
	strf.biClrUsed = 0;
	res = fwrite_DWORD(file, strf.biClrUsed);//offset+4=196
	strf.biClrImportant = 0;
	res = fwrite_DWORD(file, strf.biClrImportant);//offset+4=200

	res = f_write(file, "LIST", 4, &bw);//offset+4=204

	DWORD ddww = 16;
	res = fwrite_DWORD(file, ddww);//offset+4=208
	res = f_write(file, "odml", 4, &bw);//offset+4=212
	res = f_write(file, "dmlh", 4, &bw);//offset+4=216

	DWORD szs = 4;
	res = fwrite_DWORD(file, szs);//offset+4=220
////////////////////////////////////////
	// nbr of jpgs
	DWORD totalframes = 0; //nbr_of_jpgs; must rewrite, offset ?;
	res = fwrite_DWORD(file, totalframes);//offset+4=224

	LIST movi;
	movi.dwList = 'LIST';
	res = f_write(file, "LIST", 4, &bw);//offset+4=228

	//////////////////////////////
	//movi.dwSize = len + 4 + 8*nbr_of_jpgs;   must rewrite, offset?, len, nbr_of_jpgs;
	movi.dwSize = 0;
	res = fwrite_DWORD(file, movi.dwSize);//offset+4=232
	movi.dwFourCC = 'movi';
	res = f_write(file, "movi", 4, &bw);//offset+4=236
	return res;
}

FRESULT start_output_mjpeg_avi(FIL *file, DCMI_HandleTypeDef *hdcmi, uint8_t fps, uint8_t resolution)
{
	FRESULT res=FR_OK;
	FIL temp_idx1;
	uint32_t next_idx1_offset;
	uint8_t dmabuff[MAX_AVI_BUFF];
	uint16_t width, height;
	uint16_t f_begin = 0;
	uint32_t f_end = 0;
	uint32_t idx = 0;

	uint32_t sublen=0;
	uint8_t headerFinder=0;
	UINT bw;
	uint16_t f,r;
	CHUNK data;
	uint8_t *buff;
	uint8_t frame_ok=0;

	uint32_t lastSuccessTick;
	uint16_t timeout=0;
	uint8_t adjTime=0;

	unsigned long beginTick;
	long time_diff=0;


	bAviStartRecording=1;
	HAL_DCMI_Stop(hdcmi);
	ov2640_Init(0x60, CAMERA_Movie);

	width=320;
	height=240;

	 res = f_open(&temp_idx1, "0:/temp_idx1", FA_CREATE_ALWAYS|FA_WRITE|FA_READ);
	 if (res != FR_OK) return res;

	//fps=3; // test
	 if (output_avi_header(file, fps, width, height) != FR_OK) //default fps = 15
		return res;

	totalLen=0;
	frames=0;

	next_idx1_offset=4;
	lcdSetWindow(0, 0, 319, 227);
	lastSuccessTick=HAL_GetTick();
	while(bAviStartRecording==1)
	{
		memset(dmabuff,0, MAX_AVI_BUFF);
		headerFinder=0;
		//if (frame_ok 0)
		if (frame_ok && adjTime  == 0)
		{
			time_diff = HAL_GetTick()-lastSuccessTick;

			//if (1000/fps > time_diff && time_diff > 0)
			if (1000 > time_diff)
				HAL_Delay(1000 - time_diff);
		}

		beginTick = HAL_GetTick();

		HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, ((uint32_t)dmabuff), MAX_AVI_BUFF/4);

		timeout=0;
		while((hdcmi->Instance->CR & 0x03) == 3)
		{
			if (timeout > 1000)
			{
				break; //max timeout: 1 seconds
			}
			HAL_Delay(1);
			timeout++;
		}
		if(timeout <= 1000)
		{
			frame_ok=0;
			for (idx = 0; idx<MAX_AVI_BUFF-4;idx++)
			{
				if (headerFinder == 0 && dmabuff[idx] == 0xff && dmabuff[idx+1]==0xD8)
				{
					headerFinder=1;
					f_begin=idx;
				}
				if (headerFinder==1 && dmabuff[idx]== 0xFF && dmabuff[idx+1] == 0xD9 )
				{
					if (adjTime == 0) // 1 sec
						lastSuccessTick=beginTick;

					adjTime = (adjTime+1)%fps; // 1 sec

					frame_ok=1;
					headerFinder=0;
					f_end = idx+1;

					buff = dmabuff + f_begin;

					data.dwFourCC = '00db';
					f_write(file, "00db", 4, &bw);
					sublen = f_end-f_begin+1;

					data.dwSize = sublen;
					fwrite_DWORD(file, data.dwSize);
					f = sublen / 512;
					r = sublen % 512;
					for (int i = 0; i < f; i++)
					{
						res=f_write(file, buff, 512, &bw);
						buff += 512;
					}

					if (r > 0)
					{
						res=f_write(file, buff, r, &bw);

					}

					buff = dmabuff + f_begin;

					decode_jpeg_to_tft(buff, sublen);

					if (sublen%2)
					{
						sublen++;
						res=f_write(file, '\0', 1, &bw);
					}
					frames++;
					totalLen += (sublen);
					f_write(&temp_idx1, "00db", 4, &bw);
					fwrite_DWORD(&temp_idx1, 16);//AVI_KEYFRAME=16
					fwrite_DWORD(&temp_idx1, next_idx1_offset);
					fwrite_DWORD(&temp_idx1, sublen);
					next_idx1_offset += (8+sublen);

					break;
				}

			}

		}
	}
	if (bAviStartRecording==2)
	{
		stop_output_mjpeg_avi(file, &temp_idx1);
	}
	//bAviStartRecording=2; //wait for rewriting some parameters and to close file;

	return res;
}
void stop_output_mjpeg_avi(FIL *file, FIL* temp_idx1)
{
	UINT br;
	UINT bw;
	unsigned char buf[512];
	if (bAviStartRecording != 2) return;

	f_write(file, "idx1", 4, &br);
	fwrite_DWORD(file, 4*4*frames);
	f_lseek(temp_idx1, 0);
	do {
		f_read(temp_idx1, buf, 512, &br);
		f_write(file, buf, br, &bw);
	} while (br == 512);
	f_close(temp_idx1);
	f_unlink("0:/temp_idx1");
	// rewrite file size, frames
	// file size offset 4
	DWORD size;

	//RIFF_LIST.dwSize =  150 + 12 + len + 8*frames + 8 + 4*4*frames;

	//size = 150 + 12 + totalLen + 8*frames + 8 + 4*4*frames(idx1);
	size = 150 + 12 + totalLen + 8*frames + 8 + 4*4*frames;
	f_lseek(file, 4);
	fwrite_DWORD(file, size);

	//DWORD avih  totalframes = 0; nbr_of_jpgs;;
	size = frames;
	f_lseek(file, 48);
	fwrite_DWORD(file, size);//offset+4=48

	//strh.dwLength = nbr_of_jpgs;  +4 = 36
	size=frames;
	f_lseek(file, 140); //140
	fwrite_DWORD(file, size);


	//totalFrames(dmlh)
	size=frames;
	f_lseek(file, 224); //140
	fwrite_DWORD(file, size);


	//movi.dwSize = len + 4 + 8*nbr_of_jpgs;
	size = totalLen + 4 + 8*frames;
	f_lseek(file, 232);
	fwrite_DWORD(file, size);//offset+4=232
	f_close(temp_idx1);
	f_close(file);
	bAviStartRecording=3;
}

uint8_t take_A_Picture(DCMI_HandleTypeDef *hdcmi)
{
	FIL file;
	uint8_t pBuff[MAX_PICTURE_BUFF];
	uint8_t *buff;
	uint8_t picture_ok=0;
	uint8_t headerFinder;
	uint32_t f_begin, f_end, sublen;
	uint16_t f_count, r;
	UINT bw;
	FRESULT res;
	uint32_t timeout=0;
	uint32_t testTime=0;

	char fn[64];
	memset(fn, 0, 64);
	sprintf(fn, "%simg_%05ld.jpg",SDPath, get_fattime()&0xFFFF);
	res = f_open(&file, fn, FA_CREATE_ALWAYS|FA_WRITE);
	if (res != FR_OK) return picture_ok;

	ov2640_Init(0x60, CAMERA_Picture);
	memset(pBuff, 0, MAX_PICTURE_BUFF);
	HAL_DCMI_Stop(hdcmi);
	HAL_Delay(10);
	HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, ((uint32_t)pBuff), MAX_PICTURE_BUFF/4);

	while((hdcmi->Instance->CR & 0x03) == 3)
	{
		if (timeout > 1000)
		{
			break; //max timeout: 10 seconds
		}
		HAL_Delay(10);
		timeout++;
	}
	if(timeout <= 1000)
	{
		for (int idx = 0; idx<MAX_PICTURE_BUFF;idx++)
		{
			if (headerFinder == 0 && pBuff[idx] == 0xff && pBuff[idx+1]==0xD8)
			{
				headerFinder=1;
				f_begin=idx;
			}

			if (headerFinder==1 && pBuff[idx]== 0xFF && pBuff[idx+1] == 0xD9 )
			{
				picture_ok=1;
				headerFinder=0;
				f_end = idx+1;
				buff = pBuff + f_begin;
				sublen = f_end-f_begin+1;
				f_count = sublen / 512;
				r = sublen % 512;
				for (int i = 0; i < f_count; i++)
				{

					f_write(&file, buff, 512, &bw);
					buff += 512;

				}

				if (r > 0)
				{
					f_write(&file, buff, r, &bw);

				}
			break;
			}
		}

	}

	f_close(&file);

	return picture_ok;
}

avi.h


#ifndef INC_AVI_H_
#define INC_AVI_H_
#include "main.h"
#include "fatfs.h"

enum avi_resolution
{
	RES_320X240 = 1,
	RES_640X480	= 2,
	RES_800X600	= 3,
};


typedef unsigned long DWORD;
typedef long LONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;

// AVI atoms
typedef struct {
	DWORD dwRIFF;
	DWORD dwSize;
	DWORD dwFourCC;
} RIFF;

typedef struct {
	DWORD dwFourCC;
	DWORD dwSize;
	//	BYTE* data; // dwSize in length
} CHUNK;

typedef struct {
	DWORD dwList;
	DWORD dwSize;
	DWORD dwFourCC;
	//	BYTE* data; // dwSize - 4 in length
} LIST;

typedef struct {
	DWORD dwFourCC;
	DWORD dwSize;

	DWORD dwMicroSecPerFrame;
	DWORD dwMaxBytesPerSec;
	DWORD dwPaddingGranularity;

	DWORD dwFlags;
	DWORD dwTotalFrames;
	DWORD dwInitialFrames;
	DWORD dwStreams;
	DWORD dwSuggestedBufferSize;

	DWORD dwWidth;
	DWORD dwHeight;

	DWORD dwReserved[4];
} MainAVIHeader;

typedef struct _RECT {
	LONG left;
	LONG top;
	LONG right;
	LONG bottom;
} RECT;

typedef struct {
	DWORD dwFourCC;
	DWORD dwSize;

	DWORD fccType;
	DWORD fccHandler;
	DWORD dwFlags;
	WORD  wPriority;
	WORD  wLanguage;
	DWORD dwInitialFrames;
	DWORD dwScale;
	DWORD dwRate;
	DWORD dwStart;
	DWORD dwLength;
	DWORD dwSuggestedBufferSize;
	DWORD dwQuality;
	DWORD dwSampleSize;

} AVIStreamHeader;

typedef struct {
	DWORD dwFourCC;
	DWORD dwSize;

	DWORD biSize;
	DWORD  biWidth;
	DWORD  biHeight;
	WORD  biPlanes;
	WORD  biBitCount;
	DWORD biCompression;
	DWORD biSizeImage;
	DWORD biXPelsPerMeter;
	DWORD  biYPelsPerMeter;
	DWORD biClrUsed;
	DWORD biClrImportant;
} AVI_BITMAPINFOHEADER;

typedef struct tagEXBMINFOHEADER {
	DWORD dwFourCC;
	DWORD dwSize;

	DWORD biSize;
	LONG  biWidth;
	LONG  biHeight;
	WORD  biPlanes;
	WORD  biBitCount;
	DWORD biCompression;
	DWORD biSizeImage;
	LONG  biXPelsPerMeter;
	LONG  biYPelsPerMeter;
	DWORD biClrUsed;
	DWORD biClrImportant;

} EXBMINFOHEADER;

typedef struct {
	DWORD ckid;
	DWORD dwFlags;
	DWORD dwChunkOffset;
	DWORD dwChunkLength;
} AVIINDEXENTRY;

typedef struct {
	DWORD fcc;
	DWORD cd;
	WORD wLongsPerEntry;
	char bIndexSubType;
	char bIndexType;
	DWORD nEntriesInUse;
	DWORD dwChunkId;
	DWORD dwReserved[3];
	AVIINDEXENTRY axiindex_entry;
} AVIINDEXCHUNK;

typedef struct {
	DWORD name;
	DWORD dwSize;
	DWORD dwTotalFrames;
} ODMLExtendedAVIheader;

typedef struct {
	RIFF riff_AVI;
	LIST hdrl;
	MainAVIHeader avih;
	LIST strl;
	AVIStreamHeader strh;
	EXBMINFOHEADER strf;
	LIST odml;
	ODMLExtendedAVIheader dmlh;
	LIST movi;
	CHUNK movi_data;
} avi_file;

FRESULT output_avi_header(FIL *file, uint8_t fps, uint16_t width, uint16_t height);
FRESULT start_output_mjpeg_avi(FIL *file, DCMI_HandleTypeDef *hdcmi, uint8_t fps, uint8_t resolution);
void stop_output_mjpeg_avi(FIL *file, FIL* temp_idx1);
void set_avi_output_status(uint8_t stat);
void decode_jpeg_file_to_ftf(char* fname);
void decode_jpeg_to_tft(uint8_t *buff, uint32_t len);
void play_avi_file(char* fname);
uint8_t read_avi_output_status();
uint8_t take_A_Picture(DCMI_HandleTypeDef *hdcmi);
#endif /* INC_AVI_H_ */