一、关于本demo

1.本demo中,zynq运行linux系统,包含一个自定义的PL端IP外设。

2.开发板从sd卡启动。

3.主要参考文献为xilinx ug1165 zynq embedded design tutorial,

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_2/ug1165-zynq-embedded-design-tutorial.pdf

相关参考文献为ug1144 petalinux tools reference guide 以及ug1157 petalinux tools command line guide

二、demo详解

1.应用目标与工作内容

本demo实现的功能是:PL端封装一自定义IP (即RTL模块,封装成IP核)。该IP为一8bit计数器,内含使能寄存器,接收linux 应用程序的使能控制信号。计数器的最高位连接到PL的led上,计数器被linux程序使能后,led开始闪烁。同时,将6bit引出,封装为一个中断信号,连接到arm cpu,cpu接收到该PL端中断后,在驱动程序层面以及应用程序层面,打印中断信息。这样,就实现了linux应用程序使能PL自定义IP,并接收PL IP中断的交互。

因此,本demo的目标是打通PL IP与驱动、驱动与应用程序之间的交互机制。

本demo的主要内容有一下4个部分:

1)带PL IP核的硬件设计

2)带PL设备树的linux系统镜像制作

3)PL设备的驱动开发与调试

4)Linux 应用程序开发与调试

2.整体开发流程

与开发工作内容对应,开发流程主要有一下四个阶段。

1.硬件设计

此阶段与裸板应用的硬件设计类似,在vivado内设计、封装自定义IP核,在block design内梨花IP核与zynq cpu等。完成逻辑设计。生成bit流文件以及输出hdf硬件交付文件,进入软件开发阶段。

2.修改ug1165的驱动文件blink.c,假如中断处理功能(打印信息)。

3.linux系统镜像制作

利用1阶段生成的hdf文件,以及2阶段的blink.c,进入ubuntu系统下的petalinux开发环境。参考本博客“zynq自定义PL IP核linux驱动开发demo”,制作出与逻辑硬件设计匹配的待驱动程序的linux系统镜像。

4.修改ug1165的应用程序c文件linux_blink_application.c,打印装订信息。

3.硬件设计

硬件设计的blockdesign例化自定义的计数器IP核以及zynq CPU即可。其中,自定义IP 引出计数器的最高位到板子的led管脚,同时封装为中断信号,一秒钟向CPU发送一次中断,连接到CPU的中断输入。其余部分令BD自动连接即可。自定义IP封装的过程,可参看本博客“zynq 自定义逻辑IP核的封装”一文。

zynqCPU配置为zc702的preset,并在interrupts一栏中,勾选PL-PS interrupt Ports里的IRQ_F2P,使能PL到PS的中断。

preset 已将led分配到管脚,完成BD后编译生成bit文件并export hardware。

4.驱动程序修改与解读

4.1驱动程序整体功能分析

驱动程序介于硬件与应用程序之间,运行在内核空间,直接操作硬件设备,使得应用程序可以脱离具体的硬件。应用程序运行在用户空间。从应用程序的需求反推,可以知道驱动程序需要实现下图中的这些功能。

如图所示,应用程序的需求是1.写设备寄存器(PL IP 计数使能寄存器)2.接收设备中断。用户空间的应用程序无法直接操作内核空间的内存,必须借由system call(系统调用)函数来实现对内核空间设备文件的操作。常用的system call有read ,write,ioctl等。

system call可以在用户空间直接调用,在内核驱动里,有相应的驱动函数来实现system call的操作。这些驱动函数需要开发者来设计。

本demo中,需要设计device_read函数来读取驱动程序内的中断标志,需要设计device_ioctl来操作IO设备(即PL IP),写PL IP的使能寄存器。

为此,需要做以下操作:

1.申请中断号并向系统注册中断。

2.申请IO端口,分配IO内存,并将内存映射到虚拟地址上去。

这两步操作主要由blink_driver结构体的probe成员完成,该成员是blink_probe()函数。

在执行probe函数之前,需匹配驱动与设备的name,id_table等字段,确保驱动与设备时匹配的。该工作主要由blink_driver.driver->bus->match成员完成。

再反推,要匹配设备与驱动,必先注册设备与驱动。该操作在驱动的入口函数即完成。设备注册函数的file_operation成员内注明了支持的操作种类,可按需裁剪。

4.2驱动程序解读

驱动程序与应用程序不同,没有主函数。加载驱动模块时,会执行入口函数module_init();卸载驱动模块时,会执行module_exit();函数。入口函数接出口函数会调用一系列函数,完成4.1节图的大部分功能。另外的一些函数,如device_read()等函数,则是设计好放在驱动程序里,等待应用程序中的system call函数的调用。下文从入口函数开始,按照调用关系,详细解读驱动代码blink.c该代码在ug1165文末中有下载链接。

原始驱动程序见参考文献,下面详解有所改动。代码详解:

module_init(blink_init);以及module_exit(blink_exit);为驱动函数的入口与出口函数。入口函数调用了blink_init()函数。出口函数调用blink_exit函数。

blink_init()函数首先打印一些问候信息,表明驱动进入了初始化函数。其中的printk函数为printf函数的内核空间版本,用法与printf类似。该函数可注明打印信息的紧急等级(分八级,0-7,数字越小越紧急),等级低于某个等级时,信息不会打印在串口上,但是在开发板系统的var/log/dmsg核var/log/messegges路径下看到。

chrdev_register()第一个参数表示申请的住设备号,该值为0时,系统动态分配住设备号。推荐使用动态分配,可避免设备号冲突。第二个参数为设备名称,该名称为开发板系统/dev设备节点名称。第三个参数为file_operation结构体,该结构体内的成员即为本设备支持的操作。本demo的Fops结构体代码如下:

struct file_operations Fops ={.owner = THIS_MODULE,.read = device_read,.write = device_write,.unlocked_ioctl - device_ioctl,.open -device_open,.release =device_release};

可见,本demo注册的设备声明了read,write,ioctl,open,release等几个常用的成员,可以支持语音程序内对应的system call。

mmio -ioremap(BLINK_CTRL_REG,0x100);

将自定义PL IP的外设基址(总线地址)映射到内存空间内。映射地址赋值给mmio,后面对mmio的操作即可被映射到实际的总线地址上,从而改变IP核被寄存器的值。ioremap函数的第一个参数为PL IP核设备的实际基址,该基址与BlockDesign中的基址是一致的。第二个参数为映射地址的长度,其余的程序里可设置为0x100,小于硬件设计的IP核的地址空间64K。

rc=platform_driver)register(&blink_driver)注册驱动、platform+driver_register函数会调用一系列函数,完成设备与驱动的match,进一步调用blink_probe函数,完成IO与中断的初始化操作。参数blink_driver为结构体,代码如下:

static struct platform_driver blink_driver ={.driver ={.name = DRIVER_NAME,.owner = THIS_MODULE,.of_match_table = blink_of_match},.probe = blink_probe,.remove = blink_remove};

blink_driver结构体中的driver成员的上子成员信息会被用于设备与驱动的匹配,尤其是of_match_table 成员,其应被赋值为blink_of_match,这与xilinx原始文件不同。blink_of_match成员如下:

static struct  of_devide_id blink_of_match[]={{.compatible = "xlnx,blink-ip-1.0",},{},};

compatible = "xlnx,blink-ip-1.0可从设备树文件<peta project dir> /components/plnx_workspace/device-tree-generation/pl.dtsi中查询到.pl.dtsi是根据硬件设计hdf文件编译设备树后生成的。编译设备树会在后续章节的编译系统镜像时一起完成。如果想在第一次就将此处修改正确(此时还未制作系统镜像,还没有pl.dtsi文件),可先根据hdf文件单独编译设备树,可以参考ug1157以及ug1144

pl.dtsi代码如下:

