文章目录
- 一、内核定时器原理
- 二、定时器API
- 三、使用定时器让LED灯闪烁
- 四、使用定时器对按键进行消抖
一、内核定时器原理
内核当前时间通过jiffies获取,它是内核时钟节拍数,在linux内核启动的时候,jiffies开始(按照一定频率)增加。在驱动中可以直接使用jiffies获取当前时间。
定时器每增加1走的时间由频率决定,定时器的频率可以通过make menuconfig进行选配,选配后的结果在.config文件中保存,选项是CONFIG_HZ。
linux-5.10.61内核CONFIG_HZ=100,定时器每增加1走10ms。
ubuntu的内核CONFIG_HZ=250,定时器每增加1走4ms。
二、定时器API
1.分配对象struct timer_list {struct hlist_node entry; //构成链表成员unsigned long expires; //定时器到期时间void (*function)(struct timer_list *);//定时器处理函数(定时时间到执行的函数)u32 flags; //填写为0};struct timer_list mytimer;
2.对象初始化mytimer.expires = jiffies+HZ; //定时1s钟 timer_setup(&mytimer, 定时器处理函数, 0);
3.启动定时器void add_timer(struct timer_list *timer)//功能:启动定时器,这个定时器只会执行一次(add_timer只能调用一次,多次调用内核会崩溃)int mod_timer(struct timer_list *timer, unsigned long expires)//功能:再次启动定时器
4.删除定时器int del_timer(struct timer_list *timer)
三、使用定时器让LED灯闪烁
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include "mynode.h"const char *led[3]={"led1","led2","led3"};
int core_gpiono[3];
int expend_gpiono[3];
struct cdev *led_cdev;
struct class *led_class;
struct device *led_device;
int major = 0; //主设备号
int minor = 0;
dev_t led_dev_num;struct timer_list mytimer;//定时器int my_led_open(struct inode *inode, struct file *file){return 0;
}
int my_led_close(struct inode *inode, struct file *file){return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg){return 0;
}const struct file_operations ledfops={.open=my_led_open,.release=my_led_close,.unlocked_ioctl=myled_ioctl,
};void timer_handler(struct timer_list* timer){int i;for(i=0;i<3;i++){gpio_set_value(expend_gpiono[i],!gpio_get_value(expend_gpiono[i]));}mod_timer(&mytimer,jiffies+HZ);
}static int __init mynode_init(void){struct device_node *core_node,*expend_node;int i,ret;//分配对象led_cdev = cdev_alloc();if(NULL == led_cdev){ //成功返回结构体指针,失败返回NULLpr_err("cdv_err error");return -ENOMEM;}//初始化对象:部分成员初始化cdev_init(led_cdev,&ledfops);//申请设备号:如果major为0,则动态申请,否则就静态指定if(major > 0){register_chrdev_region(MKDEV(major,minor),1,"mynode");}else if(major == 0){alloc_chrdev_region(&led_dev_num,0,1,"mynode"); major=MAJOR(led_dev_num);minor=MINOR(led_dev_num);}//注册cdev_add(led_cdev,MKDEV(major,minor),1); //自动创建设备节点led_class=class_create(THIS_MODULE,"mynode");led_device = device_create(led_class,NULL,MKDEV(major,minor),NULL,"mynode");/***gpio***///1. 获取节点//core节点core_node = of_find_node_by_path("/myleds/core_leds");if(NULL == core_node){pr_err("of_find_node_by_path error");return -EINVAL;}//expend节点expend_node = of_find_node_by_path("/myleds/expend_leds");if(NULL == expend_node){pr_err("of_find_node_by_path error");return -EINVAL;}//2.获取gpio号for(i=0;i<3;i++){//corecore_gpiono[i] = of_get_named_gpio(core_node,led[i],0);if(core_gpiono[i] < 0){pr_err("of_get_named_gpio error");return core_gpiono[i];}//expendexpend_gpiono[i] = of_get_named_gpio(expend_node,led[i],0);if(expend_gpiono[i] < 0){pr_err("of_get_named_gpio error");return expend_gpiono[i];}}//3. 申请gpiofor(i=0;i<3;i++){ret=gpio_request(core_gpiono[i],NULL);if(ret){pr_err("gpio_request error");for(i--;i>0;i--){gpio_free(core_gpiono[i]);}return ret;}}for(i=0;i<3;i++){ret=gpio_request(expend_gpiono[i],NULL);if(ret){pr_err("gpio_request error");for(i--;i>0;i--){gpio_free(expend_gpiono[i]);}for(i=0;i<3;i++){gpio_free(core_gpiono[i]);}return ret;}}//4.设置方向为输出for(i=0;i<3;i++){ret = gpio_direction_output(core_gpiono[i], 0);if (ret) {pr_err("gpio_direction_output error\n");goto err;}ret = gpio_direction_output(expend_gpiono[i], 0);if (ret) {pr_err("gpio_direction_output error\n");goto err;}}/***定时器****///2.定时器对象初始化mytimer.expires = jiffies+HZ; //定时1s钟 timer_setup(&mytimer, timer_handler, 0); //3.启动定时器add_timer(&mytimer);return 0;
err:for(i=0;i<3;i++){gpio_free(core_gpiono[i]);gpio_free(expend_gpiono[i]);}return ret;
}
static void __exit mynode_exit(void){int i;//删除定时器del_timer(&mytimer);for(i=0;i<3;i++){gpio_free(core_gpiono[i]);gpio_free(expend_gpiono[i]);}device_destroy(led_class, MKDEV(major, minor));class_destroy(led_class);cdev_del(led_cdev);unregister_chrdev_region(MKDEV(major, minor), 1);kfree(led_cdev);
}module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");
四、使用定时器对按键进行消抖
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/of_gpio.h>
// mykeys{
// interrupt-parent = <&gpiof>;
// interrupt = <7 0>,<8 0>,<9 0>;
// };struct device_node *key_node;
unsigned int key_gpiono[3];struct timer_list mytimer;//定时器
//7 8 9------2 3 1
unsigned int irqno[3]={0};void timer_handler(struct timer_list* timer){int i;for(i=0;i<3;i++){if(!gpio_get_value(key_gpiono[i])){switch(i){case 0:printk("key1 down ......");break;case 1:printk("key2 down ......");break;case 2:printk("key3 down ......");break;}}}
}irqreturn_t irq_handler(int irq, void *dev){mod_timer(&mytimer,jiffies+1);return IRQ_HANDLED;
}static int __init mynode_init(void){int i;/***GPIO***///1. 获取节点key_node = of_find_node_by_path("/mykeys");if(NULL == key_node){pr_err("of_find_node_by_path error");return -EINVAL;}printk("of_find_node_by_path success\n");//2.获取gpio号for(i=0;i<3;i++){//corekey_gpiono[i] = of_get_named_gpio(key_node,"keys",i);if(key_gpiono[i] < 0){pr_err("of_get_named_gpio error");return key_gpiono[i];}}printk("of_get_named_gpio success\n");//3. 申请gpio,是为了防止竞态/***中断***///1. 获取节点key_node = of_find_node_by_name(NULL,"mykeys");if(NULL == key_node){pr_err("of_find_node_by_name error");return -EINVAL;}//2.获取中断号for(i=0;i<3;i++){irqno[i] = irq_of_parse_and_map(key_node,i);if (irqno[i] == 0) {pr_err("irq_of_parse_and_map error\n");return -EAGAIN;}}//3.注册中断号for(i=0;i<3;i++){request_irq(irqno[i],irq_handler,IRQF_TRIGGER_FALLING,"my_IRQ_test",(void *)irqno[i]);}/***定时器****///2.定时器对象初始化mytimer.expires = jiffies+1; //定时10ms timer_setup(&mytimer, timer_handler, 0); //3.启动定时器add_timer(&mytimer);return 0;
}
static void __exit mynode_exit(void){int i=0;//注销中断号for(i=0;i<3;i++){free_irq(irqno[i],(void *)irqno[i]);}
}module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");