米尔科技
400万+工程师在用
华为|鸿蒙开发者日
直播报名
400万+工程师在用
华为|鸿蒙开发者日
直播报名

3guoyangyang7

8年用户 942经验值
擅长:嵌入式技术 EMC/EMI设计 控制/MCU
私信 关注
[技术]

【Rico Board试用体验】跟着小狂玩RicoBoard之按键中断输入

2016-12-22 00:48

不知不觉,RicoBoard的试用已经进行了大半了,关于RicoBoard的帖子加上这一篇也是第七篇了,也到了快结题的时候啦,哈哈,不知道大家发现没,小狂冠以RicoBoard的帖子,更倾向于对驱动的管理以及掌控了,想要深入理解linux还是要理解linux内核和驱动层的编写,其实看看内核会发现里边运用了好多实用的小技巧,看懂之后以后可以运用到自己的程序中去的,活学活用吗,代码永远不是重要的,重要的是代码中的思想,融会贯通后,可以在任何平台,任何语言都适用。不要做码农,要站在思想的高度考虑问题。

这篇帖子之后下一篇会讲解如何更改设备树,支持UART3和UART5,那是RicoBoard最后一篇关于驱动的帖子,然后为了完成项目,会移植ubuntu或者debian到RicoBoard,在其中做网络开发,接下来项目结项贴会写子设备设置和主机即RicoBoard设置共两篇,到此所有的试用贴完成,小狂也会去安心复习了谢谢大家支持。

今天的主题呢就是讲IO中断,学习一款新的芯片小框一直的步骤都是GPIO、UART、中断、定时器、最后才是外设。基本上了解完这些,你就可以说能玩起一款芯片了,无论是嵌入式还是单片机,当然嵌入式有更深层次的东西,你可以裸奔,也可以基于系统。IO中断是中断中最最基础的内容,不知道我这样说或大家同意不。了解了这个,其他的也就不成神马问题了。为了达到融会贯通的目的,小狂就带着大家看看到底在嵌入式中的io输入中断到底和单片中的IO中断有什么不同。

首先我们先看看单片机中的io输入中断的操作会如何执行看图

1.png

我们在程序初始化阶段会初始化中断向量,书写中断服务函数。启动中断后,会一直监控设置的IO是否有指定的变化,比如高电平啊,低电平啊,上升沿啊,下降沿啊,或者双沿触发。进入中断后对于按键来说可能需要消抖操作然后才能执行相应的中断服务程序。坑能说道这里,大家就会说啦,我都会啊,对啊,我知道这对于一个老手来说是最简单不过的程序啦,但是不知道大家有没有注意过,在嵌入式中也是遵循这种写法的,这个不是我瞎说的,有空可以研究研究gpio-key.c通用的按键驱动程序就知道啦。这里带着大家按照上边的步骤一点点写我们自己的io输入的中断驱动程序,希望大家看到后能有所领悟。谢谢支持。

正式分析代码之前,小框使用的驱动框架是标准的字符设备驱动框架,为什么这么说,因为在linux的设备模型中有一个总的驱动模型就是kobject,除此之外衍生除了各种驱动模型,其实原理都是一样只不过在基础之上又封装了一层,显得高大上罢了。比如Platform驱动模型,misc驱动模型,还有各个子系统的驱动模型,等等。没有最好的只有最合适的。

一、设置中断操作

设置驱动在我们的操作中其实就是请求IO,设置IO为输入,然后请求中断号,前两步在GPIO一节中我们已经讲过,这里不再赘述。讲重点,中断号申请

request_irq(unsignedint irq, irq_handler_t handler, unsigned long flags,const char*name, void *dev)

irq为要申请的中断向量号,handler为中断服务函数,flags为中断触发标志,有上升沿,下降沿,高电平,低电平和双边沿触发,name为申请去个名字,dev为传给中断服务函数的数据。同样的道理有申请就应该有释放

voidfree_irq(unsigned int irq, void *dev_id)

参数的具体含义参考申请的函数。这里小狂需要说明一下,小狂使用的中断号申请函数是gpiolib中的gpio_to_irq函数,这个中断号是又封装了一层,具体的其他中断号的定义可以参考内核文件。

