借鉴了很多材料以及走了很多弯路,最终终于实现了拍照储存并显示的功能
——————————————————————
- 材料:正点原子STM32f103,正点原子ov7725(带FIFO),平平无奇的16G SD卡
-
板子架好了就这样,旁边的东西是别的项目的,其实一开始用的是网上随便买的一款,但是不知道是因为代码不对还是数据传输问题,总是花屏,最好的情况就是能显示强光源
怀疑是数据干扰的问题,在网上搜了很多资料,大部分都说的是数据干扰的问题,要D0~D7八根数据线和其他线分开捆绑,用短线,最后也没有解决,索性换了个正点官方的ov7725摄像头,插上,问题立刻解决。
接线如下:
OV7670\OV7725模块 ------ STM32开发板
OV_D0~D7 ----------- PB0~7
OV_SCL ------------ PC4
OV_SDA ------------ PC5
OV_VSYNC ------------ PA15
FIFO_RRST ----------- PA4
FIFO_OE ----------- PA11
FIFO_WRST ------------ PA0
FIFO_WEN ------------ PA12
FIFO_RCLK ------------ PA1
接下来是代码主体部分
我们的目标是将摄像头捕捉的画面在按下按键后将图片以bmp格式储存在SD卡内。
bmp.h头文件
#ifndef __BMP_H__
#define __BMP_H__
#include "sys.h"
#define BMP_USE_MALLOC 1 //定义是否使用malloc,这里我们选择使用malloc
#define BMP_DBUF_SIZE 2048 //定义bmp解码数组的大小(最少应为LCD宽度*3)
//END/
//BMP信息头
typedef __packed struct
{
u32 biSize ; //说明BITMAPINFOHEADER结构所需要的字数。
long biWidth ; //说明图象的宽度,以象素为单位
long biHeight ; //说明图象的高度,以象素为单位
u16 biPlanes ; //为目标设备说明位面数,其值将总是被设为1
u16 biBitCount ; //说明比特数/象素,其值为1、4、8、16、24、或32
u32 biCompression ; //说明图象数据压缩的类型。其值可以是下述值之一:
//BI_RGB:没有压缩;
//BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);
//BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
//BI_BITFIELDS:每个象素的比特由指定的掩码决定。
u32 biSizeImage ; //说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0
long biXPelsPerMeter ; //说明水平分辨率,用象素/米表示
long biYPelsPerMeter ; //说明垂直分辨率,用象素/米表示
u32 biClrUsed ; //说明位图实际使用的彩色表中的颜色索引数
u32 biClrImportant ; //说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。
}BITMAPINFOHEADER ;
//BMP头文件
typedef __packed struct
{
u16 bfType ; //文件标志.只对'BM',用来识别BMP位图类型
u32 bfSize ; //文件大小,占四个字节
u16 bfReserved1 ;//保留
u16 bfReserved2 ;//保留
u32 bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的的偏移量
}BITMAPFILEHEADER ;
//彩色表
typedef __packed struct
{
u8 rgbBlue ; //指定蓝色强度
u8 rgbGreen ; //指定绿色强度
u8 rgbRed ; //指定红色强度
u8 rgbReserved ;//保留,设置为0
}RGBQUAD ;
//位图信息头
typedef __packed struct
{
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bmiHeader;
u32 RGB_MASK[3]; //调色板用于存放RGB掩码.
//RGBQUAD bmiColors[256];
}BITMAPINFO;
typedef RGBQUAD * LPRGBQUAD;//彩色表
//图象数据压缩的类型
#define BI_RGB 0 //没有压缩.RGB 5,5,5.
#define BI_RLE8 1 //每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);
#define BI_RLE4 2 //每个象素4比特的RLE压缩编码,压缩格式由2字节组成
#define BI_BITFIELDS 3 //每个象素的比特由指定的掩码决定。
///
//BMP编解码函数
u8 stdbmp_decode(const u8 *filename);
u8 minibmp_decode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u16 acolor,u8 mode);//尺寸小于240*320的bmp图片解码.
u8 bmp_encode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 mode);
///
#endif
bmp.c
```c
#include "piclib.h"
#include "bmp.h"
#include "string.h"
#include "ff.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//图片解码 驱动代码-bmp解码部分
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2014/3/14
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//升级说明
//无
//
//不使用内存分配
#if BMP_USE_MALLOC == 0
FIL f_bfile;
u8 bmpreadbuf[BMP_DBUF_SIZE];
#endif
//标准的bmp解码,解码filename这个BMP文件
//速度比较慢.主要
//filename:包含路径的文件名
//返回值:0,成功;
// 其他,错误码.
u8 stdbmp_decode(const u8 *filename)
{
FIL* f_bmp;
u16 br;
u16 count;
u8 rgb ,color_byte;
u16 x ,y,color;
u16 countpix=0;//记录像素
//x,y的实际坐标
u16 realx=0;
u16 realy=0;
u8 yok=1;
u8 res;
u8 *databuf; //数据读取存放地址
u16 readlen=BMP_DBUF_SIZE;//一次从SD卡读取的字节数长度
u8 *bmpbuf; //数据解码地址
u8 biCompression=0; //记录压缩方式
u16 rowlen; //水平方向字节数
BITMAPINFO *pbmp; //临时指针
#if BMP_USE_MALLOC == 1 //使用malloc
databuf=(u8*)mymalloc(readlen); //开辟readlen字节的内存区域
if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
f_bmp=(FIL *)mymalloc(sizeof(FIL)); //开辟FIL字节的内存区域
if(f_bmp==NULL) //内存申请失败.
{
myfree(databuf);
return PIC_MEM_ERR;
}
#else //不使用malloc
databuf=bmpreadbuf;
f_bmp=&f_bfile;
#endif
res=f_open(f_bmp,(const TCHAR*)filename,FA_READ);//打开文件
if(res==0)//打开成功.
{
f_read(f_bmp,databuf,readlen,(UINT*)&br); //读出readlen个字节
pbmp=(BITMAPINFO*)databuf; //得到BMP的头部信息
count=pbmp->bmfHeader.bfOffBits; //数据偏移,得到数据段的开始地址
color_byte=pbmp->bmiHeader.biBitCount/8; //彩色位 16/24/32
biCompression=pbmp->bmiHeader.biCompression;//压缩方式
picinfo.ImgHeight=pbmp->bmiHeader.biHeight; //得到图片高度
picinfo.ImgWidth=pbmp->bmiHeader.biWidth; //得到图片宽度
ai_draw_init();//初始化智能画图
//水平像素必须是4的倍数!!
if((picinfo.ImgWidth*color_byte)%4)rowlen=((picinfo.ImgWidth*color_byte)/4+1)*4;
else rowlen=picinfo.ImgWidth*color_byte;
//开始解码BMP
color=0;//颜色清空
x=0 ;
y=picinfo.ImgHeight;
rgb=0;
//对于尺寸小于等于设定尺寸的图片,进行快速解码
realy=(y*picinfo.Div_Fac)>>13;
bmpbuf=databuf;
while(1)
{
while(count<readlen) //读取一簇1024扇区 (SectorsPerClust 每簇扇区数)
{
if(color_byte==3) //24位颜色图
{
switch (rgb)
{
case 0:
color=bmpbuf[count]>>3; //B
break ;
case 1:
color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
break;
case 2 :
color+=((u16)bmpbuf[count]<<8)&0XF800;//R
break ;
}
}else if(color_byte==2) //16位颜色图
{
switch(rgb)
{
case 0 :
if(biCompression==BI_RGB)//RGB:5,5,5
{
color=((u16)bmpbuf[count]&0X1F); //R
color+=(((u16)bmpbuf[count])&0XE0)<<1; //G
}else //RGB:5,6,5
{
color=bmpbuf[count]; //G,B
}
break ;
case 1 :
if(biCompression==BI_RGB)//RGB:5,5,5
{
color+=(u16)bmpbuf[count]<<9; //R,G
}else //RGB:5,6,5
{
color+=(u16)bmpbuf[count]<<8; //R,G
}
break ;
}
}else if(color_byte==4)//32位颜色图
{
switch (rgb)
{
case 0:
color=bmpbuf[count]>>3; //B
break ;
case 1:
color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
break;
case 2 :
color+=((u16)bmpbuf[count]<<8)&0XF800;//R
break ;
case 3 :
//alphabend=bmpbuf[count];//不读取 ALPHA通道
break ;
}
}else if(color_byte==1)//8位色,暂时不支持,需要用到颜色表.
{
}
rgb++;
count++ ;
if(rgb==color_byte) //水平方向读取到1像素数数据后显示
{
if(x<picinfo.ImgWidth)
{
realx=(x*picinfo.Div_Fac)>>13;//x轴实际值
if(is_element_ok(realx,realy,1)&&yok)//符合条件
{
pic_phy.draw_point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF-1,color);//显示图片
//POINT_COLOR=color;
//LCD_DrawPoint(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF);
//SRAMLCD.Draw_Point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,color);
}
}
x++;//x轴增加一个像素
color=0x00;
rgb=0;
}
countpix++;//像素累加
if(countpix>=rowlen)//水平方向像素值到了.换行
{
y--;
if(y==0)break;
realy=(y*picinfo.Div_Fac)>>13;//实际y值改变
if(is_element_ok(realx,realy,0))yok=1;//此处不改变picinfo.staticx,y的值
else yok=0;
x=0;
countpix=0;
color=0x00;
rgb=0;
}
}
res=f_read(f_bmp,databuf,readlen,(UINT *)&br);//读出readlen个字节
if(br!=readlen)readlen=br; //最后一批数据
if(res||br==0)break; //读取出错
bmpbuf=databuf;
count=0;
}
f_close(f_bmp);//关闭文件
}
#if BMP_USE_MALLOC == 1 //使用malloc
myfree(databuf);
myfree(f_bmp);
#endif
return res; //BMP显示结束.
}
//小尺寸的bmp解码,解码filename这个BMP文件
//filename:包含路径的文件名
//x,y,width,height:开窗大小
//acolor:附加的alphablend的颜色(这个仅对32位色bmp有效!!!)
//mode:模式(除了bit5,其他的均只对32位色bmp有效!!!)
// bit[7:6]:0,仅使用图片本身和底色alphablend;
// 1,仅图片和acolor进行alphablend,并且不适用附加的透明度;
// 2,底色,acolor,图片,一起进行alphablend;
// bit5:保留
// bit4~0:0~31,使用附加alphablend的透明程度
//返回值:0,成功;
// 其他,错误码.
u8 minibmp_decode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u16 acolor,u8 mode)//尺寸小于240*320的bmp图片解码.
{
FIL* f_bmp;
u16 br;
u8 color_byte;
u16 tx,ty,color;
//tx,ty的实际坐标
u8 res;
u16 i,j;
u8 *databuf; //数据读取存 放地址
u16 readlen=BMP_DBUF_SIZE;//一次从SD卡读取的字节数长度,不能小于LCD宽度*3!!!
u8 *bmpbuf; //数据解码地址
u8 biCompression=0; //记录压缩方式
u16 rowcnt; //一次读取的行数
u16 rowlen; //水平方向字节数
u16 rowpix=0; //水平方向像素数
u8 rowadd; //每行填充字节数
u16 tmp_color;
u8 alphabend=0xff; //代表透明色为0,完全不透明
u8 alphamode=mode>>6; //得到模式值,0/1/2
BITMAPINFO *pbmp; //临时指针
//得到窗体尺寸
picinfo.S_Height=height;
picinfo.S_Width=width;
#if BMP_USE_MALLOC == 1 //使用malloc
databuf=(u8*)mymalloc(readlen); //开辟readlen字节的内存区域
if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
f_bmp=(FIL *)mymalloc(sizeof(FIL)); //开辟FIL字节的内存区域
if(f_bmp==NULL) //内存申请失败.
{
myfree(databuf);
return PIC_MEM_ERR;
}
#else
databuf=bmpreadbuf;
f_bmp=&f_bfile;
#endif
res=f_open(f_bmp,(const TCHAR*)filename,FA_READ);//打开文件
if(res==0)//打开成功.
{
f_read(f_bmp,databuf,sizeof(BITMAPINFO),(UINT*)&br);//读出BITMAPINFO信息
pbmp=(BITMAPINFO*)databuf; //得到BMP的头部信息
color_byte=pbmp->bmiHeader.biBitCount/8; //彩色位 16/24/32
biCompression=pbmp->bmiHeader.biCompression;//压缩方式
picinfo.ImgHeight=pbmp->bmiHeader.biHeight; //得到图片高度
picinfo.ImgWidth=pbmp->bmiHeader.biWidth; //得到图片宽度
//水平像素必须是4的倍数!!
if((picinfo.ImgWidth*color_byte)%4)rowlen=((picinfo.ImgWidth*color_byte)/4+1)*4;
else rowlen=picinfo.ImgWidth*color_byte;
rowadd=rowlen-picinfo.ImgWidth*color_byte; //每行填充字节数
//开始解码BMP
color=0;//颜色清空
tx=0 ;
ty=picinfo.ImgHeight-1;
if(picinfo.ImgWidth<=picinfo.S_Width&&picinfo.ImgHeight<=picinfo.S_Height)
{
rowcnt=readlen/rowlen; //一次读取的行数
readlen=rowcnt*rowlen; //一次读取的字节数
rowpix=picinfo.ImgWidth; //水平像素数就是宽度
f_lseek(f_bmp,pbmp->bmfHeader.bfOffBits); //偏移到数据起始位置
while(1)
{
res=f_read(f_bmp,databuf,readlen,(UINT *)&br); //读出readlen个字节
bmpbuf=databuf; //数据首地址
if(br!=readlen)rowcnt=br/rowlen; //最后剩下的行数
if(color_byte==3) //24位BMP图片
{
for(j=0;j<rowcnt;j++) //每次读到的行数
{
for(i=0;i<rowpix;i++)//写一行像素
{
color=(*bmpbuf++)>>3; //B
color+=((u16)(*bmpbuf++)<<3)&0X07E0; //G
color+=(((u16)*bmpbuf++)<<8)&0XF800; //R
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}else if(color_byte==2)//16位BMP图片
{
for(j=0;j<rowcnt;j++)//每次读到的行数
{
if(biCompression==BI_RGB)//RGB:5,5,5
{
for(i=0;i<rowpix;i++)
{
color=((u16)*bmpbuf&0X1F); //R
color+=(((u16)*bmpbuf++)&0XE0)<<1; //G
color+=((u16)*bmpbuf++)<<9; //R,G
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
}else //RGB 565
{
for(i=0;i<rowpix;i++)
{
color=*bmpbuf++; //G,B
color+=((u16)*bmpbuf++)<<8; //R,G
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}else if(color_byte==4) //32位BMP图片
{
for(j=0;j<rowcnt;j++) //每次读到的行数
{
for(i=0;i<rowpix;i++)
{
color=(*bmpbuf++)>>3; //B
color+=((u16)(*bmpbuf++)<<3)&0X07E0; //G
color+=(((u16)*bmpbuf++)<<8)&0XF800; //R
alphabend=*bmpbuf++; //ALPHA通道
if(alphamode!=1) //需要读取底色
{
tmp_color=pic_phy.read_point(x+tx,y+ty);//读取颜色
if(alphamode==2)//需要附加的alphablend
{
tmp_color=piclib_alpha_blend(tmp_color,acolor,mode&0X1F); //与指定颜色进行blend
}
color=piclib_alpha_blend(tmp_color,color,alphabend/8); //和底色进行alphablend
}else tmp_color=piclib_alpha_blend(acolor,color,alphabend/8); //与指定颜色进行blend
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;//x轴增加一个像素
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}
if(br!=readlen||res)break;
}
}
f_close(f_bmp);//关闭文件
}else res=PIC_SIZE_ERR;//图片尺寸错误
#if BMP_USE_MALLOC == 1 //使用malloc
myfree(databuf);
myfree(f_bmp);
#endif
return res;
}
//BMP编码函数
//将当前LCD屏幕的指定区域截图,存为16位格式的BMP文件 RGB565格式.
//保存为rgb565则需要掩码,需要利用原来的调色板位置增加掩码.这里我们已经增加了掩码.
//保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.
//filename:存放路径
//x,y:在屏幕上的起始坐标
//mode:模式.0,仅仅创建新文件的方式编码;1,如果之前存在文件,则覆盖之前的文件.如果没有,则创建新的文件.
//返回值:0,成功;其他,错误码.
u8 bmp_encode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 mode)
{
FIL* f_bmp;
u16 bmpheadsize; //bmp头大小
BITMAPINFO hbmp; //bmp头
u8 res=0;
u16 tx,ty; //图像尺寸
u16 *databuf; //数据缓存区地址
u16 pixcnt; //像素计数器
u16 bi4width; //水平像素字节数
if(width==0||height==0)return PIC_WINDOW_ERR; //区域错误
if((x+width-1)>lcddev.width)return PIC_WINDOW_ERR; //区域错误
if((y+height-1)>lcddev.height)return PIC_WINDOW_ERR; //区域错误
#if BMP_USE_MALLOC == 1 //使用malloc
databuf=(u16*)mymalloc(1024); //开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.
if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
f_bmp=(FIL *)mymalloc(sizeof(FIL)); //开辟FIL字节的内存区域
if(f_bmp==NULL) //内存申请失败.
{
myfree(databuf);
return PIC_MEM_ERR;
}
#else
databuf=(u16*)bmpreadbuf;
f_bmp=&f_bfile;
#endif
bmpheadsize=sizeof(hbmp);//得到bmp文件头的大小
mymemset((u8*)&hbmp,0,sizeof(hbmp));//置零空申请到的内存.
hbmp.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);//信息头大小
hbmp.bmiHeader.biWidth=width; //bmp的宽度
hbmp.bmiHeader.biHeight=height; //bmp的高度
hbmp.bmiHeader.biPlanes=1; //恒为1
hbmp.bmiHeader.biBitCount=16; //bmp为16位色bmp
hbmp.bmiHeader.biCompression=BI_BITFIELDS;//每个象素的比特由指定的掩码决定。
hbmp.bmiHeader.biSizeImage=hbmp.bmiHeader.biHeight*hbmp.bmiHeader.biWidth*hbmp.bmiHeader.biBitCount/8;//bmp数据区大小
hbmp.bmfHeader.bfType=((u16)'M'<<8)+'B';//BM格式标志
hbmp.bmfHeader.bfSize=bmpheadsize+hbmp.bmiHeader.biSizeImage;//整个bmp的大小
hbmp.bmfHeader.bfOffBits=bmpheadsize;//到数据区的偏移
hbmp.RGB_MASK[0]=0X00F800; //红色掩码
hbmp.RGB_MASK[1]=0X0007E0; //绿色掩码
hbmp.RGB_MASK[2]=0X00001F; //蓝色掩码
if(mode==1)res=f_open(f_bmp,(const TCHAR*)filename,FA_READ|FA_WRITE);//尝试打开之前的文件
if(mode==0||res==0x04)res=f_open(f_bmp,(const TCHAR*)filename,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件
if((hbmp.bmiHeader.biWidth*2)%4)//水平像素(字节)不为4的倍数
{
bi4width=((hbmp.bmiHeader.biWidth*2)/4+1)*4;//实际要写入的宽度像素,必须为4的倍数.
}else bi4width=hbmp.bmiHeader.biWidth*2; //刚好为4的倍数
if(res==FR_OK)//创建成功
{
res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部
for(ty=y+height-1;hbmp.bmiHeader.biHeight;ty--)
{
pixcnt=0;
for(tx=x;pixcnt!=(bi4width/2);)
{
if(pixcnt<hbmp.bmiHeader.biWidth)databuf[pixcnt]=LCD_ReadPoint(tx,ty);//读取坐标点的值
else databuf[pixcnt]=0Xffff;//补充白色的像素.
pixcnt++;
tx++;
}
hbmp.bmiHeader.biHeight--;
res=f_write(f_bmp,(u8*)databuf,bi4width,&bw);//写入数据
}
f_close(f_bmp);
}
#if BMP_USE_MALLOC == 1 //使用malloc
myfree(databuf);
myfree(f_bmp);
#endif
return res;
}
ov7725.c
```c
#include "sys.h"
#include "ov7725.h"
#include "ov7725cfg.h"
#include "timer.h"
#include "delay.h"
#include "usart.h"
#include "sccb.h"
#include "exti.h"
//初始化OV7725
//返回0:成功
//返回其他值:错误代码
u8 OV7725_Init(void)
{
u16 i=0;
u16 reg=0;
//设置IO
RCC->APB2ENR|=1<<2; //先使能外设PORTA时钟
RCC->APB2ENR|=1<<3; //先使能外设PORTB时钟
GPIOA->CRL&=0XFFF0FF00;
GPIOA->CRL|=0X00030033; //PA0/1/4 输出
GPIOA->ODR|=1<<4;
GPIOA->ODR|=3<<0;
GPIOA->CRH&=0X00F00FFF;
GPIOA->CRH|=0X83033000; //PA15 输入、PA11/12/14输出
GPIOA->ODR|=3<<14;
GPIOA->ODR|=3<<11;
JTAG_Set(SWD_ENABLE);
SCCB_Init(); //初始化SCCB 的IO口
if(SCCB_WR_Reg(0x12,0x80))return 1; //复位SCCB
delay_ms(50);
reg=SCCB_RD_Reg(0X1c); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(0X1d); //读取厂家ID 低八位
if(reg!=OV7725_MID)
{
printf("MID:%d\r\n",reg);
return 1;
}
reg=SCCB_RD_Reg(0X0a); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(0X0b); //读取厂家ID 低八位
if(reg!=OV7725_PID)
{
printf("HID:%d\r\n",reg);
return 2;
}
//初始化 OV7725,采用QVGA分辨率(320*240)
for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
{
SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);
}
return 0x00; //ok
}
//OV7725功能设置
//白平衡设置
//0:自动模式
//1:晴天
//2,多云
//3,办公室
//4,家里
//5,夜晚
void OV7725_Light_Mode(u8 mode)
{
switch(mode)
{
case 0: //Auto,自动模式
SCCB_WR_Reg(0x13, 0xff); //AWB on
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 1://sunny,晴天
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x5a);
SCCB_WR_Reg(0x02, 0x5c);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 2://cloudy,多云
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x58);
SCCB_WR_Reg(0x02, 0x60);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 3://office,办公室
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x84);
SCCB_WR_Reg(0x02, 0x4c);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 4://home,家里
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x96);
SCCB_WR_Reg(0x02, 0x40);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 5://night,夜晚
SCCB_WR_Reg(0x13, 0xff); //AWB on
SCCB_WR_Reg(0x0e, 0xe5);
break;
}
}
//色度设置
//sat:-4~+4
void OV7725_Color_Saturation(s8 sat)
{
if(sat>=-4 && sat<=4)
{
SCCB_WR_Reg(USAT,(sat+4)<<4);
SCCB_WR_Reg(VSAT,(sat+4)<<4);
}
}
//亮度设置
//bright:-4~+4
void OV7725_Brightness(s8 bright)
{
u8 bright_value,sign;
switch(bright)
{
case 4:
bright_value = 0x48;
sign = 0x06;
break;
case 3:
bright_value = 0x38;
sign = 0x06;
break;
case 2:
bright_value = 0x28;
sign = 0x06;
break;
case 1:
bright_value = 0x18;
sign = 0x06;
break;
case 0:
bright_value = 0x08;
sign = 0x06;
break;
case -1:
bright_value = 0x08;
sign = 0x0e;
break;
case -2:
bright_value = 0x18;
sign = 0x0e;
break;
case -3:
bright_value = 0x28;
sign = 0x0e;
break;
case -4:
bright_value = 0x38;
sign = 0x0e;
break;
}
SCCB_WR_Reg(BRIGHT, bright_value);
SCCB_WR_Reg(SIGN, sign);
}
//对比度设置
//contrast:-4~+4
void OV7725_Contrast(s8 contrast)
{
if(contrast >= -4 && contrast <=4)
{
SCCB_WR_Reg(CNST,(0x30-(4-contrast)*4));
}
}
//特效设置
//0:普通模式
//1,负片
//2,黑白
//3,偏红色
//4,偏绿色
//5,偏蓝色
//6,复古
void OV7725_Special_Effects(u8 eft)
{
switch(eft)
{
case 0://正常
SCCB_WR_Reg(0xa6, 0x06);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0x80);
break;
case 1://黑白
SCCB_WR_Reg(0xa6, 0x26);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0x80);
break;
case 2://偏蓝
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0xa0);
SCCB_WR_Reg(0x61, 0x40);
break;
case 3://复古
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x40);
SCCB_WR_Reg(0x61, 0xa0);
break;
case 4://偏红
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0xc0);
break;
case 5://偏绿
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x60);
SCCB_WR_Reg(0x61, 0x60);
break;
case 6://反相
SCCB_WR_Reg(0xa6, 0x46);
break;
}
}
//设置图像输出窗口
//width:输出图像宽度,<=320
//height:输出图像高度,<=240
//mode:0,QVGA输出模式;1,VGA输出模式
//QVGA模式可视范围广但近物不是很清晰,VGA模式可视范围小近物清晰
void OV7725_Window_Set(u16 width,u16 height,u8 mode)
{
u8 raw,temp;
u16 sx,sy;
if(mode)
{
sx=(640-width)/2;
sy=(480-height)/2;
SCCB_WR_Reg(COM7,0x06); //设置为VGA模式
SCCB_WR_Reg(HSTART,0x23); //水平起始位置
SCCB_WR_Reg(HSIZE,0xA0); //水平尺寸
SCCB_WR_Reg(VSTRT,0x07); //垂直起始位置
SCCB_WR_Reg(VSIZE,0xF0); //垂直尺寸
SCCB_WR_Reg(HREF,0x00);
SCCB_WR_Reg(HOutSize,0xA0); //输出尺寸
SCCB_WR_Reg(VOutSize,0xF0); //输出尺寸
}
else
{
sx=(320-width)/2;
sy=(240-height)/2;
SCCB_WR_Reg(COM7,0x46); //设置为QVGA模式
SCCB_WR_Reg(HSTART,0x3f); //水平起始位置
SCCB_WR_Reg(HSIZE, 0x50); //水平尺寸
SCCB_WR_Reg(VSTRT, 0x03); //垂直起始位置
SCCB_WR_Reg(VSIZE, 0x78); //垂直尺寸
SCCB_WR_Reg(HREF, 0x00);
SCCB_WR_Reg(HOutSize,0x50); //输出尺寸
SCCB_WR_Reg(VOutSize,0x78); //输出尺寸
}
raw=SCCB_RD_Reg(HSTART);
temp=raw+(sx>>2);//sx高8位存在HSTART,低2位存在HREF[5:4]
SCCB_WR_Reg(HSTART,temp);
SCCB_WR_Reg(HSIZE,width>>2);//width高8位存在HSIZE,低2位存在HREF[1:0]
raw=SCCB_RD_Reg(VSTRT);
temp=raw+(sy>>1);//sy高8位存在VSTRT,低1位存在HREF[6]
SCCB_WR_Reg(VSTRT,temp);
SCCB_WR_Reg(VSIZE,height>>1);//height高8位存在VSIZE,低1位存在HREF[2]
raw=SCCB_RD_Reg(HREF);
temp=((sy&0x01)<<6)|((sx&0x03)<<4)|((height&0x01)<<2)|(width&0x03)|raw;
SCCB_WR_Reg(HREF,temp);
SCCB_WR_Reg(HOutSize,width>>2);
SCCB_WR_Reg(VOutSize,height>>1);
SCCB_RD_Reg(EXHCH);
temp = (raw|(width&0x03)|((height&0x01)<<2));
SCCB_WR_Reg(EXHCH,temp);
}
ov7725.h
#include "sys.h"
#include "ov7725.h"
#include "ov7725cfg.h"
#include "timer.h"
#include "delay.h"
#include "usart.h"
#include "sccb.h"
#include "exti.h"
//
//ALIENTEK MiniSTM32开发板
//OV7725 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2017/11/1
//版本:V1.0
//
//初始化OV7725
//返回0:成功
//返回其他值:错误代码
u8 OV7725_Init(void)
{
u16 i=0;
u16 reg=0;
//设置IO
RCC->APB2ENR|=1<<2; //先使能外设PORTA时钟
RCC->APB2ENR|=1<<3; //先使能外设PORTB时钟
GPIOA->CRL&=0XFFF0FF00;
GPIOA->CRL|=0X00030033; //PA0/1/4 输出
GPIOA->ODR|=1<<4;
GPIOA->ODR|=3<<0;
GPIOA->CRH&=0X00F00FFF;
GPIOA->CRH|=0X83033000; //PA15 输入、PA11/12/14输出
GPIOA->ODR|=3<<14;
GPIOA->ODR|=3<<11;
JTAG_Set(SWD_ENABLE);
SCCB_Init(); //初始化SCCB 的IO口
if(SCCB_WR_Reg(0x12,0x80))return 1; //复位SCCB
delay_ms(50);
reg=SCCB_RD_Reg(0X1c); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(0X1d); //读取厂家ID 低八位
if(reg!=OV7725_MID)
{
printf("MID:%d\r\n",reg);
return 1;
}
reg=SCCB_RD_Reg(0X0a); //读取厂家ID 高八位
reg<<=8;
reg|=SCCB_RD_Reg(0X0b); //读取厂家ID 低八位
if(reg!=OV7725_PID)
{
printf("HID:%d\r\n",reg);
return 2;
}
//初始化 OV7725,采用QVGA分辨率(320*240)
for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
{
SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);
}
return 0x00; //ok
}
//OV7725功能设置
//白平衡设置
//0:自动模式
//1:晴天
//2,多云
//3,办公室
//4,家里
//5,夜晚
void OV7725_Light_Mode(u8 mode)
{
switch(mode)
{
case 0: //Auto,自动模式
SCCB_WR_Reg(0x13, 0xff); //AWB on
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 1://sunny,晴天
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x5a);
SCCB_WR_Reg(0x02, 0x5c);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 2://cloudy,多云
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x58);
SCCB_WR_Reg(0x02, 0x60);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 3://office,办公室
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x84);
SCCB_WR_Reg(0x02, 0x4c);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 4://home,家里
SCCB_WR_Reg(0x13, 0xfd); //AWB off
SCCB_WR_Reg(0x01, 0x96);
SCCB_WR_Reg(0x02, 0x40);
SCCB_WR_Reg(0x0e, 0x65);
SCCB_WR_Reg(0x2d, 0x00);
SCCB_WR_Reg(0x2e, 0x00);
break;
case 5://night,夜晚
SCCB_WR_Reg(0x13, 0xff); //AWB on
SCCB_WR_Reg(0x0e, 0xe5);
break;
}
}
//色度设置
//sat:-4~+4
void OV7725_Color_Saturation(s8 sat)
{
if(sat>=-4 && sat<=4)
{
SCCB_WR_Reg(USAT,(sat+4)<<4);
SCCB_WR_Reg(VSAT,(sat+4)<<4);
}
}
//亮度设置
//bright:-4~+4
void OV7725_Brightness(s8 bright)
{
u8 bright_value,sign;
switch(bright)
{
case 4:
bright_value = 0x48;
sign = 0x06;
break;
case 3:
bright_value = 0x38;
sign = 0x06;
break;
case 2:
bright_value = 0x28;
sign = 0x06;
break;
case 1:
bright_value = 0x18;
sign = 0x06;
break;
case 0:
bright_value = 0x08;
sign = 0x06;
break;
case -1:
bright_value = 0x08;
sign = 0x0e;
break;
case -2:
bright_value = 0x18;
sign = 0x0e;
break;
case -3:
bright_value = 0x28;
sign = 0x0e;
break;
case -4:
bright_value = 0x38;
sign = 0x0e;
break;
}
SCCB_WR_Reg(BRIGHT, bright_value);
SCCB_WR_Reg(SIGN, sign);
}
//对比度设置
//contrast:-4~+4
void OV7725_Contrast(s8 contrast)
{
if(contrast >= -4 && contrast <=4)
{
SCCB_WR_Reg(CNST,(0x30-(4-contrast)*4));
}
}
//特效设置
//0:普通模式
//1,负片
//2,黑白
//3,偏红色
//4,偏绿色
//5,偏蓝色
//6,复古
void OV7725_Special_Effects(u8 eft)
{
switch(eft)
{
case 0://正常
SCCB_WR_Reg(0xa6, 0x06);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0x80);
break;
case 1://黑白
SCCB_WR_Reg(0xa6, 0x26);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0x80);
break;
case 2://偏蓝
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0xa0);
SCCB_WR_Reg(0x61, 0x40);
break;
case 3://复古
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x40);
SCCB_WR_Reg(0x61, 0xa0);
break;
case 4://偏红
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x80);
SCCB_WR_Reg(0x61, 0xc0);
break;
case 5://偏绿
SCCB_WR_Reg(0xa6, 0x1e);
SCCB_WR_Reg(0x60, 0x60);
SCCB_WR_Reg(0x61, 0x60);
break;
case 6://反相
SCCB_WR_Reg(0xa6, 0x46);
break;
}
}
//设置图像输出窗口
//width:输出图像宽度,<=320
//height:输出图像高度,<=240
//mode:0,QVGA输出模式;1,VGA输出模式
//QVGA模式可视范围广但近物不是很清晰,VGA模式可视范围小近物清晰
void OV7725_Window_Set(u16 width,u16 height,u8 mode)
{
u8 raw,temp;
u16 sx,sy;
if(mode)
{
sx=(640-width)/2;
sy=(480-height)/2;
SCCB_WR_Reg(COM7,0x06); //设置为VGA模式
SCCB_WR_Reg(HSTART,0x23); //水平起始位置
SCCB_WR_Reg(HSIZE,0xA0); //水平尺寸
SCCB_WR_Reg(VSTRT,0x07); //垂直起始位置
SCCB_WR_Reg(VSIZE,0xF0); //垂直尺寸
SCCB_WR_Reg(HREF,0x00);
SCCB_WR_Reg(HOutSize,0xA0); //输出尺寸
SCCB_WR_Reg(VOutSize,0xF0); //输出尺寸
}
else
{
sx=(320-width)/2;
sy=(240-height)/2;
SCCB_WR_Reg(COM7,0x46); //设置为QVGA模式
SCCB_WR_Reg(HSTART,0x3f); //水平起始位置
SCCB_WR_Reg(HSIZE, 0x50); //水平尺寸
SCCB_WR_Reg(VSTRT, 0x03); //垂直起始位置
SCCB_WR_Reg(VSIZE, 0x78); //垂直尺寸
SCCB_WR_Reg(HREF, 0x00);
SCCB_WR_Reg(HOutSize,0x50); //输出尺寸
SCCB_WR_Reg(VOutSize,0x78); //输出尺寸
}
raw=SCCB_RD_Reg(HSTART);
temp=raw+(sx>>2);//sx高8位存在HSTART,低2位存在HREF[5:4]
SCCB_WR_Reg(HSTART,temp);
SCCB_WR_Reg(HSIZE,width>>2);//width高8位存在HSIZE,低2位存在HREF[1:0]
raw=SCCB_RD_Reg(VSTRT);
temp=raw+(sy>>1);//sy高8位存在VSTRT,低1位存在HREF[6]
SCCB_WR_Reg(VSTRT,temp);
SCCB_WR_Reg(VSIZE,height>>1);//height高8位存在VSIZE,低1位存在HREF[2]
raw=SCCB_RD_Reg(HREF);
temp=((sy&0x01)<<6)|((sx&0x03)<<4)|((height&0x01)<<2)|(width&0x03)|raw;
SCCB_WR_Reg(HREF,temp);
SCCB_WR_Reg(HOutSize,width>>2);
SCCB_WR_Reg(VOutSize,height>>1);
SCCB_RD_Reg(EXHCH);
temp = (raw|(width&0x03)|((height&0x01)<<2));
SCCB_WR_Reg(EXHCH,temp);
}
exit.c
#include "exti.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "ov7725.h" #include "ov7725.h"
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1) //WK_UP按键
{
LED0=!LED0;
LED1=!LED1;
}
EXTI->PR=1<<0; //清除LINE0上的中断标志位
}
//外部中断9~5服务程序
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) //按键0
{
LED0=!LED0;
}
EXTI->PR=1<<5; //清除LINE5上的中断标志位
}
外部中断15~10服务程序
//void EXTI15_10_IRQHandler(void)
//{
// delay_ms(10); //消抖
// if(KEY1==0) //按键1
// {
// LED1=!LED1;
// }
// EXTI->PR=1<<15; //清除LINE15上的中断标志位
//}
//外部中断初始化程序
//初始化PA0,PC5,PA15为中断输入.
void EXTI_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRL&=0XFFFFFFF0;//PA0设置成输入
GPIOA->CRL|=0X00000008;
GPIOA->CRH&=0X0F0FFFFF;//PA13,15设置成输入
GPIOA->CRH|=0X80800000;
GPIOA->ODR|=1<<13; //PA13上拉,PA0默认下拉
GPIOA->ODR|=1<<15; //PA15上拉
Ex_NVIC_Config(GPIO_A,0,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_C,5,FTIR); //下降沿触发
Ex_NVIC_Config(GPIO_A,15,FTIR); //下降沿触发
MY_NVIC_Init(2,2,EXTI0_IRQn,2); //抢占2,子优先级2,组2
MY_NVIC_Init(2,1,EXTI9_5_IRQn,2); //抢占2,子优先级1,组2
MY_NVIC_Init(2,0,EXTI15_10_IRQn,2); //抢占2,子优先级0,组2
}
/
//中断服务函数
u8 ov_sta;
void EXTI15_10_IRQHandler(void)
{
if(EXTI->PR&(1<<15))//是15线的中断
{
if(ov_sta<2)
{
if(ov_sta==0)
{
OV7725_WRST=0; //复位写指针
OV7725_WRST=1;
OV7725_WREN=1; //允许写入FIFO
}else OV7725_WREN=0; //禁止写入FIFO
ov_sta++;
}
}
EXTI->PR=1<<15; //清除LINE15上的中断标志位
}
//外部中断初始化程序
//初始化PA15为中断输入.
void EXTI15_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
JTAG_Set(SWD_ENABLE); //关闭JTAG
GPIOA->CRH&=0X0FFFFFFF; //PA15设置成输入
GPIOA->CRH|=0X80000000;
GPIOA->ODR|=1<<15; //PA15上拉
Ex_NVIC_Config(GPIO_A,15,FTIR);//下降沿触发
MY_NVIC_Init(2,1,EXTI15_10_IRQn,2);//抢占2,子优先级1,组2
}
拍照
extern u8 ov_sta; //在exit.c里面定义
extern u8 ov_frame; //在timer.c里面定义
UINT fnum;
void camera_refresh(u8 mode){
u32 i,j;
u16 color;
BITMAPINFO bmp;
u8 res;
if(ov_sta&&(mode==0))//拍照
{
printf("开始保存图片\r\n");
//打开文件,若不存在就创建
res = f_open(file, "0:/PICTURE/DANGER.bmp", FA_OPEN_ALWAYS | FA_WRITE|FA_READ);
//文件打开成功
if(res == FR_OK)
{
//填写文件信息头信息
bmp.bmfHeader.bfType = 0x4D42; //bmp类型
bmp.bmfHeader.bfSize= 54 + 320*240*2; //文件大小(信息结构体+像素数据)
bmp.bmfHeader.bfReserved1 = 0x0000; //保留,必须为0
bmp.bmfHeader.bfReserved2 = 0x0000;
bmp.bmfHeader.bfOffBits=54; //位图信息结构体所占的字节数
//填写位图信息头信息
bmp.bmiHeader.biSize=40; //位图信息头的大小
bmp.bmiHeader.biWidth=320; //位图的宽度
bmp.bmiHeader.biHeight=240; //图像的高度
bmp.bmiHeader.biPlanes=1; //目标设别的级别,必须是1
bmp.bmiHeader.biBitCount=16; //每像素位数
bmp.bmiHeader.biCompression=3; //RGB555格式
bmp.bmiHeader.biSizeImage=320*240*2; //实际位图所占用的字节数(仅考虑位图像素数据)
bmp.bmiHeader.biXPelsPerMeter=0; //水平分辨率
bmp.bmiHeader.biYPelsPerMeter=0; //垂直分辨率
bmp.bmiHeader.biClrImportant=0; //说明图像显示有重要影响的颜色索引数目,0代表所有的颜色一样重要
bmp.bmiHeader.biClrUsed=0; //位图实际使用的彩色表中的颜色索引数,0表示使用所有的调色板项
//RGB565格式掩码
bmp.RGB_MASK[0] = 0X00F800;
bmp.RGB_MASK[1] = 0X0007E0;
bmp.RGB_MASK[2] = 0X00001F;
//写文件头进文件
res= f_write(file, &bmp, sizeof(bmp), &fnum);
}
//读指针复位
OV7725_CS=0;
OV7725_RRST=0; //开始复位读指针
OV7725_RCK=0;
OV7725_RCK=1;
OV7725_RCK=0;
OV7725_RRST=1; //复位读指针结束
OV7725_RCK=1;
/*图像花屏的原因在于读取时的干扰和读取时漏掉几个像素*/
for(i=0;i<240;i++)
{
for(j=0;j<320;j++)
{
GPIOB->CRL=0X88888888;
OV7725_RCK=0;
color=OV7725_DATA; //读数据
OV7725_RCK=1;
color<<=8;
OV7725_RCK=0;
color|=OV7725_DATA; //读数据
OV7725_RCK=1;
GPIOB->CRL=0X33333333;
LCD_WR_DATA(color);
//写位图信息头进内存卡
f_write(file, &color, sizeof(color), &fnum);
}
}
//关闭文件
f_close(file);
delay_ms(1000);
return;
}
//不需要保存图片,继续刷新LCD
if(ov_sta&&mode==1)
{
LCD_Scan_Dir(U2D_L2R); //从上到下,从左到右
LCD_WriteRAM_Prepare(); //开始写入GRAM
//读指针复位
OV7725_CS=0;
OV7725_RRST=0; //开始复位读指针
OV7725_RCK=0;
OV7725_RCK=1;
OV7725_RCK=0;
OV7725_RRST=1; //复位读指针结束
OV7725_RCK=1;
/*图像花屏的原因在于读取时的干扰和读取时漏掉几个像素*/
for(i=0;i<240;i++)
{
for(j=0;j<320;j++)
{
GPIOB->CRL=0X88888888;
OV7725_RCK=0;
color=OV7725_DATA; //读数据
OV7725_RCK=1;
color<<=8;
OV7725_RCK=0;
color|=OV7725_DATA; //读数据
OV7725_RCK=1;
GPIOB->CRL=0X33333333;
LCD_WR_DATA(color);
}
}
OV7725_CS=1;
OV7725_RCK=0;
OV7725_RCK=1;
EXTI->PR=1<<15;
ov_sta=0; //清零帧中断标记
ov_frame++;
LCD_Scan_Dir(DFT_SCAN_DIR); //恢复默认扫描方向
}
}
图片显示
/***************显示图片*****************/
u16 pic_get_tnum(u8 *path)
{
u8 res;
u16 rval=0;
DIR tdir; //临时目录
FILINFO tfileinfo; //临时文件信息
u8 *fn;
res=f_opendir(&tdir,(const TCHAR*)path); //打开目录
tfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
tfileinfo.lfname=mymalloc(tfileinfo.lfsize);//为长文件缓存区分配内存
if(res==FR_OK&&tfileinfo.lfname!=NULL)
{
while(1)//查询总的有效文件数
{
res=f_readdir(&tdir,&tfileinfo); //读取目录下的一个文件
if(res!=FR_OK||tfileinfo.fname[0]==0)break; //错误了/到末尾了,退出
fn=(u8*)(*tfileinfo.lfname?tfileinfo.lfname:tfileinfo.fname);
res=f_typetell(fn);
if((res&0XF0)==0X50)//取高四位,看看是不是图片文件
{
rval++;//有效文件数增加1
}
}
}
return rval;
}
int showpicture(){
u8 res;
DIR picdir; //图片目录
FILINFO picfileinfo;//文件信息
u8 *fn; //长文件名
u8 *pname; //带路径的文件名
u16 totpicnum; //图片文件总数
u16 curindex; //图片当前索引
u8 key; //键值
u8 pause=0; //暂停标记
u8 t;
u16 temp;
u16 *picindextbl; //图片索引表
while(font_init()) //检查字库
{
LCD_ShowString(60,50,200,16,16,"Font Error!");
delay_ms(200);
LCD_Fill(60,50,240,66,WHITE);//清除显示
delay_ms(200);
}
while(f_opendir(&picdir,"0:/PICTURE"))//打开图片文件夹
{
Show_Str(60,170,240,16,"PICTURE文件夹错误!",16,0);
delay_ms(200);
LCD_Fill(60,170,240,186,WHITE);//清除显示
delay_ms(200);
}
totpicnum=pic_get_tnum("0:/PICTURE"); //得到总有效文件数
while(totpicnum==NULL)//图片文件为0
{
Show_Str(60,170,240,16,"没有图片文件!",16,0);
delay_ms(200);
LCD_Fill(60,170,240,186,WHITE);//清除显示
delay_ms(200);
}
picfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
picfileinfo.lfname=mymalloc(picfileinfo.lfsize); //为长文件缓存区分配内存
pname=mymalloc(picfileinfo.lfsize); //为带路径的文件名分配内存
picindextbl=mymalloc(2*totpicnum); //申请2*totpicnum个字节的内存,用于存放图片索引
while(picfileinfo.lfname==NULL||pname==NULL||picindextbl==NULL)//内存分配出错
{
Show_Str(60,170,240,16,"内存分配失败!",16,0);
delay_ms(200);
LCD_Fill(60,170,240,186,WHITE);//清除显示
delay_ms(200);
}
//记录索引
res=f_opendir(&picdir,"0:/PICTURE"); //打开目录
if(res==FR_OK)
{
curindex=0;//当前索引为0
while(1)//全部查询一遍
{
temp=picdir.index; //记录当前index
res=f_readdir(&picdir,&picfileinfo); //读取目录下的一个文件
if(res!=FR_OK||picfileinfo.fname[0]==0)break; //错误了/到末尾了,退出
fn=(u8*)(*picfileinfo.lfname?picfileinfo.lfname:picfileinfo.fname);
res=f_typetell(fn);
if((res&0XF0)==0X50)//取高四位,看看是不是图片文件
{
picindextbl[curindex]=temp;//记录索引
curindex++;
}
}
}
Show_Str(60,170,240,16,"开始显示...",16,0);
delay_ms(1500);
piclib_init(); //初始化画图
curindex=0; //从0开始显示
res=f_opendir(&picdir,(const TCHAR*)"0:/PICTURE"); //打开目录
while(res==FR_OK)//打开成功
{
dir_sdi(&picdir,picindextbl[curindex]); //改变当前目录索引
res=f_readdir(&picdir,&picfileinfo); //读取目录下的一个文件
if(res!=FR_OK||picfileinfo.fname[0]==0)break; //错误了/到末尾了,退出
fn=(u8*)(*picfileinfo.lfname?picfileinfo.lfname:picfileinfo.fname);
strcpy((char*)pname,"0:/PICTURE/"); //复制路径(目录)
strcat((char*)pname,(const char*)fn); //将文件名接在后面
LCD_Clear(BLACK);
ai_load_picfile(pname,0,0,lcddev.width,lcddev.height,1);//显示图片
Show_Str(2,2,240,16,pname,16,1); //显示图片名字
t=0;
while(1)
{
key=KEY_Scan(0); //扫描按键
if(t>250)key=1; //模拟一次按下KEY0
if((t%20)==0)LED0=!LED0;//LED0闪烁,提示程序正在运行.
if(key==KEY1_PRES) //上一张
{
if(curindex)curindex--;
else curindex=totpicnum-1;
break;
}else if(key==KEY0_PRES)//下一张
{
curindex++;
if(curindex>=totpicnum)curindex=0;//到末尾的时候,自动从头开始
break;
}else if(key==WKUP_PRES)
{
pause=!pause;
LED1=!pause; //暂停的时候LED1亮.
}
if(pause==0)t++;
delay_ms(10);
}
res=0;
}
}