一,硬件分析:
1.打开TQ2440的底板原理图找到按键测试的模块,如下图所示:
从图我们知道,控制按键k1 k2 k3 k4 的管脚为EINT1 EINT4 EINT2 EINT0 ,当按键按下时,管脚输出低电平,当按键没有被按下时,管脚输出高电平。
2.打开TQ2440核心板原理图找到EINT1 EINT4 EINT2 EINT0所对应的cpu控制引脚,如下图所示:
从图我们可以知道,EINT1 EINT4 EINT2 EINT0 对应的cpu控制引脚为GPF1 GPF4 GPF2 GPF0 。
3.打开s3c2410的芯片手册查看引脚GPF1 GPF4 GPF2 GPF0 ,如下图所示 :
如图我们知道如何将cpu引脚 GPF1 GPF4 GPF2 GPF0 设置成中断模式,比如我要将GPF0设置成为中断模式,我只需要将其寄存器二进制的第1位和第0位设置成为1和0即可,但是我们只需要知道原理即可,具体将引脚设置成为中断模式,我们可以利用linux内核中的一些宏来实现。
二,按键点亮LED驱动程序源码Button6.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/irq.h> //定义 IRQ_TYPE_EDGE_BOTH
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/wait.h>
#define DEVICE_NAME "button6" //设备名称
#define DEVICE_MAJOR 2898 //主设备号
#define LED_ON 1 //定义LED亮为1
#define LED_OFF 0 //定义LED暗为0
//定义按键中断结构体
struct button_irq
{
int irq; //中断号
int pin; //中断控制引脚
int pin_setting; //中断控制引脚的设置(设置控制引脚为中断模式)
int number; //按键编号
char *name; //按键名称
};
//按键数组,包含4个按键的信息
//S3C2410_GPF0_EINT0在Regs-gpio.h中
static struct button_irq button_irqs [] =
{
{IRQ_EINT1, S3C2410_GPF1, S3C2410_GPF1_EINT1, 0, "KEY1"}, //按键K1
{IRQ_EINT4, S3C2410_GPF4, S3C2410_GPF4_EINT4, 1, "KEY2"}, //按键K2
{IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, 2, "KEY3"}, //按键K3
{IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, 3, "KEY4"}, //按键K4
};
//保存按键值的数组
//volatile关键字意义:每次读和写都是用内存中的值而不是CPU寄存器的值
static volatile int key_values [] = {0, 0, 0, 0};
static unsigned long led_table [] ={ S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8,};//LED控制引脚
//LED控制引脚的设置(设置引脚为输出模式)
static unsigned int led_cfg_table [] ={ S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP,};
//等待队列:
//等待队列头,当应用程序读取按键时,如果此时没有按键按下,程序就休眠
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定义并初始化等待队列
static volatile int ev_press = 0; //按键是否被按的标志(按下和松开都算)
//中断处理函数
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq *button_irqs = (struct button_irq*)dev_id; //获取request_irq注册中断时关联的参数信息
int up = s3c2410_gpio_getpin(button_irqs->pin); //注册中断
if (up)
key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
else
key_values[button_irqs->number] = (button_irqs->number + 1); //根据中断注册情况设置按键的值
ev_press = 1; //表示中断发生了
wake_up_interruptible(&button_waitq); //唤醒休眠的进程
return IRQ_RETVAL(IRQ_HANDLED);//返回中断信息
}
//定义open方法
static int buttons_open(struct inode *inode, struct file *file)
{
int i;
int err;
for (i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting); //设置中断控制引脚为中断模式
err = request_irq(button_irqs[i].irq, buttons_interrupt, NULL, button_irqs[i].name, (void *)&button_irqs[i]); //申请中断
if (err) //返回值为非零值表示申请中断不成功
break;
}
if (err) //申请中断错误处理
{
for (; i >= 0; i--)
{
//释放已经注册的中断
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return -EBUSY; //返回值表示中断已被占用且不能共享
}
//配置LED引脚为输出模式
for (i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
return 0;
}
//定义close方法
static int buttons_close(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++)
{
// 释放已经注册的中断
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return 0;
}
//定义read方法
static int buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err;
//if (!ev_press) //如果没有按键按下
while(!ev_press) //如果没有按键按下
{
if (filp->f_flags & O_NONBLOCK) //如果是非阻塞读取设备,那么程序就直接返回 -EAGAIN
return -EAGAIN; //不阻塞,下次再来read
else
wait_event_interruptible(button_waitq, ev_press); //阻塞读取设备
}
ev_press = 0;
//把按键值的信息从内核空间复制到用户空间
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
memset((void *)key_values, 0, sizeof(key_values));//清零
return err ? -EFAULT : min(sizeof(key_values), count);
}
//select方法
static unsigned int buttons_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); //将等待队列添加到poll_table中
if (ev_press)
mask |= POLLIN | POLLRDNORM; //设备可读的掩码
return mask; //返回设备可读的掩码
}
//定义ioctl方法
static int leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
if(arg > 4)
{
return -EINVAL;
}
switch(cmd)
{
case LED_ON: //如果是点亮
s3c2410_gpio_setpin(led_table[arg], 0);//LED控制引脚输出低电平,此时灯亮
return 0;
case LED_OFF://如果是熄灭
s3c2410_gpio_setpin(led_table[arg], 1);//LED控制引脚输出高电平,此时灯灭
return 0;
default:
return -EINVAL;
}
return 0;
}
//定义file_operations方法
static struct file_operations buttons_fops =
{
.owner = THIS_MODULE,
.open = buttons_open,
.release = buttons_close,
.read = buttons_read,
.poll = buttons_poll,
.ioctl = leds_ioctl,
};
//声明自动创建设备文件的类
static struct class *button_class;
//驱动程序加载函数的实现
static int __init buttons_init(void)
{
int ret;
int i;
printk("TQ2440/SKY2440 LEDS!\n"); //输出初始化信息
ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &buttons_fops); //注册设备
if (ret)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//设备节点文件自动创建的实现
button_class = class_create(THIS_MODULE, DEVICE_NAME);//注册一个类,使mdev可以在"/dev/"目录下面建立设备节点
if(IS_ERR(button_class))
{
printk("Err: failed in Button_leds class. \n");
return -1;
}
device_create(button_class, NULL, MKDEV(DEVICE_MAJOR, 0), NULL, DEVICE_NAME);//创建一个设备节点,节点名为DEVICE_NAME
for(i=0;i<4;++i) //熄灭4盏LED灯
{
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]); //配置LED的控制引脚为输出模式
s3c2410_gpio_setpin(led_table[i],1); //使LED控制引脚输出高电平,灯灭
}
printk(DEVICE_NAME " initialized\n");//打印信息,内核中的打印用printk函数
return 0;
}
//驱动程序卸载函数的实现
static void __exit buttons_exit(void)
{
unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME);//注销设备
device_destroy(button_class, MKDEV(DEVICE_MAJOR, 0)); //删掉设备节点
class_destroy(button_class); //注销类
}
module_init(buttons_init);//驱动模块加载声明,执行“insmod tope-buttons.ko”命令时调用的函数
module_exit(buttons_exit); //驱动模块加载声明,执行“rmmod tope-buttons”命令时调用的函数
MODULE_LICENSE("GPL");//遵循的协议
源码分析: