按键-中断

1、介绍

1、本节来写使用中断的按键程序,使用中断的时候有两个重要的函数,使用"request_irq()"函数来注册一个中断程序,
使用"free_irq"函数来卸载注册的中断服务函数。
2、既然是使用中断,那么就不能像使用轮询的时候那么消耗CPU资源,使用休眠和唤醒的机制
应用程序在读按键引脚状态的时候,如果没有按键按下的使用让程序休眠在这里,使用"wait_event_interruptible"函数
在中断服务程序里唤醒休眠的程序,程序继续往下执行,返回read的读取结果,使用"wake_up_interruptible"函数

2、程序

static struct file_operations key_ops = {
    .owner   =   THIS_MODULE,
 .open    = key_open,
 .read    = key_read,
 .release = key_close,
};

//入口函数
int major;
static int key_init(void)
{
  /* 1、注册字符设备 */
 major = register_chrdev(0, "key_irq", &key_ops);
 /* 2、注册类以及设备 */
 key_class = class_create(THIS_MODULE, "key_irq");
  key_device = class_device_create(key_class, NULL, MKDEV(major,0), NULL,"buttons");
 /* 3、映射地址  把按键要使用的引脚控制寄存器的物理地址映射为虚拟地址*/
 gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
 gpfdat = gpfcon + 1;
 gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
 gpgdat = gpgcon + 1;
  return 0;
 }
 //出口函数
static void key_exit(void)
{
 /* 1、卸载字符设备 */
 unregister_chrdev(major, "key_irq");
  /* 2、卸载设备和类 */
 class_device_unregister(key_device);
 class_destroy(key_class);
  /* 3、取消地址映射 */
 iounmap(gpgcon);
 iounmap(gpfcon);
 }
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");

必要的框架写完之后,来编写驱动函数

//open函数,在该函数里进行中断的注册
static int key_open(struct inode *inode, struct file *file)
{
 /* 注册中断,会自动配置引脚为中断模式 */
 request_irq(IRQ_EINT0,  button_irq,IRQT_BOTHEDGE,"s2",&pins[0]);
 request_irq(IRQ_EINT2,  button_irq,IRQT_BOTHEDGE,"s3",&pins[1]);
  request_irq(IRQ_EINT11, button_irq,IRQT_BOTHEDGE,"s4",&pins[2]);
 request_irq(IRQ_EINT19, button_irq,IRQT_BOTHEDGE,"s5",&pins[3]); 
 return 0;
}

//相反的,在close函数里卸载中断函数
int key_close(struct inode *inode, struct file *file)
{ 
free_irq(IRQ_EINT0,  &pins[0]);
 free_irq(IRQ_EINT2,  &pins[1]);
 free_irq(IRQ_EINT11, &pins[2]);
 free_irq(IRQ_EINT19, &pins[3]);
return 0;
}

//应用程序的读取接口
ssize_t key_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
	 /* 如果没有按键按下,休眠 */
 wait_event_interruptible(button_waitq, ev_press);
  /* 如果有按键按下,直接返回 */
 copy_to_user(buf,&key_val,1);
  ev_press = 0;
 return 1;
}
//中断函数
static irqreturn_t button_irq(int irq,void * dev_id)
{
	struct pin_desc *p = (struct pin_desc*)dev_id;
	unsigned int pin_val;
	pin_val = s3c2410_gpio_getpin(p->pin);
	if (pin_val) {	
	  	key_val = 0x80 | p->key_val; 
	} else { //按下 	  
		  key_val = p->key_val; 
 	}
 	 ev_press = 1;
 	wake_up_interruptible(&button_waitq);
  	return IRQ_RETVAL(IRQ_HANDLED);
}

变量区

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
struct pin_desc {
 unsigned int pin;
 unsigned int key_val;
};
struct pin_desc pins[4] = {
 {S3C2410_GPF0, 0x01},
 {S3C2410_GPF2, 0x02},
 {S3C2410_GPG3, 0x03},
 {S3C2410_GPG11,0x04},
};

应用程序

 while (1) {
  int ret = read(fd,&key,1);
    if (ret < 0) {
    printf("read error\n");
      break;   
   }
    printf("key_val = %d\n",key);
}

3、总结

对上面的程序进行总结,
1、在read函数里,应用程序会来读取此时的引脚状态,如果没有发生中断,也就是没有按键被按下,
此时会调用"wait_event_interruptible(button_waitq, ev_press)"阻塞在那里,这个函数会判断ev_press
如果该值为0,则就会阻塞,这个值初始化为0,也就是一开始会阻塞
2、如果按下按键,进入中断函数,首先会获取传入的参数,这个参数中包含了发生中断的按键的引脚,
获取到引脚值之后使用函数"s3c2410_gpio_getpin"来判断引脚的状态,如果是高电平是处于松开的状态,
对全局变量进行赋值之后,使用"wake_up_interruptible(&button_waitq);"将阻塞队列唤醒,同时"ev_press = 1;",返回
3、在read函数里读取到条件满足,可以继续执行时会从阻塞的地方继续往下执行,全局变量已经在中断函数里赋值过了,
这里直接把变量值返回给应用程序即可。
发布了33 篇原创文章 · 获赞 2 · 访问量 1021

猜你喜欢

转载自blog.csdn.net/weixin_41791581/article/details/103440828