ZYNQ7020_LINUX_VDMA

一、目标

编写VDMA驱动 ,通过HDMI显示一幅图。

二、准备工作

1、生成图片数组

选取一张640*480大小的图片,保存为imag.jpeg,导入到Matlab中执行以下程序,生成vdma_img.h文件。

image = imread('image.jpeg');
img = zeros(1,921600);
for i=1:480
    for j=1:640
    img((i-1)*640*3 +(j-1)*3 + 1 ) = image(i,j,3);
    img((i-1)*640*3 +(j-1)*3 + 2 ) = image(i,j,2);
    img((i-1)*640*3 +(j-1)*3 + 3 ) = image(i,j,1);
    end
end

fid=fopen('vdma_img.h','wt'); 
fprintf(fid,'const unsigned char Image_Shanks[921600] = {');
fprintf(fid,'%d,\n',img);  
fprintf(fid,'};');
fclose(fid);  

2、生成.bit文件

vivado工程可以在米联客的ov7725工程上修改:将摄像头相关的IP删除,保留VDMA、Video-out、HDMI。其中VDMA关闭写通道,读通道设置为Master。
在这里插入图片描述在这里插入图片描述

3、修改设备树

在设备树文件中添加VDMA描述,重新生成设备树文件。

vdma@43000000 {
			compatible = "chihong,vdma0";
			chihong,num-frame = <0x03>;
			reg = <0x43000000 0x100>;	
			interrupts = <0 29 4>;
			};

三、裸机程序

1、VDMA初始化

因为只用到了读通道,所以只初始化读通道相关的寄存器。

	Xil_Out32((vdmaRegBase + 0x00), 0x4);//reset vdma
	for (int i = 0; i < 3; i++)
		Xil_Out32((vdmaRegBase + 0x5C + 0x04 * i), bufAddr);	
	Xil_Out32((vdmaRegBase + 0x58), column); 		
	Xil_Out32((vdmaRegBase + 0x54), column); 		
	Xil_Out32((vdmaRegBase + 0x00), (1<<0) | (1<<1) | (1<<3) | (1<<7)/*| (1<<12)*/ | (1<<14)); 		
	Xil_Out32((vdmaRegBase + 0x50), row); 			

2、图片写入内存

裸机下图片刷入内存参考米联客的show_img();程序。修改后如下。

void show_img(u32 x, u32 y, u32 disp_base_addr, const unsigned char * addr, u32 size_x, u32 size_y)
{
	//计算图片 左上角坐标
	u32 i=0;
	u32 j=0;
	u32 r,g,b;
	u32 start_addr=disp_base_addr;
	//start_addr = disp_base_addr + 4*x + y*4*COLUMNS;
	for(j=0;j<size_y;j++)
	{
		for(i=0;i<size_x;i++)
		{
			b = *(addr+(i+j*size_x)*3+0);
			g = *(addr+(i+j*size_x)*3+1);
			r = *(addr+(i+j*size_x)*3+2);

			Xil_Out8((start_addr+(i+j*COLUMNS)*3),b);
			Xil_Out8((start_addr+1+(i+j*COLUMNS)*3),g);
			Xil_Out8((start_addr+2+(i+j*COLUMNS)*3),r);
		}
	}
	Xil_DCacheFlush();
}

四、驱动介绍

1、驱动结构

中断号通过设备树获取:

static int vdma_get_irq_number(void)
{
	int irq;

	struct device_node *dev_node, *chan_node;
	char dev_compativle[40] = "chihong,vdma0";

	dev_node = of_find_compatible_node(NULL, NULL,dev_compativle);
	if(dev_node == NULL){
		printk("can't find vdma node \n");
		return -1;
	}
	irq = irq_of_parse_and_map(device_node, 0);
	return irq;
}

VDMA初始化按照裸机下的自己编写就好。申请好内存之后需要把之前生成的vdma_img.h中的图片数组刷进去。

	for(j=0;j<(size);j++)
	{
		memset(buf->virt_addr + j, Image_Shanks[j], 1);
	}

以下是程序的主要结构。

static DEFINE_SEMAPHORE(chvdma_lock);	
static int chvdma_open(struct inode *inode, struct file *filep)
{
	if (filep->f_flags & O_NONBLOCK) {
		if (down_trylock(&chvdma_lock))
			return -EBUSY;
	}
	else {
		down(&chvdma_lock);
	}
	filep->private_data = chvdma_devp;

	printk("##chvdma_drv_open done\n");
	return 0;
}

int chvdma_release(struct inode *inode, struct file *filep)
{
	up(&chvdma_lock);
	printk("##chvdma_lock_release done\n");
	return 0;
}
static struct file_operations chvdma_fops = {
    .owner   =  THIS_MODULE,
    .open    =  chvdma_open,        
    .release =  chvdma_release,
};
static int chvdma_init(void)
{
	int ret;
	ret = chvdma_devp_init();
	if(ret != 0) {
		printk("failed to kzalloc\n");
		return -1;
	}
	major = register_chrdev(0, DEVICE_NAME, &chvdma_fops);
	if (major < 0){
		printk("failed to register device %s.\n", DEVICE_NAME);
		goto register_fail;
	}
	chvdma_devp->chvdma_class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(chvdma_devp->chvdma_class)) {
		printk("failed to create moudle class %s.\n", DEVICE_NAME);
		goto class_fail;
	}
	chvdma_devp->chvdma_class_dev = device_create(chvdma_devp->chvdma_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
	if (IS_ERR(chvdma_devp->chvdma_class_dev)) {
		printk("##chvdma_init: failed to create device %s.\n", DEVICE_NAME);
		goto device_fail;
	}
	ret = vdma_init(chvdma_devp);
	if(ret != 0) {
		printk("##chvdma_init: failed to init vdma \n");
		goto vdma_fail;
	}
	init_waitqueue_head(&(chvdma_devp->chvdma_wait));
	printk("##chvdma_init: done \n");
	
	return 0;

	vdma_fail:
		vdma_exit(chvdma_devp);
	device_fail:
		class_unregister(chvdma_devp->chvdma_class);
		class_destroy(chvdma_devp->chvdma_class);
	class_fail:
		unregister_chrdev(major, DEVICE_NAME);
	register_fail:
		chvdma_devp_exit();
	printk("failed \n");
	return -1;		
}

static void chvdma_exit(void)
{	
	vdma_exit(chvdma_devp);	

	device_del(chvdma_devp->chvdma_class_dev);	
	device_destroy(chvdma_devp->chvdma_class, MKDEV(major, 0));	
	class_unregister(chvdma_devp->chvdma_class);	
	class_destroy(chvdma_devp->chvdma_class);	
	unregister_chrdev(major, DEVICE_NAME);	
	chvdma_devp_exit();	
	
	printk("done \n");
}

module_init(chvdma_init);
module_exit(chvdma_exit);
MODULE_AUTHOR("ch");
MODULE_DESCRIPTION("chvdma moudle dirver");
MODULE_VERSION("v0.0");
MODULE_LICENSE("Dual BSD/GPL");

2、结果

通过共享文件夹,获得编译好的驱动之后,执行insmod指令。
在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Chi_Hong/article/details/84674469
今日推荐