主频需要160MHz以上,80MHz主频读取会丢数据,读取过程中要关闭所有中断否则会出现丢数据花屏现象,还有一个重要的地方需要注意,PCLK速度过慢同时照片信息量多时,jpeg文件过大也会花一部分,像如这样
这样
出现部分花屏原因是因为ov2640发现以当前PCLK的速度已经无法在一帧照片的时间内将jpeg文件发送完,索性直接就停止发送了,在单片机允许的速度下适当的调整PCLK的速度可以发送更大的jpeg图片,调整的方法就是设置0xD3寄存器
SCCB_WR_Reg(0XD3,0X14);//0X30:48MHz/48 = 1MHz;0X18:48MHz/24 = 2MHz;0X14:48MHz/20 = 2.4MHz;0X0F:48MHz/16 = 3MHz;0X0C:48MHz/12 = 4MHz
手册
废话不多说上样张102 KB (104,520 字节)
下位机程序
main.c
#include <stdio.h>
#include "wm_hal.h"
#include "ov2640.h"
#include "general.h"void Error_Handler(void);
static void GPIO_Init(void);static void GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIO_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_PIN_RESET);GPIO_InitStruct.Pin = GPIO_PIN_8| GPIO_PIN_12| GPIO_PIN_14;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8| GPIO_PIN_12| GPIO_PIN_14, GPIO_PIN_RESET);
// //设置摄像头数据接口GPIO_InitStruct.Pin = GPIO_PIN_9| GPIO_PIN_11| GPIO_PIN_13| GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);//设置摄像头数据接口GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5| GPIO_PIN_6 | GPIO_PIN_7 ;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);HAL_NVIC_SetPriority(GPIOB_IRQn, 0);HAL_NVIC_EnableIRQ(GPIOB_IRQn);}int main(void)
{SystemClock_Config(CPU_CLK_160M);printf("enter main\r\n");HAL_Init();GPIO_Init();HAL_Delay(5000);printf("ov2640_init\r\n");ov2640_init();while (1){HAL_Delay(5000);}return 0;
}void Error_Handler(void)
{while (1){}
}void assert_failed(uint8_t *file, uint32_t line)
{printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
ov2640.c
#include "ov2640.h"
#include "ov2640cfg.h"
#include "wm_hal.h"
#include "general.h"#define OV2640_VSYNC HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9) #define OV2640_PWDN_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_14, GPIO_PIN_SET) //POWER DOWN控制信号
#define OV2640_PWDN_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_14, GPIO_PIN_RESET) //POWER DOWN控制信号#define OV2640_RST_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET) //复位控制信号
#define OV2640_RST_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET) //复位控制信号#define OV2640_HREF HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) //HREF信号
#define OV2640_PCLK HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13) //PCLK信号//#define OV2640_SCL HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) //读SCL信号
#define OV2640_SCL_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET) //写SCL高
#define OV2640_SCL_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET) //写SCL低#define OV2640_SDA HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_10) //读SDA信号
#define OV2640_SDA_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET) //写SDA信号
#define OV2640_SDA_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET) //读SDA信号#define OV2640_JPEG_WIDTH 1600 //320//1600 //JPEG拍照的宽度
#define OV2640_JPEG_HEIGHT 1200 //240//1200 //JPEG拍照的高度#define SCCB_ID 0X60 //OV2640的IDuint8_t buffer[200*1024];static void gpio_sda_output()
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}static void gpio_sda_input()
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}unsigned char ov2640_read_data(GPIO_TypeDef *GPIOx)
{unsigned char data = GPIOx->DATA&0x00FF;//数据输入端口return data;
}//SCCB起始信号
//当时钟为高的时候,数据线的高到低,为SCCB起始信号
//在激活状态下,SDA和SCL均为低电平
void SCCB_Start(void)
{OV2640_SDA_H; //数据线高电平 OV2640_SCL_H; //在时钟线高的时候数据线由高至低delay_us(50); OV2640_SDA_L;delay_us(50); OV2640_SCL_L; //数据线恢复低电平,单操作函数必要
}//SCCB停止信号
//当时钟为高的时候,数据线的低到高,为SCCB停止信号
//空闲状况下,SDA,SCL均为高电平
void SCCB_Stop(void)
{OV2640_SDA_L;delay_us(50); OV2640_SCL_H;delay_us(50); OV2640_SDA_H; delay_us(50);
} //产生NA信号
void SCCB_No_Ack(void)
{delay_us(50);OV2640_SDA_H;OV2640_SCL_H;delay_us(50);OV2640_SCL_L; delay_us(50);OV2640_SDA_L;delay_us(50);
}//SCCB,写入一个字节
//返回值:0,成功;1,失败.
uint8_t SCCB_WR_Byte(uint8_t dat)
{uint8_t j,res; for(j=0;j<8;j++) //循环8次发送数据{if(dat&0x80){OV2640_SDA_H;}else{OV2640_SDA_L;}dat<<=1;delay_us(50);OV2640_SCL_H;delay_us(50);OV2640_SCL_L; } gpio_sda_input(); //设置SDA为输入 delay_us(50);OV2640_SCL_H; //接收第九位,以判断是否发送成功delay_us(50);if(OV2640_SDA == GPIO_PIN_SET){res=1; //SDA=1发送失败,返回1//printf("SDA=1发送失败,返回1\r\n");}else {res=0; //SDA=0发送成功,返回0//printf("SDA=0发送成功,返回0\r\n");}OV2640_SCL_L; gpio_sda_output(); //设置SDA为输出 return res;
} //SCCB 读取一个字节
//在SCL的上升沿,数据锁存
//返回值:读到的数据
uint8_t SCCB_RD_Byte(void)
{uint8_t temp=0,j; gpio_sda_input(); //设置SDA为输入 for(j=8;j>0;j--) //循环8次接收数据{ delay_us(50);OV2640_SCL_H;temp=temp<<1;if(OV2640_SDA == GPIO_PIN_SET){temp++; }delay_us(50);OV2640_SCL_L; } gpio_sda_output(); //设置SDA为输出 return temp;
} //写寄存器
//返回值:0,成功;1,失败.
uint8_t SCCB_WR_Reg(uint8_t reg,uint8_t data)
{uint8_t res=0;SCCB_Start(); //启动SCCB传输if(SCCB_WR_Byte(SCCB_ID)){res=1;//写器件ID }delay_us(100);if(SCCB_WR_Byte(reg)){res=1; //写寄存器地址 } delay_us(100);if(SCCB_WR_Byte(data)){res=1; //写数据 } SCCB_Stop(); return res;
}//读寄存器
//返回值:读到的寄存器值
uint8_t SCCB_RD_Reg(uint8_t reg)
{uint8_t val=0;SCCB_Start(); //启动SCCB传输SCCB_WR_Byte(SCCB_ID); //写器件ID delay_us(100); SCCB_WR_Byte(reg); //写寄存器地址 delay_us(100); SCCB_Stop(); delay_us(100); //设置寄存器地址后,才是读SCCB_Start();SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令 delay_us(100);val=SCCB_RD_Byte(); //读取数据SCCB_No_Ack();SCCB_Stop();return val;
}uint8_t ov2640_start()
{uint16_t i;uint16_t reg;gpio_sda_output(); OV2640_RST_H; //结束复位OV2640_PWDN_H; //POWER OFFHAL_Delay(1000);OV2640_PWDN_L; //POWER ONHAL_Delay(100);OV2640_RST_L; //复位OV2640HAL_Delay(100);OV2640_RST_H; //结束复位 SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01); //操作sensor寄存器SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80); //软复位OV2640HAL_Delay(50); reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH); //读取厂家ID 高八位reg<<=8;reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL); //读取厂家ID 低八位if(reg!=OV2640_MID){printf("MID:%d\r\n",reg);return 1;}reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH); //读取厂家ID 高八位reg<<=8;reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL); //读取厂家ID 低八位if(reg!=OV2640_PID){printf("HID:%d\r\n",reg);//return 2;} //初始化 OV2640,采用SXGA分辨率(1600*1200) for(i=0;i<sizeof(ov2640_uxga_init_reg_tbl)/2;i++){SCCB_WR_Reg(ov2640_uxga_init_reg_tbl[i][0],ov2640_uxga_init_reg_tbl[i][1]);} return 0x00; //ok
}//OV2640切换为JPEG模式
void OV2640_JPEG_Mode(void)
{uint16_t i=0;//设置:YUV422格式for(i=0;i<(sizeof(ov2640_yuv422_reg_tbl)/2);i++){SCCB_WR_Reg(ov2640_yuv422_reg_tbl[i][0],ov2640_yuv422_reg_tbl[i][1]); } //设置:输出JPEG数据for(i=0;i<(sizeof(ov2640_jpeg_reg_tbl)/2);i++){SCCB_WR_Reg(ov2640_jpeg_reg_tbl[i][0],ov2640_jpeg_reg_tbl[i][1]); }
}//OV2640切换为RGB565模式
void OV2640_RGB565_Mode(void)
{uint16_t i=0;//设置:RGB565输出for(i=0;i<(sizeof(ov2640_rgb565_reg_tbl)/2);i++){SCCB_WR_Reg(ov2640_rgb565_reg_tbl[i][0],ov2640_rgb565_reg_tbl[i][1]); }
} //设置图像输出大小
//OV2640输出图像的大小(分辨率),完全由该函数确定
//width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
//返回值:0,设置成功
// 其他,设置失败
uint8_t OV2640_OutSize_Set(uint16_t width,uint16_t height)
{uint16_t outh;uint16_t outw;uint8_t temp; if(width%4)return 1;if(height%4)return 2;outw=width/4;outh=height/4; SCCB_WR_Reg(0XFF,0X00); SCCB_WR_Reg(0XE0,0X04); SCCB_WR_Reg(0X5A,outw&0XFF); //设置OUTW的低八位SCCB_WR_Reg(0X5B,outh&0XFF); //设置OUTH的低八位temp=(outw>>8)&0X03;temp|=(outh>>6)&0X04;SCCB_WR_Reg(0X5C,temp); //设置OUTH/OUTW的高位 SCCB_WR_Reg(0XE0,0X00); return 0;
}//OV2640拍照jpg图片
//返回值:0,成功
// 其他,错误代码
uint16_t ov2640_jpg_photo()
{uint8_t res=0;uint32_t bwr;uint32_t i=0;uint32_t jpeglen=0;
// uint8_t isJpeg = 0;OV2640_JPEG_Mode(); //切换为JPEG模式 OV2640_OutSize_Set(OV2640_JPEG_WIDTH,OV2640_JPEG_HEIGHT); SCCB_WR_Reg(0XFF,0X00);SCCB_WR_Reg(0XD3,30);SCCB_WR_Reg(0XFF,0X01);SCCB_WR_Reg(0X11,0X1); HAL_NVIC_DisableIRQ(SYS_TICK_IRQn);for(i=0;i<10;i++) //丢弃20帧,等待OV2640自动调节好(曝光白平衡之类的){while(OV2640_VSYNC == GPIO_PIN_SET); while(OV2640_VSYNC == GPIO_PIN_RESET); } while(OV2640_VSYNC == GPIO_PIN_SET) //开始采集jpeg数据{while(OV2640_HREF){ while(OV2640_PCLK == GPIO_PIN_RESET); buffer[jpeglen]=ov2640_read_data(GPIOA);while(OV2640_PCLK == GPIO_PIN_SET); jpeglen++;} } printf("jpeg data size:%d\r\n",jpeglen); //串口打印JPEG文件大小 HAL_NVIC_EnableIRQ(SYS_TICK_IRQn);HAL_Delay(1000);for(i = 0;i < jpeglen;i++){printf("%c",buffer[i]);}return res;
} void ov2640_init()
{while(ov2640_start()) //初始化OV2640{HAL_Delay(1000);}printf("ov2640_init_ok\r\n");while(1){ov2640_jpg_photo();HAL_Delay(2000);}
}
ov2640.h
#ifndef __OV2640_H__
#define __OV2640_H__//
#define OV2640_MID 0X7FA2
#define OV2640_PID 0X2642//当选择DSP地址(0XFF=0X00)时,OV2640的DSP寄存器地址映射表
#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 //当选择传感器地址(0XFF=0X01)时,OV2640的DSP寄存器地址映射表
#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 0x62void ov2640_init();#endif
ov2640cfg.h
#ifndef __OV2640_CFG_H__
#define __OV2640_CFG_H__
//OV2640 UXGA初始化寄存器序列表
//此模式下帧率为15帧
//UXGA(1600*1200)
const unsigned char ov2640_uxga_init_reg_tbl[][2]=
{ 0xff, 0x00,0x2c, 0xff,0x2e, 0xdf,0xff, 0x01,0x3c, 0x32,//0x11, 0x01,0x09, 0x02,0x04, 0xD8,//水平镜像,垂直翻转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,0x46, 0x00,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,//UXGA 1600*12000x17, 0x11,0x18, 0x75,0x19, 0x01,0x1a, 0x97,0x32, 0x36,0x03, 0x0f, 0x37, 0x40,// 0x4f, 0xca,0x50, 0xa8,0x5a, 0x23,0x6d, 0x00,0x6d, 0x38,//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, 0xef,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, 0xc8,0xc1, 0x96,0x8c, 0x00,0x86, 0x3d,0x50, 0x00,0x51, 0x90,0x52, 0x2c,0x53, 0x00,0x54, 0x00,0x55, 0x88,0x5a, 0x90,0x5b, 0x2C,0x5c, 0x05,0xd3, 0x82,//auto设置要小心//0xc3, 0xed,0x7f, 0x00,0xda, 0x09,0xe5, 0x1f,0xe1, 0x67,0xe0, 0x00,0xdd, 0x7f,0x05, 0x00,
};
//OV2640 SVGA初始化寄存器序列表
//此模式下,帧率可以达到30帧
//SVGA 800*600
const unsigned char ov2640_svga_init_reg_tbl[][2]=
{ 0xff, 0x00,0x2c, 0xff,0x2e, 0xdf,0xff, 0x01,0x3c, 0x32,//0x11, 0x01,0x09, 0x02,0x04, 0xD8,//水平镜像,垂直翻转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,0x46, 0x22,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, 0x40,//SVGA 800*6000x17, 0x11,0x18, 0x43,0x19, 0x00,0x1a, 0x4b,0x32, 0x09,0x37, 0xc0,//0x4f, 0xca,0x50, 0xa8,0x5a, 0x23,0x6d, 0x00,0x3d, 0x38,//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, 0x82,//auto设置要小心//0xc3, 0xed,0x7f, 0x00,0xda, 0x09,0xe5, 0x1f,0xe1, 0x67,0xe0, 0x00,0xdd, 0x7f,0x05, 0x00,
};
const unsigned char ov2640_yuv422_reg_tbl[][2]=
{0xFF, 0x00, 0xDA, 0x10,0xD7, 0x03,0xDF, 0x00,0x33, 0x80,0x3C, 0x40,0xe1, 0x77,0x00, 0x00,
};
const unsigned char ov2640_jpeg_reg_tbl[][2]=
{0xff, 0x01, 0xe0, 0x14,0xe1, 0x77,0xe5, 0x1f,0xd7, 0x03,0xda, 0x10,0xe0, 0x00,
};
const unsigned char ov2640_rgb565_reg_tbl[][2]=
{0xFF, 0x00,0xDA, 0x09,0xD7, 0x03,0xDF, 0x02,0x33, 0xa0,0x3C, 0x00,0xe1, 0x67,0xff, 0x01, 0xe0, 0x00,0xe1, 0x00,0xe5, 0x00,0xd7, 0x00, 0xda, 0x00,0xe0, 0x00,
};
#endif
上位机程序.net开发,协议很简单,先发一行特定字符,包含图片大小,然后直接发送图片数据
程序
private byte[] buffer = new byte[200 * 1024];
private int offset = 0;
private int length = 0;
private bool isData = false;
public void DataCallBack(Object sender, SerialDataReceivedEventArgs e)
{try{if (isData==false){var data = _serialPort.ReadLine();if (!string.IsNullOrEmpty(data) && data.Length > 4){if(data.IndexOf("jpeg data size:")>=0){var lengthStr = data.Split(':');if(lengthStr != null && lengthStr.Length>1){length = int.Parse(lengthStr[1]);isData = true;}Console.WriteLine(data);}}}else{int thisLength = _serialPort.Read(buffer, offset, length - offset);offset = offset + thisLength;if(offset >= length){Console.WriteLine($"接收完一张图片,offset:{offset},length:{length}");string path = Directory.GetCurrentDirectory();DirectoryCheak(path + "\\out\\");bool ispictrue = false;List<byte> data = new List<byte>();for(int i = 1;i< length;i++){//Console.WriteLine($"i:{i},length:{length}");if (ispictrue == true){data.Add(buffer[i]);}if((buffer[i - 1] == 0xFF)&& (buffer[i] == 0xd8)){ispictrue = true;data.Add(buffer[i - 1]);data.Add(buffer[i]);}if((buffer[i - 1] == 0xFF) && (buffer[i] == 0xd9)){data.Add(buffer[i - 1]);data.Add(buffer[i]);ispictrue = false;break;}}WriteFileUsingBinaryWriter(path + "\\out\\",DateTime.Now.ToString("yyyyMMdd-HHmmss.fff") + ".jpg", data.ToArray(), data.Count());offset = 0;length = 0;isData = false;}}}catch (Exception ex){Console.WriteLine("串口解析出错" + ex.ToString());}
}public void WriteFileUsingBinaryWriter(string path,string fileName,byte[] data,int length)
{var outputStream = File.Create(path + fileName);using (var writer = new BinaryWriter(outputStream)){writer.Write(data, 0, length);}
}/// <summary>
/// 效验文件夹,没有就创建
/// </summary>
private void DirectoryCheak(string path)
{if (false == System.IO.Directory.Exists(path)){System.IO.Directory.CreateDirectory(path);}
}private void OpenSerialPort_Click(object sender, RoutedEventArgs e)
{_serialPort = new SerialPort();_serialPort.PortName = SerialPort.Text;_serialPort.BaudRate = 115200;_serialPort.Parity = Parity.None;_serialPort.DataBits = 8;_serialPort.StopBits = StopBits.One;_serialPort.ReadTimeout = 10000;//单位毫秒_serialPort.WriteTimeout = 10000;//单位毫秒//设置串口字节接收缓冲值,通常为1//获得接收后,触发事件处理_serialPort.ReceivedBytesThreshold = 1;_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataCallBack);try{_serialPort.Open();//Console.WriteLine(serialPort.IsOpen.ToString());OpenSerialPort.IsEnabled = false;CloseSerialPort.IsEnabled = true;}catch (Exception ex){MessageBox.Show("串口打开失败" + ex.ToString());System.Environment.Exit(0);//退出应用程序}
}private void CloseSerialPort_Click(object sender, RoutedEventArgs e)
{try{if (_serialPort.IsOpen == true){_serialPort.Close();OpenSerialPort.IsEnabled = true;CloseSerialPort.IsEnabled = false;}}catch (Exception ex){MessageBox.Show("串口关闭失败" + ex.ToString());System.Environment.Exit(0);//退出应用程序}
}