7.Linux按键驱动-定时器

1.定时器内核函数

2.思路

bash 复制代码
在probe函数中,设置/添加定时器,设置超时时间为无穷。
在按键中断程序中,将超时时间设置为较小的值。
当超时时间到的时候,会调用按键的中断处理函数。

3.编程

3.1 在按键结构体中,添加timer_list成员

3.2在probe函数中,设置定时器,修改定时器超时时间,添加定时器。

3.3在按键中断函数中,修改定时器超时时间

3.4在超时函数中,唤醒等待队列,执行原来中断服务程序执行的那些任务

4.程序

应用:

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>

static int fd;

/* 信号处理函数 */
/*static void sig_func(int sig)
{
	int val;
	int num;
	read(fd,&val,4);
	num  = val >>8;
	printf("get button: %d val = %d\n", num, val - num *(1 << 8));
}*/

int main(int argc, char** argv)
{
	
	int val;
	int num;
	struct pollfd fds[1];
	int timeout_ms = 5000;
	int ret;
	int flags;
	int i;
	/* 判断输入值是否合法 */
	if(argc != 2)
	{
		printf("Usage ./button_app /dev/devname\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd == -1)
	{
		printf("open err\n");
	}
	else
	{
		printf("open success\n");
	}

	/* 初始化文件监听描述符 */
	/*fds[0].fd     = fd;
	fds[0].events = POLLIN;*/

	/*绑定SIGIO和sig_func */
	//signal(SIGIO, sig_func);
	
	/* APP把自己的pid告诉驱动 */
	//fcntl(fd, F_SETOWN, getpid());/* APP把自己的pid告诉驱动程序 */
	//flags = fcntl(fd,F_GETFL);/* 获得标志位 */
	//fcntl(fd, F_SETFL, flags | FASYNC);/* 使能异步通知 */
	//printf("设置了FASYNC\n");
	/*while(1)
	{
		printf("www.100ask.net \n");
		sleep(2);
	}*/
	
	
	/*while(1)
	{
		ret = poll(fds, 1, timeout_ms);
		if((ret == 1) && (fds[0].revents == POLLIN))
		{
			read(fd, &val, 4);
			num  = val >>8;
			printf("get button: %d val = %d\n", num, val - num *(1 << 8));
		}
		else
		{
			printf("timeout\n");
		}
	}*/

	
	for (i = 0; i < 10; i++)
	{
		if (read(fd, &val, 4) == 4)
			printf("get button: 0x%x\n", val);
		else
			printf("get button: -1\n");
	}


	/* 设置打开方式为阻塞 */
	flags = fcntl(fd,F_GETFL);
	fcntl(fd,F_SETFL,flags & ~O_NONBLOCK);
	while(1)
	{
		if (read(fd, &val, 4) == 4)
		{
			num  = val >>8;
			printf("get button: %d val = %d\n", num, val - num *(1 << 8));
		}
		else
			printf("get button: -1\n");
	}


	
	close(fd);
	return 0;
}

驱动:

c 复制代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>


/* 定义结构体来描述gpio */
struct gpio_key{
	int gpio;
	struct gpio_desc* gpiod;
	int flag;
	int irq;
	struct timer_list key_timer;
};

/* 定义全局变量来存储设备树中的所有gpio节点信息 */
static struct gpio_key* gpio_keys_100ask;

/* 字符设备的主设备号 */
static unsigned int major = 0;
static struct class *gpio_class;
//static int g_key = 0;

/* 定义等待队列 */
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
struct fasync_struct * button_fasync;




/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

int is_key_buf_empty(void)
{
	return (r == w);
}

int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}

static ssize_t gpio_read(struct file *fp, char __user *buf, size_t size, loff_t * offset)
{
	int err;
	int key;
	if(is_key_buf_empty() && (fp->f_flags & O_NONBLOCK))
	{
		return -EAGAIN;
	}
	wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
	key = get_key();
	//err = copy_to_user(buf, &g_key, 4);
	err = copy_to_user(buf, &key, 4);
	//g_key = 0;
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 4;
}

static unsigned int gpio_poll(struct file *fp, struct poll_table_struct *wait)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_key_wait, wait);
	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_fasync(int fd , struct file *file, int on)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	if (fasync_helper(fd, file, on, &button_fasync) >= 0)
	{
		return 0;
	}
	else
	{
		return -EIO;
	}
}




static const struct file_operations gpio_fops = {
	.owner = THIS_MODULE,
	.read  = gpio_read,
	.poll  = gpio_poll,
	.fasync = gpio_fasync,
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key* gpio_key = dev_id;
	//int val;
	//int key;
	//val = gpio_get_value(gpio_key->gpio);
	//printk("key %d %d\n", gpio_key->gpio, val);
	//g_key = (gpio_key->gpio << 8) | val;
	//key = (gpio_key->gpio << 8) | val;
	
	//put_key(key);
	//wake_up_interruptible(&gpio_key_wait);

	/* 发信号 */
	//kill_fasync(&button_fasync, SIGIO, POLL_IN);
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 修改定时器超时时间 */
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	mod_timer(&gpio_key->key_timer, jiffies + HZ / 5);
	return IRQ_HANDLED;
}