amba_pl:amba_pl{#address-cells = <1>;#size-cells =<1>;compatible = "simple-bus";ranges;blink_ip_0: blink_ip@43c00000{compatible = "xlnx,blink-ip-1.0";interrupt-parent = <&intc>;interrups = <0 29 1>;reg = <0x43c00000 0x10000>;xlnx,s00-axi-addr-width = <0x4>;xlnx,s00-axi-data-width = <0x20>;};};

pl.dtsi文件可以看到设备的基址,以及映射到内核空间的中断号29,该中断号与逻辑硬件设计的中断号相差32,(硬件中断号是61。其他的中断号也符号这个关系)

interrups的第一个参数为中断类型,0表示共享中断,第二个参数为映射中断号,第三个参数为触发类型,0表示无触发,1表示上升沿触发,2表示下降沿触发,4表示高电平触发,8表示低电平触发。

回到blink_driver函数,该函数只赋值了三个成员,其他成员在platform_driver_register调用的子函数里被设置为默认值,包括match成员。

.probe = blink_probe,将blink_probe赋值给probe成员,probe成员会被platform_driver_register的子函数调用,最终完成IO以及中断的初始化工作。

.remove = blink_remove;blink_remove函数在卸载驱动时,会被platform_driver_register的子函数调用,用于释放中断,释放IO内存空间等。

再看platform_driver_register函数。其内部函数调用关系如下图所示。

platform_driver_register函数调用的都是linux kernel源码 内的系统函数。该函数先调用driver_match_device函数,匹配设备与驱动,然后调用really_probe函数,进一步调用blink_probe函数,blink_probe为blink.c中开发者设计的函数,用于IO与中断的初始化。

blink_probe函数详解:

r_mem=...获取IO资源,但是并未获取实质的物理内存。r_mem时一个指向resource结构体的指针,resource结构体包含的首个成员是start,用于表示所申请到的资源的首地址(根据第二个参数申请资源类型的不同,start有不同的含义,例如,当申请中断资源时,start表示申请的首个中断号)。执行完platform_get_resource函数后,返回一个申请到的resource结构体,内含start,end,name,flags,desc以及嵌套的resource等成员。我们需要关注start以及end成员,因为涉及到内存操作地址。

lp=...为lp结构体(即blink_local结构体)分配物理内存空间。blink_local结构体内含irq,mem_start,mem_end,*base_addr等成员,可用来保存IO设备的中断,内存起止,基址等信息。kmalloc函数的第一个参数为申请内存的大小,第二个参数为申请内存的种类。不同种类的内存具有不同的操作属性。例程中的GFP_KERNEL是普通类型。

dev_set_drvdata(dev,lp);将分配好内存的lp结构体信息保存到设备的driver_data成员。(此时lp内的信息还未更新,是默认值)

lp->..两行将lp的起止地址信息赋值给lp对应的成员。

if (!request_mem_region)...申请IO内存。request_mem_region向内核申请物理内存,第一个参数为起始地址,第二个参数为申请的内存长度,第三个参数为设备名称。

lp->base_addr...将IO物理内存地址映射到内核虚拟地址。

r_irq...获取中断资源,与获取IO资源区别在于第二个参数

lp->irq = r_irq->start;将获取的中断主要的首中断号赋值给lp结构体的irq成员。lp起中转作用。

rc=request_irq...申请中断。request_irq第一个参数为申请中断资源时分配的中断号,第二个参数为中断处理函数,发生中断后将调用该函数处理中断。本demo的中断处理函数如下:

static irqreturn_t blink_irq(int irq,void *lp){printk(blink interrupt\n);intc_flag++;printk("kintc is %x\n0",intc_flag);return IRQ_HANDLED;}

主要是打印信息,表明进入了中断处理函数。

第三个参数为中断触发类型,与pl.dtsi一致。第四个参数为驱动名称。第五个参数将被传给第二个参数的中断处理函数使用。

intc_flag记录中断发生的次数,是全局变量。将被device_read函数引用,以便通过read system call上传到用户空间的应用程序中去。

总体而言,blink_probe函数实现了IO端口和中断申请等初始化操作;将IO内存映射到虚拟地址,以便device_ioctl函数操作寄存器;指定指定处理函数,实现中断信号的打印和上传。

两个驱动函数device_read和device_ioctl解读:

copy_to_user函数将中断处理函数的中断计数变量intc_flag上传到用户空间。第一个参数为用于缓存的变量,在应用程序内需要利用该参数以获取数据。第二个参数是需要上传的数据,第三个参数是上传数据的长度。

ioctl_num参数是应用程序ioctl system call传下来的命令码。不同的命令码对应device_ioctl内不同的操作。假如传下来的命令码是IOCTL_ON_LED,则打开PL计数器使能寄存器,PL模块开始计数,led开始闪烁。

set_blink_ctrl函数会打印一条信息,并通过虚拟地址将PL模块的首个寄存器写为1。

5.系统镜像制作

参考本博客“zynq自定义PL IP核linux驱动开发demo”一文

6.应用程序修改以及编译

6.1应用程序解读

应用程序的主要操作是打印一系列用户交互信息,获取键盘的输入信息,以完成打开计数器使能核关闭使能寄存器的操作。在闪灯期间,通过system call读取驱动程序中断标志intc_flag。应用程序为linux_blinked_app.c,可在官方下载。

6.2应用程序编译

应用程序编译成elf文件,可在petalinux环境中编译,参考本博客“zynq自定义PL IP核linux驱动开发demo”一文。也可以直接使用交叉编译器产生。步骤如下:

1.将linux_blinked_aoo.c以及blink.h放在同一文件夹下。

2.需在linux主机安装vivado的软件开发环境SDK,若环境变量设置正确,输入arm-linux后按tab键,会自动补全出arm-linux-gnueabihf-交叉编译器。若未设置好,也可手动source SDK安装路径下的settings64.sh脚本,即可添加交叉编译器。脚本路径为xxx/Xilinx/SDK/2016.4/settins64.sh

3.在linux_blinked_aoo,c所在的文件夹下,输入arm-linux-gnueabihf-gcc linux_blinked_app.c -o blink即可编译出blink可执行文件。

7.执行应用程序

1.将5节产生的BOOT.BIN和image.ub以及6节产生的blink可执行文件拷贝到系统SD卡,重启开发板。

2.在串口命令行内,cd /lib/modules/4.9.0-xilinx-v201x.x/extra/ 该路径下有驱动模块的目标文件blink.ko

3.加载驱动模块 modprobe blink.ko

4.生成设备节点 mknod /dev/blink_Dev  c  244  0

5.进入sd卡, cd /mnt  假设SD卡是挂载在.mnt路径下

6.执行blink。./blink

即可看到一样程序被执行,输入1,打开ledled

开发板led开始闪烁,并且串口持续打印kintc中断次数。

输入0,led停止闪烁,串口停止打印中断次数

卸载驱动:rmmod blink

串口会打印Goodbye module world.

参考源码:

驱动blink.c无改动

/*  blink.c - The simplest kernel module.*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>#include <linux/fs.h>#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>#include <asm/uaccess.h> /* for get_user and put_user */
#include <asm/io.h>
#include "blink.h"
#define SUCCESS 0
#define DEVICE_NAME "/dev/blink_Dev"#define BLINK_CTRL_REG 0x43C00000
static unsigned long *mmio;
static int major_num;/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
static int Device_Open = 0;static void set_blink_ctrl(void)
{printk("KERNEL PRINT : set_blink_ctrl \n\r");*(unsigned int *)mmio = 0x1;
}static void reset_blink_ctrl(void)
{printk("KERNEL PRINT : reset_blink_ctrl \n\r");*(unsigned int *)mmio = 0x0;
}
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{#ifdef DEBUGprintk(KERN_INFO "device_open(%p)\n", file);#endif/** We don't want to talk to two processes at the same time*/if (Device_Open)return -EBUSY;Device_Open++;/** Initialize the message*/
//  Message_Ptr = Message;try_module_get(THIS_MODULE);return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file)
{#ifdef DEBUGprintk(KERN_INFO "device_release(%p,%p)\n", inode, file);#endif/** We're now ready for our next caller*/Device_Open--;module_put(THIS_MODULE);return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read( struct file *file, /* see include/linux/fs.h */char __user * buffer, /* buffer to be filled with data */size_t length, /* length of the buffer */loff_t * offset)
{return SUCCESS;
}
/*
* This function is called when somebody tries to
* write into our device file.
*/
static ssize_t device_write(struct file *file,const char __user * buffer, size_t length, loff_t * offset)
{return SUCCESS;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
long device_ioctl(          struct file *file, /* ditto */unsigned int ioctl_num, /* number and param for ioctl */unsigned long ioctl_param)
{
//  int i;char *temp;
//  char ch;/** Switch according to the ioctl called*/switch (ioctl_num) {case IOCTL_ON_LED:temp = (char *)ioctl_param;set_blink_ctrl();break;case IOCTL_STOP_LED:temp = (char *)ioctl_param;reset_blink_ctrl();break;}return SUCCESS;
}
/* Module Declarations */
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations Fops = {.owner = THIS_MODULE,       .read = device_read,.write = device_write,.unlocked_ioctl = device_ioctl,.open = device_open,.release = device_release, /*close */                               };/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("blink - loadable module template generated by petalinux-create -t modules");#define DRIVER_NAME "blink"/* Simple example of how to receive command line parameters to your module.Delete if you don't need them */
unsigned myint = 0xdeadbeef;
char *mystr = "default";module_param(myint, int, S_IRUGO);
module_param(mystr, charp, S_IRUGO);struct blink_local {int irq;unsigned long mem_start;unsigned long mem_end;void __iomem *base_addr;
};static irqreturn_t blink_irq(int irq, void *lp)
{printk("blink interrupt\n");return IRQ_HANDLED;
}static int blink_probe(struct platform_device *pdev)
{int rc = 0;struct resource *r_irq; /* Interrupt resources */struct resource *r_mem; /* IO mem resources */struct device *dev = &pdev->dev;struct blink_local *lp = NULL;dev_info(dev, "Device Tree Probing\n");/* Get iospace for the device */r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!r_mem) {dev_err(dev, "invalid address\n");return -ENODEV;}lp = (struct blink_local *) kmalloc(sizeof(struct blink_local), GFP_KERNEL);if (!lp) {dev_err(dev, "Cound not allocate blink device\n");return -ENOMEM;}dev_set_drvdata(dev, lp);lp->mem_start = r_mem->start;lp->mem_end = r_mem->end;if (!request_mem_region(lp->mem_start,lp->mem_end - lp->mem_start + 1,DRIVER_NAME)) {dev_err(dev, "Couldn't lock memory region at %p\n",(void *)lp->mem_start);rc = -EBUSY;goto error1;}lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);if (!lp->base_addr) {dev_err(dev, "blink: Could not allocate iomem\n");rc = -EIO;goto error2;}/* Get IRQ for the device */r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (!r_irq) {dev_info(dev, "no IRQ found\n");dev_info(dev, "blink at 0x%08x mapped to 0x%08x\n",(unsigned int __force)lp->mem_start,(unsigned int __force)lp->base_addr);return 0;}lp->irq = r_irq->start;rc = request_irq(lp->irq, &blink_irq, 0, DRIVER_NAME, lp);if (rc) {dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",lp->irq);goto error3;}dev_info(dev,"blink at 0x%08x mapped to 0x%08x, irq=%d\n",(unsigned int __force)lp->mem_start,(unsigned int __force)lp->base_addr,lp->irq);return 0;
error3:free_irq(lp->irq, lp);
error2:release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:kfree(lp);dev_set_drvdata(dev, NULL);return rc;
}static int blink_remove(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct blink_local *lp = dev_get_drvdata(dev);free_irq(lp->irq, lp);release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);kfree(lp);dev_set_drvdata(dev, NULL);return 0;
}#ifdef CONFIG_OF
static struct of_device_id blink_of_match[] = {{ .compatible = "vendor,blink", },{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, blink_of_match);
#else
# define blink_of_match
#endifstatic struct platform_driver blink_driver = {.driver = {.name = DRIVER_NAME,.owner = THIS_MODULE,.of_match_table = blink_of_match,},.probe       = blink_probe,.remove       = blink_remove,
};static int __init blink_init(void)
{int rc = 0;printk("<1>Hello module world.\n");printk("<1>Module parameters were (0x%08x) and \"%s\"\n", myint,mystr);/** Register the character device (atleast try)*/major_num = register_chrdev(0,DEVICE_NAME, &Fops);/** Negative values signify an error*/if (major_num < 0) {printk(KERN_ALERT "%s failed with \n","Sorry, registering the character device ");}mmio = ioremap(BLINK_CTRL_REG,0x100);printk("%s: Registers mapped to mmio = 0x%x  \n",__FUNCTION__,mmio);printk(KERN_INFO "%s the major device number is %d.\n","Registeration is a success", major_num);printk(KERN_INFO "If you want to talk to the device driver,\n");printk(KERN_INFO "create a device file by following command. \n \n");printk(KERN_INFO "mknod %s c %d 0\n\n", DEVICE_NAME, major_num);printk(KERN_INFO "The device file name is important, because\n");printk(KERN_INFO "the ioctl program assumes that's the file you'll use\n");rc =  platform_driver_register(&blink_driver);return rc;
}static void __exit blink_exit(void)
{unregister_chrdev(major_num,DEVICE_NAME);platform_driver_unregister(&blink_driver);printk(KERN_ALERT "Goodbye module world.\n");
}module_init(blink_init);
module_exit(blink_exit);

驱动头文件

/*
* blink.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (in chardev.c) and the process calling ioctl (ioctl.c)
*/
#ifndef BLINK_H
#define BLINK_H
#include <linux/ioctl.h>
/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAGIC_NUM 100
/*
* TURN ON LED ioctl
*/
#define IOCTL_ON_LED _IOR(MAGIC_NUM, 0, char *)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAGIC_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
/*
* STOP LED BLINK ioctl
*/
#define IOCTL_STOP_LED _IOR(MAGIC_NUM, 1, char *)#define DEBUG#define DEVICE_FILE_NAME "/dev/blink_Dev"
#endif

应用程序linux_blinked_app.c,有改动

/*
* ioctl.c - the process to use ioctl's to control the kernel module
*
* Until now we could have used cat for input and output. But now
* we need to do ioctl's, which require writing our own process.
*/
/*
* device specifics, such as ioctl numbers and the
* major device file.
*/
#include "blink.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> /* open */
#include <unistd.h> /* exit */
#include <sys/ioctl.h> /* ioctl */
/*
* Functions for the ioctl calls
*/
void ioctl_ON_LED(int file_desc)
{int ret_val;ret_val = ioctl(file_desc, IOCTL_ON_LED, NULL);if (ret_val < 0){printf("ioctl_ON_LED failed:%d\n", ret_val);exit(-1);}
}
void ioctl_OFF_LED(int file_desc)
{int ret_val;ret_val = ioctl(file_desc, IOCTL_STOP_LED,NULL);if (ret_val < 0){printf("ioctl_OFF_LED failed:%d\n", ret_val);exit(-1);}
}
/*
* Main - Call the ioctl functions
*/
int main()
{int Choice;int exitflag=1;int file_desc;unsigned char intc_flag;//add by shimmy_leeprintf("################################ \n\r");printf("      Blink LED Application  \n\r");printf("################################ \n\r");file_desc = open(DEVICE_FILE_NAME, O_RDWR | O_SYNC);if (file_desc < 0) {printf("Can't open device file: %s\n", DEVICE_FILE_NAME);exit(-1);}while (exitflag){printf("************************************************ \n\r");printf("Press '1' to Start Blink LED TEST \n\r");printf("Press '0' to Stop Blink LED TEST \n\r");printf("************************************************ \n\r");scanf("%d",&Choice);printf("Choice  :: %d \n\r",Choice);read(file_desc,&intc_flag,1);//add by shimmy_leeif(Choice == 1){printf("uintc is %x\n",intc_flag);ioctl_ON_LED(file_desc);exitflag  = 1;}else if (0 == Choice ){ioctl_OFF_LED(file_desc);exitflag   = 1;}else{exitflag  = 0;}}close(file_desc);printf("################################ \n\r");printf("      Bye Blink LED Application  \n\r");printf("################################ \n\r");return 0;
}

应用程序头文件

/*
* blink.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (in chardev.c) and the process calling ioctl (ioctl.c)
*/
#ifndef BLINK_H
#define BLINK_H
#include <linux/ioctl.h>
/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAGIC_NUM 100
/*
* TURN ON LED ioctl
*/
#define IOCTL_ON_LED _IOR(MAGIC_NUM, 0, char *)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAGIC_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
/*
* STOP LED BLINK ioctl
*/
#define IOCTL_STOP_LED _IOR(MAGIC_NUM, 1, char *)#define DEBUG#define DEVICE_FILE_NAME "/dev/blink_Dev"
#endif

ZYNQ Linux 逻辑端(PL)中断demo相关推荐

  1. linux ps-pl中断,zynq linux驱动之PL-PS中断

    PC:Windows 10 虚拟机:ubuntu 16.04 vivado:2017.04 PetaLinux:2017.04 开发板:黑金AX7010 根文件系统:debian8 --------- ...

  2. zynq Linux软件中断,zynq linux驱动之PL-PS中断【转】

    PC:Windows 10 虚拟机:ubuntu 16.04 vivado:2017.04 的的PetaLinux:2017.04 开发板:黑金AX7010 根文件系统:debian8 ------- ...

  3. zynq linux如何使用pl ip,ZYNQ+linux网口调试笔记(3)PL-ETH

    1. 开发环境 Windows SDK 2017.4 Ubuntu Petalinux 2017.4 硬件平台:米联客ZYNQ开发板MIZ7035 2. 开发目标 在ZYNQ上使用gigE Visio ...

  4. zynq linux如何使用pl ip,米联客FDMA IP在LINUX下实现PL和PS数据共享测试总结

    本帖最后由 ぉ沙皮狗的忧伤 于 2019-10-12 10:22 编辑 1.先讲一下一个小问题,我将FDMA裸机测试的.bit文件重命名为system.bit.bin文件放入SD卡启动时,在VDMA的 ...

  5. 米尔科技Zynq pl中断的linux驱动

    一.目标 在米尔科技zynq的开发平台上,通过zynq的按键开关,实现pl中断. 二.分析 原理图 挂在pl端,需要在vivado上进行引脚锁定.配置如下 当然可以通过中断函数操作三色灯,也可以不用管 ...

  6. ZYNQ7000搭建嵌入式Linux操作系统---增加PL端外设(以太网)篇

    ZYNQ7000搭建嵌入式Linux操作系统-增加PL端以太网 一.VIVADO工程建立 二.VIVADO工程设置 三.虚拟机下生成内核和uboot.elf 四.SDK生成设备树和BOOT.bin 五 ...

  7. (124)FPGA面试题-ZYNQ的PS和PL端怎么交互的?

    1.1 FPGA面试题-ZYNQ的PS和PL端怎么交互的? 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-ZYNQ的PS和PL端怎么交互的: 5)结束语 ...

  8. zynq linux多个中断注册,zynq linux 中断号如何对应

    在linux系统下,中断号跟BD中zynq7000 processer中配置的生成的中断号不是直接对应的,中间有一个"-32" 的关系,如下 For Shared Peripera ...

  9. 视频流 zynq Linux 至 pl,AR# 46913: Zynq-7000 示例设计:使用面向 DEVCFG 的 Linux 驱动程序对 PL 进行编程...

    解决方案 说明: 要使用面向 devcfg 的 Linux 驱动程序对 PL 进行编程,比特流应转换为二进制. 此操作可使用 BootGen 工具. BootGen 工具使用"BIF&quo ...

最新文章

  1. 好莱坞电影公司系列电影
  2. 1、lombok的初始使用
  3. 记录 Git命令速查表
  4. 项目实战解决 java.sql.SQLException: Unable to load authentication plugin ‘caching_sha2_password‘.
  5. 通过rpm包安装、配置及卸载mysql的详细过程.
  6. springboot项目和云服务器,以及域名的申请和使用(后续持续更新)
  7. C#.Net工作笔记010---c#中的静态扩展方法_可动态给string等_添加共通方法好用
  8. hexbin_Power BI Desktop中的Hexbin散点图
  9. QA:rustup-init error: caused by: 拒绝访问。 (os error 5)出现权限拒绝
  10. [CTSC 1999]拯救大兵瑞恩[网络流24题]孤岛营救问题
  11. NGINX实现负载均衡,并利用PHP实现session入库
  12. can 自动波特率 linux,CAN特殊波特率如何计算
  13. Java面经总结(1)
  14. C# 合并Excel工作表
  15. 基于android的学生考勤请假app
  16. 力扣-两数之和 (梦开始的地方)
  17. Sort of sort
  18. 为美女纹身--壁纸+视频生成器
  19. 浅谈Java类加载:ClassLoader
  20. 数据仓库、主题域、主题概念与定义

热门文章

  1. 2022四川最新建筑施工架子工(建筑特种作业)模拟考试试题及答案
  2. Java 独占锁ReentrantLock、读(悲观读)写锁ReentrantReadWriteLock、读(乐观读/悲观读)写锁StampedLock
  3. 非监督特征学习与深度学习(十四)--------循环神经网络
  4. C语言编程>第十六周 ⑧ S是一个由数字和字母字符串组成的字符串,由变量len传入字符串长度。请补充fun函数,该函数的功能是把字符串s中的数字字符转换成数字并存放到整型数组a中
  5. wordpress插件_9个最佳WordPress产品组合插件
  6. IOG交互软件---Intelligent Pixel Annotation Tool (IPAT)---安装说明
  7. 电商商家必看!海外抖音TikTok选品教程大放送
  8. 瓶中阳光——雪莉之美
  9. 英文文档翻译软件-汉语文章翻译成英语
  10. pynq-z2 使用PL做流水灯