本實驗使用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 轉接版
開發轉體環境:
STM32CubeIDE and HAL library。
STM32F407VET6使用介面 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: 本實驗輸出的檔案。
Playback:
本實驗利用STM32CubeIDE環境下整合提供的LIBJPEG,作為解碼JPEG到RGB565格式,以便展示靜態照片;
動態影片: 根據RIFF header 的dwMicroSecPerFrame 取得影片中每個影格輸出到TFT 影像的速度,而每個影格由LIST/move/00db取得JPEG影像大小與內容,以正確速度decode JPEG到TFT,完成播放avi 檔。
成果影片:
VIDEO
程式碼:
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_ */