前言
本文基于S3C2440开发板。
一、原理图
二、芯片手册
三、驱动程序
button_drv.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/wait.h>
static struct class *buttons_class;
static struct class_device *buttons_class_devs;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
struct button_irq_desc{
int irq;
unsigned long flags;
char *name;
};
static struct button_irq_desc button_irq[]={
{IRQ_EINT19, IRQF_TRIGGER_FALLING, "KEY1"}, /* K1 */
{IRQ_EINT11, IRQF_TRIGGER_FALLING, "KEY2"}, /* K2 */
{IRQ_EINT2, IRQF_TRIGGER_FALLING, "KEY3"}, /* K3 */
{IRQ_EINT0, IRQF_TRIGGER_FALLING, "KEY4"}, /* K4 */
};
static volatile int press_cnt[]={0,0,0,0};
/* 等待队列:
* 当没有按键被按下时,如果有进程调用s3c2440_buttons_read函数,
* 它将休眠
*/
/* 中断事件标志, 中断服务程序将它置1,s3c2440_buttons_read将它清0 */
static volatile int ev_press = 0;
static irqreturn_t button_interrupt(int irq,void *dev_id)
{
volatile int *press_cnt=(volatile int *)dev_id;
*press_cnt = *press_cnt +1;
ev_press=1;
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c2440_button_open(struct inode *inode, struct file *file)
{
int i;
int err;
for(i=0;i<sizeof(button_irq)/sizeof(button_irq[0]);i++)
{
err=request_irq(button_irq[i].irq,button_interrupt,
button_irq[i].flags,button_irq[i].name,(void *)&press_cnt[i]); //注册进内核的异常体系结构中
if (err)
break;
}
if (err)
{
i--;
for(;i>0;i--)
{
free_irq(button_irq[i].irq,(void *)&press_cnt[i]);
}
return -EBUSY;
}
return 0;
}
static int s3c2440_button_close(struct inode *inode, struct file *file)
{
int i;
for(i=0;i<sizeof(button_irq)/sizeof(button_irq[0]);i++)
{
free_irq(button_irq[i].irq,(void *)&press_cnt[i]);
}
return 0;
}
static ssize_t s3c2440_button_read(struct file *file, char __user *buff, size_t size, loff_t *ppos)
{
unsigned long err;
/* 如果ev_press等于0,休眠 */
wait_event_interruptible(button_waitq, ev_press);
ev_press=0;
err=copy_to_user(buff,(const void*)press_cnt,min(sizeof(press_cnt),size));
memset((void *)press_cnt,0,sizeof(press_cnt));
return err ? -EFAULT : 0;
}
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = s3c2440_button_open,
.read = s3c2440_button_read,
.release = s3c2440_button_close,
};
int major;
static int s3c2440_button_init(void)
{
major = register_chrdev(0, "button_drv", &button_drv_fops ); // 注册, 告诉内核
buttons_class = class_create(THIS_MODULE, "buttondrv");
buttons_class_devs = class_device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "buttons");
return 0;
}
static void s3c2440_button_exit(void)
{
unregister_chrdev(major, "button_drv"); // 卸载
class_destroy(buttons_class);
}
module_init(s3c2440_button_init);
module_exit(s3c2440_button_exit);
MODULE_LICENSE("GPL");
makefile文件:
KERN_DIR = /home/book/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += button_drv.o
/home/book/system/linux-2.6.22.6 编译过的内核目录
四、驱动程序的测试
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int i;
int ret;
int fd;
int press_cnt[4];
fd = open("/dev/buttons", 0); // 打开设备
if (fd < 0) {
printf("Can't open /dev/buttons\n");
return -1;
}
// 这是个无限循环,进程有可能在read函数中休眠,当有按键被按下时,它才返回
while (1) {
// 读出按键被按下的次数
ret = read(fd, press_cnt, sizeof(press_cnt));
if (ret < 0) {
printf("read err!\n");
continue;
}
for (i = 0; i < sizeof(press_cnt)/sizeof(press_cnt[0]); i++) {
// 如果被按下的次数不为0,打印出来
if (press_cnt[i])
printf("K%d has been pressed %d times!\n", i+1, press_cnt[i]);
}
}
close(fd);
return 0;
}
五、结果分析