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指令。