2.png

这个是我们申请中断号的具体函数。我们申请GPIO5_9为外部输入触发源。

具体的电路如下图所示

3.png

可能有小伙伴说啦,为什么不选用板子自带的KEY呢,这个很简单,因为设备树已经定义了这两个按键,设备已经注册过了,要想自己重新写的话必须把设备树中的key屏蔽掉取代板子中的设备文件。所以小狂这里就选用其他的io了,用根线短接效果一样。哈哈。偷个懒。

中断注销程序如下图所示

4.png

二、 中断服务函数(回调函数)

回调函数是中断中最重要的内容,我们所有操作的实现都在这里,我们的中断服务函数很简单,我们会在这里实现消抖操作。中断函数的书写必须满足如下格式

static irqreturn_t xxx_irq(intirq, void *dev_id)


irq会传入响应的中断号,这个在中断共享里是十分有用的,dev_id是中断注册的时候传入的数据。这里要说一下linux下驱动中断的处理模型,分为上半部和下半部,上半部只负责简单的硬件处理,下半部一般做复杂的数据处理传送之类的。其实这里理解起来很简单,你就想象一下在单片中我们中断怎么处理,一般只对某些变量进行简单的加减和置位,在主函数大循环中进行数据的处理响应,大家想想是不是一回事,只不过在系统中实现这个功能比较麻烦罢了,中断占用的时间越少,系统的执行效率越高,中断是独占性操作,时间越长也效率也就越低。


下面看看一看我们的中断函数是什么样子的


5.png

途中黑色的我们先不要去管,那个是延时操作所要做的事,其实我们就打印了一次进入中断了,然后返回了一个中断已经处理的标志。

三、 消抖操作

想一想我们在单片机中的消抖是怎么做的,我们一般是延时然后再判断是否按键按下,如果按下了再执行相应的操作,对不对,没错,linux下驱动程序也是这么写的,不过我们要借用linux下的time内核定时器,这个定时器是嵌入到内核中的,不会卡在中断中,这个是必须的,因为刚才我们说过了,我们在中断中不弄调用死的延时函数,会把进程卡在中断中的,这个不是我们想要的。我们需要下面的几个函数

init_timer(struct timer_list*timer)

这个是初始化我们定义的定时器

setup_timer(_timer, _fn,_data, _flags)

初始化我们定义的定时器的参数


Timer为timer_list指定的定时器,_fn为回到函数,_data为传到定时器的参数,_flags一般为0.

mod_timer(struct timer_list*timer, unsigned long expires)

这个函数为从新设定定时器的到期时间,每次执行的时候都会重新定义,在新的expires到来后才会执行定时器函数。

看我们具体的实现函数
6.png
我们在设备打开的时候初始化定时器
7.png

每次中断来的时候都会重新设置定时器的值,每到msecs_to_jIFfies(dev->timer_debounce)指定的时间到了就会执行回到函数

8.png

我们在回到函数中重新判断是否输入到来,如果到来就打印相应的操作。大家看看是不是跟单片机的思路一模一样呢,哈哈。万物皆相同,学到最后我们学习的应该只是方法,其他得都见鬼去吧。是不是这个道理。

四、 源码分析
设备结构体
9.png
注册函数
10.png
注销函数
这里的注销顺序一定是和注册反着来的,否则就会出现空指针。
11.png
五、 验证
添加模块
12.png
打开设备
13.png
让我们短接硬件,看看效果
14.png
然后串口控制台就会输出
15.png

我们自己写的io输入驱动已经能正常工作啦,有木有很开心,哈哈,其实,一切都是那么简单,我们不要把问题想复杂了。

六、 总结

又到了总结的时候啦,还是那句话,其实不知道大家有没有发现这篇文章的中心,其实不是在说怎么写io中断该怎么写,而是告诉大家一定要注重思想,忽略代码,代码神马的都见鬼去吧。重其义,不重其行,才能达到融会贯通,人机合一,哈哈。期待小狂下一篇帖子不会等太久哦,哈哈

最后的最后附上代码

key_interrupt.zip (2.21 KB)
(下载次数: 5, 2016-12-22 00:55 上传)

更多回帖

打开APP