OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5192|回复: 0

[XILINX] 【正点原子FPGA连载】第二十七章 OV7725照相机实验--摘自【正点原子】领航者ZYNQ之嵌入式开发指南_V1.2

[复制链接]

1107

主题

1118

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4615
金钱
4615
注册时间
2019-5-8
在线时间
1218 小时
发表于 2020-9-23 10:50:11 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2020-9-23 10:49 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:905624739 点击加入群聊
5)关注正点原子公众号,获取最新资料更新
1.jpg
1120.png

第二十七章OV7725照相机实验

在“OV7725摄像头LCD显示实验”中,我们将摄像头采集的图像显示在LCD上。而在“SD卡读BMP图片LCD显示实验”中,我们从SD卡中读取BMP图片,通过LCD显示。本章我们将综合上述两个实验的内容,将OV7725采集的图像以BMP图片的格式存在SD卡中,从而实现拍照功能。
本章包括以下几个部分:      
1.1        简介
1.2        实验任务
1.3        硬件设计
1.4        软件设计
1.5        下载验证

1.1 简介
本次实验在“OV7725摄像头LCD显示实验”的基础上进行,有关OV7725摄像头采集及LCD显示部分的内容,请大家参考前面的章节。除此之外,要将采集的图片以BMP格式存入SD卡,我们还需要了解BMP图片的格式,以及SD卡图片存取的知识。这部分内容请大家参考“SD卡读BMP图片LCD显示实验”。
由上述两个章节的内容我们知道,OV7725摄像头会将采集的图像以RGB888的格式存在DDR显存中。而真彩色的BMP图片中,图像部分同样是RGB888格式。因此,在利用OV7725实现拍照功能时,我们只需要给显存中的图像数据加上BMP文件头,然后再将它们写到SD卡中即可。
1.2 实验任务
本章的实验任务是使用OV7725摄像头,实现照相机功能。在拍照时,将摄像头采集的图像以BMP文件的格式存入SD卡中。
1.3 硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
image002.png

图 25.3.1 系统框图
可以看出,本次实验的系统框图是在“OV7725摄像头LCD显示实验”的基础上添加了SD卡模块。SD卡控制器位于ZYNQPS端,通过MIO与SD卡连接。
首先修改“OV7725摄像头LCD显示实验”中ZYNQ PS的配置,使能其SD卡控制器,如下图所示:
image004.png

图 25.3.2 SD卡配置界面
同时我们还需要勾选SD卡控制器的“Card Detect”接口,并将Bank 1的电平设置为“LVCOMS 1.8V”,如下图所示:
image006.png

