【程序】STM32F103RE单片机利用外部中断和DMA获取OV2640摄像头拍摄的照片,并通过串口发送到电脑上

PC端程序:在port.txt里面写好串口号,然后运行Release里面的程序接收图像,该程序是用Visual Studio 2012编译的
单片机端程序:dcmi_ov2640.h和dcmi_ov2640.c是从STM32F4官方标准库里面的OV2640 DCMI例程里面找到的,经过了修改后移植到了STM32F1单片机上

程序下载地址:https://pan.baidu.com/s/1zhG3V1egXW9NBl9Pu6Bekw

【开发板】

【拍摄的图像】

[1600x1200]

[800x600]

【截图】

PC端程序运行结果:

扫描二维码关注公众号,回复: 1110339 查看本文章

采集到的图像:


【采集原理】

PB0(VSYNC)为图像的开始/结束信号。OV2640的时序规定,当HREF=1时PCLK出现上升沿可采集一次数据(一字节)。HREF和PCLK是通过一个与非门接到PB9上的。如图所示,只要与非门的输出端PB9出现下降沿就可以采集数据。PB9对应定时器4的输入捕获通道4,但该通道不能触发DMA请求,只有通道3可以,所以通过TIM_CCMR2_CC3S位的设置把TIM4_CH4映射到TIM4_CH3上。TIM_CCER_CC3P=1表示下降沿触发,然后将TIM_DIER_CC3DE置1就可以产生DMA请求了。每产生一次DMA请求,DMA就自动复制一次PC0~7上的1字节数据。

【程序】

[PC端程序:接收图像]

#include <stdio.h>
#include <Windows.h>
#include <Shlwapi.h>

#pragma comment(lib, "Shlwapi.lib")

char buffer[131072]; // 接收缓冲区
int buffer_size = 0; // 缓冲区已用空间
HANDLE hPort; // 打开的串口

int convert_data(char *data, int len);
void save_file(const void *data, int len);

// 对数据进行CRC校验, len包括CRC校验码的长度
int check_data(const void *data, int len)
{
	DWORD crc = 0xffffffff;
	DWORD temp;
	int i, j;
	
	for (i = 0; i < len; i += 4)
	{
		if (i <= len - 8 || i == len - 4)
			temp = *(LPDWORD)((LPBYTE)data + i);
		else
		{
			temp = 0;
			memcpy(&temp, (LPBYTE)data + i, len - i - 4);
			i = len - 8;
		}

		crc ^= temp;
		for (j = 0; j < 32; j++)
		{
			if (crc & 0x80000000)
				crc = (crc << 1) ^ 0x4c11db7;
			else
				crc <<= 1;
		}
	}

	return crc == 0;
}

void convert_all(void)
{
	char *pbuf = buffer;
	char *pend;
	int remaining_size = buffer_size;
	int section_size;
	
	while ((pend = (char *)memchr(pbuf, '\r', remaining_size)) != NULL) // 按换行符分割收到的数据
	{
		section_size = (int)(pend - pbuf); // 数据大小
		if (convert_data(pbuf, section_size)) // 如果数据内容为标准的十六进制字符串
		{
			if (check_data(pbuf, section_size / 2)) // 检验CRC
				save_file(pbuf, section_size / 2 - 4); // 保存文件
		}

		// 跳过\r\n, 继续往后搜索
		remaining_size -= section_size + 2;
		if (remaining_size <= 0) // 若\r\n后没有数据了, 或只有\r, 或没有\r\n, 则停止搜索
			break;
		pbuf = pend + 2;
	}

	if (remaining_size > 0)
	{
		// 未处理的数据前移, 为新收到的数据腾出空间
		buffer_size = remaining_size;
		memmove(buffer, pbuf, remaining_size);
	}
	else
		buffer_size = 0;
}

// 将十六进制字符串格式转换成二进制数据
int convert_data(char *data, int len)
{
	char ch;
	int i;
	for (i = 0; i < len; i++)
	{
		if (data[i] >= '0' && data[i] <= '9')
			ch = data[i] - '0';
		else if (data[i] >= 'A' && data[i] <= 'F')
			ch = data[i] - 'A' + 10;
		else
			return 0;

		if (i % 2 == 0)
			data[i / 2] = ch << 4;
		else
			data[i / 2] |= ch;
	}
	return 1;
}

// 根据port.txt写的串口号打开串口
int open_comm(void)
{
	char name[30];
	COMMTIMEOUTS timeouts = {0};
	DCB dcb = {0};
	FILE *fp;
	int port_id;

	// 获取串口号
	fopen_s(&fp, "port.txt", "r");
	if (fp == NULL)
	{
		printf("Cannot open port.txt!\n");
		return 0;
	}
	fscanf_s(fp, "COM%d", &port_id);
	sprintf_s(name, sizeof(name), "\\\\.\\COM%d", port_id);
	fclose(fp);

	// 打开串口
	hPort = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, (DWORD)NULL, NULL, OPEN_EXISTING, (DWORD)NULL, NULL);
	if (hPort == INVALID_HANDLE_VALUE)
	{
		printf("Cannot open COM%d!\n", port_id);
		return 0;
	}

	// 设置波特率
	dcb.BaudRate = CBR_115200;
	dcb.ByteSize = 8;
	dcb.Parity = NOPARITY;
	dcb.StopBits = ONESTOPBIT;
	dcb.DCBlength = sizeof(DCB);
	if (!SetCommState(hPort, &dcb))
	{
		CloseHandle(hPort);
		printf("Cannot setup COM%d!\n", port_id);
		return 0;
	}

	// 设置超时时间
	timeouts.ReadTotalTimeoutConstant = 20000;
	if (!SetCommTimeouts(hPort, &timeouts))
	{
		CloseHandle(hPort);
		printf("Cannot setup the timeouts of COM%d!\n", port_id);
		return 0;
	}
	return 1;
}

