按键中断只驱动编程之逐步改进

本文详述了按键驱动编程的进化过程,从查询方式获取按键值到利用中断处理、等待队列、POLL机制、异步通知、同步互斥阻塞等方法。介绍了各种方式的优缺点,如查询方式耗CPU资源,中断处理配合等待队列仍有读取等待,POLL机制避免阻塞但仍有超时等待,异步通知则由驱动程序主动触发读取。同时探讨了原子操作、信号量、自旋锁、中断屏蔽等实现互斥的技术,并讨论了按键消抖的必要性和定时器的解决方案。
摘要由CSDN通过智能技术生成

目录

硬件操作

1,看原理图:查找引脚定义。

2,设置 4 个引脚为输入引脚:

1.查询方式获取按键值

驱动

应用

Makefile

测试总结

2.按键中断处理+等待队列

驱动

应用

3.设备驱动-POLL 机制

驱动

应用

4.设备驱动异步通知

驱动

应用

5.字符设备驱动 同步互斥阻塞

1. 原子操作

驱动

应用

2. 信号量

驱动

应用

3.自旋锁

4.中断屏蔽

几种方式总结


 

 

硬件:JZ2440

系统:linux2.6.22

硬件操作

1,看原理图:查找引脚定义。

所以可以知道:

S2---->EINT0---->GPF0

S3---->EINT2---->GPF2

S4---->EINT11---->GPG3

S5---->EINT19---->GPF11

2,设置 4 个引脚为输入引脚:

从上面的原理图可以看到,低电平要从开关接地端输入。

要有电流流过,就是有压差,从上面的原理图上知道, KEY 一端接了高电平,当 KEY 没按下时,这个 KEY 接 2440 的引脚都是高电平。当 KEY 按键按下,就接通了地,这时这些引脚就返回 0 。

 

 

1.查询方式获取按键值

驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

#include <linux/miscdevice.h>
#include <linux/device.h>
/*2.操作key*/
/*定义配置寄存器和数据寄存器指针*/
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

volatile unsigned long *gpgcon = NULL;
volatile unsigned long *gpgdat = NULL;
/*2.1查看原理图和芯片数据手册*/
/*
S2---->EINT0---->GPF0----->[1:0]
S3---->EINT2---->GPF2----->[5:4]
S4---->EINT11---->GPG3----->[7:6]
S5---->EINT19---->GPF11----->[23:22]
GPFCON---->0x56000050------->00 = intput    
GPFDAT---->0x56000054------->0为高有效

GPGCON---->0x56000060------->00 = intput    
GPGDAT---->0x56000064------->0为高有效
*/
/*1.5文件操作方法集合具体实现*/
static int second_open(struct inode *pinode, struct file *pfile)
{
	/*2.3配置配置寄存器为输入*/
	/*先清零*/
	*gpfcon &= ~((0x3 << (0*2)) | (0x3 << (2*2)));
	*gpgcon &= ~((0x3 << (3*2)) | (0x3 << (11*2)));
	
	return 0;
}
static ssize_t second_read(struct file *pfile, char __user *buf, size_t size, loff_t *poff)
{
	unsigned char key_val[4];
	int regval;
	if(size != sizeof(key_val))
	{
		return -EINVAL;
	}
	regval = *gpfdat;
	key_val[0] = (regval & (1<<0))?1:0;
	key_val[1] = (regval & (1<<2))?1:0;

	regval = *gpgdat;
	key_val[2] = (regval & (1<<3))?1:0;
	key_val[3] = (regval & (1<<11))?1:0;
	copy_to_user(buf, key_val, sizeof(key_val));

	
	return sizeof(key_val);
}

#define SECOND_NAME "second_name"
struct class *second_class;
struct class_device *second_device_class;
/*1.3文件操作方法集合*/
static const struct file_operations second_chrdev_fops  =
{
	.owner 		= THIS_MODULE,
	.open 		= second_open,
	.read	    = second_read,
	
};


