首先说一下本人萌新一名,刚接触嵌入式三四个多月,由于工作需要,不得不研究嵌入式操作系统,一开始觉得挺新鲜的,后面感觉坑越来越深,问题越来越多,但还好自己也没放弃,经过一段时间的学习,积累,慢慢把很多问题都解决了。
今天想讲一下如何在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,是不是感觉很完美,出现这个说明大功告成!!!
好了,最简单的中断就完成了,看着很简单,其实里面的东西有很多,大家慢慢学习体会吧。