Linux按键驱动程序设计(3)-按键操作硬件实现

1、复习裸机按键操作驱动

#define GPGCON  (volatile unsigned long *)0x56000060

/*
 * K1,K2,K3,K4对应GPG0、GPG3、GPG5、GPG6
 */
#define GPG0_int     (0x2<<(0*2))
#define GPG3_int     (0x2<<(3*2))
#define GPG5_int     (0x2<<(5*2))
#define GPG6_int     (0x2<<(6*2))

#define GPG0_msk    (3<<(0*2))
#define GPG3_msk    (3<<(3*2))
#define GPG5_msk    (3<<(5*2))
#define GPG6_msk    (3<<(6*2))

void button_init()
{   
    	*(GPGCON) &= ~(GPG0_msk | GPG3_msk | GPG5_msk | GPG6_msk);
	*(GPGCON) |= GPG0_int | GPG3_int | GPG5_int | GPG6_int;
}

有裸机驱动代码可以知道,首先对按键IO口的状态进行清零(设置为保留状态,具体要看芯片数据手册),然后把IO口设置为中断模式。

 

 

2、Linux驱动程序中实现

一般来讲硬件初始化函数可以在2个地方完成,一个是模块初始化函数,另一个设备文件打开函数。在哪里实现没有明确的规定,我们在模块初始化中完成。

/*按键的硬件初始化函数*/
void key_hw_init(void)
{
	unsigned int *gpio_config;
	unsigned short data;

	//物理地址到虚拟地址的映射
	gpio_config = ioremap(GPGCON, 4);
	//为了不破坏原有寄存器的值
	data = readw(gpio_config);
	data &= ~0x03;
	data |= 0x02;

	//写入寄存器中
	writew(data, gpio_config);
}

然后是中断号的确定,需要参照芯片手册,找到按键对应的IO口,然后查看它相关联的外部中断号,但是这个并不是Linux内核中的中断号,打开内核代码中的Irqs.h选择对应自己芯片的那个文件,可以看到这个一大段的宏:

#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)

/* main cpu interrupts */
#define IRQ_EINT0      S3C2410_IRQ(0)	    /* 16 */
#define IRQ_EINT1      S3C2410_IRQ(1)
#define IRQ_EINT2      S3C2410_IRQ(2)
#define IRQ_EINT3      S3C2410_IRQ(3)
#define IRQ_EINT4t7    S3C2410_IRQ(4)	    /* 20 */
#define IRQ_EINT8t23   S3C2410_IRQ(5)
#define IRQ_RESERVED6  S3C2410_IRQ(6)	    /* for s3c2410 */
#define IRQ_CAM        S3C2410_IRQ(6)	    /* for s3c2440,s3c2443 */
#define IRQ_BATT_FLT   S3C2410_IRQ(7)
#define IRQ_TICK       S3C2410_IRQ(8)	    /* 24 */
#define IRQ_WDT	       S3C2410_IRQ(9)	    /* WDT/AC97 for s3c2443 */
#define IRQ_TIMER0     S3C2410_IRQ(10)
#define IRQ_TIMER1     S3C2410_IRQ(11)
#define IRQ_TIMER2     S3C2410_IRQ(12)
#define IRQ_TIMER3     S3C2410_IRQ(13)
#define IRQ_TIMER4     S3C2410_IRQ(14)
#define IRQ_UART2      S3C2410_IRQ(15)
#define IRQ_LCD	       S3C2410_IRQ(16)	    /* 32 */
#define IRQ_DMA0       S3C2410_IRQ(17)	    /* IRQ_DMA for s3c2443 */
#define IRQ_DMA1       S3C2410_IRQ(18)
#define IRQ_DMA2       S3C2410_IRQ(19)
#define IRQ_DMA3       S3C2410_IRQ(20)
#define IRQ_SDI	       S3C2410_IRQ(21)
#define IRQ_SPI0       S3C2410_IRQ(22)
#define IRQ_UART1      S3C2410_IRQ(23)
#define IRQ_RESERVED24 S3C2410_IRQ(24)	    /* 40 */
#define IRQ_NFCON      S3C2410_IRQ(24)	    /* for s3c2440 */
#define IRQ_USBD       S3C2410_IRQ(25)
#define IRQ_USBH       S3C2410_IRQ(26)
#define IRQ_IIC	       S3C2410_IRQ(27)
#define IRQ_UART0      S3C2410_IRQ(28)	    /* 44 */
#define IRQ_SPI1       S3C2410_IRQ(29)
#define IRQ_RTC	       S3C2410_IRQ(30)
#define IRQ_ADCPARENT  S3C2410_IRQ(31)

mini2440板子中的按键1对应的中断号是EINT8,因此是 IRQ_EINT8(这里是s3c2410的芯片有点不一样,因此不是IRQ_EINT8t23),因此中断注册函数应该这么写,这里中断触发设置成下降沿触发

/*注册中断函数*/
request_irq(IRQ_EINT8, key_interupter, IRQF_TRIGGER_FALLING, "key", 0);

 

在中断服务函数中,由于这里不是共享的设备号,所以不需要判断是否是自己的中断了,同时中断号IRQ_EINT8属于芯片级的,系统会自动清除,只有外部设备产生中断是需要清除中断标志位。最终中断服务程序如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>

#define GPGCON  	0x56000060

/*按键的硬件初始化函数*/
void key_hw_init(void)
{
	unsigned int *gpio_config;
	unsigned short data;

	//物理地址到虚拟地址的映射
	gpio_config = ioremap(GPGCON, 4);
	//为了不破坏原有寄存器的值
	data = readw(gpio_config);
	data &= ~0x03;
	data |= 0x02;

	//写入寄存器中
	writew(data, gpio_config);
}
/*按键中断服务函数*/
irqreturn_t key_interupter(int irq, void *dev_id)
{
	//检测设备是否产生中断
	
	//清除中断标志位(处理器级别的标志位系统会自动清除)

	//打印信息
	printk("key down!\n");
	return 0;
}

/*设备文件打开操作*/
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(void)
{
	/*注册混杂设备*/
	misc_register(&key_miscdev);

	/*注册中断函数*/
	request_irq(IRQ_EINT8, key_interupter, IRQF_TRIGGER_FALLING, "key", 0);

	/*初始化硬件*/
	key_hw_init();
	return 0;
}

static void key_exit(void)
{
	/*注销设备*/
	misc_deregister(&key_miscdev);

	/*注销中断服务程序*/
	free_irq(IRQ_EINT8, 0);
}


MODULE_LICENSE("GPL");

module_init(key_init);
module_exit(key_exit);


Makefile:

obj-m := key.o

KDIR := /home/unix/NO.3/2-Linux/linux-mini2440

all:
	make -C $(KDIR) M=$(PWD) modules CROSS_CIMPILE=arm-linux- ARCH=arm

clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order


编译后把key.ko安装到开饭板中,

#insmod key.ko

按下mini2440中的第一个按键,如果打印出对于信息,说明就编写成功了,但是由于按键的抖动会打印出多个信息,这是需要改进的地方。

更多Linux资料及视频教程点击这里

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值