一、按下按键(中断框架requst_irq)
1、CPU进入异常模式b vector_irq + offset
2、调用irq_user
3、b
asm_do_IRQ
4、irq_desc[irq]
-> handle_irq
5、handle_edge_irq :
① desc -> chip
-> ack(irq) :清中断
②
hadle_IRQ_event :处理中断
取出action链表中的成员,执行action -> handler
(1)发生中断时,CPU执行异常向量 vector_irq 的代码。
(2)在 vector_irq 里面,最终会调用中断处理的总入口函数asm_do_IRQ。
(3)asm_do_IRQ 根据中断号调用 irq_desc 数组项中的handle_irq。
(4)handle_irq 会使用chip成员中的函数来设置硬件。
(5)handle_irq 逐个调用用户在 action 链表中注册的处理函数。
二、注册中断处理程序
int
request_irq(unsigned int
irq, //中断号
irq_handler_t
handler, //处理函数
unsigned long
irqflags, //触发方式
const char
*devname, //函数名
void
*dev_id) //设备号
(Manage.c
(kernel\irq))
{
① 分配irqaction结构,该结构中的成员指向传入的参数
② setup_irq(irq, action);
a、找到 irq_desc[irq]
在 irq_desc[irq] ->
action 链表里加入传入参数
b、desc->chip->set_type //将对应的引脚设置为中断引脚
c、desc->chip->startup(irq);/desc->chip->enable(irq); //使能中断
}
三、卸载中断处理程序
free_irq(irq , *dev_id)
出链,禁止中断
四、驱动程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static
struct class *thirddrv_class;
static
struct
class_device *thirddrv_class_dev;
volatile
unsigned long *gpfcon;
volatile
unsigned long *gpfdat;
volatile
unsigned long *gpgcon;
volatile
unsigned long *gpgdat;
static
DECLARE_WAIT_QUEUE_HEAD(button_waitq);
//定义并初始化等待队列头部,DECLARE_WAIT_QUEUE_HEAD(name);
static volatile int ev_press = 0;
struct
pin_desc{
unsigned int pin;
unsigned int key_val;
};
static
unsigned char key_val;
struct
pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
};
static
irqreturn_t buttons_irq(int irq, void
*dev_id) //中断处理函数
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(pindesc->pin);
//系统函数,能够读出引脚值
if (pinval)
{
key_val = 0x80 | pindesc->key_val;
}
else
{
key_val = pindesc->key_val;
}
ev_press =
1;
wake_up_interruptible(&button_waitq);
//唤醒队列,void
wake_up_interruptible(wait_queue_head_t
*queue);唤醒以queue作为等待列头部的队列中所有的进程。与wait_event_interruptuble()和wait_event_interruptuble_timeout()成对使用。
return
IRQ_RETVAL(IRQ_HANDLED); // 接收到了准确的中断信号,并且作了相应正确的处理
//中断处理例程应当返回一个值指示是否真正处理了一个中断。如果处理例程发现设备确实需要处理,应当返回IRQ_HANDLED;否则返回值IRQ_NONE。
}
static int
third_drv_open(struct inode *inode, struct file *file)
{
//
有中断请求时,会自动将能设置为中断模式的引脚设为中断模式,所以这里不用配置引脚模式,都自动配置成了中断模式11
request_irq(IRQ_EINT0, buttons_irq,
IRQT_BOTHEDGE, "S2", &pins_desc[0]);
request_irq(IRQ_EINT2, buttons_irq,
IRQT_BOTHEDGE, "S3", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4",
&pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5",
&pins_desc[3]);
//buttons_irq是中断处理函数,所以我们需要在驱动里边去实现这个函数
//request_irq(unsigned int irq, irq_handler_t handler, unsigned
long flags, const char *name,void *dev);
// irq是要申请的硬件中断号;handler是向系统登记的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev参数将被传递给它;flags是中断处理的属性,可以指定中断的触发方式以及处理方式,IRQT_BOTHEDGE表示双边沿触发。
return 0;
}
ssize_t
third_drv_read(struct file *file, char __user *buf, size_t size,
loff_t *f_pos)
filp是文件结构体指针;buf是用户空间内存的地址,该地址在内核空间不宜直接读写;count是要读的字节数;f_pos是读的位置相对于文件开头的偏移
{
if (size != 1)
return -EINVAL;
wait_event_interruptible(button_waitq,
ev_press);
//阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。wait_event_interruptible(queue,condition);queue作为等待队列头部的队列被唤醒,condition必须满足,否则继续阻塞。
copy_to_user(buf, &key_val,
1);
//从内核去中读取数据到用户区
//unsigned long copy_to_user(void _user *to,
const void *from, unsigned long
n);如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。*to是用户空间的指针,*from是内核空间指针,n表示从内核空间向用户空间拷贝数据的字节数
ev_press =
0; //清零
return 1;
}
int
third_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0,
&pins_desc[0]); //释放中断,参数分别为中断号和引脚描述
free_irq(IRQ_EINT2,
&pins_desc[1]); //free_irq(IRQ_EINT0, &pins_desc[0]);表示释放对应的中断
free_irq(IRQ_EINT11, &pins_desc[2]);
free_irq(IRQ_EINT19, &pins_desc[3]);
return 0;
}
static
struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE,
.open = third_drv_open,
.read = third_drv_read,
.release = third_drv_close,
};
int
major;
static int
third_drv_init(void)
{
major = register_chrdev(0, "third_drv",
&sencod_drv_fops);
//
register_chrdev(MEM_MAJOR,"mem",&memory_fops),向内核注册了一个字符设备。第一个参数是主设备号,0代表动态分配。第二个参数是设备的名字,第三个参数是文件操作指针。
thirddrv_class = class_create(THIS_MODULE, "third_drv");
thirddrv_class_dev = class_device_create(thirddrv_class, NULL,
MKDEV(major, 0), NULL, "buttons");
//在驱动初始化的代码,调用内核的class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用class_device_create
(…)函数来在/dev目录下创建相应的设备节点。
gpfcon = (volatile unsigned long *)ioremap(0x56000050,
16); //地址映射
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060,
16);
gpgdat = gpgcon + 1;
return 0;
}
static
void third_drv_exit(void)
{
unregister_chrdev(major, "third_drv");
class_device_unregister(thirddrv_class_dev);
class_destroy(thirddrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return 0;
}
module_init(third_drv_init);
module_exit(third_drv_exit);
MODULE_LICENSE("GPL");
五、用户程序:
#include
#include
#include
#include
#include
int
main(int argc, char **argv)
{
int fd;
unsigned char key_val;
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
while (1)
{
//read(fd, &key_val, 1);
//printf("key_val = 0x%x\n", key_val);
sleep(5);
}
return 0;
}