做黑白照相机的,希望可以给大家参考
实验器材:
探索者STM32F4开发板
实验目的:
学习OV2640摄像头,BMP编码和文件系统的使用,实现一个简单的照相机.
硬件资源:
1,DS0(连接在PF9),DS1(连接在PF10)
2,串口1(波特率:115200,PA9/PA10连接在板载USB转串口芯片CH340上面)
3,ALIENTEK 2.8/3.5/4.3/7寸TFTLCD模块(通过FSMC驱动,FSMC_NE4接LCD片选/A6接RS)
4,按键KEY0(PE4)/KEY1(PE3)/KEY_UP(PA0,也称之为WK_UP)
5,SD卡,通过SDIO(SDIO_D0~D4(PC8~PC11),SDIO_SCK(PC12),SDIO_CMD(PD2))连接
6,蜂鸣器(PF8)
7,DCMI接口(用于驱动OV2640摄像头模块)
8,定时器3(用于打印摄像头帧率等信息)
9,ALIENTEK OV2640摄像头模块,连接关系为:
OV2640模块 ------------ STM32开发板
OV_D0~D7 ------------ PE6/PE5/PB6/PC11/PC9/PC8/PC7/PC6
OV_SCL ------------ PD6
OV_SDA ------------ PD7
OV_VSYNC ------------ PB7
OV_HREF ------------ PA4
OV_RESET ------------ PG15
OV_PCLK ------------ PA6
OV_PWDN ------------ PG9
实验现象:
本实验开机的时候先检测字库,然后检测SD卡根目录是否存在PHOTO文件夹,如果不存在则创建,如果创建失败,则报错(提示拍照功能不可用)。在找到SD卡的PHOTO文件夹后,开始初始化OV2640,在初始化成功之后,就一直在屏幕显示OV2640拍到的内容。
按下KEY0,可以拍bmp图片照片(分辨率为:LCD辨率)。拍照保存成功之后,蜂鸣器会发出“滴”的一声,提示拍照成功。
DS0还是用于指示程序运行状态,DS1用于提示DCMI帧中断。
注意事项:
1,4.3寸和7寸屏需要比较大电流,USB供电可能不足,请用外部电源适配器(推荐外接12V 1A电源).
2,本例程在LCD_Init函数里面(在ILI93xx.c),用到了printf,如果不初始化串口1,将导致液晶无法显示!!
3,该实验须自备SD卡和ALIENTEK OV2640模块各一个.
(摄像头初始化为YUV422格式,输送到LCD屏上显示,当按下拍照键后,读取屏幕点数据,把RGB565格式的双字节提取为单字节的Y量,即亮度值,而后,逐点写入SD卡后生成灰度BMP文件。)
单片机源程序如下:
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
- #include "lcd.h"
- #include "key.h"
- #include "usmart.h"
- #include "sram.h"
- #include "malloc.h"
- #include "w25qxx.h"
- #include "sdio_sdcard.h"
- #include "ff.h"
- #include "exfuns.h"
- #include "fontupd.h"
- #include "text.h"
- #include "piclib.h"
- #include "string.h"
- #include "math.h"
- #include "dcmi.h"
- #include "ov2640.h"
- #include "beep.h"
- #include "timer.h"
- u8 ov2640_mode=0; //工作模式:0,RGB565模式;1,JPEG模式
- #define jpeg_dma_bufsize 5*1024 //定义JPEG DMA接收时数据缓存jpeg_buf0/1的大小(*4字节)
- volatile u32 jpeg_data_len=0; //buf中的JPEG有效数据长度(*4字节)
- volatile u8 jpeg_data_ok=0; //JPEG数据采集完成标志
- //0,数据没有采集完;
- //1,数据采集完了,但是还没处理;
- //2,数据已经处理完成了,可以开始下一帧接收
-
- u32 *jpeg_buf0; //JPEG数据缓存buf,通过malloc申请内存
- u32 *jpeg_buf1; //JPEG数据缓存buf,通过malloc申请内存
- u32 *jpeg_data_buf; //JPEG数据缓存buf,通过malloc申请内存
- //处理JPEG数据
- //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
- void jpeg_data_process(void)
- {
- u16 i;
- u16 rlen;//剩余数据长度
- u32 *pbuf;
- if(ov2640_mode)//只有在JPEG格式下,才需要做处理.
- {
- if(jpeg_data_ok==0) //jpeg数据还未采集完?
- {
- DMA2_Stream1->CR&=~(1<<0); //停止当前传输
- while(DMA2_Stream1->CR&0X01); //等待DMA2_Stream1可配置
- rlen=jpeg_dma_bufsize-DMA2_Stream1->NDTR;//得到剩余数据长度
- pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾,继续添加
- if(DMA2_Stream1->CR&(1<<19))for(i=0;i<rlen;i++)pbuf[i]=jpeg_buf1[i];//读取buf1里面的剩余数据
- else for(i=0;i<rlen;i++)pbuf[i]=jpeg_buf0[i];//读取buf0里面的剩余数据
- jpeg_data_len+=rlen; //加上剩余长度
- jpeg_data_ok=1; //标记JPEG数据采集完按成,等待其他函数处理
- }
- if(jpeg_data_ok==2) //上一次的jpeg数据已经被处理了
- {
- DMA2_Stream1->NDTR=jpeg_dma_bufsize;//传输长度为jpeg_buf_size*4字节
- DMA2_Stream1->CR|=1<<0; //重新传输
- jpeg_data_ok=0; //标记数据未采集
- jpeg_data_len=0; //数据重新开始
- }
- }
- }
- //jpeg数据接收回调函数
- void jpeg_dcmi_rx_callback(void)
- {
- u16 i;
- u32 *pbuf;
- pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾
- if(DMA2_Stream1->CR&(1<<19))//buf0已满,正常处理buf1
- {
- for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf0[i];//读取buf0里面的数据
- jpeg_data_len+=jpeg_dma_bufsize;//偏移
- }else //buf1已满,正常处理buf0
- {
- for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf1[i];//读取buf1里面的数据
- jpeg_data_len+=jpeg_dma_bufsize;//偏移
- }
- }
- //切换为OV2640模式
- void sw_ov2640_mode(void)
- {
- OV2640_PWDN=0;//OV2640 Power Up
- //GPIOC8/9/11切换为 DCMI接口
- GPIO_AF_Set(GPIOC,8,13); //PC8,AF13 DCMI_D2
- GPIO_AF_Set(GPIOC,9,13); //PC9,AF13 DCMI_D3
- GPIO_AF_Set(GPIOC,11,13); //PC11,AF13 DCMI_D4
- }
- //切换为SD卡模式
- void sw_sdcard_mode(void)
- {
- OV2640_PWDN=1;//OV2640 Power Down
- //GPIOC8/9/11切换为 SDIO接口
- GPIO_AF_Set(GPIOC,8,12); //PC8,AF12
- GPIO_AF_Set(GPIOC,9,12); //PC9,AF12
- GPIO_AF_Set(GPIOC,11,12); //PC11,AF12
- }
- //文件名自增(避免覆盖)
- //mode:0,创建.bmp文件;1,创建.jpg文件.
- //bmp组合成:形如"0:PHOTO/PIC13141.bmp"的文件名
- //jpg组合成:形如"0:PHOTO/PIC13141.jpg"的文件名
- void camera_new_pathname(u8 *pname,u8 mode)
- {
- u8 res;
- u16 index=0;
- while(index<0XFFFF)
- {
- if(mode==0)sprintf((char*)pname,"0:PHOTO/PIC%05d.bmp",index);
- else sprintf((char*)pname,"0:PHOTO/PIC%05d.jpg",index);
- res=f_open(ftemp,(const TCHAR*)pname,FA_READ);//尝试打开这个文件
- if(res==FR_NO_FILE)break; //该文件名不存在=正是我们需要的.
- index++;
- }
- }
- //OV2640拍照jpg图片
- //返回值:0,成功
- // 其他,错误代码
- u8 ov2640_jpg_photo(u8 *pname)
- {
- FIL* f_jpg;
- u8 res=0;
- u32 bwr;
- u16 i;
- u8* pbuf;
- f_jpg=(FIL *)mymalloc(SRAMIN,sizeof(FIL)); //开辟FIL字节的内存区域
- if(f_jpg==NULL)return 0XFF; //内存申请失败.
- ov2640_mode=1;
- sw_ov2640_mode(); //切换为OV2640模式
- dcmi_rx_callback=jpeg_dcmi_rx_callback;//回调函数
- DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,2,1);;//DCMI DMA配置(双缓冲模式)
- OV2640_JPEG_Mode(); //切换为JPEG模式
- OV2640_ImageWin_Set(0,0,1600,1200);
- OV2640_OutSize_Set(1600,1200);//拍照尺寸为1600*1200
- DCMI_Start(); //启动传输
- while(jpeg_data_ok!=1); //等待第一帧图片采集完
- jpeg_data_ok=2; //忽略本帧图片,启动下一帧采集
- while(jpeg_data_ok!=1); //等待第二帧图片采集完,第二帧,才保存到SD卡去.
- DCMI_Stop(); //停止DMA搬运
- ov2640_mode=0;
- sw_sdcard_mode(); //切换为SD卡模式
- res=f_open(f_jpg,(const TCHAR*)pname,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件
- if(res==0)
- {
- printf("jpeg data size:%d\r\n",jpeg_data_len*4);//串口打印JPEG文件大小
- pbuf=(u8*)jpeg_data_buf;
- for(i=0;i<jpeg_data_len*4;i++)//查找0XFF,0XD8
- {
- if((pbuf[i]==0XFF)&&(pbuf[i+1]==0XD8))break;
- }
- if(i==jpeg_data_len*4)res=0XFD;//没找到0XFF,0XD8
- else//找到了
- {
- pbuf+=i;//偏移到0XFF,0XD8处
- res=f_write(f_jpg,pbuf,jpeg_data_len*4-i,&bwr);
- if(bwr!=(jpeg_data_len*4-i))res=0XFE;
- }
- }
- jpeg_data_len=0;
- f_close(f_jpg);
- sw_ov2640_mode(); //切换为OV2640模式
- OV2640_RGB565_Mode(); //RGB565模式
- DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,1,1,0);//DCMI DMA配置
- myfree(SRAMIN,f_jpg);
- return res;
- }
- int main(void)
- {
- u8 res;
- u8 *pname; //带路径的文件名
- u8 key; //键值
- u8 i;
- u8 sd_ok=1; //0,sd卡不正常;1,SD卡正常.
- u8 scale=1; //默认是全尺寸缩放
- u8 msgbuf[15]; //消息缓存区
- Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz
- delay_init(168); //延时初始化
- uart_init(84,115200); //初始化串口波特率为115200
- LED_Init(); //初始化LED
- usmart_dev.init(84); //初始化USMART
- TIM3_Int_Init(10000-1,8400-1);//10Khz计数,1秒钟中断一次
- LCD_Init(); //LCD初始化
- FSMC_SRAM_Init(); //初始化外部SRAM.
- BEEP_Init(); //蜂鸣器初始化
- KEY_Init(); //按键初始化
- W25QXX_Init(); //初始化W25Q128
- my_mem_init(SRAMIN); //初始化内部内存池
- my_mem_init(SRAMEX); //初始化内部内存池
- my_mem_init(SRAMCCM); //初始化CCM内存池
- exfuns_init(); //为fatfs相关变量申请内存
- f_mount(fs[0],"0:",1); //挂载SD卡
- POINT_COLOR=RED;
- while(font_init()) //检查字库
- {
- LCD_ShowString(30,50,200,16,16,"Font Error!");
- delay_ms(200);
- LCD_Fill(30,50,240,66,WHITE);//清除显示
- delay_ms(200);
- }
- Show_Str(30,50,200,16,"Explorer STM32F4开发板",16,0);
- Show_Str(30,70,200,16,"照相机实验",16,0);
- Show_Str(30,90,200,16,"KEY0:拍照(bmp格式)",16,0);
- Show_Str(30,110,200,16,"KEY1:拍照(jpg格式)",16,0);
- Show_Str(30,130,200,16,"WK_UP:FullSize/Scale",16,0);
- Show_Str(30,150,200,16,"2014年5月16日",16,0);
- res=f_mkdir("0:/PHOTO"); //创建PHOTO文件夹
- if(res!=FR_EXIST&&res!=FR_OK) //发生了错误
- {
- Show_Str(30,150,240,16,"SD卡错误!",16,0);
- delay_ms(200);
- Show_Str(30,170,240,16,"拍照功能将不可用!",16,0);
- sd_ok=0;
- }
- jpeg_buf0=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存
- jpeg_buf1=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存
- jpeg_data_buf=mymalloc(SRAMEX,300*1024); //为jpeg文件申请内存(最大300KB)
- pname=mymalloc(SRAMIN,30);//为带路径的文件名分配30个字节的内存
- while(pname==NULL||!jpeg_buf0||!jpeg_buf1||!jpeg_data_buf) //内存分配出错
- {
- Show_Str(30,190,240,16,"内存分配失败!",16,0);
- delay_ms(200);
- LCD_Fill(30,190,240,146,WHITE);//清除显示
- delay_ms(200);
- }
- while(OV2640_Init())//初始化OV2640
- {
- Show_Str(30,190,240,16,"OV7670 错误!",16,0);
- delay_ms(200);
- LCD_Fill(30,190,239,206,WHITE);
- delay_ms(200);
- }
- Show_Str(30,190,200,16,"OV2640 正常",16,0);
- delay_ms(2000);
- // OV2640_RGB565_Mode(); //JPEG模式
- OV2640_YUV422_Mode();
- DCMI_Init(); //DCMI配置
- DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,1,1,0);//DCMI DMA配置
- OV2640_OutSize_Set(lcddev.width,lcddev.height);
- DCMI_Start(); //启动传输
- while(1)
- {
- key=KEY_Scan(0);//不支持连按
- if(key&&key!=KEY2_PRES)
- {
- DCMI_Stop(); //停止显示
- if(key==WKUP_PRES)
- {
- u16 i,l,color1,color2;
- scale=!scale;
- if(scale==0)
- {
- OV2640_ImageWin_Set((1600-lcddev.width)/2,(1200-lcddev.height)/2,lcddev.width,lcddev.height);//1:1真实尺寸
- OV2640_OutSize_Set(lcddev.width,lcddev.height);
- sprintf((char*)msgbuf,"Full Size 1:1");
- }else
- {
- OV2640_ImageWin_Set(0,0,1600,1200); //全尺寸缩放
- OV2640_OutSize_Set(lcddev.width,lcddev.height);
- sprintf((char*)msgbuf,"Scale");
- }
- LCD_ShowString(30,50,210,16,16,msgbuf);//显示提示内容
-
- /*
- for(i=0;i<240;i++)
- {
- for(l=0;l<800;l++)
- {
- color1=LCD_ReadPoint(l,i);
- color2=(((color1>>0)&0xf800)+((color1>>5)&0x07e0)+((color1>>11)&0x001f));
- LCD_Fast_DrawPoint(l,240+i,color2);
- }
- }*/
- delay_ms(800);
- }else if(sd_ok)//SD卡正常才可以拍照
- {
- sw_sdcard_mode(); //切换为SD卡模式
- if(key==KEY0_PRES) //BMP拍照
- {
- camera_new_pathname(pname,0);//得到文件名
- res=bmp_encode(pname,0,0,lcddev.width,lcddev.height,0);
- }else if(key==KEY1_PRES)//JPG拍照
- {
- camera_new_pathname(pname,1);//得到文件名
- res=ov2640_jpg_photo(pname);
- if(scale==0)
- {
- OV2640_ImageWin_Set((1600-lcddev.width)/2,(1200-lcddev.height)/2,lcddev.width,lcddev.height);//1:1真实尺寸
- OV2640_OutSize_Set(lcddev.width,lcddev.height);
- }else
- {
- OV2640_ImageWin_Set(0,0,1600,1200); //全尺寸缩放
- }
- OV2640_OutSize_Set(lcddev.width,lcddev.height);
- }
- sw_ov2640_mode(); //切换为OV2640模式
- if(res)//拍照有误
- {
- Show_Str(30,130,240,16,"写入文件错误!",16,0);
- }else
- {
- Show_Str(30,130,240,16,"拍照成功!",16,0);
- Show_Str(30,150,240,16,"保存为:",16,0);
- Show_Str(30+42,150,240,16,pname,16,0);
- BEEP=1; //蜂鸣器短叫,提示拍照完成
- delay_ms(100);
- }
- }else //提示SD卡错误
- {
- Show_Str(30,130,240,16,"SD卡错误!",16,0);
- Show_Str(30,150,240,16,"拍照功能不可用!",16,0);
- }
- BEEP=0; //关闭蜂鸣器
- if(key!=WKUP_PRES)delay_ms(1800);//非尺寸切换,等待1.8秒钟
- DCMI_Start(); //开始显示
- }
- delay_ms(10);
- i++;
- if(i==20)//DS0闪烁.
- {
- i=0;
- LED0=!LED0;
- }
- }
- }
复制代码
所有资料51hei提供下载:
黑白照相机实验.rar
(1008.85 KB, 下载次数: 64)
|