软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述在前文中,我们已经掌握了uifdma_vbuf3.0 ip的使用,本文实现一个综合性的实验,把摄像头的数据以BMP格式保存到TF卡中。 本文实验目的: 1:掌握uifdma_vbuf3.0的地址空间分配方法 2:利用多缓存机制,把内存中的图像以BMP格式保存到TF卡 2系统框图
3硬件电路分析硬件接口和子卡模块请阅读“附录1” 配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。 4搭建SOC系统工程
4.1PL图形化编
以上代码中OV5640数据格式转为RGB888输入到uifdma_dbuf的写端口,经过uifdma_dbuf后进入fdma,之后通过AXI_interconnect 进入到ZYNQ DDR中。 SDK代码中,PS(ARM)读取内存中保存的摄像头采样数据,并且通过HDMI实时显示。当按下快门按键的时候,点亮开发板的LED,同时把内存中的数据以BMP格式保存到SD卡中 。 下面具体看下关键几个IP的参数设置 1:uifdma_dbuf设置由于输入数据是视频数据流形式,所以需要使能视频传输功能,这里也只使用到了写通道,所以读通道也不需要使能。 一般对于摄像头采集,设置3帧缓存就够用。WBaseaddr缓存的基地址只要设置合适的值即可,这里设置0x08000000 =128MB,这样保留了低128MB给应用程序使用。 由于一幅图片的大小为1280*720*4= 3600Bytes 因此WDsizebits设置每个缓存的大小,2^23次方=8MBYTE够用。 在SDK中的地址空间分配对应如下: #define BUF_BASE_SIZE 0x08000000
#define BUF_RANG_SIZE 0x800000
#define BUF1_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*0;
#define BUF2_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*1;
#define BUF3_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*2;
2:uiFDMA设置Fdma的数据位宽可以设置128这样效率最高。
3:AXI Interconnect设置设置FIFO可以增加数据的吞吐能力
以上代码中,调用了米联客uispi7606 IP CORE采集模拟数据,并且把采集好的数据写入到uifdmadbuf中,为了方便实验中观察数据,把第八个通道的AD数据改成了计数器。实际项目中可以把这个替换成第八个通道的ADC数据。 4.2设置地址分配需要注意uifdma_dbuf的axi-lite接口地址,这个地址我们会在SDK 代码中用到读寄存器。
4.3添加PIN约束1:选中PROJECT MANAGERà Add SourcesàAdd or create constraints,添加XDC约束文件。
2:打开提供例程,复制约束文件中的管脚约束到XDC文件,或者查看原理图,自行添加管脚约束,并保存。 以下是添加配套工程路径下已经提供的pin脚文件。配套工程的pin脚约束文件在uisrc/04_pin路径 4.4编译并导出平台文件1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:生成Bit文件。 4:导出到硬件: FileàExport HardwareàInclude bitstream 5:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
5搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 5.1创建SDK Platform工程
ZYNQ的SD1支持FAT文件系统,我们的ZYNQ SD1接到了TF卡上,因此可以把保存的图片放到TF卡中。为了使用文件系统,需要对库做一些设置:
5.2创建photograph_fdma——工程
6SDK程序分析
6.1 cam_photograph.c主程序
1:uifdma_dbuf 设置的地址空间
为了可以把uifdma_dbuf写入到DDR指定地址的数据读出来,首先需要对地址进行定义
#define VIDEO_OUT_HSIZE 1280*4
#define VIDEO_OUT_STRIDE 1280*4
#define VIDEO_OUT_VSIZE 720
#define IMG_SIZE VIDEO_OUT_HSIZE*VIDEO_OUT_VSIZE
#define BUF_BASE_SIZE 0x08000000
#define BUF_RANG_SIZE 0x800000
#define BUF1_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*0;
#define BUF2_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*1;
#define BUF3_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*2;
u8 *UIFDMA_DBUF[IMG_SIZE] __attribute__ ((aligned(256)));
#define BUF3_ADDR BUF_BASE_SIZE + BUF_RANG_SIZE*2;
u8 *UIFDMA_DBUF[IMG_SIZE] __attribute__ ((aligned(256)));
|
2:指针数字指向三个缓存地址
给分配的内存指针数字指向FPGA中uifdma_dbuf中设置的地址空间:
UIFDMA_DBUF[0] = (u8*)BUF1_ADDR;
UIFDMA_DBUF[1] = (u8*)BUF2_ADDR;
UIFDMA_DBUF[2] = (u8*)BUF3_ADDR;
|
3:设置VDMA的地址
video_out_ReadCfg.FrameStoreStartAddr[0] = (INTPTR)UIFDMA_DBUF[0];
video_out_ReadCfg.FrameStoreStartAddr[1] = (INTPTR)UIFDMA_DBUF[1];
video_out_ReadCfg.FrameStoreStartAddr[2] = (INTPTR)UIFDMA_DBUF[2];
|
4:启动采集和停止采集
XGpio_DiscreteWrite(&rstn_5640, 1, 0x1);//启动采集
XGpio_DiscreteWrite(&rstn_5640, 1, 0x0);//停止采集
|
5:本方案核心程序核心程序中,不停读取按键的状态,当由按键按下,则当VDMA中断产生后,立马开始保存图片,这样做的目前是未来更好让图形静止在显示器中,模拟相机的拍照状态。另外在开发板上会有一个LED在保存图片的时候常亮。如果按键没有按下,则实时显示当前的摄像头图像。 while(1){
delay_cnt =0;
if(button_down() && cap_button == 0)//read button when button io is 0 enable img grap
cap_button =1;
else if(wfram1_cap_done == 1)//wait vmda s2mm write channel intrrupt
{
XGpio_DiscreteWrite(&rstn_5640, 1, 0x0);//stop camera
XGpio_DiscreteSet(&button_led, 1, AXI_GPIO_BIT1);//open led
//set img name
img_cnt++ ;
sprintf(imgName, "%04u.bmp", img_cnt) ;
printf("Write to SD Card...\r\n") ;
//frush cache data all into ddr
Xil_DCacheInvalidateRange((INTPTR) UIFDMA_DBUF[fdma_buf], IMG_SIZE) ;
//write img to sdcard
bmp_write(imgName, (char *)&BMODE_1280x720, (char *)UIFDMA_DBUF[fdma_buf], VIDEO_OUT_STRIDE) ;
printf("Successfully Take img, img Name is %s\r\n", imgName) ;
XGpio_DiscreteClear(&button_led, 1, AXI_GPIO_BIT1);//close led
cap_button =0;//clear for next
wfram1_cap_done =0;//clear for next
XGpio_DiscreteWrite(&rstn_5640, 1, 0x1);//restar camera
}
}
|
6.2vdma_pl.c程序
本方案为了获得更好的演示效果,采取了使用 vdma parked 模式,控制显存的切换,否则采用 circle 模式会导致部分图形在保存的时候不能完整显示(因为保存需要很长时间写 TF 卡,我们会暂停图形数据流,内存中最新的图形可能是不完整的)
1:vdma_parked设置
int Video_Out_MM2SSetup(u16 DeviceID, XAxiVdma * InstancePtr , XAxiVdma_DmaSetup ReadCfg ,int Vsize, int Hsize, int Stride)
{
int Status;
XAxiVdma_Config *Config;
XAxiVdma_FrameCounter FrameCfg;
ReadCfg.VertSizeInput = Vsize;
ReadCfg.HoriSizeInput = Hsize;
ReadCfg.Stride = Stride;
ReadCfg.FrameDelay = 0; /* This example does not test frame delay */
ReadCfg.EnableCircularBuf = 0;
ReadCfg.EnableSync = 0; /* No Gen-Lock */
ReadCfg.PointNum = 0;
ReadCfg.EnableFrameCounter = 0; /* Endless transfers */
ReadCfg.FixedFrameStoreAddr = 0; /* We are not doing parking */
Config = XAxiVdma_LookupConfig(DeviceID);
if (NULL == Config) {
xil_printf("XAxiVdma_LookupConfig failure\r\n");
return XST_FAILURE;
}
Status = XAxiVdma_CfgInitialize(InstancePtr, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
xil_printf("XAxiVdma_CfgInitialize failure\r\n");
return XST_FAILURE;
}
XAxiVdma_SetFrameCounter(InstancePtr, &FrameCfg);
Status = XAxiVdma_DmaConfig(InstancePtr, XAXIVDMA_READ, &ReadCfg);
if (Status != XST_SUCCESS) {
xil_printf("Read channel config failed %d\r\n", Status);
return XST_FAILURE;
}
/* Set the buffer addresses for transfer in the DMA engine
* The buffer addresses are physical addresses
*/
Status = XAxiVdma_DmaSetBufferAddr(InstancePtr, XAXIVDMA_READ,ReadCfg.FrameStoreStartAddr);
if (Status != XST_SUCCESS) {
xil_printf("Read channel set buffer address failed %d\r\n", Status);
return XST_FAILURE;
}
XAxiVdma_SetCallBack(InstancePtr, XAXIVDMA_HANDLER_GENERAL, Video_Out_MM2SCallBack,(void *)InstancePtr, XAXIVDMA_READ);
XAxiVdma_SetCallBack(InstancePtr, XAXIVDMA_HANDLER_ERROR,Video_Out_MM2SErrorCallBack, (void *)InstancePtr,
XAXIVDMA_READ);
return XST_SUCCESS;
}
| 2:缓存的切换
static void Video_Out_MM2SCallBack(void *CallbackRef, u32 Mask)
{
if (Mask & XAXIVDMA_IXR_FRMCNT_MASK) {
if(cap_button)
wfram1_cap_done = 1;
else
wfram1_cap_done = 0;
XAxiVdma_StartParking(&video_out, fdma_buf,XAXIVDMA_READ);
}
}
|
6.3pl_intr.c程序
该程序中响应来自 uifdma_dbuf 的中断,每当一帧图形发送完毕即产生一次 PL 中断,在 PL 中断中读取uifdma_dbuf 的当前缓存号
void PS_RX_intr_Handler(void *param)
{
fdma_buf = Xil_In32((UINTPTR)FDMA_DBUF_BASE_ADDR);
}
|
7方案演示
7.1硬件准备本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON (注意新版本的 MLK-H3-CZ08-7100FC(米联客 7X 系列),支持 JTAG 模式,对于老版本的核心板,JTAG 调试 的时候一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
7.2实验结果按下按键拍照,拍照当前照片静止
用读卡器读取TF卡保存的图片
可以看到我们录制的视频中拍照的结果对比
本实验也提供了 CEPX3,双目摄像头的采集方案
|