如何在arm-linux里面实现外部中断呢

2019-07-12 23:11发布

首先说一下本人萌新一名,刚接触嵌入式三四个多月,由于工作需要,不得不研究嵌入式操作系统,一开始觉得挺新鲜的,后面感觉坑越来越深,问题越来越多,但还好自己也没放弃,经过一段时间的学习,积累,慢慢把很多问题都解决了。 今天想讲一下如何在arm-linux里面实现外部中断,这也是我在学习嵌入式过程中遇到的最大麻烦,前前后后大概卡了两三个星期,后来慢慢摸索,网上寻找,终于调通了,现在把大致的流程说一下。众所周知,想要在arm-linux里面实现外部中断,肯定要编写相应的驱动程序,也就是我们俗称的中断驱动,配合硬件,实现中断功能。废话不多说,我们进入实验流程。

实验目的

本次的实验目的是,通过按键实现中断。比如,我按个键,屏幕就会打印  hello, world  ,这个实验是不是很简单,但里面包含的东西有很多,我们可以慢慢学习。

实验条件

实验的条件当然是我们用了什么硬件,软件等等,这里,我采用的是赛灵思的一块开发板,ZC702,arm芯片是zynq7000系列,fpga是k7系列,属于arm加fpga的SOC芯片,功能很强大。软件的话就是Vivado2018 加 虚拟机Ubuntu16.04。

实验前提

当然了,这篇文章并不是给刚学习嵌入式的人看的,如何在arm装操作系统,如何配置串口等等都是基本操作,这里就不废话了。前提是,arm上已经有了linux操作系统,并且可以跟你的虚拟机相连。

实验步骤

首先,要完成中断,必须配置好硬件,也就是俗称的bit文件,如何配置这个bit文件呢,就是在Vivado里面完成的。Vivado的基本操作我也不多说了,这里给大家看一下硬件的block连线框图。 可以看到这个ip框图连接很简单,就用到了zynq block,并在里面配置了一个PL到PS的中断,然后这个中断外接按键,我这里设置的按键是SW5,可以在ug850手册上查到该按键的引脚,如图所示为G19。 然后就可以生成bit文件了,并导入到SDK中,在SDK中配置BOOT.bin文件。 好了,硬件部分配置就结束了,现在就是很关键的驱动程序如何编写。废话不多说,直接上驱动程序代码。 #include #include #include #include #define DEVICE_NAME "gpio_irq" unsigned int irq; static int major; static struct class *cls; static struct device *dev; static struct file_operations gpio_irq_fops = { .owner = THIS_MODULE, }; static struct of_device_id gpio_irq_of_match[] = { {.compatible = "xlnx,gpio_irq", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, gpio_irq_of_match); static irqreturn_t irq_handler(int irq, void *data) { printk("hello, world! "); return IRQ_HANDLED; } static int gpio_irq_probe(struct platform_device *pdev) { int ret; printk("match ok ! "); irq = platform_get_irq(pdev, 0); printk("irq: %d ", irq); ret = request_irq(irq, irq_handler, IRQ_TYPE_EDGE_RISING, DEVICE_NAME, NULL); if (ret < 0) goto err; major = register_chrdev(0, DEVICE_NAME, &gpio_irq_fops); cls = class_create(THIS_MODULE, "gpio_irq_class"); dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "gpio_irq_devcie"); return 0; err: return ret; } static int gpio_irq_remove(struct platform_device *pdev) { printk("remove! "); device_unregister(dev); class_destroy(cls); unregister_chrdev(major, DEVICE_NAME); free_irq(irq, NULL); return 0; } static struct platform_driver gpio_irq_driver = { .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, .of_match_table = gpio_irq_of_match, }, .probe = gpio_irq_probe, .remove = gpio_irq_remove, }; module_platform_driver(gpio_irq_driver); MODULE_AUTHOR("csdn"); MODULE_LICENSE("GPL"); 这是一个很简单的platform驱动代码模板,主要就是由probe和remove组成。在加载驱动的时候,首先会进入probe函数进行初始化,但关键问题就在如何进入这个probe函数呢。这个跟platform驱动的原理有关,主要是要设备和驱动相匹配,才会进入probe函数,我这里特地打印了一个match ok,表面设备和驱动已经匹配,可以进行初始化了。 这里还有一个很重要的步骤,就是配置设备树,为什么要配置设备数,因为设备树里面现在还没有按键的中断号,需要我们人为添加,所以SW5的中断号是多少呢,同样,查看手册,发现中断号是61,但写在设备树里面的应该是减掉32的,也就是29,至于为什么要减掉32,网上有很多说明,主要是要区分是否为spi。 好了,现在知道中断号了,我们就可以直接修改设备树了,由于我们的芯片是zynq系列,所以我们就找zynq的设备树,具体的目录是在你的内核文件夹的arch/arm/boot/dts下面,找到zynq-7000.dtsi,打开修改为如图所示。就是添加红 {MOD}方框里的代码,可以看到中断号为29,这样驱动在加载的时候,就会通过compatible里面的字符串来将设备和驱动相匹配。

实验结果

后面的操作就很简单了,我们将描述硬件的BOOT.bin,设备树devicetree.dtb,驱动模块gpio_irq.ko拷入到sd卡中,上电运行,就可以了。可以看到按了三次,出现三个hello,world!。我们也可以cat /proc/interrupts,可以看到内存分配的24号中断就是我们所设置的gpio_irq,中断号也就是61,是不是感觉很完美,出现这个说明大功告成!!! 好了,最简单的中断就完成了,看着很简单,其实里面的东西有很多,大家慢慢学习体会吧。