Menu Close

ZYNQ Linux PL-PS 按键中断

petalinux 环境下,实现FPGA 开发板下的按键中断

 

开发环境

Ubuntu:     16.04

Vivado:      2018.3

Petalinux:  2018.3

开发板:       FII_7030

 

 

创建fpga 工程

%title插图%num

创建zynq cpu :

%title插图%num

添加concat模块(IP),用于线束宽展(需要多少,可以配置ip)

%title插图%num

添加按键中断源(3 pin),右键点击空白处

%title插图%num

 

%title插图%num

再次添加KEY_IRQ_1 => EDGE, RISING EDGE

再次添加KEY_IRQ_2 => EDGE, RISING EDGE

 

 

连接中断到zynq cpu 上

%title插图%num

保存zynq cpu,导出system_wrapper

%title插图%num

 

修改xdc 文件, 添加key_irq_0,  key_irq_1, key_irq_2

%title插图%num

%title插图%num

导出sdk 工程

%title插图%num

创建petalinux 工程: (进入ubuntu)

petalinux-create -t project –template zynq -n fii_class_key_irq

 

%title插图%num

cd fii_class_key_irq

petalinux-config –get-hw-description ~/work/FII_KEY_IRQ/FII_7030_CLASS.sdk/

 

不用修改, 直接保存,退出

修改user 设备树文件

%title插图%num

相对应的中断,查找ug585手册为 pl【2:0】 == irq29 – 31

%title插图%num

添加key irq 驱动 (key-irq-drv.c)

petalinux-create -t modules –name key-irq-drv

修改key-irq-drv.c文件

 

%title插图%num

key-irq-drv.c 代码如下:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/delay.h>
 
#include <linux/dma-mapping.h>
 
#include <linux/pm.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/dma-buf.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/dmaengine.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/init.h>
 
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/errno.h>    /* error codes */
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
 
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/pci.h>
 
#include <linux/time.h>
#include <linux/timer.h>
 
 
 
 


static char             devname[16];
static int              major;
static int              mijor;
static struct class*    key_irq_class;
//static void __iomem*  base_address;   
//static resource_size_t  remap_size;  
static int              key_irq_id;
static int              curr_key_irq_id = 0;
static struct device*   dev;
 


 
 
#define DEVICE_NAME "key_irq_drv"
 
 
static volatile int irq_is_open = 0;
 
static struct fasync_struct *irq_async;
 
 
static int irq_drv_open(struct inode *Inodestruct file *File)
{
    irq_is_open = 1;
    printk("driver:irq_drv_open = %d\n", irq_is_open);
    return 0;
}
 
 
int irq_drv_release (struct inode *inodestruct file *file)
{
    irq_is_open = 0;
    return 0;
}
 
 
 
static ssize_t irq_drv_read(struct file *filechar __user *bufsize_t countloff_t *ppos)
{
    copy_to_user(buf, &curr_key_irq_id, sizeof(int)); 
//  printk("driver:drv_read->key_irq_id = %d\n", curr_key_irq_id);
    return 0;
}
 
static ssize_t irq_drv_write(struct file *fileconst char __user *bufsize_t countloff_t *ppos)
{
    return 0;
}
 
static int irq_drv_fasync (int fdstruct file *filpint on)
{
    //初始化/释放 fasync_struct 结构体 (fasync_struct->fa_file->f_owner->pid)
    return fasync_helper (fd, filp, on, &irq_async);
}
 
 
static struct file_operations irq_fops = {  
    .owner          = THIS_MODULE,
    .open           = irq_drv_open,
    .read           = irq_drv_read, 
    .write          = irq_drv_write,
    .fasync         = irq_drv_fasync,
    .release        = irq_drv_release,
};
 
static irqreturn_t irq_interrupt(int key_irq_idvoid *dev_id)
{
    curr_key_irq_id = key_irq_id;
    printk("driver:key_irq_id = %d\n", key_irq_id);
    if(irq_is_open)
    {
        //发送信号SIGIO信号给fasync_struct 结构体所描述的PID,触发应用程序的SIGIO信号处理函数
        kill_fasync (&irq_async, SIGIO, POLL_IN);
    }
    return IRQ_HANDLED;
}


static int fii_device_get_id(struct platform_device *pdev)
{
    int id = pdev->id;
#ifdef CONFIG_OF
    const __be32 *prop;


    prop = of_get_property(pdev->dev.of_node"port-number"NULL);
    if (prop)
        id = be32_to_cpup(prop);
#endif
    return id;
}


static int irq_probe(struct platform_device *pdev)
{
    int                 err;
    struct device *tmp_dev;
    int  id;
    char * driver_name;
    
    memset(devname,0,16);
//  strcpy(devname, DEVICE_NAME);



    id = fii_device_get_id(pdev);
    if (id < 0)
        printk("key-irq-drv: %s%s \n",__FILE__, __LINE__);



    /* There is a need to use unique driver name */
    driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d",
                     DEVICE_NAME, id);
    
    strcpy(devname, driver_name);
    
    major = register_chrdev(0, devname, &irq_fops);
 
    key_irq_class = class_create(THIS_MODULE, devname);
    mijor = 1;
    
    tmp_dev = device_create(key_irq_class, &pdev->devMKDEV(major, mijor), NULL, devname);
    if (IS_ERR(tmp_dev)) {
        class_destroy(key_irq_class);
        unregister_chrdev(major, devname);
        return 0;
    }
    
    
    key_irq_id = platform_get_irq(pdev,0);
    if (key_irq_id <= 0)
        return -ENXIO;
 
    dev = &pdev->dev;
    
    err = request_threaded_irq(key_irq_id, NULL,
                irq_interrupt,
                IRQF_TRIGGER_RISING | IRQF_ONESHOT,
//              devname, pdev);             
                DEVICE_NAME, NULL);             



                   
    if (err) {
        printk(KERN_ALERT "irq_probe key_irq_id error=%d\n", err);
 
        goto fail;
    }
    else
        printk("driver:key_irq_id = %d,  devname = %s\n", key_irq_id, devname);
 
 
    return 0;
 
fail:
    
    free_irq(key_irq_id, NULL);
 
    device_destroy(key_irq_class, MKDEV(major, mijor)); 
    class_destroy(key_irq_class);
    unregister_chrdev(major, devname);
 
    return -ENOMEM;
 
}
 
static int irq_remove(struct platform_device *pdev)
{
    device_destroy(key_irq_class, MKDEV(major, mijor)); 
    class_destroy(key_irq_class);
    unregister_chrdev(major, devname);
    
 
    free_irq(key_irq_id, NULL);
    printk("driver:irq_remove ID= %d\n", key_irq_id);
 
    return 0;
}
 
static int irq_suspend(struct device *dev)
{
 
    return 0;
}
 
static int irq_resume(struct device *dev)
{
 
    return 0;
}
 
static const struct dev_pm_ops irq_pm_ops = {
    .suspend = irq_suspend,
    .resume  = irq_resume,
};
 
 
//MODULE_DEVICE_TABLE(platform, irq_driver_ids);
 
static const struct of_device_id irq_of_match[] = {
    {.compatible = "FII,key_irq" },
    {/* sentinel */},
};



MODULE_DEVICE_TABLE(of, irq_of_match);
 
 
static struct platform_driver irq_driver = {
    .probe = irq_probe,
    .remove = irq_remove,
    .driver = {
        .owner          = THIS_MODULE,
        .name           = "key_irq",
        .pm             = &irq_pm_ops,
        .of_match_table = irq_of_match,
    },
};
 
module_platform_driver(irq_driver);
 
MODULE_INFO(intree, "Y"); 
//MODULE_LICENSE("GPL v2");
MODULE_LICENSE("GPL"); 
 


 

编译petalinux 系统:

petalinux-config -c kernel

不用修改, 直接保存,退出

 

petalinux-config -c rootfs

 

%title插图%num %title插图%num

保存,退出

 

petalinux-build

 

petalinux-package –boot –fsbl ./images/linux/zynq_fsbl.elf –fpga –u-boot –force

 

将BOOT.BIN 和 image.ub  拷贝到sd 卡上

%title插图%num

上电启动开发板

 

在/dev 目录下可以找到key_irq_drv0,key_irq_drv1,key_irq_drv2, 三个设备

ls /dev

%title插图%num

在/proc 目录下 查看interrupts 可以看到这三个驱动的信息

cat /proc/interrupts

%title插图%num

 

分别按 menu , up, return 三个按键 (开发板上)

%title插图%num

 

可以看到中断的输出信息

 

驱动程序基本结构:

static int irq_drv_open(struct inode *Inode, struct file *File)

{

….

return 0;

}

int irq_drv_release (struct inode *inode, struct file *file)

{

…..

return 0;

}

static ssize_t irq_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

{

return 0;

}

static ssize_t irq_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)

{

return 0;

}

 

 

 

static struct file_operations irq_fops = {

.owner       = THIS_MODULE, 

.open = irq_drv_open, //内核函数,应用程序通过open 函数与之对应

.read = irq_drv_read,   //内核函数,应用程序通过read 函数与之对应

.write = irq_drv_write,  //内核函数,应用程序通过write 函数与之对应

.fasync   = irq_drv_fasync,  //内核函数,创建fasync_struct 结构体,为中断处理使用

.release     = irq_drv_release,  //内核函数,应用程序通过close 函数与之对应

};

 

 

 