图 25.3.3 使能CD接口
有关ZYNQ PS中SD卡控制器的介绍及配置方法请大家参考“SD卡读写TXT文本实验”。
ZYNQ处理系统就配置完成后,重新对设计进行验证,并执行“Generate Output Products”操作。由于SD卡控制器通过MIO连接到PS端的引脚上,因此不需要我们手动进行管脚分配,直接在左侧Flow Navigator导航栏中找点击“Generate Bitstream”,重新生成Bitstream文件
在生成Bitstream之后,我们先在菜单栏选择File > Launch SDK,启动SDK软件,可以看到已经加载了OV7725摄像头实验所创建的工程。
然后在Vivado菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中勾选“Include bitstream”。然后提示是否覆盖之前的文件,点击“Yes”。
1.4 软件设计
在重新导出硬件之后,SDK软件中会弹出警告,提示我们硬件信息有改动。然后我们点击“OK”确认,工具重新进行编译。为了能够在软件中读写SD卡中的文件,我们需要设置BSP工程,添加并设置FATFS库。具体的方法请参考“SD卡读写TXT文本实验”软件设计部分。
接下来修改main.c文件。首先我们在文件的开头添加了一些头文件,全局变量,以及函数声明等,代码如下所示:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "xil_types.h"
  5. #include "xil_cache.h"
  6. #include "xparameters.h"
  7. #include "xgpio.h"
  8. #include "xaxivdma.h"
  9. #include "xaxivdma_i.h"
  10. #include "display_ctrl/display_ctrl.h"
  11. #include "ov7725/ov7725_init.h"
  12. #include "vdma_api/vdma_api.h"
  13. #include "emio_sccb_cfg/emio_sccb_cfg.h"
  14. #include "ff.h"
  15. #include "xil_cache.h"

  16. //宏定义
  17. #define FRAME_BUFFER_NUM   3                          //帧缓存个数
  18. #define DYNCLK_BASEADDR   XPAR_AXI_DYNCLK_0_BASEADDR //动态时钟基地址
  19. #define VDMA_ID           XPAR_AXIVDMA_0_DEVICE_ID   //VDMA器件ID
  20. #define DISP_VTC_ID       XPAR_VTC_0_DEVICE_ID       //VTC器件ID
  21. #define AXI_GPIO_0_ID     XPAR_AXI_GPIO_0_DEVICE_ID  //PL端  AXI GPIO 0(lcd_id)器件ID
  22. #define AXI_GPIO_0_CHANEL  1                          //使用AXI GPIO(lcd_id)通道1

  23. //全局变量
  24. XAxiVdma     vdma;
  25. DisplayCtrl  dispCtrl;
  26. XGpio        axi_gpio_inst;   //PL端 AXI GPIO 驱动实例
  27. VideoMode    vd_mode;

  28. //文件系统
  29. static FATFS fatfs;
  30. //BMP图片文件头
  31. u8 bmp_head[54= {
  32.        0x42,0x4d,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36,0x0,0x0,0x0,0x28,0x0,
  33.        0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x18,0x0,0x0,0x0,
  34.        0x0,0x0,0x0,0x0,0x0,0x0,0xc4,0xe,0x0,0x0,0xc4,0x0e,0x0,0x0,0x0,0x0,
  35.        0x0,0x0,0x0,0x0,0x0,0x0 };
  36. //BMP图片各参数偏移地址
  37. UINT *bf_size    = (UINT *)(bmp_head +0x2);
  38. UINT *bmp_width  = (UINT *)(bmp_head + 0x12);
  39. UINT *bmp_height = (UINT *)(bmp_head +0x16);
  40. UINT *bmp_size   =(UINT *)(bmp_head +0x22);
  41. //BMP图片编号
  42. int  pic_cnt = 0;

  43. //抓拍的图片显存地址
  44. unsigned int const bmp_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR+ 0x11000000);
  45. //frame buffer的起始地址
  46. unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR+ 0x1000000);
  47. //LCD ID
  48. unsigned int lcd_id=0;

  49. //将显存图像以BMP格式写入SD卡
  50. void write_sd_bmp(u8 *frame);
