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资料及视频教程点击这里