精华内容
下载资源
问答
  • 本文介绍使用单片机控制采集130W像素摄像头的方法。本章使用的摄像头为青青子木ZM系列,被动型130W像素,防雨广角型摄像头
  • 基于单片机串口摄像头模块的读写C语言源程序
  • 串口摄像机,采用《标准串口摄像机协议》(见附录),并提供单片机C语言读取图像的程序例程及VC6.0的主要函数例程,以方便用户移值,快速上手;提供PC端测试程序。 产品特点 1、具有较低照度(月光级、星光级、...

    M32S 串口摄像机普遍应用于水利、油田、电力、环保、森林防火、边境安全、野外监测、仓库防盗防火等;具有超低照度,高可靠性的特点。
    本串口摄像机,采用《标准串口摄像机协议》(见附录),并提供单片机C语言读取图像的程序例程及VC6.0的主要函数例程,以方便用户移值,快速上手;提供PC端测试程序。

    产品特点
    1、具有较低照度(月光级、星光级、星光+),在光线较暗或晚上的环境,仍然拍照得较为理想的效果;城市环境无需补光,夜视距离高达2000米;
    2、通讯端口具有防浪涌保护,提高产品可靠性;
    3、宽电压电源输入9~28V;
    4、电源防反接保护;
    在这里插入图片描述

    展开全文
  • PORTMON.EXE 你个可以监视已经被别的软件打开的串口数据分析软件。 如果你希望捕获自己串口程序发送的数据以及问题。 如果你希望分析别的软件利用串口发送的数据命令格式。...串口摄像头: type

    PORTMON.EXE   你个可以监视已经被别的软件打开的串口数据分析软件。

    如果你希望捕获自己串口程序发送的数据以及问题。
    如果你希望分析别的软件利用串口发送的数据命令格式。这个工具可以帮你。

    SSCOM.EXE   串口发送和调试工具,做单片机/嵌入式系统的应该要知道,除非你有更好的。当然WINDOWS带的超级终端也是不错的。


    串口摄像头:

    typedef struct{
        unsigned char Len;
        unsigned char Buff[10];
    }CMD_BUFFER;
    CMD_BUFFER Cmd;

    bool SendCommand(void)
    {
          unsigned char CmdHeader[4] = {0x55, 0x41, 0x52, 0x54};       //'UART'
          UART_SendData(CmdHeader,4);
          UART_SendData(Cmd.Buff,Cmd.Len);
          UART_RdReady();
          return true;
    }
      void ClrCmdBuff(void)
    {
        int i;
        
        for(i=0;i<7;i++)    Cmd.Buff[i]=0x0;
    }
    //停止更新Frame buffer
    void CmdStopFBuf(void)
    {
        ClrCmdBuff();
        Cmd.Buff[0] = 0xcA;

        Cmd.Len = 7;
        SendCommand();
    }
    //启动更新Frame buffer
    void CmdResumeFBuf(void)
    {
        ClrCmdBuff();
        Cmd.Buff[0] = 0xCB;

        Cmd.Len = 7;
        SendCommand();
    //获取Frame buffer大小
    long CmdGetFBufLen(void)
    {
        long dwFrameLen;
        
        ClrCmdBuff();
        Cmd.Buff[0] = 0xc9;

        Cmd.Len = 7;
        SendCommand();
        if(UART_GetData(RecBuff,3))
        {
    dwFrameLen = ( ((DWORD)(RecBuff[0]) & 0xFF) << 16 ) + 
    ( ((DWORD)(RecBuff[1]) & 0xFF) << 8 ) +
    ( ((DWORD)(RecBuff[2]) & 0xFF) );
        return dwFrameLen;
        }
        else
            return false;
    }
    //读取Frame buffer指定偏移量开始的指定大小,如果您的系统RAM足够大,也可以一次读取完。
    bool CmdReadFBuf(long offset, unsigned int rd_len)
    {
        
        Cmd.Buff[0] = 0xc7;
        Cmd.Buff[1] = (BYTE)( offset >> 16 );
        Cmd.Buff[2] = (BYTE)( offset >> 8 );
        Cmd.Buff[3] = (BYTE)( offset );
        Cmd.Buff[4] = (BYTE)0x00;
        Cmd.Buff[5] = (BYTE)( rd_len >> 8 );
        Cmd.Buff[6] = (BYTE)( rd_len );

        Cmd.Len = 7;
        SendCommand();
        
        if(UART_GetData(RecBuff,rd_len)==0)
            return false;
        return true;
    }

    功能介绍:
    1.    CMOS SENSOR,分辨率:VGA 640X480
    2.    图像压缩格式:JPG, 通过串口数据通讯读取
    3.    3种工作模式:实时图像模式、运动图像监测模式和省电模式
    4.    通讯方式:RS232, RS488;最高波特率:115200
    5.    视频复合(CVBS)信号输出


    :http://blog.sina.com.cn/ezant


    展开全文
  • 使用stm32f103zet6单片机驱动摄像头,通过串口将图像实时传输到上位机进行显示,内含上位机和整个工程源码,实测可用
  • 单片机串口数据处理

    千次阅读 2018-05-17 18:08:53
    例如:GPRS模块、GPS模块、语音模块、热敏微型打印机、串口摄像头等等。在与这些模块进行数据通信都离不开串口,而对于串口的操作,由于串口本身没有标准的通信协议,所以很难做到非常统一的操作过程。一般来说,...

    随着硬件系统的模块化发展,很多电子产品都做出模块并采用串口进行数据通信。例如:GPRS模块、GPS模块、语音模块、热敏微型打印机、串口摄像头等等。在与这些模块进行数据通信都离不开串口,而对于串口的操作,由于串口本身没有标准的通信协议,所以很难做到非常统一的操作过程。一般来说,不同的模块其有着特殊的通信协议,我们只能根据其协议进行数据解码。

      虽然说串口没有标准协议,但是我们却可以把它们的相似部分提取出来,做成模块化的程序,方便代码的移植和理解。下面我们简单谈到串口数据的处理方法。。。。。
    

    串口数据处理流程:

    一般来说,串口数据的接收都是采用中断方式,中断中只复制把串口发送的数据放入数据缓冲区中。而发送一般都是采用查询方式比较方便。不管是与什么设备通信,这一点完全是一致的。所以,我们完全可以把这部分代码独立起来。

    定义数据结构如下:
    typedef struct
    {
    u16 WtCnt; // 写指针
    u16 RdCnt;// 读指针
    u16 BufLen;缓冲尺寸
    u8 *RwBuf;// 读写缓冲

    } DF_RCV;
    复制代码
    对于这个结构来说非常简单,参数1是用于结束数据计数,参数2为处理数据计数,参数3为缓冲的大小,参数4为缓冲区指针,这里用指针是为了保证这个结构的独立,否则无法满足各种需求。

    实现函数:

    1. 初始化函数

      本函数用于对串口结构体中的各种数据进行初始化。
      /****************************************************************************

      • FunctionName : DFInit()
      • Description : 初始化
      • EntryParameter : None
      • ReturnValue : None
        ****************************************************************************/
        void DFInit(DF_RCV *pRcv)
        {
        u16 i;

      pRcv->WtCnt = 0x0000;
      pRcv->RdCnt = 0x0000;

      for (i=0; iBufLen; i++)
      {
      pRcv->RwBuf[i] = 0x00;
      }
      }
      复制代码

    2. 接收一字节数据

      本函数用于把串口中断接收的数据放入数据缓冲区中,并且接收计数器加1.
      /****************************************************************************

      • FunctionName : DFWriteByte()
      • Description : 数据接收(接收中断调用)
      • EntryParameter : None
      • ReturnValue : None
        ****************************************************************************/
        void DFWriteByte(u8 dat, DF_RCV *pRcv)
        {
        pRcv->RwBuf[pRcv->WtCnt] = dat; // 数据存入

      if (++(pRcv->WtCnt) >= pRcv->BufLen) // 缓冲判断
      {
      pRcv->WtCnt = 0;
      }
      }
      复制代码

    3. 读取一字节数据

      本函数用于从接收缓冲区中读取未处理的一字节数据,读计数器加1.
      /****************************************************************************

      • FunctionName : DFReadByte()
      • Description : 从接受缓冲中读取一字节数据
      • EntryParameter : None
      • ReturnValue : 返回读取数据
        ****************************************************************************/
        u8 DFReadByte(DF_RCV *pRcv)
        {
        u8 val = 0x00;

      val = pRcv->RwBuf[pRcv->RdCnt]; // 读取一字节
      if (++(pRcv->RdCnt) >= pRcv->BufLen)
      {
      pRcv->RdCnt = 0; // 清零
      }

      return val; // 返回数据
      }
      复制代码

    4. 获取缓冲区中未处理数据的长度

      本函数用于读取串口缓冲区中还未处理的数据的大小。
      /****************************************************************************

      • FunctionName : DFGetLen()
      • Description : 获取缓冲区中未读数据长度
      • EntryParameter : None
      • ReturnValue : 返回数据长度
        ****************************************************************************/
        u16 DFGetLen(DF_RCV *pRcv)
        {
        return ((pRcv->WtCnt >= pRcv->RdCnt) ? ((pRcv->WtCnt - pRcv->RdCnt)) :
        ((pRcv->WtCnt + pRcv->BufLen) - pRcv->RdCnt));
        }
        复制代码
        有了以上几个函数,串口的处理就非常简单了。这几个函数可以应用到任何串口中,也可以应用到任务微处理器上,一致非常简单,应用也非常方便。下面我们说说实际的应用。

    这部分代码为应用代码

    为了保证数据的相对独立和模块化,下面代码将写入应用代码中,和上面的程序不能放在相同的文件中。

    1. 数据定义

      首先需要定义一个缓冲区,这个缓冲区的大小根据实际应用定义,其大小一般为数据帧的最大值的2倍。之后需要定义一个DF_RCV数据,在这个数据中需要初始化这个结构图的参数。特别需要注意,缓冲的大小,和缓冲区指针赋值。
      u8 AU_Buf[AU_BUF_ZISE] = {0};
      DF_RCV AU_Rvc = {0, 0, AU_BUF_ZISE, AU_Buf};
      复制代码

    2. 编写数据接收函数

      本函数把串口数据放入缓冲区中,此函数必须在串口中断中调用。
      /****************************************************************************

      • FunctionName : AURcvDat()
      • Description : 串口数据接收(串口中断服务调用)
      • EntryParameter : None
      • ReturnValue : None
        ****************************************************************************/
        void AURcvDat(u8 dat)
        {
        DFWriteByte(dat, &AU_Rvc);
        }
        复制代码
    3. 数据处理函数

      本函数判断缓冲区中是否有数据,如果有,逐个读取并处理。
      /****************************************************************************

      • FunctionName : AUTaskCtrl()
      • Description : 通信数据处理
      • EntryParameter : None
      • ReturnValue : None
        ****************************************************************************/
        void AUTaskCtrl(void)
        {
        u8 tmpDat;
        u16 i, len = 0;
        static u8 sendMark = 0;

      len = DFGetLen(&AU_Rvc); // 获取未读数据长度
      for (i=0; i < len; i++)
      {
      tmpDat = DFReadByte(&AU_Rvc); // 读一字节数据
      AU_PrcRcvDat(tmpDat);
      }
      }

    复制代码

    函数AU_PrcRcvDat(tmpDat)是数据处理函数,首先是数据帧判断,如果是一帧数据,就进行相应操作,并把操作结果返回。了解了这个过程,串口的编程就变得非常简单。而且我们在读程序时,只要看懂一个串口处理过程,其他串口的程序就自然懂了,非常方便吧。
    关键字:单片机 串口数据处理
    编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/article_2016071427571.html

    展开全文
  • 例程1:摄像头识别小圆,然后通过串口输出小圆的xy坐标。单片机可以直接接受,具体串口配置自己配置。 例程2:VSwin32命令控制台程序 调用串口,可以根据自己需要配置串口,完成windows与单片机的数据传输。可作为...
  • PC端程序:在port.txt里面写好串口号,然后运行Release里面的程序接收图像,该程序是用Visual Studio 2012编译的单片机端程序:dcmi_ov2640.h和dcmi_ov2640.c是从STM32F4官方标准库里面的OV2640 DCMI例程里面找到的...

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

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

    【开发板】

    这个开发板上的摄像头自带了一个贴片与非门,HREF和PCLK进行与非后连接到单片机的PB9端口上。该摄像头上还自带有一个24MHz的晶振给OV2640提供时钟。

    微雪(WaveShare)OV2640摄像头没有与非门和晶振。如果使用这种摄像头,那么就必须外接一个74HC00的与非门(1A接HREF,1B接PCLK,1Y接PB9,不用的输入端要接高电平,不用的输出端悬空),然后选择一个单片机的定时器输出比较通道(如TIM3_CH2),输出24MHz的时钟信号到摄像头的XCLK引脚上,给摄像头提供时钟。代码如下:

    // 利用TIM3_CH2在PB5上为摄像头XCLK提供24MHz的时钟
    void OV2640_EnableClock(void)
    {
      RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
      AFIO->MAPR |= AFIO_MAPR_TIM3_REMAP_1;
      GPIOB->CRL = (GPIOB->CRL & 0xff0fffff) | 0xb00000; // PB5设为复用推挽输出
      
      TIM3->ARR = 2; // 72MHz/(2+1)=24MHz
      TIM3->PSC = 0; // 不分频
      TIM3->EGR = TIM_EGR_UG;
      
      TIM3->CCR2 = 1; // 决定占空比
      TIM3->CCMR1 = TIM_CCMR1_OC2M; // PWM2模式
      TIM3->CCER = TIM_CCER_CC2E; // 打开输出比较
      TIM3->CR1 = TIM_CR1_CEN; // 打开定时器
    }

    以上这段代码在调用OV2640_Init前调用就可以了。微雪OV2640摄像头的SIOC、SIOD分别是I2C的SCL和SDA。D2~D9接到PC0~PC7上,RET和PWDN引脚可以悬空。不过RET引脚默认是没有接到摄像头上的。根据微雪OV2640摄像头的原理图,必须要将背面左边从下往上数第三个跳线(R2)用焊锡丝接通之后,才能使用RET引脚。此外,原理图上RET引脚还通过R3接了一个4.7kΩ电阻和104电容组成的上电复位电路。

    接与非门可以方便DMA捕获数据,不受HREF的干扰。虽然OV2640的手册上说,将COM10寄存器的Bit[5]置1也可以实现与非门的功能,但是笔者试了一下不行,置1后PCLK引脚上就完全没有时钟信号输出了。

    【拍摄的图像】

    [1600x1200]

    [800x600]

    采集到的JPEG图像,前面几十字节内容一般都是固定不变的。比如开头四个字节通常为FFD8FFE0。根据这一点,可以检查采集过程中是否有字节丢失的情况。

    【截图】

    PC端程序运行结果:

    采集到的图像:


    【采集原理】

    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****/
    展开全文
  • 例程1:摄像头识别小圆,然后通过串口输出小圆的xy坐标。单片机可以直接接受,具体串口配置自己配置。 例程2:VSwin32命令控制台程序 调用串口,可以根据自己需要配置串口,完成windows与单片机的数据传输。可作为...
  • 第13 章 LabVIEW 串口通信程序设计第13 章 LabVIEW 串口通信程序设计以PC 作为上位机,以调制解调器(Modem )、串行打印机、各种监控模块、PLC 、摄像头云台、数控机床、单片机及智能设备等作为下位机广泛应用于测控...
  • 目标是基于RT Thread实时操作系统,将Micropython、OpenMV、USB摄像头+模拟摄像头+STM32本身自带的DVP摄像头,在OpenMV框架下,全部引入进来。这样的话,该软件平台同时具备了RTOS、Python脚本、USB OpenMV串口驱动...
  • 大致是需要用摄像头检测圆,然后把圆的xy坐标发送给单片机。 但是网上大部分都是基于MFC串口控件的例程。大海捞针找到了个win32命令控制台的串口例程 ,自己改了一下贡献出来。   直接调用API实现串口通信   ...
  • 飞思卡尔智能车摄像头上位机…

    千次阅读 2014-11-13 23:08:23
    原文地址:飞思卡尔智能车摄像头上位机采集程序作者:玲声依旧美 ... S0为单片机RXD,接到串口模块的TXD中,S1为单片机TXD,接到单片机RXD中  波特率默认为9600,用户可自行修改波特率,图像大小为64*50,帧头帧
  • VB串口通信源码210个

    千次下载 热门讨论 2013-10-15 11:32:27
    065、VB开发的RS232串口图像处理器驱动(摄像头驱动) 066、VB开发的串口通信源码 067、VB开发的串口与三菱FX PLC通讯源码 068、VB控制串口232通讯,对飞利浦M1卡内数据进行处理,支持密码修改等 069、VB利用Mscomm控件...
  • 串口编程云台

    2016-11-30 14:50:14
    云台设备,说白了就是一个单片机驱动电机或者继电器从而实现摄像头在垂直方向的运动,水平方向的摆动,以及电子镜头的聚焦光圈变化和变倍。   上位机通过串口连到单片机。真正的控制功能是通过上位机向串口发送...
  • 基于STM32的OV7725摄像头拍照实验

    万次阅读 多人点赞 2018-04-15 13:44:55
    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线+鹰眼OV7725摄像头(注意,为了减少摄像头连线的麻烦,建议初学者选取单片机时选用带有摄像头接口的板子)工程介绍:需要移植FatFs文件系统,同时需要了解BMP...
  • 单片机根据sensor 控制串口摄像头图像采集和压缩处理,采集图像通过彩信的格式发送到用户手机,同时可以通过短信命令随时查看系统状态,或者摄像头采集图像,监控现场情况。  国内传统的电子防盗报警系统方案...
  • 这款iPhone的屏幕换过、没有面容识别、后置摄像头、电池换过。爱思助手到底能检测出几项问题呢?这台iPhoneXS Max显示屏看着和原装屏幕的区别还是非常大的。可以看到屏幕很浑浊,不清晰。原装显示屏更加清晰透彻。...
  • pc串口通信

    2016-07-27 01:14:50
    如调制解调器,穿行打印机,各种监控模块,plc,摄像头云台,数控机床,单片机及相关智能设备,甚至路由器也不一般。 计算机测控系统主控机一般采用pc或plc ,通过串口与测控模块相连,测控模块在连接相应的传感器...
  • 单片机根据sensor 控制串口摄像头图像采集和压缩处理,采集图像通过彩信的格式发送到用户手机,同时可以通过短信命令随时查看系统状态,或者摄像头采集图像,监控现场情况。  国内传统的电子防盗报警系统方案...
  • 采用opencv usb摄像头 提取小球位置 并串口输出给单片机 借鉴了很多网上的资源 实在没法一一列举,引用 了 对开源精神表示感谢 室友的美背出镜。。 # -*- coding:utf-8 -*- import serial import numpy as np ...
  • STM32F103C8T6驱动ov2640拍照串口传输到上位机

    千次阅读 多人点赞 2019-01-04 23:13:09
    最近想玩玩摄像头,于是在网上找找性价比比较高的摄像头(笔者喜欢用最廉价的东西做最牛逼的事情,给我一根足够长的杠杆,我就能撬动整个宇宙,哈哈哈~~~),之前用过OV7670这款摄像头,不过这款摄像头单片机要求较...
  • 串口通信深度探究

    千次阅读 2014-05-25 14:39:44
    所要求的软/硬件开发环境都很低,广泛应用于计算机及相关领域,如调制解调器(Modem)、串行打印机、各种监控模块、PLC、摄像头云台、数控机床、单片机及相关智能设备,甚至路由器也不例外(通过串口设置参数)。...
  • 摄像头单片机串口相连,连接好WiFi模块,将各个模块初始化完成。 连接电脑与WiFi模块,最后使用UDP协议,因为TCP协议有调错功能,会影响数据的传送,而UDP则没有,可以实现实时数据传输,就算有错误也会跳过...
  • 51单片机驱动ov2640做JPEG图片采集程序实例,51单片机选用的是新华龙的C8051F380,摄像头为200W像素的OV2640,通过串口传送数据给上位机,图片格式为JPEG
  • 主要涵盖串口通信的理论基础、Visual c++集成开发环境简介、MSComm控件串口编程、Windows API串口编程、TAPI通信编程、串口实现双机互连、串口调试精灵、串口控制Modem设备、串口控制单片机串口控制PLC、串口控制...
  • 星慈光零基础51单片机视频教程60讲,配套HJ-C52开发板,采用STC89C52RC,AT89S52单片机演示,从最基础的IO口操作,用简单C语言控制电平 0 1 有小学文化的同学都能看得明白,学习内部资源,中断,定时器、串口通信,...
  • 星慈光零基础51单片机视频教程60讲,配套HJ-C52开发板,采用STC89C52RC,AT89S52单片机演示,从最基础的IO口操作,用简单C语言控制电平 0 1 有小学文化的同学都能看得明白,学习内部资源,中断,定时器、串口通信,...
  • 星慈光零基础51单片机视频教程60讲,配套HJ-C52开发板,采用STC89C52RC,AT89S52单片机演示,从最基础的IO口操作,用简单C语言控制电平 0 1 有小学文化的同学都能看得明白,学习内部资源,中断,定时器、串口通信,...
  • 星慈光零基础51单片机视频教程60讲,配套HJ-C52开发板,采用STC89C52RC,AT89S52单片机演示,从最基础的IO口操作,用简单C语言控制电平 0 1 有小学文化的同学都能看得明白,学习内部资源,中断,定时器、串口通信,...

空空如也

空空如也

1 2 3 4
收藏数 63
精华内容 25
关键字:

串口摄像头单片机