/* 定时器超时函数 */
static void key_timer_expire(unsigned long arg)
{
	struct gpio_key* gpio_key = arg;
	int val;
	int key;
	val = gpio_get_value(gpio_key->gpio);
	//printk("key %d %d\n", gpio_key->gpio, val);
	//g_key = (gpio_key->gpio << 8) | val;
	key = (gpio_key->gpio << 8) | val;
	
	put_key(key);
	wake_up_interruptible(&gpio_key_wait);

	/* 发信号 */
	kill_fasync(&button_fasync, SIGIO, POLL_IN);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 修改定时器超时时间 */
	//mod_timer(&gpio_key->key_timer, jiffies + HZ / 5);
}

static int gpio_probe(struct platform_device *pdev)
{
	int count, i;
	struct device_node *node;
	int err;

	node = pdev->dev.of_node;
	count = of_gpio_count(node);
	if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}
	
	/* 申请资源 */
	gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	if (!gpio_keys_100ask)
	{
		printk("kzalloc error\n");
		return -1;
	}

	/* 获得资源 */
	for (i = 0; i < count; i++)
	{
		gpio_keys_100ask[i].gpio = of_get_gpio(node, i);
		if (gpio_keys_100ask[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
		gpio_keys_100ask[i].irq   = gpio_to_irq(gpio_keys_100ask[i].gpio);

		/* 申请中断 */
		err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "test1_gpio_keys_100ask", &gpio_keys_100ask[i]);
		if (err) 
		{
			printk("request_irq err\n");
		}

		/* 设置定时器 */
		setup_timer(&gpio_keys_100ask[i].key_timer, key_timer_expire, &gpio_keys_100ask[i]);
		//设置定时器超时时间为无穷
		gpio_keys_100ask[i].key_timer.expires = ~0;
		/* 添加定时器 */
		add_timer(&gpio_keys_100ask[i].key_timer);
		
	}

	/* 注册字符设备 */
	major = register_chrdev(major, "100ask_key", &gpio_fops);
	if(major < 0)
	{
		printk("register_chrdev err'\n");
		return -1;
	}

	/* 注册类 */
	gpio_class = class_create(THIS_MODULE, "100ask_key_class");

	/* 注册设备 */
	device_create(gpio_class, NULL, MKDEV(major,0), NULL, "100ask_key_button");
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int gpio_remove(struct platform_device *pdev)
{
	int count, i;
	struct device_node *node;

	node = pdev->dev.of_node;
	count = of_gpio_count(node);
	device_destroy(gpio_class, MKDEV(major,0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_key");

	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
	}
	kfree(gpio_keys_100ask);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


/*
* 在设备树中添加的节点的compatible属性为:"test1_100ask,test1_gpio_key"
*/
static const struct of_device_id gpio_key_of_match[] = {
	{.compatible = "test1_100ask,test1_gpio_key"},
	{/* 这里必须要有一个空项,表示结束 */}
};

static struct platform_driver gpio_driver = {
	.driver = {
		.name	= "test1_gpio_keys_100ask",
		.of_match_table = gpio_key_of_match,
	},
	.probe	= gpio_probe,
	.remove = gpio_remove,
};

/* 基于platform总线来实现这个程序 */
static int gpio_init(void)  
{
	int ret;

	ret = platform_driver_register(&gpio_driver);
	if (ret != 0)
	{
		printk("platform_driver_register err\n");
		return -1;
	}
	else
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	return ret;
}

static void gpio_exit(void)
{
	platform_driver_unregister(&gpio_driver);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

Makefile:

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
all:

	make -C $(KERN_DIR) M=`pwd` modules
	$(CROSS_COMPILE)gcc -o button_app button_app.c
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -rf button_app
obj-m += button_drv.o

5.执行结果

c 复制代码
由于硬件本身的消抖效果很好,添加定时器之后,会导致一些按键值消息,比如0值(松开按键时)
长按时,这个0值才会出现。



6.文件路径

c 复制代码
/home/book/nfs_rootfs/CSDN/01_gpio_irq/07_read_key_irq_poll_fasync_block_timer
相关推荐
运维-大白同学6 分钟前
将django+vue项目发布部署到服务器
服务器·vue.js·django
糖豆豆今天也要努力鸭14 分钟前
torch.__version__的torch版本和conda list的torch版本不一致
linux·pytorch·python·深度学习·conda·torch
烦躁的大鼻嘎23 分钟前
【Linux】深入理解GCC/G++编译流程及库文件管理
linux·运维·服务器
乐大师23 分钟前
Deepin登录后提示“解锁登陆密钥环里的密码不匹配”
运维·服务器
ac.char29 分钟前
在 Ubuntu 上安装 Yarn 环境
linux·运维·服务器·ubuntu
敲上瘾30 分钟前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
长弓聊编程1 小时前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.1 小时前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
梅见十柒1 小时前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生
Koi慢热1 小时前
路由基础(全)
linux·网络·网络协议·安全