老查的ARM学习笔记:chapter-1(按键驱动程序设计)

前面的博客中,有一篇通过按键玩中断的文章,不过那里的程序是裸机,也就是没有加系统下设计的程序,也就和在单片机中设计的程序一样比较简单。现在我们来看看按键的驱动程序在linux系统下是如何设计的。

1 混杂设备驱动模型**

1 混杂设备驱动描述
首先我们先来了解一下什么是混杂设备驱动模型。混杂设备其实是字符设备中的一种,主设备号是10,次设备号不同的设备称为混杂设备,在linux中,用struct miscdevice来描述一个混杂设备,从内核源码中复制过来结构原型为

struct miscdevice  {
    int minor;              /*次设备号*/
    const char *name;        /*设备名*/
    const struct file_operations *fops;  /*文件操作*/
    struct list_head list;
    struct device *parent;
    struct device *this_device;
    const char *nodename;
    mode_t mode;
};

2 注册混杂设备驱动
linux中使用misc_register函数来注册一个混杂设备驱动
int misc_register(struct miscdevice *misc)
3 注销混杂设备驱动
misc_deregister(struct miscdevice *misc)
通过上面的概述,可以通过下面的导图来搭建一个简单的按键模型驱动key.c。
这里写图片描述

#include<linux/module.h>
#include<linux/init.h>
#include<linux/miscdevice.h>

MODULE_LICENSE("GPL");

int key_open(struct inode *node,struct file *filp)
{
    return 0;
}

struct file_operations key_fops=
{
    .open = key_open,   
};

struct miscdevice key_miscdev = {
     .minor = 200,
     .name = "key",
     .fops = &key_fops, 
};

static int key_init()
{
    misc_register(&key_miscdev);
    return 0;
}

static void key_exit()
{
    misc_deregister(&key_miscdev); //注销混杂设备
}


module_init(key_init);
module_exit(key_exit);

2 中断处理流程分析

按键驱动程序中,一般采用中断的方式去处理,那我们就先来看看中断处理的步骤是什么样的,在裸机程序部分当中,步骤可分为以下三步。
1 中断存在统一入口,这个入口在start.S汇编文件中,每当中断发生的时候,都会把irq发送给pc。
2 注册中断处理程序。
3 根据中断源的编号来调用中断处理程序。
在linux中,统一的入口也是存在的,在entry-arm.S文件当中的irq_svc。这里我就来简单说一下这个流程是怎么工作的
1 首先,程序通过irq_svc找到中断入口。
2 其次拿到产生中断源的编号,也就是中断号。在裸机中,直接调取中断源和中断号就直接去操作了,但是在linux系统中,引入了irq_desc数据结构,其中含有已经有注册好的处理函数。
3 最后根据取出来事先注册好的中断处理函数来运行。

上面分析的就是为了说明在驱动中如果要用中断,驱动程序该干嘛。驱动程序有两个作用,第一是实现中断处理程序,第二个是注册中断到linux系统中。
中断处理程序设计步骤为:
这里写图片描述

1 注册中断

这步是在按键中断初始化中进行的,request_irq函数用于注册中断。

int request_irq(unsigned int irq,void (*handler)(int, void*, struct pt_regs *),
unsigned long flags,
const char *devname,
void *dev_id)

参数说明:
unsigned int irq :中断号
void(handler)(int,void ):中断处理函数
unsigned long flags:与中断管理有关的各种选项
const char *devname:设备名
void *dev_id:共享中断时使用
在flags参数中, 可以选择一些与中断管理有关的选项,如:
. IRQF_DISABLED(SA_INTERRUPT) 快速中断
如果设置该位,表示是一个“快速”中断处理程序;如果没有设置该位,那么就是一个“慢速”中断处理程序。
. IRQF_SHARED(SA_SHIRQ) 共享中断该位表明该中断号是多个设备共享的。对于共享中断,dev_id不同对应不同的设备中断,这就是dev_id 的作用。
快/慢速中断的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是“开启中断”标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其他类型的中断仍可以得到服务。

2 中断处理

中断处理函数的原型是
irqreturn_t(int ib_irq, void *dev_id,)
这步是独立定义的,处理程序还包括
1检查设备是否产生了中断
2 清除中断产生的标志
3 相应的硬件操作

3 注销处理

当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们注销, 使用函数:

void free_irq(unsigned int irq, void *dev_id)  

这步是在按键模块卸载时进行的

3 按键驱动程序设计

在Linux中,硬件的初始化通常在两个地方进行。一个是在open函数实现,另外一个是在模块的初始化中实现。习惯性的在模块的初始化中实现硬件的初始化void key_hw_init()。按键的初始化要参考之前裸机程序,硬件原理图以及相关GPIO设置这里先贴上OK6410开发板上的按键硬件原理图部分:
这里写图片描述
这里写图片描述
这里写图片描述
从图中可以看出ok6410的按键中断是通过GPNCON控制寄存器来确定其功能的。
首先定义GPNCON的宏#define GPNCON 0x7f008830,S2按键,也就是KEYINT对应的位为GPN0,设置为10为外部中断。由于在linux中,不能直接使用物理地址,需要先将其转化为虚拟地址,使用的函数是gpio_config=ioremap(GPNCON,4),参数4意为分配给地址的虚拟地址4个字节,从原有寄存器中读取值使用readw(gpio_config)。
在request_irq()注册中,因为当按键按下去的时候触发中断,所以这里把第三个参数设置为下降沿触发中断,IRQF_TRIGGER_FALLING,那么第一个参数中断号irqnumber是怎么获得的呢?
在内核源码irqs.h中可以找到与芯片手册中一一对应的中断号,这个中断号就是注册函数中第一个参数。
这里写图片描述
从图中可以看出,外部中断0—4使用的中断源为INT_EINT0。
这里写图片描述
这里写图片描述
在OK6410的内核源码irqs.h可以看出,中断号需要加上一个偏移,这个偏移是32,当然在2410上也需要加上一个偏移,只不过2410上的偏移是16。这样的方式称为软中断。硬件产生的中断号只是一个序号,和芯片手册上是对应的序号,这个序号需要加上32才成为linux当中的中断号。不过内核代码已经帮我们加上了这个偏移,所以我们在填写参数的时候只需要把相应的中断名放进去就行了,由于这里采用的是S2按键对应的是EINT0中断,所以第一个参数就是IRQ_EINT0
根据以上分析,可以在原有代码上增加中断处理部分,以下为增加按键中断功能后的代码。

#include<linux/module.h>
#include<linux/init.h>
#include<linux/miscdevice.h>
#include<linux/interrupt.h>
#include<linux/io.h>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值