目录
硬件: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