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()
{
    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中的第一个按键,如果打印出对于信息,说明就编写成功了,但是由于按键的抖动会打印出多个信息,这是需要改进的地方。
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值