/*1.首先写好驱动摸版*/
/*注:
编译成模块时module_init和module_exit
编译到内核时: __init和__exit起作用
*/
int major;
static int __init second_chrdev_init(void)
{

	/*1.1注册杂项设备*/
	//早期经典字符设备,需手动创设备节点,一个主设备号只可以注册一次
	/*
	主设备号,当 major 传递 0 时候表示由内核自动分配一个可用的主设备号.
	设备名,不需要和/dev 下对应节点名相同
	早期经典字符设备需要手动创建设备
	如果应用使用的是的/dev/chrdev_name
	mknod /dev/chrdev_name c 252 0
  或mknod /dev/chrdev_name c 252 1
  或mknod /dev/chrdev_name c 252 x
但是mknod /dev/chrdev_name c 250 x
一旦主设备号不是252,即是节点名相同都是open fail
结论:应用程序寻找程序程序不是通过设备名,而是通过设备号
	*/
	/*
	注:这里的节点名不一定要和/dev下一致,这里注册是/proc/devices下的名字
	一旦注册成功改主设备下的次设备号全部用完
	*/
	major = register_chrdev(0, SECOND_NAME, &second_chrdev_fops);
	printk("<0>major = %d\n",major);
	/*
	自动创建设备文件结点
	类的所有者, 固定是 THIS_MODULE
	类名,随便,能有含义最好, 不是 /dev/ 下设备的名字。这个名字决定了/sys/class/name
	*/
	second_class = class_create(THIS_MODULE, "second");
	/* /dev/xyz */
	second_device_class = class_device_create(second_class, NULL, MKDEV(major, 0), NULL, "second_device");
	
	/*2.2将物理地址和虚拟地址进行映射*/
	//用ioremap(开始地址  结束大小)
	/*
	先映射gpfcon为虚拟地址,F寄存器:gpfcon:0x56000050 gpfcon:0x56000054  
	gpfcon:0x56000058  Reserved:0x5600005c,2440为32位CPU,所以4个4字节为16细字节,
	故这里映射16字节
	*/
	gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
	//这里指针+1是以上面unsigned long为单位的
	gpfdat = gpfcon + 1;

	gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
	//这里指针+1是以上面unsigned long为单位的
	gpgdat = gpgcon + 1;
	return 0;
}

static void __exit second_chrdev_exit(void)
{
	/*1.2注销杂项设备*/
	unregister_chrdev(major, SECOND_NAME);
	class_device_unregister(second_device_class);
	class_destroy(second_class);
	iounmap(gpfcon);
	iounmap(gpgcon);
}


module_init(second_chrdev_init);//模块安装时执行初始化函数
module_exit(second_chrdev_exit);//模块卸载时执行卸载函数
MODULE_LICENSE("GPL");

 

应用

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SECOND_DEV "/dev/second_device"
int main(int argc, char **argv)
{
	int fd = -1;
	int val = 0;
	unsigned char key_val[4];
	int cnt  = 0;

	fd = open(SECOND_DEV, O_RDWR);
	if(fd < 0)
	{
		printf("open fial\n");
		return -1;
	}

	while(1)
	{
		read(fd, key_val, sizeof(key_val));
		if(!key_val[0] || !key_val[1] || !key_val[2] || !key_val[3])
		{
			printf("%04d press is: key_val[0]:%d,key_val[1]:%d,key_val[2]:%d,key_val[3]:%d\n",cnt++,
				key_val[0],key_val[1],key_val[2],key_val[3]);
		}
		sleep(1);
	}
	
	
	return 0;
}




Makefile

KERN_DIR = /work/system/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	arm-linux-gcc second_test.c -o second_test
	cp second_test *.ko  /work/nfs_root/fs_mini_mdev

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= second_drv.o

测试总结

按键时间要足够长才会检测到,短按检测不到

抖动的很厉害。查询的按键方式因为里面有一个死循环 while,会占用 CPU 资源:占用率接近100%

 

2.按键中断处理+等待队列

 

驱动

//1.首先添加头文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/irq.h>

int major = 0;
#define THIRD_DRV_NAME "third_drv_name"

static struct class* third_class;
static struct class_device* third_device;

/*2.操作key*/
/*定义配置寄存器和数据寄存器指针*/
/*2.1查看原理图和芯片数据手册*/
/*
S2---->EINT0---->GPF0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值