概述
内核定时器是基于未来时间点的一个定时器,以当前系统节拍数“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 %sn",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 %sn",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 drivern");
if(inputPara) //这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
{
printk(KERN_ERR "input Param is %dn",inputPara);
}
printk(KERN_ERR "registers platform drivern");
return 0;
}
static void charDev_exit(void)
{
printk(KERN_ERR "remove platform drivern");
if(IS_ERR(gpio_res)){
printk(KERN_ERR "not get gpio infon");
}
else{
gpiod_put(gpio_res);
}
if(irq >= 0)
free_irq(irq,NULL);
printk(KERN_ERR "gpio_freen");
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");
最后
以上就是标致水杯为你收集整理的自学linux驱动从入门到放弃(十六)内核定时器的使用的全部内容,希望文章能够帮你解决自学linux驱动从入门到放弃(十六)内核定时器的使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复