本實驗使用STM32F407VE、OV2640與ILI9341 16 bits parallel(含XPT2046 touch)製作一組數位相機。
主要功能:
- Still photo capture: JPEG format(1280x960)
- Movie record: RIFF-AVI Motion JPEG(320x240 at least 3fps)
- Playback: 在TFT 上顯示 JPEG與AVI
- Media: 影像儲存在SD card
- Button: Touch
主要組件:
- STM32F407VE: STM32_F4VE開發版
- OV2640鏡頭
- SD Card
- ILI9341 TFT: 16bit parallel mode and XPT2046 touch
- 轉接版
- SDIO
- FSMC
- DCMI
Middleware:
- FATFS
- 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說明:
- 0xDA(when 0xFF=00):Image Mode; 本實驗分別設定為RGB565(0x09)輸出給TFT,Live顯示; JPEG(0x10)輸出,儲存照片與影片。
- 0x5A(when 0xFF=00):Output width; 實際寬度除以4,輸出320給TFT,值為0x50。1280給JPEG照片ˊ值為0x40。
- 0x5B(when 0xFF=00):Output Height; 實際寬度除以4,輸出240給TFT,值為0x3C。1280給JPEG照片ˊ值為0xf0。
- 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。
RIFF(resource interchange file format),下圖為本實驗輸出AVI檔RIFF header。
動態影片: 根據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>© 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_ */