复制代码
首先在代码的14行,我们包含了ff.h头文件,这样才可以通过调用FATFS库函数来向SD卡中写入BMP图片文件。
然后在代码的31至45行,我们定义了一系列与BMP文件格式相关的变量。包括BMP文件头以及各参数的偏移地址等。这些变量我们在“SD卡读BMP图片LCD显示实验”中有过详细介绍,如果对它们不熟悉的话,请大家参考相应的章节。
另外,我们在内存中指定了一个存储空间,用于存储摄像头抓拍到的图片数据。这个存储空间的起始地址保存在变量bmp_addr中,如程序第48行所示。需要注意的是,这个起始地址不能与VDMA的帧缓存空间有重叠。
最后,在代码的55行,我们声明了一个函数“write_sd_bmp(u8 *frame)”。这个函数可以将抓拍的图片以BMP格式写入SD卡中,它的参数就是变量bmp_addr所指定的地址。然后我们在main函数中将调用这个函数,实现拍照功能。
main函数的代码如下所示:
  1. int main(void)
  2. {
  3.      intstatus = 0;
  4.      charcmd;                   //串口输入的拍照指令
  5.      int  rd_index;              //VDMA读通道操作的帧缓存编号
  6.      unsignedint rd_fram_addr;  //VDMA读通道操作的帧缓存地址

  7.      emio_init();             //初始化EMIO
  8.      status = ov7725_init();  //初始化ov7725
  9.      if(status == 0)
  10.          xil_printf("OV7725detected successful!\r\n");
  11.      else
  12.          xil_printf("OV7725detected failed!\r\n");

  13.      //获取LCD的ID
  14.      XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID);
  15.      lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
  16.      xil_printf("LCDID: %x\r\n",lcd_id);

  17.      //根据获取的LCD的ID号来进行video参数的选择
  18.      switch(lcd_id){
  19.          case 0x4342 : vd_mode =VMODE_480x272; break;  //4.3寸屏,480*272分辨率
  20.          case 0x4384 : vd_mode =VMODE_800x480; break;  //4.3寸屏,800*480分辨率
  21.          case 0x7084 : vd_mode =VMODE_800x480; break;  //7寸屏,800*480分辨率
  22.          case 0x7016 : vd_mode =VMODE_1024x600; break; //7寸屏,1024*600分辨率
  23.          case 0x1018 : vd_mode =VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
  24.          default : vd_mode = VMODE_800x480; break;
  25.      }

  26.      //配置VDMA
  27.      run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
  28.                              frame_buffer_addr,0,0,BOTH);
  29.      //因摄像头和RGB LCD屏的分辨率不匹配,清空DDR3帧缓存空间
  30.      //最后一个参数表示清零的字节数,由于RGB888数据格式占用3个字节,因此最后乘以3
  31.      memset(frame_buffer_addr,0,vd_mode.height*vd_mode.width*FRAME_BUFFER_NUM*3);
  32.      Xil_DCacheFlush();
  33.      //初始化Displaycontroller
  34.      DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
  35.      //设置VideoMode
  36.      DisplaySetMode(&dispCtrl, &vd_mode);
  37.      DisplayStart(&dispCtrl);

  38.      //根据VDMA显存大小给BMP文件头赋值
  39.      *bmp_width  = vd_mode.width;
  40.      *bmp_height = vd_mode.height;
  41.      *bmp_size   = vd_mode.width * vd_mode.height * 3;
  42.      *bf_size    = *bmp_size + 54;
  43.      //挂载文件系统
  44.      f_mount(&fatfs,"",1);

  45.      //根据串口输入的指令控制拍照过程
  46.      while(1){
  47.          //用户输入串口指令,指令为单个字符
  48.          scanf("%c",&cmd);
  49.          //输入字符'c'时,抓拍摄像头图片
  50.          if(cmd=='c'){
  51.              printf("capturepicture\n");
  52.              //获取当前读通道操作的帧缓存编号
  53.              rd_index = XAxiVdma_CurrFrameStore(&vdma, XAXIVDMA_READ);
  54.              printf("currentread frame is %d\n",rd_index);
  55.              //读通道驻停在当前帧
  56.              XAxiVdma_StartParking(&vdma, rd_index, XAXIVDMA_READ);
  57.              //并获取当前帧的起始地址
  58.              rd_fram_addr = frame_buffer_addr + vd_mode.height*vd_mode.width*3*rd_index;
  59.              //将当前帧的图像拷贝到抓拍图片缓存区域
  60.              memcpy((void *)bmp_addr,(void *)rd_fram_addr,vd_mode.height*vd_mode.width*3);
  61.              //结束读通道驻停过程,继续在多帧之间进行切换
  62.              XAxiVdma_StopParking(&vdma, XAXIVDMA_READ);
  63.              //将抓拍图片缓存区域中的图像以BMP格式写入SD卡
  64.              write_sd_bmp((u8 *)bmp_addr);
  65.              //BMP图片编号累加
  66.              pic_cnt++;
  67.          }
  68.      }

  69.      return 0;
  70. }
复制代码
main函数主要是在OV7725摄像头LCD显示实验的基础上,增加了拍照相关的代码,如程序99至130行所示。
因为不同尺寸的屏幕对应不同大小的VDMA帧缓存空间,所以我们最终写入SD卡的BMP图片的大小也不一样。因此在代码的99至103行,我们需要根据VDMA的显存大小,来指定BMP文件头中的参数。主要包括BMP图片的分辨率、图像数据的大小,以及BMP文件的大小等参数。其中BMP文件比BMP图像数据要多54个字节,多出来的这部分就是我们给图像数据添加的BMP文件头。
在程序的107至130行,我们通过一个while(1)死循环,来不断判断用户输入的串口指令。当检测到用户输入字符’c’时,就利用摄像头进行拍照并以BMP图片的格式存入SD卡。
由于在OV7725摄像头LCD显示实验中,我们给VDMA定义了三个帧缓存空间,读通道和写通道按照同步锁相机制循环对这三帧显存进行读写操作。因此在拍照时,首先要通过XAxiVdma_CurrFrameStore()函数来判断当前VDMA的读通道正在操作的是哪一个帧缓存空间,如程序第115行所示。由于同步锁相机制会禁止读写通道同时访问同一个帧缓存,因此在检测到正确的用户串口指令时,读通道正在操作的帧缓存中存储的是一幅完整的图像。然后通过XAxiVdma_StartParking( )函数使VDMA读通道驻停在该帧缓存中,也就是说读通道将不会在多个显存中进行切换显示,而是反复读同一帧,此时写通道也无法再对该帧进行写操作。这样就可以保证我们在保存该帧图片时,图片不会被新的摄像头数据给破坏掉,否则我们拍照得到的图片就不再是完整的一帧。
接下来我们根据获取的帧缓存编号rd_index计算出该帧的起始地址,并通过memcpy( )函数将该帧缓存中的图像数据拷贝到BMP图片存储区域。拷贝完成后,用XAxiVdma_StopParking( )函数结束VDMA读通道的驻停过程,这样摄像头就可以正常显示了。当然我们也可以直接操作该帧缓存中的图像数据,但是在VDMA读通道驻停的过程中,LCD上显示的摄像头图案是静止的;由于图片写入SD卡的过程相对较慢,因此我们先将VDMA帧缓存中的数据拷贝出来,以减小对摄像头显示的影响。
最后,在程序的第126行,我们通过write_sd_bmp( )函数将拷贝到BMP图片存储区域中的数据以BMP文件的格式写到SD卡中。写入完成后,BMP图片的编号会加1,该编号将用于对存入SD卡的BMP图片进行命名。
write_sd_bmp( )函数的代码如下所示:
  1. //向SD卡中写BMP图片
  2. void write_sd_bmp(u8 *frame)
  3. {
  4.      FIL     fil;            //文件对象
  5.      UINT    bw;             //写文件函数返回已写入的字节数
  6.      char    pic_name[20];   //字符串,用于存储BMP文件名

  7.      //打印BMP图片信息(宽/高/图片大小),以及BMP文件大小
  8.      xil_printf("width= %d, height = %d, size = %d, file size = %d bytes \n\r",
  9.              *bmp_width,*bmp_height,*bmp_size,*bf_size);

  10.      //给BMP图片的文件名编号
  11.      sprintf(pic_name,"picture%04u.bmp",pic_cnt);
  12.      //打开BMP文件,如果不存在则创建该文件
  13.      f_open(&fil,pic_name,FA_CREATE_ALWAYS| FA_WRITE);

  14.      //移动文件读写指针到文件开头
  15.      f_lseek(&fil,0);
  16.      //写入BMP文件头
  17.      f_write(&fil,bmp_head,54,&bw);
  18.      //写入抓拍的图片
  19.      f_write(&fil,frame,*bmp_size,&bw);
  20.      //关闭文件
  21.      f_close(&fil);
  22.      xil_printf("write%s done! \n\r",pic_name);
  23. }
