设计框图如下:
采用Xilinx官方推荐的VDMA架构实现图像缓存和显示,除OV5640摄像头采集和HDMI输出外,其他ip均采用Xilinx官方IP实现。
这里说明一下:
OV5640摄像头图像数据经VDMA三帧缓存后有2路输出
1路输出HDMI显示器显示;
1路通过FATFS文件系统写入sd卡中存放,存放的数据格式位bmp图片;
开发板的zynq主控是zynq7100,工程实现功能如下:
1.实现OV5640摄像头HDMI输出;
2.实现sd卡FATFS文件系统简单读写测试;
3.实现OV5640摄像头循环拍照,3秒拍摄一张图片;
4.实现OV5640摄像头按键拍照,板载PS侧按键按一下拍照;
工程BD部分如下:
工程代码架构如下:
工程SDK代码架构如下:
SDK代码说明:
设置了2个宏定义:
//#define FATFS_TEST
//#define KEY_CAMERA
#define FATFS_TEST:FATFS文件系统测试:
默认为注释掉,程序执行摄像头循环拍照功能;
取消注释则程序执行FATFS文件系统读写测试;
#define KEY_CAMERA:摄像头按键拍照功能;
默认为注释掉,程序执行摄像头循环拍照功能;
取消注释则程序执行摄像头按键拍照功能;
zynq配置这里主要是要使能i2c外设和sd卡外设,i2c外设使能是为了配置OV5640摄像头,sd卡外设使能则是为了存放拍摄的照片,除此之外,还要使能一个mio,因为mio连接了一个PS侧的按键,后面要依靠此按键来触发拍照;
在嵌入式领域,sd卡可以跑FATFS文件系统,Xilinx的SDK开发环境已经集成了FATFS库,所以我们只需在使用前配置一下就可以了,非常方便,设置方法如下:
1.vivado导出比特流并启动sdk;
2.sdk新建一个工程;
3.接下来添加 FATFS 库。需要注意的是,先关闭 system.mss 的界面,再添加 FATFS库,否则有可能导致 FATFS 库添加失败。
system.mss 界面关闭后,右击 micro_sd_rw_bsp,选择“Board Support Package Setting”。
在弹出的界面中勾选“xilffs”,xilffs 即为 FATFS 库
勾选后,会在左侧 Overview 的 standalone 一栏出现 xilffs,点击 xilffs。可以看到 use_lfn 的默认设置为
false,即不使能。use_lfn 用于设置是否使能长文件名以及文件名的小写字母,这里将 use_lfn 设置为 true,
点击“OK”按钮完成设置。
设置完成后,在 sd_rw_txt_bsp→ps_cortexa9_0→libsrc 一栏下,会多出 FATFS 的库函数。
在摄像头拍照实验之前,需要先对FATFS文件系统进行测试,看是否在sd卡上能跑通;
测是方法如下:
第一步:在sd卡中新建一个txt文件;
第二步:新建的txt文件中写入一段测试字符串;
第三步:读出txt文件中上一步写入的测试字符串;
第四步:比较读写字符串内容是否完全一致;也可以拔出sd卡在电脑中查看内容;
在SDK中建立了sd读写的头文件和源文件,在源文件中含有读写测试的功能函数;
在进行sd卡文件系统FATFS读写测试之前,应在主函数中取消#define FATFS_TEST的注释:
#define FATFS_TEST
//#define KEY_CAMERA
写数据如下:
const char src_str[100] = "I love Xilinx FPGAs ~~~~~~~~~~~~"; //定义文本内容
下面给出读写测试函数源码;
int helai_sd_test(){
int len;
int i;
char dest_str[100] = "";
sd_mount(); //挂载SD卡
len = strlen(src_str); //计算字符串长度
sd_write_data(FILE_NAME,(u32)src_str,len); //SD卡写数据
sd_read_data(FILE_NAME,(u32)dest_str,len); //SD卡读数据
//比较写入的字符串和读出的字符串是否相等
//for(i=0;i
// xil_printf("read data =%c\r\n",dest_str[i]);
// }
if (strcmp(src_str, dest_str) == 0) xil_printf("src_str is equal to dest_str,SD card test success!\n");
else xil_printf("src_str is not equal to dest_str,SD card test failed!\n");
return 0;
}
测试结果:
串口观测读写内容的对比结果为正确:
拔出SD卡用读卡器到电脑端直接查看内容:
为了保证拍照的正确性,调试拍照之前,必须保证图像数据采集的正确,所以直接接上显示器输出,分辨率位720P,这部分由VDMA配置;
这部分功能为实现OV5640摄像头循环拍照,3秒拍摄一张图片;
bmp图片有固定的数据头,所以直接在程序中定义该部分,如下:
//BMP图片文件头
u8 bmp_head[54] = {
0x42,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x28,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x18,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xe,0x0,0x0,0xc4,0x0e,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0 };
随后就是将图像的rgb数据部分和数据头部分一起写入sd卡中形成bmp图片;
直接给出主函数代码:
void main(){
XGpioPs_Config_key=XGpioPs_LookupConfig(GPIO_KEY);
XGpioPs_CfgInitialize(&XgpioPS_key, XGpioPs_Config_key, XGpioPs_Config_key->BaseAddr);
XGpioPs_SetDirectionPin(&XgpioPS_key, 51, 0);
// Initialize OV5640 regesiter
int rd_index; //VDMA读通道操作的帧缓存编号
unsigned int rd_fram_addr; //VDMA读通道操作的帧缓存地址
I2C_config_init();
helai_vdma();
#ifdef FATFS_TEST
helai_sd_test();
while(1);
#else
//最后一个参数表示清零的字节数,由于RGB888数据格式占用3个字节,因此最后乘以3
memset(0x01000000,0,1280*720*3*3);
Xil_DCacheFlush();
//根据VDMA显存大小给BMP文件头赋值
*bmp_width = 1280;
*bmp_height = 720;
*bmp_size = 1280*720*3;
*bf_size = *bmp_size + 54;
f_mount(&fatfs,"",1); //挂载文件系统
while (1){
#ifdef KEY_CAMERA
int key;
key=XGpioPs_ReadPin(&XgpioPS_key, 51);
if(key==0){
usleep(20000);
if(key==0){
printf("capture picture\n");
rd_index = XAxiVdma_CurrFrameStore(&vdma, XAXIVDMA_READ); //获取当前读通道操作的帧缓存编号
printf("current read frame is %d\n",rd_index);
XAxiVdma_StartParking(&vdma, rd_index, XAXIVDMA_READ); //读通道驻停在当前帧
rd_fram_addr = 0x01000000 + 1280*720*3*rd_index; //并获取当前帧的起始地址
memcpy((void *)bmp_addr,(void *)rd_fram_addr,1280*720*3); //将当前帧的图像拷贝到抓拍图片缓存区域
XAxiVdma_StopParking(&vdma, XAXIVDMA_READ); //结束读通道驻停过程,继续在多帧之间进行切换
write_sd_bmp((u8 *)bmp_addr); //将抓拍图片缓存区域中的图像以BMP格式写入SD卡
pic_cnt++; //BMP图片编号累加
}
}
#else
printf("capture picture\n");
rd_index = XAxiVdma_CurrFrameStore(&vdma, XAXIVDMA_READ); //获取当前读通道操作的帧缓存编号
printf("current read frame is %d\n",rd_index);
XAxiVdma_StartParking(&vdma, rd_index, XAXIVDMA_READ); //读通道驻停在当前帧
rd_fram_addr = 0x01000000 + 1280*720*3*rd_index; //并获取当前帧的起始地址
memcpy((void *)bmp_addr,(void *)rd_fram_addr,1280*720*3); //将当前帧的图像拷贝到抓拍图片缓存区域
XAxiVdma_StopParking(&vdma, XAXIVDMA_READ); //结束读通道驻停过程,继续在多帧之间进行切换
write_sd_bmp((u8 *)bmp_addr); //将抓拍图片缓存区域中的图像以BMP格式写入SD卡
pic_cnt++; //BMP图片编号累加
sleep(3);
#endif
}
#endif
}
这部分功能为实现OV5640摄像头按键拍照,板载PS侧按键按一下拍照;
在使用之前,应在主函数中取消#define KEY_CAMERA的注释:
//#define FATFS_TEST
#define KEY_CAMERA
开发板:zynq7100开发板;
开发环境:vivado2019.1;
输入:OV5640摄像头,720P,RGB888格式;
输出:HDMI输出,SD卡bmp图片;
如下:
可私,可得到工程的某度网盘链接,不要发邮箱地址,工程太大发不了邮箱;