void save_file(const void *data, int len)
{
	char name[100];
	FILE *fp;
	size_t folder_len;
	SYSTEMTIME systime;

	// 根据当前日期创建文件夹
	GetLocalTime(&systime);
	sprintf_s(name, sizeof(name), "images/%d-%02d-%02d", systime.wYear, systime.wMonth, systime.wDay);
	if (!PathFileExistsA("images"))
		CreateDirectoryA("images", NULL);
	if (!PathFileExistsA(name))
		CreateDirectoryA(name, NULL);

	// 根据当前时间生成文件名
	folder_len = strlen(name);
	sprintf_s(name + folder_len, sizeof(name) - folder_len, "/%d-%02d-%02d_%02d%02d%02d_%d.jpg", systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
	folder_len++;

	// 保存图像数据
	fopen_s(&fp, name, "wb");
	if (fp == NULL)
	{
		printf("Cannot open file %s!\n", name + folder_len);
		return;
	}
	fwrite(data, 1, len, fp);
	fclose(fp);
	printf("%s (%d bytes)\n", name + folder_len, len);
}

int main(void)
{
	DWORD size;

	if (!open_comm())
		return 0;

	while (1)
	{
		WriteFile(hPort, "RECV", 4, &size, NULL); // 向串口发送数据
		ReadFile(hPort, buffer + buffer_size, sizeof(buffer) - buffer_size, &size, NULL); // 从串口接收数据
		if (size == 0)
			break;
		buffer_size += size;
		
		convert_all(); // 提取有效数据并保存
		if (buffer_size == sizeof(buffer))
		{
			printf("Buffer is full! Drop!\n");
			buffer_size = 0;
		}
	}

	CloseHandle(hPort);
	return 0;
}

[单片机程序:采集并通过串口发送图像]

main.c:(使用DMA方式采集——强烈推荐)

#include <stdio.h>
#include <stm32f10x.h>
#include <string.h>
#include "dcmi_ov2640.h"

#define STATE_START 0x01
#define STATE_STOP 0x02

uint8_t image_buffer[64480]; // 如果存储空间不够了, 把这个数组改小就行了
uint8_t image_state = 0; // 图像捕获状态
uint32_t image_size; // 图像的大小

// 精确延时n毫秒
void delay(uint16_t nms)
{
  TIM6->ARR = 10 * nms - 1;
  TIM6->PSC = 7199;
  TIM6->EGR = TIM_EGR_UG;
  TIM6->SR &= ~TIM_SR_UIF;
  TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;
  while ((TIM6->SR & TIM_SR_UIF) == 0);
}

// 向串口发送图像数据, 并在末尾附上CRC校验码
void dump(const void *data, uint32_t size)
{
  uint8_t value;
  uint32_t i;
  uint32_t temp;
  
  CRC->CR = CRC_CR_RESET;
  for (i = 0; i < size; i++)
  {
    // 输出图像数据
    value = *((uint8_t *)data + i);
    printf("%02X", value);
    
    // 每4字节计算一次CRC
    if ((i & 3) == 0)
    {
      if (i + 4 <= size)
        CRC->DR = *(uint32_t *)((uint8_t *)data + i);
      else
      {
        temp = 0;
        memcpy(&temp, (uint8_t *)data + i, size - i);
        CRC->DR = temp;
      }
    }
  }
  
  // 输出CRC
  temp = CRC->DR;
  temp = (temp >> 24) | ((temp >> 8) & 0xff00) | ((temp & 0xff00) << 8) | ((temp & 0x00ff) << 24);
  printf("%08X\n", temp);
}

int fputc(int ch, FILE *fp)
{
  if (fp == stdout)
  {
    if (ch == '\n')
    {
      while ((USART1->SR & USART_SR_TXE) == 0);
      USART1->DR = '\r';
    }
    while ((USART1->SR & USART_SR_TXE) == 0);
    USART1->DR = ch;
  }
  return ch;
}

int main(void)
{
  RCC->AHBENR |= RCC_AHBENR_CRCEN | RCC_AHBENR_DMA1EN;
  RCC->APB1ENR = RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM4EN | RCC_APB1ENR_TIM6EN;
  RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN;
  
  GPIOA->CRH = (GPIOA->CRH & 0xfffff00f) | 0x4b0; // 串口发送引脚PA9设为复用推挽输出
  GPIOB->CRL = (GPIOB->CRL & 0x00ffffff) | 0xff000000; // PB6~7连接摄像头的I2C接口, 设为复用开漏输出
  // PB0(VSYNC)和PB9(=~(HREF & PCLK))为浮空输入
  // PC0~7是摄像头的8位数据引脚, 为浮空输入
  
  USART1->BRR = SystemCoreClock / 115200;
  USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
  
  I2C1->CR2 = 36; // APB1总线频率: 36MHz
  I2C1->CCR = 1800; // 速率: 10kHz (不可太高, 否则会导致Ack Failure; 计算公式为PCLK1/nf, 标准模式下n=2)
  I2C1->TRISE = 37; // 标准模式下为PCLK+1
  I2C1->CR1 = I2C_CR1_PE; // 打开I2C总线
  
  // 每当PB9上出现下降沿时, 就发送一次DMA请求, 采集GPIOC低8位的数据
  TIM4->CCMR2 = TIM_CCMR2_CC3S_1; // PB9(TIM4_CH4)映射到TIM4_CH3上
  TIM4->CCER = TIM_CCER_CC3P; // 下降沿触发
  TIM4->CCER |= TIM_CCER_CC3E; // 打开TIM4_CH3的输入捕获
  // 无需让定时器4开始计时, 这里只使用该定时器的一个输入捕获通道
  
  // 配置TIM4_CH3对应的DMA通道
  DMA1_Channel5->CMAR = (uint32_t)image_buffer;
  DMA1_Channel5->CPAR = (uint32_t)&GPIOC->IDR;
  DMA1_Channel5->CNDTR = sizeof(image_buffer); // 超出部分会被自动丢弃!! 所以图像不能大于64480字节
  DMA1_Channel5->CCR = DMA_CCR5_PL | DMA_CCR5_MINC | DMA_CCR5_EN;
  
  OV2640_Init(JPEG_800x600);
  
  // 打开PB0外部中断
  AFIO->EXTICR[0] = AFIO_EXTICR1_EXTI0_PB;
  EXTI->IMR = EXTI_IMR_MR0;
  EXTI->RTSR = EXTI_RTSR_TR0; // PB0上的上升沿能触发中断
  EXTI->FTSR = EXTI_FTSR_TR0; // PB0上的下降沿也能触发中断
  NVIC_EnableIRQ(EXTI0_IRQn); // 允许执行中断服务函数
  
  while (1)
  {
    if (image_state == (STATE_START | STATE_STOP))
    {
      printf("size=%d\n", image_size);
      dump(image_buffer, image_size); // 通过串口发送图像, 然后附上CRC校验值
      
      // 让DMA内部指针回到数组的开头
      DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
      DMA1_Channel5->CNDTR = sizeof(image_buffer);
      DMA1_Channel5->CCR |= DMA_CCR5_EN;
      
      image_state = 0; // 允许采集新图像 (这条语句一次性把START和STOP都清0了)
    }
  }
}

void EXTI0_IRQHandler(void)
{
  EXTI->PR = EXTI_PR_PR0; // 清除中断标志位
  if (GPIOB->IDR & GPIO_IDR_IDR0)
  {
    // PB0上升沿表示图像数据传输开始
    if (image_state != 0)
      return; // 如果图像已经开始采集了, 就忽略这个开始信号
    image_state = STATE_START;
    
    // 打开TIM4_CH3对应的DMA通道, 开始采集数据
    TIM4->DIER = TIM_DIER_CC3DE; // 允许PB9上的下降沿触发DMA请求
  }
  else
  {
    // PB0下降沿表示图像数据传输结束
    if ((image_state & STATE_START) == 0 || (image_state & STATE_STOP))
      return; // 忽略没有开始信号的结束信号, 以及重复的结束信号
    image_state |= STATE_STOP;
    
    TIM4->DIER &= ~TIM_DIER_CC3DE; // 停止采集
    image_size = sizeof(image_buffer) - DMA1_Channel5->CNDTR; // 总量-剩余数据量=图像大小
  }
}

main.c:(只用外部中断采集——不推荐,因为中断频率太高,中断处理函数稍微多写几行代码就会出问题)

#include <stdio.h>
#include <stm32f10x.h>
#include <string.h>
#include "dcmi_ov2640.h"

#define STATE_START 0x01
#define STATE_STOP 0x02

uint8_t image_buffer[64480]; // 如果存储空间不够了, 把这个数组改小就行了
uint8_t image_state = 0; // 图像捕获状态
uint32_t image_size; // 图像的大小

// 精确延时n毫秒
void delay(uint16_t nms)
{
  TIM6->ARR = 10 * nms - 1;
  TIM6->PSC = 7199;
  TIM6->EGR = TIM_EGR_UG;
  TIM6->SR &= ~TIM_SR_UIF;
  TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;
  while ((TIM6->SR & TIM_SR_UIF) == 0);
}

// 向串口发送图像数据, 并在末尾附上CRC校验码
void dump(const void *data, uint32_t size)
{
  uint8_t value;
  uint32_t i;
  uint32_t temp;
  
  CRC->CR = CRC_CR_RESET;
  for (i = 0; i < size; i++)
  {
    // 输出图像数据
    value = *((uint8_t *)data + i);
    printf("%02X", value);
    
    // 每4字节计算一次CRC
    if ((i & 3) == 0)
    {
      if (i + 4 <= size)
        CRC->DR = *(uint32_t *)((uint8_t *)data + i);
      else
      {
        temp = 0;
        memcpy(&temp, (uint8_t *)data + i, size - i);
        CRC->DR = temp;
      }
    }
  }
  
  // 输出CRC
  temp = CRC->DR;
  temp = (temp >> 24) | ((temp >> 8) & 0xff00) | ((temp & 0xff00) << 8) | ((temp & 0x00ff) << 24);
  printf("%08X\n", temp);
}

int fputc(int ch, FILE *fp)
{
  if (fp == stdout)
  {
    if (ch == '\n')
    {
      while ((USART1->SR & USART_SR_TXE) == 0);
      USART1->DR = '\r';
    }
    while ((USART1->SR & USART_SR_TXE) == 0);
    USART1->DR = ch;
  }
  return ch;
}

int main(void)
{
  RCC->AHBENR |= RCC_AHBENR_CRCEN;
  RCC->APB1ENR = RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM6EN;
  RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN;
  
  GPIOA->CRH = (GPIOA->CRH & 0xfffff00f) | 0x4b0; // 串口发送引脚PA9设为复用推挽输出
  GPIOB->CRL = (GPIOB->CRL & 0x00ffffff) | 0xff000000; // PB6~7连接摄像头的I2C接口, 设为复用开漏输出
  // PB0(VSYNC)和PB9(=~(HREF & PCLK))为浮空输入
  // PC0~7是摄像头的8位数据引脚, 为浮空输入
  
  USART1->BRR = SystemCoreClock / 115200;
  USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
  
  I2C1->CR2 = 36; // APB1总线频率: 36MHz
  I2C1->CCR = 1800; // 速率: 10kHz (不可太高, 否则会导致Ack Failure; 计算公式为PCLK1/nf, 标准模式下n=2)
  I2C1->TRISE = 37; // 标准模式下为PCLK+1
  I2C1->CR1 = I2C_CR1_PE; // 打开I2C总线
  
  OV2640_Init(JPEG_800x600);
  
  // 打开PB0和PB9外部中断
  AFIO->EXTICR[0] = AFIO_EXTICR1_EXTI0_PB;
  AFIO->EXTICR[2] = AFIO_EXTICR3_EXTI9_PB;
  EXTI->IMR = EXTI_IMR_MR0;
  EXTI->RTSR = EXTI_RTSR_TR0; // PB0上的上升沿能触发中断
  EXTI->FTSR = EXTI_FTSR_TR0 | EXTI_FTSR_TR9; // PB0和PB9上的下降沿能触发中断
  NVIC_EnableIRQ(EXTI0_IRQn); // 允许执行中断服务函数
  NVIC_EnableIRQ(EXTI9_5_IRQn);
  
  while (1)
  {
    if (image_state == (STATE_START | STATE_STOP))
    {
      printf("size=%d\n", image_size);
      dump(image_buffer, image_size); // 通过串口发送图像, 然后附上CRC校验值
      image_state = 0; // 允许采集新图像 (这条语句一次性把START和STOP都清0了)
    }
  }
}

void EXTI0_IRQHandler(void)
{
  EXTI->PR = EXTI_PR_PR0; // 清除中断标志位
  if (GPIOB->IDR & GPIO_IDR_IDR0)
  {
    // PB0上升沿表示图像数据传输开始
    if (image_state != 0)
      return; // 如果图像已经开始采集了, 就忽略这个开始信号
    
    image_state = STATE_START;
    image_size = 0;
    EXTI->IMR |= EXTI_IMR_MR9; // 开始采集
  }
  else
  {
    // PB0下降沿表示图像数据传输结束
    if ((image_state & STATE_START) == 0 || (image_state & STATE_STOP))
      return; // 忽略没有开始信号的结束信号, 以及重复的结束信号
    image_state |= STATE_STOP;
    EXTI->IMR &= ~EXTI_IMR_MR9; // 停止采集
  }
}

void EXTI9_5_IRQHandler(void)
{
  uint8_t data;
  EXTI->PR = EXTI_PR_PR9;
  data = GPIOC->IDR & 0xff;
  if ((image_state & (STATE_START | STATE_STOP)) == STATE_START && image_size < sizeof(image_buffer))
    image_buffer[image_size++] = data;
}

dcmi_ov2640.h:

/**
  ******************************************************************************
  * @file    DCMI/DCMI_CameraExample/dcmi_ov2640.h
  * @author  MCD Application Team
  * @version V1.8.0
  * @date    04-November-2016
  * @brief   Header for dcmi_ov2640.c module
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __DCMI_OV2640_H
#define __DCMI_OV2640_H

/* Exported types ------------------------------------------------------------*/
typedef struct
{
  uint8_t Manufacturer_ID1;
  uint8_t Manufacturer_ID2;
  uint8_t PIDH;
  uint8_t PIDL;
} OV2640_IDTypeDef;

/* Image Sizes enumeration */
typedef enum   
{
  BMP_QQVGA             =   0x00,        /* BMP Image QQVGA 160x120 Size */
  BMP_QVGA              =   0x01,        /* BMP Image QVGA 320x240 Size */
  JPEG_160x120          =   0x02,        /* JPEG Image 160x120 Size */
  JPEG_176x144          =   0x03,        /* JPEG Image 176x144 Size */
  JPEG_320x240          =   0x04,        /* JPEG Image 320x240 Size */
  JPEG_352x288          =   0x05,        /* JPEG Image 352x288 Size */
  JPEG_800x600          =   0x06,        /* JPEG Image 800x600 Size */
  JPEG_1600x1200        =   0x07         /* JPEG Image 1600x1200 Size */
} ImageFormat_TypeDef;

/* Exported constants --------------------------------------------------------*/

#define OV2640_DEVICE_WRITE_ADDRESS    0x60
#define OV2640_DEVICE_READ_ADDRESS     0x61

/* OV2640 Registers definition when DSP bank selected (0xFF = 0x00) */
/* OV2640 Registers definition when DSP bank selected (0xFF = 0x00) */
#define OV2640_DSP_R_BYPASS     0x05
#define OV2640_DSP_Qs           0x44
#define OV2640_DSP_CTRL         0x50
#define OV2640_DSP_HSIZE1       0x51
#define OV2640_DSP_VSIZE1       0x52
#define OV2640_DSP_XOFFL        0x53
#define OV2640_DSP_YOFFL        0x54
#define OV2640_DSP_VHYX         0x55
#define OV2640_DSP_DPRP         0x56
#define OV2640_DSP_TEST         0x57
#define OV2640_DSP_ZMOW         0x5A
#define OV2640_DSP_ZMOH         0x5B
#define OV2640_DSP_ZMHH         0x5C
#define OV2640_DSP_BPADDR       0x7C
#define OV2640_DSP_BPDATA       0x7D
#define OV2640_DSP_CTRL2        0x86
#define OV2640_DSP_CTRL3        0x87
#define OV2640_DSP_SIZEL        0x8C
#define OV2640_DSP_HSIZE2       0xC0
#define OV2640_DSP_VSIZE2       0xC1
#define OV2640_DSP_CTRL0        0xC2
#define OV2640_DSP_CTRL1        0xC3
#define OV2640_DSP_R_DVP_SP     0xD3
#define OV2640_DSP_IMAGE_MODE   0xDA
#define OV2640_DSP_RESET        0xE0
#define OV2640_DSP_MS_SP        0xF0
#define OV2640_DSP_SS_ID        0x7F
#define OV2640_DSP_SS_CTRL      0xF8
#define OV2640_DSP_MC_BIST      0xF9
#define OV2640_DSP_MC_AL        0xFA
#define OV2640_DSP_MC_AH        0xFB
#define OV2640_DSP_MC_D         0xFC
#define OV2640_DSP_P_STATUS     0xFE
#define OV2640_DSP_RA_DLMT      0xFF

/* OV2640 Registers definition when sensor bank selected (0xFF = 0x01) */
#define OV2640_SENSOR_GAIN       0x00
#define OV2640_SENSOR_COM1       0x03
#define OV2640_SENSOR_REG04      0x04
#define OV2640_SENSOR_REG08      0x08
#define OV2640_SENSOR_COM2       0x09
#define OV2640_SENSOR_PIDH       0x0A
#define OV2640_SENSOR_PIDL       0x0B
#define OV2640_SENSOR_COM3       0x0C
#define OV2640_SENSOR_COM4       0x0D
#define OV2640_SENSOR_AEC        0x10
#define OV2640_SENSOR_CLKRC      0x11
#define OV2640_SENSOR_COM7       0x12
#define OV2640_SENSOR_COM8       0x13
#define OV2640_SENSOR_COM9       0x14
#define OV2640_SENSOR_COM10      0x15
#define OV2640_SENSOR_HREFST     0x17
#define OV2640_SENSOR_HREFEND    0x18
#define OV2640_SENSOR_VSTART     0x19
#define OV2640_SENSOR_VEND       0x1A
#define OV2640_SENSOR_MIDH       0x1C
#define OV2640_SENSOR_MIDL       0x1D
#define OV2640_SENSOR_AEW        0x24
#define OV2640_SENSOR_AEB        0x25
#define OV2640_SENSOR_W          0x26
#define OV2640_SENSOR_REG2A      0x2A
#define OV2640_SENSOR_FRARL      0x2B
#define OV2640_SENSOR_ADDVSL     0x2D
#define OV2640_SENSOR_ADDVHS     0x2E
#define OV2640_SENSOR_YAVG       0x2F
#define OV2640_SENSOR_REG32      0x32
#define OV2640_SENSOR_ARCOM2     0x34
#define OV2640_SENSOR_REG45      0x45
#define OV2640_SENSOR_FLL        0x46
#define OV2640_SENSOR_FLH        0x47
#define OV2640_SENSOR_COM19      0x48
#define OV2640_SENSOR_ZOOMS      0x49
#define OV2640_SENSOR_COM22      0x4B
#define OV2640_SENSOR_COM25      0x4E
#define OV2640_SENSOR_BD50       0x4F
#define OV2640_SENSOR_BD60       0x50
#define OV2640_SENSOR_REG5D      0x5D
#define OV2640_SENSOR_REG5E      0x5E
#define OV2640_SENSOR_REG5F      0x5F
#define OV2640_SENSOR_REG60      0x60
#define OV2640_SENSOR_HISTO_LOW  0x61
#define OV2640_SENSOR_HISTO_HIGH 0x62

/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */

void OV2640_Reset(void);
void OV2640_ReadID(OV2640_IDTypeDef *OV2640ID);
void OV2640_Init(ImageFormat_TypeDef ImageFormat);
void OV2640_QQVGAConfig(void);
void OV2640_QVGAConfig(void);
void OV2640_JPEGConfig(ImageFormat_TypeDef ImageFormat);
void OV2640_BrightnessConfig(uint8_t Brightness);
void OV2640_ContrastConfig(uint8_t value1, uint8_t value2);
void OV2640_BandWConfig(uint8_t BlackWhite);
void OV2640_ColorEffectsConfig(uint8_t value1, uint8_t value2);
void OV2640_WriteReg(uint8_t addr, uint8_t data);
uint8_t OV2640_ReadReg(uint8_t addr);

#endif /* __DCMI_OV2640_H */

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

dcmi_ov2640.c:

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

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stm32f10x.h>
#include "dcmi_ov2640.h"

void delay(uint16_t nms);

/** @addtogroup STM32F4xx_StdPeriph_Examples
  * @{
  */

/** @addtogroup DCMI_CameraExample
  * @{
  */ 

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* QQVGA 160x120 */
const char OV2640_QQVGA[][2] =
{
  0xff, 0x00,
  0x2c, 0xff,
  0x2e, 0xdf,
  0xff, 0x01,
  0x3c, 0x32,
  0x11, 0x00,
  0x09, 0x02,
  0x03, 0xcf,
  0x04, 0x08,
  0x13, 0xe5,
  0x14, 0x48,
  0x2c, 0x0c,
  0x33, 0x78,
  0x3a, 0x33,
  0x3b, 0xfb,
  0x3e, 0x00,
  0x43, 0x11,
  0x16, 0x10,
  0x39, 0x02,
  0x35, 0x88,
  0x22, 0x0a,
  0x37, 0x40,
  0x23, 0x00,
  0x34, 0xa0,
  0x36, 0x1a,
  0x06, 0x02,
  0x07, 0xc0,
  0x0d, 0xb7,
  0x0e, 0x01,
  0x4c, 0x00,
  0x4a, 0x81,
  0x21, 0x99,
  0x24, 0x3a,
  0x25, 0x32,
  0x26, 0x82,
  0x5c, 0x00,
  0x63, 0x00,
  0x5d, 0x55,
  0x5e, 0x7d,
  0x5f, 0x7d,
  0x60, 0x55,
  0x61, 0x70,
  0x62, 0x80,
  0x7c, 0x05,
  0x20, 0x80,
  0x28, 0x30,
  0x6c, 0x00,
  0x6d, 0x80,
  0x6e, 0x00,
  0x70, 0x02,
  0x71, 0x96,
  0x73, 0xe1,
  0x3d, 0x34,
  0x5a, 0x57,
  0x4f, 0xbb,
  0x50, 0x9c,
  0x0f, 0x43,
  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,
  0x7c, 0x00,
  0x7d, 0x02,
  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, 0x00,
  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,
  0xbf, 0x00,
  0xba, 0xf0,
  0xbc, 0x64,
  0xbb, 0x02,
  0xb6, 0x3d,
  0xb8, 0x57,
  0xb7, 0x38,
  0xb9, 0x4e,
  0xb3, 0xe8,
  0xb4, 0xe1,
  0xb5, 0x66,
  0xb0, 0x67,
  0xb1, 0x5e,
  0xb2, 0x04,
  0xc7, 0x00,
  0xc6, 0x51,
  0xc5, 0x11,
  0xc4, 0x9c,
  0xcf, 0x02,
  0xa6, 0x00,
  0xa7, 0xe0,
  0xa7, 0x10,
  0xa7, 0x1e,
  0xa7, 0x21,
  0xa7, 0x00,
  0xa7, 0x28,
  0xa7, 0xd0,
  0xa7, 0x10,
  0xa7, 0x16,
  0xa7, 0x21,
  0xa7, 0x00,
  0xa7, 0x28,
  0xa7, 0xd0,
  0xa7, 0x10,
  0xa7, 0x17,
  0xa7, 0x21,
  0xa7, 0x00,
  0xa7, 0x28,
  0xc0, 0xc8,
  0xc1, 0x96,
  0x86, 0x1d,
  0x50, 0x00,
  0x51, 0x90,
  0x52, 0x18,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x88,
  0x57, 0x00,
  0x5a, 0x90,
  0x5b, 0x18,
  0x5c, 0x05,
  0xc3, 0xef,
  0x7f, 0x00,
  0xda, 0x00,
  0xe5, 0x1f,
  0xe1, 0x67,
  0xe0, 0x00,
  0xdd, 0xff,
  0x05, 0x00,
  0xff, 0x01,
  0xff, 0x01,
  0x12, 0x00,
  0x17, 0x11,
  0x18, 0x75,
  0x19, 0x01,
  0x1a, 0x97,
  0x32, 0x36,
  0x4f, 0xbb,
  0x6d, 0x80,
  0x3d, 0x34,
  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,
  0xe0, 0x04,
  0x8c, 0x00,
  0x87, 0xd0,
  0xe0, 0x00,
  0xff, 0x00,
  0xe0, 0x14,
  0xe1, 0x77,
  0xe5, 0x1f,
  0xd7, 0x03,
  0xda, 0x10,
  0xe0, 0x00,
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0xc8,
  0xc1, 0x96,
  0x86, 0x1d,
  0x50, 0x00,
  0x51, 0x90,
  0x52, 0x2c,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x88,
  0x57, 0x00,
  0x5a, 0x90,
  0x5b, 0x2c,
  0x5c, 0x05,
  0xe0, 0x00,
  0xd3, 0x04,
  0xff, 0x00,
  0xc3, 0xef,
  0xa6, 0x00,
  0xa7, 0xdd,
  0xa7, 0x78,
  0xa7, 0x7e,
  0xa7, 0x24,
  0xa7, 0x00,
  0xa7, 0x25,
  0xa6, 0x06,
  0xa7, 0x20,
  0xa7, 0x58,
  0xa7, 0x73,
  0xa7, 0x34,
  0xa7, 0x00,
  0xa7, 0x25,
  0xa6, 0x0c,
  0xa7, 0x28,
  0xa7, 0x58,
  0xa7, 0x6d,
  0xa7, 0x34,
  0xa7, 0x00,
  0xa7, 0x25,
  0xff, 0x00,
  0xe0, 0x04,
  0xe1, 0x67,
  0xe5, 0x1f,
  0xd7, 0x01,
  0xda, 0x08,
  0xda, 0x09,
  0xe0, 0x00,
  0x98, 0x00,
  0x99, 0x00,
  0xff, 0x01,
  0x04, 0x28,
  0xff, 0x01,
  0x12, 0x40,
  0x17, 0x11,
  0x18, 0x43,
  0x19, 0x00,
  0x1a, 0x4b,
  0x32, 0x09,
  0x4f, 0xca,
  0x50, 0xa8,
  0x5a, 0x23,
  0x6d, 0x00,
  0x39, 0x12,
  0x35, 0xda,
  0x22, 0x1a,
  0x37, 0xc3,
  0x23, 0x00,
  0x34, 0xc0,
  0x36, 0x1a,
  0x06, 0x88,
  0x07, 0xc0,
  0x0d, 0x87,
  0x0e, 0x41,
  0x4c, 0x00,
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0x64,
  0xc1, 0x4b,
  0x86, 0x35,
  0x50, 0x92,
  0x51, 0xc8,
  0x52, 0x96,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x00,
  0x57, 0x00,
  0x5a, 0x28,
  0x5b, 0x1e,
  0x5c, 0x00,
  0xe0, 0x00,
  0xff, 0x01,
  0x11, 0x00,
  0x3d, 0x38,
  0x2d, 0x00,
  0x50, 0x65,
  0xff, 0x00,
  0xd3, 0x04,
  0x7c, 0x00,
  0x7d, 0x04,
  0x7c, 0x09,
  0x7d, 0x28,
  0x7d, 0x00,
};

/* QVGA 320x240 */
const unsigned char OV2640_QVGA[][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,
  0x4a, 0x81,
  0x21, 0x99,
  0x24, 0x40,
  0x25, 0x38,
  0x26, 0x82,
  0x5c, 0x00,
  0x63, 0x00,
  0x46, 0x3f,
  0x0c, 0x3c,
  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,
  0x12, 0x00,
  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, 0x00,
  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, 0x3c,
  0x5c, 0x00,
  0xd3, 0x04,
  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, 0x3f, // 最慢速采集
  //0x11, 0x30, // 慢速采集
  0x11, 0x18,
  
  // 不能使用下面两种速率, 因为DMA反应不过来, 会出现丢字节现象
  //0x11, 0x00, // 快速采集
  //0x11, 0x80, // 最快速采集
  
  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, 0x7f,
  0xc3, 0xed,
  0x7f, 0x00,
  0xda, 0x00,
  0xe5, 0x1f,
  0xe1, 0x67,
  0xe0, 0x00,
  0xdd, 0x7f,
  0x05, 0x00,

  0x12, 0x40,
  0xd3, 0x7f,
  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,
};

/* JPG 160x120 */
const unsigned char OV2640_160x120_JPEG[][2] =
{
  0xff, 0x01,
  0x12, 0x40,
  0x17, 0x11,
  0x18, 0x43,
  0x19, 0x00,
  0x1a, 0x4b,
  0x32, 0x09,
  0x4f, 0xca,
  0x50, 0xa8,
  0x5a, 0x23,
  0x6d, 0x00,
  0x39, 0x12,
  0x35, 0xda,
  0x22, 0x1a,
  0x37, 0xc3,
  0x23, 0x00,
  0x34, 0xc0,
  0x36, 0x1a,
  0x06, 0x88,
  0x07, 0xc0,
  0x0d, 0x87,
  0x0e, 0x41,
  0x4c, 0x00,
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0x64,
  0xc1, 0x4b,
  0x86, 0x35,
  0x50, 0x92,
  0x51, 0xc8,
  0x52, 0x96,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x00,
  0x57, 0x00,
  0x5a, 0x28,
  0x5b, 0x1e,
  0x5c, 0x00,
  0xe0, 0x00,
};

/* JPG, 176x144 */
const unsigned char OV2640_176x144_JPEG[][2] =
{
  0xff, 0x01,
  0x12, 0x40,
  0x17, 0x11,
  0x18, 0x43,
  0x19, 0x00,
  0x1a, 0x4b,
  0x32, 0x09,
  0x4f, 0xca,
  0x50, 0xa8,
  0x5a, 0x23,
  0x6d, 0x00,
  0x39, 0x12,
  0x35, 0xda,
  0x22, 0x1a,
  0x37, 0xc3,
  0x23, 0x00,
  0x34, 0xc0,
  0x36, 0x1a,
  0x06, 0x88,
  0x07, 0xc0,
  0x0d, 0x87,
  0x0e, 0x41,
  0x4c, 0x00,
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0x64,
  0xc1, 0x4b,
  0x86, 0x35,
  0x50, 0x92,
  0x51, 0xc8,
  0x52, 0x96,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x00,
  0x57, 0x00,
  0x5a, 0x2c,
  0x5b, 0x24,
  0x5c, 0x00,
  0xe0, 0x00,
};

/* JPG 320x240 */
const unsigned char OV2640_320x240_JPEG[][2] =
{
  0xff, 0x01,
  0x12, 0x40,
  0x17, 0x11,
  0x18, 0x43,
  0x19, 0x00,
  0x1a, 0x4b,
  0x32, 0x09,
  0x4f, 0xca,
  0x50, 0xa8,
  0x5a, 0x23,
  0x6d, 0x00,
  0x39, 0x12,
  0x35, 0xda,
  0x22, 0x1a,
  0x37, 0xc3,
  0x23, 0x00,
  0x34, 0xc0,
  0x36, 0x1a,
  0x06, 0x88,
  0x07, 0xc0,
  0x0d, 0x87,
  0x0e, 0x41,
  0x4c, 0x00,
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0x64,
  0xc1, 0x4b,
  0x86, 0x35,
  0x50, 0x89,
  0x51, 0xc8,
  0x52, 0x96,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x00,
  0x57, 0x00,
  0x5a, 0x50,
  0x5b, 0x3c,
  0x5c, 0x00,
  0xe0, 0x00,
};

/* JPG 352x288 */
const unsigned char OV2640_352x288_JPEG[][2] =
{
  0xff, 0x01,
  0x12, 0x40,
  0x17, 0x11,
  0x18, 0x43,
  0x19, 0x00,
  0x1a, 0x4b,
  0x32, 0x09,
  0x4f, 0xca,
  0x50, 0xa8,
  0x5a, 0x23,
  0x6d, 0x00,
  0x39, 0x12,
  0x35, 0xda,
  0x22, 0x1a,
  0x37, 0xc3,
  0x23, 0x00,
  0x34, 0xc0,
  0x36, 0x1a,
  0x06, 0x88,
  0x07, 0xc0,
  0x0d, 0x87,
  0x0e, 0x41,
  0x4c, 0x00,
  
  0xff, 0x00,
  0xe0, 0x04,
  0xc0, 0x64,
  0xc1, 0x4b,
  0x86, 0x35,
  0x50, 0x89,
  0x51, 0xc8,
  0x52, 0x96,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x00,
  0x57, 0x00,
  0x5a, 0x58,
  0x5b, 0x48,
  0x5c, 0x00,
  0xe0, 0x00,
};

/* JPG 800x600 */
const unsigned char OV2640_800x600_JPEG[][2] =
{
  0xff, 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, 0x35,
  0x50, 0x89,
  0x51, 0x90,
  0x52, 0x2c,
  0x53, 0x00,
  0x54, 0x00,
  0x55, 0x88,
  0x57, 0x00,
  0x5a, 0xc8,
  0x5b, 0x96,
  0x5c, 0x00,
  0xd3, 0x02,
  0xe0, 0x00,
};

/* JPG 1600x1200 */
// 输出的图像大约为60KB, 而STM32F103RE的SRAM只有64KB
const unsigned char OV2640_1600x1200_JPEG[][2]=
{
  0xff, 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, 0x90,
  0x5b, 0x2c,
  0x5c, 0x05,
  0xd3, 0x02,
  0xe0, 0x00
};

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Resets the OV2640 camera.
  * @param  None
  * @retval None
  */
void OV2640_Reset(void)
{
  OV2640_WriteReg(OV2640_DSP_RA_DLMT, 0x01);
  OV2640_WriteReg(OV2640_SENSOR_COM7, 0x80);
}

/**
  * @brief  Reads the OV2640 Manufacturer identifier.
  * @param  OV2640ID: Pointer to the OV2640 Manufacturer identifier
  * @retval None
  */
void OV2640_ReadID(OV2640_IDTypeDef *OV2640ID)
{
  OV2640_WriteReg(OV2640_DSP_RA_DLMT, 0x01);
  OV2640ID->Manufacturer_ID1 = OV2640_ReadReg(OV2640_SENSOR_MIDH);
  OV2640ID->Manufacturer_ID2 = OV2640_ReadReg(OV2640_SENSOR_MIDL);
  OV2640ID->PIDH = OV2640_ReadReg(OV2640_SENSOR_PIDH);
  OV2640ID->PIDL = OV2640_ReadReg(OV2640_SENSOR_PIDL);
}

/**
  * @brief  Configures DCMI/DMA to capture image from the OV2640 camera.
  * @param  ImageFormat: Image format BMP or JPEG 
  * @param  BMPImageSize: BMP Image size
  * @retval None
  */
void OV2640_Init(ImageFormat_TypeDef ImageFormat)
{
  OV2640_IDTypeDef OV2640ID;
  OV2640_ReadID(&OV2640ID);
  printf("Manufacturer ID: 0x%04x, ", (OV2640ID.Manufacturer_ID1 << 8) | OV2640ID.Manufacturer_ID2);
  printf("Product ID: 0x%04x\n", (OV2640ID.PIDH << 8) | OV2640ID.PIDL);
  
  OV2640_JPEGConfig(ImageFormat);
  //OV2640_BrightnessConfig(0x20);
}

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

  OV2640_Reset();
  delay(200);

  /* Initialize OV2640 */
  for(i=0; i<(sizeof(OV2640_QQVGA)/2); i++)
  {
    OV2640_WriteReg(OV2640_QQVGA[i][0], OV2640_QQVGA[i][1]);
    delay(2);
  }
}

