说明:
平台设备:正点原子IMX6ULL
第一部分:
修改设备树,在跟节点下添加key设备:
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
status = "okay";
};
第二部分:
irqkeytasklet.c 驱动文件:
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define KEY_CNT 1
#define KEY_NAME "irqkeytasklet"
atomic_t key_status = ATOMIC_INIT(0); //定义整型原子变量, 保存按键状态, 设置初始值为0
struct tasklet_struct btn_tasklet; //使用tasklet实现中断分层,在此申请tasklet_struct结构体变量
struct key_dev{
dev_t devid; //设备号
struct cdev cdev; //字符设备
struct class *class; //类
struct device *device; //设备
int major; //主设备号
int minor; //次设备号
struct device_node *nd; //设备节点
int gpio_number;
int irq_number;
};
struct key_dev irqkey;
void btn_tasklet_handler(unsigned long data) //中断下半部分
{
int counter = 1;
mdelay(200);
printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
mdelay(200);
printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
mdelay(200);
printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
mdelay(200);
printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
mdelay(200);
printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
}
static irqreturn_t irqkey_hander(int irq, void *dev_id)
{
printk(KERN_ERR "button_irq_hander----------inter \n");
atomic_inc(&key_status); //按键状态加1
tasklet_schedule(&button_tasklet); //触发中断下半部分
printk(KERN_ERR "button_irq_hander----------exit \n");
return IRQ_HANDLED;
}
static int irqkey_open(struct inode *inode, struct file *filp)
{
int error = -1;
irqkey.nd = of_find_node_by_path("/key"); //获取按键设备树节点
if(NULL == irqkey.nd)
{
printk("of_find_node_by_path error!");
return -1;
}
irqkey.gpio_number = of_get_named_gpio(irqkey.nd, "key-gpio", 0); //获取按键所使用的GPIO
if(0 == irqkey.nd)
{
printk("of_get_named_gpio error!");
return -1;
}
error = gpio_request(irqkey.gpio_number, "irqkey_gpio"); //申请GPIO
if(error != 0)
{
printk("gpio_request error");
gpio_free(irqkey.gpio_number);
return -1;
}
gpio_direction_input(irqkey.gpio_number); //引脚设置, 将引脚设置为输入
irqkey.irq_number = irq_of_parse_and_map(irqkey.nd, 0); //获取引脚中断号
error = request_irq(irqkey.irq_number, irqkey_hander, IRQF_TRIGGER_RISING, "irq_key", &irqkey); //申请中断
if(error != 0)
{
printk("request_irq error");
free_irq(irqkey.irq_number, &irqkey);
return -1;
}
tasklet_init(&btn_tasklet, btn_tasklet_handler, 0); //tasklet初始化
return 0;
}
static ssize_t irqkey_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int error = -1;
int button_countervc = 0;
button_countervc = atomic_read(&key_status); //读取按键状态值
error = copy_to_user(buf, &button_countervc, sizeof(button_countervc)); //拷贝到用户空间
if(error < 0)
{
printk("copy_to_user error");
return -1;
}
atomic_set(&key_status, 0); //清零按键状态值
return 0;
}
static ssize_t irqkey_release(struct inode *inode, struct file *filp)
{
gpio_free(irqkey.gpio_number); //释放引脚
free_irq(irqkey.irq_number, &irqkey); //释放中断
return 0;
}
static struct file_operations irqkey_fops = {
.owner = THIS_MODULE,
.open = irqkey_open,
.read = irqkey_read,
.release = irqkey_release,
};
static int __init irqkey_init(void)
{
if(irqkey.major)
{
irqkey.devid = MKDEV(irqkey.major, 0);
register_chrdev_region(irqkey.devid, KEY_CNT, KEY_NAME);
}
else
{
alloc_chrdev_region(&irqkey.devid, 0, KEY_CNT, KEY_NAME);
irqkey.major = MAJOR(irqkey.devid);
irqkey.minor = MINOR(irqkey.devid);
}
printk("irqkey major = %d, minor = %d \r\n", irqkey.major, irqkey.minor);
irqkey.cdev.owner = THIS_MODULE; //初始化字符设备并向Linux内核添加
cdev_init(&irqkey.cdev, &irqkey_fops);
cdev_add(&irqkey.cdev, irqkey.devid, KEY_CNT);
irqkey.class = class_create(THIS_MODULE, KEY_NAME); //创建设备类
if(IS_ERR(irqkey.class))
{
return PTR_ERR(irqkey.class);
}
irqkey.device = device_create(irqkey.class, NULL, irqkey.devid, NULL, KEY_NAME); //创建设备
if(IS_ERR(irqkey.device))
{
return PTR_ERR(irqkey.device);
}
return 0;
}
static void __exit irqkey_exit(void)
{
cdev_del(&irqkey.cdev); //注销字符设备
unregister_chrdev_region(irqkey.devid, KEY_CNT); //注销设备号
device_destroy(irqkey.class, irqkey.devid); //注销设备节点
class_destroy(irqkey.class); //注销设备类
}
module_init(irqkey_init);
module_exit(irqkey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LMENG");
第三部分:
Makefile 文件:
KERNELDIR := /home/lmeng/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := irqkey.o
build : kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
第四部分:
irqkeyApp.c 文件:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int error = -20;
int button_status = 0;
int fd;
char *filename;
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0)
{
printf("can‘t open file %s\r\n", filename);
return -1;
}
printf("wait button down... \n");
printf("wait button down... \n");
do
{
error = read(fd, &button_status, sizeof(button_status)); //读取按键状态
if (error < 0)
{
printf("read file error! \n");
}
usleep(1000 * 100);
} while (0 == button_status);
printf("button Down !\n");
error = close(fd);
if (error < 0)
{
printf("close file error! \n");
}
return 0;
}
第五部分:
实验结果: