自学linux驱动从入门到放弃(十六)内核定时器的使用

内核定时器是基于未来时间点的一个定时器,以当前系统节拍数“jiffies”,向后延时多长时间(转换为系统节拍数)。

1.头文件

#include <linux/timer.h>

2.timer数据结构

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct hlist_node	entry;
	unsigned long		expires;
	void			(*function)(unsigned long);
	unsigned long		data;
	u32			flags;
	int			slack;

#ifdef CONFIG_TIMER_STATS
	int			start_pid;
	void			*start_site;
	char			start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
	struct lockdep_map	lockdep_map;
#endif
};

3.定时器时间单位

        在内核源码目录下,有一个隐藏的文件.config,打开之后可以看到

        这里代表内核每秒钟会产生100个系统节拍(tick),也就是10ms一次,会产生一次tick中断,每次中断 'jiffies'会 +1 ,定时的时间是基于当前的系统节拍(jiffies),向后延时定时的时间。

4.使用定时器

4.1 定时器接口初始化

4.1.1 定义timer结构体

struct timer_list key_timer;

4.1.2 调用 setup_timer

#define setup_timer(timer, fn, data)
参数描述
timer定时器结构体指针
fn超时函数

data

传入超时函数的参数

4.1.3 初始化定时器结构体

        结构体中的function成员已经在上一步setup_timer中初始化完成,所以这里只需要对超时时间expires进行初始化。

key_timer.expires = jiffies + msecs_to_jiffies(m);

这里有两个常用函数

msecs_to_jiffies(const unsigned int m);// ms转换为系统节拍数
usecs_to_jiffies(const unsigned int u);// us转换为系统节拍数

4.2 把定时器添加进内核 add_timer

void add_timer(struct timer_list *timer)

4.3 修改超时时间 mod_timer

mod_timer(struct timer_list * timer, unsigned long expires)

4.4 删除定时器 del_timer

del_timer(struct timer_list * timer)

5.按键消抖实际应用

        在probe函数中,注册中断之前安装定时器

//	irq = gpiod_to_irq(gpio_res);				//将gpio转换为中断号
//	irq = irq_of_parse_and_map(pdev->dev.of_node,0);	//通过设备树结点获取中断信息

	setup_timer(&key_timer,, key_timer_expire, NULL);    
	key_timer.expires = 0;    //这里把expires设置为0,add_timer后超时函数会立刻被执行,后面会在中断中重新设置
	add_timer(&key_timer);
	
	irq = of_irq_get(pdev->dev.of_node, 0);

	printk(KERN_ERR "irq is %d!!\n",irq);
	ret = request_irq(irq, key_fun, IRQF_TRIGGER_RISING, "key_interrupt", NULL);	//申请中断号

        原来在gpio的中断函数中读取按键值,唤醒进程,现在gpio的中断函数只需要做一件事"mod_timer"

irqreturn_t key_fun(int irq,void *args)
{
	printk(KERN_ERR "key_fun is action!!\n");
	mod_timer(&key_timer, msecs_to_jiffies(20));

	return IRQ_HANDLED;
}

        如果有抖动的话,每次都会进入中断,并向后再延时一段时间,如果进入了定时器的超时函数,说明延时的时间内没有抖动,则在定时器的超时函数中读取按键值,唤醒进程。

static void key_timer_expire(unsigned long data)
{
	printk(KERN_ERR "kernel: key_timer_expire is action !! \n");
	key_val = gpiod_get_raw_value(gpio_res);
	printk(KERN_ERR "kernel: key value is %x \n",key_val);
	ring_key_put(key_val);	
	wake_up_interruptible(&q_key_wait);

}

6.完整代码

#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/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/timer.h>

#define RING_BUF_LEN	64
#define NEXT_POS(x)	((x+1)%RING_BUF_LEN)
static int g_keys[RING_BUF_LEN];
static int r,w;

static int is_buffer_empty(void)
{
	return (w == r);
}

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

static void ring_key_put(int x)
{
	if( ! is_buffer_full())
	{
		g_keys[w] = x;
		w = NEXT_POS(w);
	}
}

static int ring_key_pop(void)
{
	int key = 0;
	if(! is_buffer_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


static DECLARE_WAIT_QUEUE_HEAD(q_key_wait);
int key_val = 0;

int inputPara=0;

int dev_major;
int dev_minor=0;
char *chardev_device_name="aloncharDevice";	//设备驱动名字
struct class *class;
char *chardev_class_name="aloncharClass";	//类名字
char *charDev_node_name="aloncharTest";		//设备节点名字
struct gpio_desc *gpio_res =NULL;
int irq = -1;

struct timer_list key_timer;

static void key_timer_expire(unsigned long data)
{
	printk(KERN_ERR "kernel: key_timer_expire is action !! \n");
	key_val = gpiod_get_raw_value(gpio_res);
	printk(KERN_ERR "kernel: key value is %x \n",key_val);
	ring_key_put(key_val);	
	wake_up_interruptible(&q_key_wait);

}


irqreturn_t key_fun(int irq,void *args)
{
	printk(KERN_ERR "key_fun is action!!\n");
	mod_timer(&key_timer, msecs_to_jiffies(20));

	return IRQ_HANDLED;
}


module_param(inputPara, int, S_IRUSR);		//insmod的时候传入的参数,参数名字inputPara,类型int,S_IRUSR是权限

/*
static volatile unsigned int *GPIO8_CLKGATE_CON;
static volatile unsigned int *GPIO_PORT_DR;
static volatile unsigned int *GPIO_PORT_DDR;
static volatile unsigned int *GRF_GPIO8A_IOMUX;
*/


int charDev_open (struct inode *inode, struct file *file){

/*
	*led_reg_vir_addr[GPIO8_CLKGATE_CON] = (1<<(16+8))|(0<<8);	//31:16 write mask :8 gpio8 clk gate,0:enable
	*led_reg_vir_addr[GRF_GPIO8A_IOMUX] = (1<<(16+4))|(~(1<<4));//31:16 write mask :4 0:fuc gpio
	*led_reg_vir_addr[GPIO_PORT_DDR] |= (1<<2);	//gpio8_A2 set output
*/
	printk("open charTest node OK!\n");


	return 0;
}

ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){

	int err;
	wait_event_interruptible(q_key_wait, (!is_buffer_empty()));
	key_val = ring_key_pop();
	err = copy_to_user(buf,&key_val,1);
	printk(KERN_ERR "KEY VAL is %d \n",key_val);
	key_val = 0;
	return 0;
}


ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){

	return 0;
}


static unsigned int charDev_poll(struct file *fp, poll_table * wait)
{

	poll_wait(fp, &q_key_wait, wait);
	return is_buffer_empty() ? 0 : POLLIN | POLLRDNORM;
}



static const struct file_operations f_op = {	//file_operations结构体
	.open = charDev_open,
	.read = charDev_read,
	.write = charDev_write,
	.poll = charDev_poll,
	.owner = THIS_MODULE,
}; 

int led_probe(struct platform_device *pdev){


	int ret;
	printk(KERN_ERR "dts match driver!!\n");

	gpio_res = gpiod_get_index(&pdev->dev,"key",0,GPIOD_OUT_LOW);	//获取名为key的gpio属性
	if(IS_ERR(gpio_res))
	{
		printk(KERN_ERR "gpiod_get_index is error!!\n");
		return (PTR_ERR(gpio_res));
	}
	printk(KERN_ERR "get gpio is ok!!\n");
//	ret = gpiod_direction_output(gpio_res,0);
	ret = gpiod_direction_input(gpio_res);		//将引脚设置为输入
	if(ret < 0)
	{
		printk(KERN_ERR "gpio_direction_input is error!!\n");
		return -1;		
	}
//	irq = gpiod_to_irq(gpio_res);				//将gpio转换为中断号
//	irq = irq_of_parse_and_map(pdev->dev.of_node,0);	//通过设备树结点获取中断信息

	setup_timer(&key_timer, key_timer_expire, 0);
	key_timer.expires = 0;
	add_timer(&key_timer);
	
	irq = of_irq_get(pdev->dev.of_node, 0);

	printk(KERN_ERR "irq is %d!!\n",irq);
	ret = request_irq(irq, key_fun, IRQF_TRIGGER_RISING, "key_interrupt", NULL);	//申请中断号
	
	if(ret < 0)
	{
		printk(KERN_ERR "request_irq is failed!!\n");
	}
	
	dev_major = register_chrdev(0,chardev_device_name,&f_op);	//注册设备号,f_op结构体
	if(dev_major < 0)
	{
		printk("can not regist char device!\n");
		return dev_major;
	}
	printk(KERN_ERR "dev_major = %d,chardev device name is %s\n",dev_major,chardev_device_name);
	class = class_create(THIS_MODULE, chardev_class_name);		//注册类
	if(IS_ERR(class))
	{
		printk("can not create charDev class!");
		unregister_chrdev(dev_major,chardev_device_name);
		return PTR_ERR(class);
			
	}
	printk(KERN_ERR "chardev class name is %s\n",chardev_class_name);
	device_create(class, NULL, MKDEV(dev_major,dev_minor), NULL, charDev_node_name);	//注册设备节点
	printk(KERN_ERR "chardev node create ok!\n");
	



	
	return 0;
}

int led_remove(struct platform_device *pdev){


	return 0;
}

const struct of_device_id alon_of_match_table[] = {
	{.compatible = "key_test"},
	{}
};

struct platform_driver led_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "alon_charDevPlt_test",
		.of_match_table = alon_of_match_table
	}
};


static int charDev_init(void)			//init函数,MODULE_INIT的时候会进行初始化,当前框架里面
{										//注册设备号,注册类,注册设备节点都在这里
	


	int ret = 0;

	ret = platform_driver_register(&led_driver);
	if(ret < 0){
		printk(KERN_ERR "platform driver register failed \n");
		return ret;
	}
	printk(KERN_ERR "insmod platform driver\n");


	if(inputPara)			//这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
	{
		printk(KERN_ERR "input Param is %d\n",inputPara);
	}
	printk(KERN_ERR "registers platform driver\n");	


	return 0;
}

static void charDev_exit(void)
{


	printk(KERN_ERR "remove platform driver\n");
	if(IS_ERR(gpio_res)){
		printk(KERN_ERR "not get gpio info\n");
	}
	else{
		gpiod_put(gpio_res);
	}
	if(irq >= 0)
		free_irq(irq,NULL);
		
	
	printk(KERN_ERR "gpio_free\n");
	device_destroy(class, MKDEV(dev_major,dev_minor));
	printk(KERN_ERR "remove chardev node!\n");
	class_destroy(class);
	
	printk(KERN_ERR "remove chardev class!\n");
	unregister_chrdev(dev_major,chardev_device_name);
	printk(KERN_ERR "remove chardev device!\n");

	platform_driver_unregister(&led_driver);
}



module_init(charDev_init);
module_exit(charDev_exit);
MODULE_LICENSE("GPL");

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值