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()
{
unsigned int *gpio_config;
unsigned int data;
gpio_config = ioremap(GPGCON, 4); // 物理地址到虚拟地址的映射
data = readl(gpio_config); // 读出原有寄存器值
data &= ~0b11;
data |= 0b10; // GPG0对应EINT[8]
writel(data, gpio_config); // 写入寄存器
}
- 然后是中断号的确定,需要参照芯片手册,找到按键对应的IO口,然后查看它相关联的外部中断号,但是这个并不是Linux内核中的中断号,打开内核代码中的Irqs.h选择对应自己芯片的那个文件,可以看到这个一大段的宏:
/* 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)
- Linux系统的中断号=硬件产生的中断序号+基数(2440是16,6410是32)
- mini2440板子中的按键1对应的中断号是EINT8,因此是 IRQ_EINT8
- 设置成下降沿触发: IRQF_TRIGGER_FALLING
- /*注册中断函数*/
- request_irq(IRQ_EINT8, key_init, IRQF_TRIGGER_FALLING, "mykey", 0);
在中断服务函数中,由于这里不是共享中断,所以不需要判断是否是自己的中断了,同时中断号IRQ_EINT8属于芯片级的,系统会自动清除,只有外部设备产生中断是需要清除中断标志位。最终中断服务程序如下:
- key.c
#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
irqreturn_t key_int(int irq, void *dev_id)
{
// 1.检测是否发生了按键中断
// 2.清除已经发生的按键中断
// 3.打印按键值
printk("key down!\n");
return 0;
}
// 按键的初始化函数
void key_hw_init()
{
unsigned int *gpio_config;
unsigned int data;
gpio_config = ioremap(GPGCON, 4); // 物理地址到虚拟地址的映射
data = readl(gpio_config); // 读出原有寄存器值
data &= ~0b11;
data |= 0b10; // GPG0对应EINT8
writel(data, gpio_config); // 写入寄存器
}
int key_open(struct inode *node, struct file *filp)
{
return 0;
}
const struct file_operations key_fops =
{
.open = key_open,
};
// 初始化miscdevice
struct miscdevice key_miscdev =
{
.minor = 200,
.name = "mykey",
.fops = &key_fops,
};
static int button_init()
{
// 注册miscdevice
misc_register(&key_miscdev);
// 注册中断处理程序
request_irq(IRQ_EINT8, key_int, IRQF_TRIGGER_FALLING, "mykey", 0); // IRQF_TRIGGER_FALLING下降沿产生中断
// 按键初始化
key_hw_init();
return 0;
}
static void button_exit()
{
// 注销miscdevice
misc_deregister(&key_miscdev);
// 注销中断处理程序
free_irq(IRQ_EINT8, 0);
}
MODULE_LICENSE("GPL");
module_init(button_init);
module_exit(button_exit);
- Makefile
obj-m := key.o
KDIR := /home/S4_a/part3/lesson3/lesson-2440/linux-mini2440
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
- 编译后把key.ko安装到开饭板中,
- #insmod key.ko
- 按下mini2440中的第一个按键,如果打印出对于信息,说明就编写成功了,但是由于按键的抖动会打印出多个信息,这是需要改进的地方。