/**
  * @brief  Configures the OV2640 camera in QVGA mode.
  * @param  None
  * @retval None
  */
void OV2640_QVGAConfig(void)
{
  uint32_t i;

  OV2640_Reset();
  delay(200);

  /* Initialize OV2640 */
  for(i=0; i<(sizeof(OV2640_QVGA)/2); i++)
  {
    OV2640_WriteReg(OV2640_QVGA[i][0], OV2640_QVGA[i][1]);
    delay(2);
  }
}

/**
  * @brief  Configures the OV2640 camera in JPEG mode.
  * @param  JPEGImageSize: JPEG image size
  * @retval None
  */
void OV2640_JPEGConfig(ImageFormat_TypeDef ImageFormat)
{
  uint32_t i;

  OV2640_Reset();
  delay(200);

  /* Initialize OV2640 */
  for(i=0; i<(sizeof(OV2640_JPEG_INIT)/2); i++)
  {
    OV2640_WriteReg(OV2640_JPEG_INIT[i][0], OV2640_JPEG_INIT[i][1]);
  }

  /* Set to output YUV422 */
  for(i=0; i<(sizeof(OV2640_YUV422)/2); i++)
  {
    OV2640_WriteReg(OV2640_YUV422[i][0], OV2640_YUV422[i][1]);
  }

  OV2640_WriteReg(0xff, 0x01);
  OV2640_WriteReg(0x15, 0x00);

  /* Set to output JPEG */
  for(i=0; i<(sizeof(OV2640_JPEG)/2); i++)
  {
    OV2640_WriteReg(OV2640_JPEG[i][0], OV2640_JPEG[i][1]);
  }

  delay(100);

  switch(ImageFormat)
  {
    case JPEG_176x144:
    {
      for(i=0; i<(sizeof(OV2640_176x144_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_176x144_JPEG[i][0], OV2640_176x144_JPEG[i][1]);
      } 
      break;
    }
    case JPEG_320x240:
    {
       for(i=0; i<(sizeof(OV2640_320x240_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_320x240_JPEG[i][0], OV2640_320x240_JPEG[i][1]);
      }
      break;
    }
    case JPEG_352x288:
    {
      for(i=0; i<(sizeof(OV2640_352x288_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_352x288_JPEG[i][0], OV2640_352x288_JPEG[i][1]);
      }
      break;
    }
  case JPEG_800x600:
    {
      for(i=0; i<(sizeof(OV2640_800x600_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_800x600_JPEG[i][0], OV2640_800x600_JPEG[i][1]);
      }
      break;
    }
  case JPEG_1600x1200:
  {
      for(i=0; i<(sizeof(OV2640_1600x1200_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_1600x1200_JPEG[i][0], OV2640_1600x1200_JPEG[i][1]);
      }
      break;
    }
    default:
    {
      for(i=0; i<(sizeof(OV2640_160x120_JPEG)/2); i++)
      {
        OV2640_WriteReg(OV2640_160x120_JPEG[i][0], OV2640_160x120_JPEG[i][1]);
      }
    }
  }
}

/**
  * @brief  Configures the OV2640 camera brightness.
  * @param  Brightness: Brightness value, where Brightness can be: 
  *         0x40 for Brightness +2,
  *         0x30 for Brightness +1,
  *         0x20 for Brightness 0,
  *         0x10 for Brightness -1,
  *         0x00 for Brightness -2,
  * @retval None
  */
void OV2640_BrightnessConfig(uint8_t Brightness)
{
  OV2640_WriteReg(0xff, 0x00);
  OV2640_WriteReg(0x7c, 0x00);
  OV2640_WriteReg(0x7d, 0x04);
  OV2640_WriteReg(0x7c, 0x09);
  OV2640_WriteReg(0x7d, Brightness);
  OV2640_WriteReg(0x7d, 0x00);
}

/**
  * @brief  Configures the OV2640 camera Black and white mode.
  * @param  BlackWhite: BlackWhite value, where BlackWhite can be: 
  *         0x18 for B&W,
  *         0x40 for Negative,
  *         0x58 for B&W negative,
  *         0x00 for Normal,
  * @retval None
  */
void OV2640_BandWConfig(uint8_t BlackWhite)
{
  OV2640_WriteReg(0xff, 0x00);
  OV2640_WriteReg(0x7c, 0x00);
  OV2640_WriteReg(0x7d, BlackWhite);
  OV2640_WriteReg(0x7c, 0x05);
  OV2640_WriteReg(0x7d, 0x80);
  OV2640_WriteReg(0x7d, 0x80);
}

/**
  * @brief  Configures the OV2640 camera color effects.
  * @param  value1: Color effects value1
  * @param  value2: Color effects value2
  *         where value1 and value2 can be: 
  *         value1 = 0x40, value2 = 0xa6 for Antique,
  *         value1 = 0xa0, value2 = 0x40 for Bluish,
  *         value1 = 0x40, value2 = 0x40 for Greenish,
  *         value1 = 0x40, value2 = 0xc0 for Reddish,
  * @retval None
  */
void OV2640_ColorEffectsConfig(uint8_t value1, uint8_t value2)
{
  OV2640_WriteReg(0xff, 0x00);
  OV2640_WriteReg(0x7c, 0x00);
  OV2640_WriteReg(0x7d, 0x18);
  OV2640_WriteReg(0x7c, 0x05);
  OV2640_WriteReg(0x7d, value1);
  OV2640_WriteReg(0x7d, value2);
}

/**
  * @brief  Configures the OV2640 camera contrast.
  * @param  value1: Contrast value1
  * @param  value2: Contrast value2
  *         where value1 and value2 can be: 
  *         value1 = 0x28, value2 = 0x0c for Contrast +2,
  *         value1 = 0x24, value2 = 0x16 for Contrast +1,
  *         value1 = 0x20, value2 = 0x20 for Contrast 0,
  *         value1 = 0x1c, value2 = 0x2a for Contrast -1,
  *         value1 = 0x18, value2 = 0x34 for Contrast -2,
  * @retval None
  */
void OV2640_ContrastConfig(uint8_t value1, uint8_t value2)
{
  OV2640_WriteReg(0xff, 0x00);
  OV2640_WriteReg(0x7c, 0x00);
  OV2640_WriteReg(0x7d, 0x04);
  OV2640_WriteReg(0x7c, 0x07);
  OV2640_WriteReg(0x7d, 0x20);
  OV2640_WriteReg(0x7d, value1);
  OV2640_WriteReg(0x7d, value2);
  OV2640_WriteReg(0x7d, 0x06);
}

void OV2640_WriteReg(uint8_t addr, uint8_t data)
{
  I2C1->CR1 |= I2C_CR1_START;
  while ((I2C1->SR1 & I2C_SR1_SB) == 0);
  I2C1->DR = OV2640_DEVICE_WRITE_ADDRESS;
  while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
  if (I2C1->SR1 & I2C_SR1_AF)
  {
    printf("I2C Acknowledge failure! addr=0x%02x, data=0x%02x\n", addr, data);
    while (1);
  }
  if ((I2C1->SR2 & I2C_SR2_TRA) == 0) // 读SR2清除ADDR
    return;
  
  I2C1->DR = addr; // 向DR送入第一个数据后, I2C_SR1_TXE不会清零, 所以可以不用等待, 直接送入第二个数据
  I2C1->DR = data;
  while ((I2C1->SR1 & I2C_SR1_BTF) == 0);
  
  I2C1->CR1 |= I2C_CR1_STOP;
  while (I2C1->CR1 & I2C_CR1_STOP);
}

uint8_t OV2640_ReadReg(uint8_t addr)
{
  uint8_t data;
  I2C1->CR1 |= I2C_CR1_START;
  while ((I2C1->SR1 & I2C_SR1_SB) == 0);
  I2C1->DR = OV2640_DEVICE_WRITE_ADDRESS;
  while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0); // 等待ADDR或AF置位
  if (I2C1->SR1 & I2C_SR1_AF)
  {
    printf("I2C Acknowledge failure! addr=0x%02x\n", addr);
    while (1);
  }
  data = I2C1->SR2;
  
  I2C1->DR = addr;
  while ((I2C1->SR1 & I2C_SR1_BTF) == 0);
  I2C1->CR1 |= I2C_CR1_START;
  while ((I2C1->SR1 & I2C_SR1_SB) == 0);
  I2C1->DR = OV2640_DEVICE_READ_ADDRESS;
  while ((I2C1->SR1 & I2C_SR1_ADDR) == 0);
  data = I2C1->SR2;
  
  I2C1->CR1 |= I2C_CR1_STOP; // 必须在接收最后一字节数据之前请求STOP
  while ((I2C1->SR1 & I2C_SR1_RXNE) == 0);
  data = I2C1->DR;
  
  while (I2C1->CR1 & I2C_CR1_STOP);
  return data;
}

/**
  * @}
  */ 

/**
  * @}
  */ 

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

猜你喜欢

转载自blog.csdn.net/zlk1214/article/details/79559466