复制代码
在程序的第148行,我们通过函数sprintf( )给拍摄的图片进行命名,命名格式为“picture + BMP图片编号”。由于每次拍照后BMP图片编号会累加,因此拍摄多张照片时,照片有不同的名称,防止新的照片覆盖之前拍摄的照片。
然后我们调用f_open( )函数在SD卡文件系统中打开BMP图片,使用f_lseek()函数将读写指针移动到文件开头。接下来连续调用两次f_write( )函数,先将BMP文件头写入打开的BMP文件,然后紧接着写入BMP图片缓存中的摄像头图片。最后通过f_close( )函数关闭文件。
如果大家对调用FATFS库函数进行文件读写不熟悉的话,请参考“SD卡读写TXT文本实验”。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
1.5 下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。除此之外还需要连接OV7725摄像头以及LCD屏幕,并插入SD卡。最后连接开发板的电源,并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,LCD会显示OV7725摄像头所采集的图像,另外在SDK Terminal窗口中会打印出LCD屏幕的ID。
然后我们在发送栏输入小写字母“c”,并点击“Send”发送。发送成功后将会触发摄像头拍照,同时打印拍照信息,如下图所示:
image008.png

图 25.5.1 串口终端中打印的信息
在图 29.5.1中,我们发送了两次拍照指令。第一次拍照时,VDMA读通道正在读帧缓存2,存入SD卡的BMP图片名为“picture 0000.bmp”。第二次拍照时,VDMA读通道正在读帧缓存0,存入SD卡的BMP图片名为“picture 0001.bmp”。
拍照完成后,我们将SD卡取下,并通过读卡器连接到电脑,查看SD卡中的文件,如下图所示:
image010.png

图 25.5.2 SD卡中存储的BMP图片
从图 29.5.2中可以看到,SD卡中成功写入了两个BMP格式的图片。我们在图片上右击查看其属性,如下图所示:
image012.png

图 25.5.3 图片属性
从图29.5.3中可以看到,SD卡中存储的BMP图片位深度为24bit,分辨率与所连接的LCD屏的分辨率一致。最后我们分别双击打开这两幅图片,如下所示:
image014.png

图 25.5.4 拍摄的BMP图片
从图 29.5.4可以看到,我们拍摄的BMP图片可以正常打开,说明本次实验在领航者ZYNQ开发板上面下载验证成功。需要注意的是,OV7725摄像头输出的图像分辨为640*480。而我们保存的BMP图片的分辨率是与LCD屏保持一致的。本次实验使用的屏幕分辨率为800*480,因此BMP图片右侧有一部分黑色区域。


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-10-3 13:25

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表