static int irq_probe(struct platform_device *pdev)

{ // 加载驱动程序

major = register_chrdev(0, devname, &irq_fops);   —- 4 注册字符设备

 

key_irq_class = class_create(THIS_MODULE, devname);  —- 5 注册 类

mijor = 1;

tmp_dev = device_create(key_irq_class, &pdev->dev, MKDEV(major, mijor), NULL, devname);  —–  5 创建设备

 

return -ENOMEM;

 

}

 

static int irq_remove(struct platform_device *pdev)

{// 卸载驱动程序

device_destroy(key_irq_class, MKDEV(major, mijor));  //删除 设备

class_destroy(key_irq_class);  // 删除类

unregister_chrdev(major, devname); // 取消字符设备

 

return 0;

}

 

static int irq_suspend(struct device *dev)

{ // 主要用于电源管理,目前不需要处理

return 0;

}

 

static int irq_resume(struct device *dev)

{ // 主要用于电源管理,目前不需要处理

return 0;

}

 

 // 电源管理

static const struct dev_pm_ops irq_pm_ops = {  —-  3

.suspend = irq_suspend,

.resume  = irq_resume,

};

 

 

//MODULE_DEVICE_TABLE(platform, irq_driver_ids);

 

static const struct of_device_id irq_of_match[] = {   —  3

{.compatible = “FII,key_irq” },

{/* sentinel */},

};

 

 

MODULE_DEVICE_TABLE(of, irq_of_match);

 

 

static struct platform_driver xxx_driver = {   —-    2

.probe = irq_probe,

.remove = irq_remove,

.driver = {

.owner    = THIS_MODULE,

.name   = “key_irq”,

.pm     = &irq_pm_ops,

.of_match_table = irq_of_match,

},

};

 

module_platform_driver(xxx_driver);        —-    1 

 

MODULE_LICENSE(“GPL v2”);

 

 

 

上面是基本的设备树驱动程序模板。

 

 

 

 

编写应用程序:

创建应用程序目录:

%title插图%num

创建应用程序文件:

%title插图%num

编译文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
 
 
int fd0,fd1,fd2;
 
//获取信号函数, signum = GIGIO = 29
void get_signal_func(int signum)
{
    int key_irq_id;
    read(fd0, &key_irq_id, sizeof(int));
    printf("key_irq_id: %d  \n", key_irq_id);
}


#define DEV_KEY_IRQ  "/dev/key_irq_drv0"
int main(int argcchar **argv)
{
    unsigned char key_val;
    int ret;
    int Oflags;
 
    //在应用程序中捕捉SIGIO信号(由驱动程序发送)
    signal(SIGIO, get_signal_func);
    
    fd0 = open(DEV_KEY_IRQ, O_RDWR);
    if (fd0 < 0)
    {
        printf("can't open!\n");
    }
    else
        printf("open device : %s \n",DEV_KEY_IRQ);
 
    //将当前进程PID设置为fd文件所对应驱动程序将要发送SIGIO,SIGUSR信号进程PID
    
    // getpid() = fii_key_app current ID
    fcntl(fd0, F_SETOWN, getpid());
    
    //获取fd的打开方式
    Oflags = fcntl(fd0, F_GETFL); 
 
    //将fd的打开方式设置为FASYNC --- 即 支持异步通知
    //该行代码执行会触发 驱动程序中 file_operations->fasync 函数 ------fasync函数调用fasync_helper初始化一个fasync_struct结构体,该结构体描述了将要发送信号的进程PID (fasync_struct->fa_file->f_owner->pid)
    fcntl(fd0, F_SETFL, Oflags | FASYNC);
 
 
    while (1)
    {
        sleep(1000);
    }
    
    return 0;
}

 

编译应用程序:

sptl

arm-linux-gnueabihf-gcc -o fii_key_app.o fii_key_app.c

 

拷贝 fii_key_app.o 到sd 卡上:

 

 

 

 

执行fii_key_app.o

 

./fii_key_app.o

 

 

按键 menu, up, return 可以看到返回结果

%title插图%num

 

程序 通过 ctrl + c 退出。

 

如果通过其他方式退出, 可能退出不是很彻底。 可以通过

ps  命令查看。比如通过ctrl + z 退出

 

%title插图%num

这时执行 ps 依然可以看到这个进程

 

%title插图%num

 

可以通过 kill -9  进程号 彻底删除

kill -9 1092

kill -9 1093

 
Posted in Linux开发与应用, SoC, SoC, 教材与教案

发表评论

相关链接