一、内核时间管理简介
make menuconfig
-> Kernel Features
-> Timer frequency (<choice> [=y])
有100Hz、200Hz、250Hz、300Hz、500Hz、1000Hz的选项,默认为100Hz,也就是1秒100个节拍,一个节拍10ms。
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
| 函数(unkown位jiffies) |
作用 |
| time_after(unkown, known) |
unkown 超过 known 的话, time_after 函数返回真 |
| time_before(unkown, known) |
known 超过 unknown 的话, time_before 函数返回真 |
| time_after_eq(unkown, known) |
与time_after一样,只是多了等于 |
| time_before_eq(unkown, known) |
与time_before一样,只是多了等于 |
| 函数 |
作用 |
| int jiffies_to_msecs(const unsigned long j) |
j2ms |
| int jiffies_to_usecs(const unsigned long j) |
j2us |
| u64 jiffies_to_nsecs(const unsigned long j) |
j2ns |
| long msecs_to_jiffies(const unsigned int m) |
ms2j |
| long usecs_to_jiffies(const unsigned int u) |
us2j |
| unsigned long nsecs_to_jiffies(u64 n) |
ns2j |
二、内核定时器简介
struct timer_list {
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);
unsigned long data;
int slack;
};
| 函数 |
描述 |
| void init_timer(struct timer_list *timer) |
初始化 timer_list 类型变量 |
| void add_timer(struct timer_list *timer) |
向 Linux 内核注册定时器,激活该定时器。 |
| int del_timer(struct timer_list * timer) |
删除一个定时器 |
| int del_timer_sync(struct timer_list *timer) |
del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除 |
| int mod_timer(struct timer_list *timer, unsigned long expires) |
mod_timer 函数用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会激活定时器! |
| 函数 |
描述 |
| void ndelay(unsigned long nsecs) |
延时ns |
| void udelay(unsigned long usecs) |
延时us |
| void mdelay(unsigned long mseces) |
延时ms |
三、定时器配置模板
struct timer_list timer
/* 定时器回调函数 */
void function(unsigned long arg)
{
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(2000))
}
void init(void)
{
init_timer(&timer);
timer.function = function;
timer.expires = jiffies + msecs_to_jiffies(2000);
timer.data = (unsigned long)&dev;
add_timer(&timer)
}
void exit(void)
{
del_timer_sync(&timer);
}
四、内核定时器在LED驱动中的应用
#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define CLOSE_CMD (_IO('a', 0x1))
#define OPEN_CMD (_IO('a', 0x2))
#define SETPERIOD_CMD (_IO('a', 0x3))
#define LEDON 1
#define LEDOFF 0
struct timer_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
int timeperiod;
struct timer_list timer;
spinlock_t lock;
};
struct timer_dev timerdev;
static int led_init(void)
{
int ret = 0;
timerdev.nd = of_find_node_by_path("/pled");
if (timerdev.nd== NULL) {
return -EINVAL;
}
timerdev.led_gpio = of_get_named_gpio(timerdev.nd ,"gpio", 0);
if (timerdev.led_gpio < 0) {
printk("can't get led\r\n");
return -EINVAL;
}
gpio_request(timerdev.led_gpio, "led");
ret = gpio_direction_output(timerdev.led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
return 0;
}
static int timer_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = &timerdev;
timerdev.timeperiod = 1000;
ret = led_init();
if (ret < 0) {
return ret;
}
return 0;
}
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev *)filp->private_data;
int timerperiod;
unsigned long flags;
switch (cmd) {
case CLOSE_CMD:
del_timer_sync(&dev->timer);
break;
case OPEN_CMD:
spin_lock_irqsave(&dev->lock, flags);
timerperiod = dev->timeperiod;
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
break;
case SETPERIOD_CMD:
spin_lock_irqsave(&dev->lock, flags);
dev->timeperiod = arg;
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
break;
default:
break;
}
return 0;
}
static struct file_operations timer_fops = {
.owner = THIS_MODULE,
.open = timer_open,
.unlocked_ioctl = timer_unlocked_ioctl,
};
void timer_function(unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev *)arg;
static int sta = 1;
int timerperiod;
unsigned long flags;
sta = !sta;
gpio_set_value(dev->led_gpio, sta);
spin_lock_irqsave(&dev->lock, flags);
timerperiod = dev->timeperiod;
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}
static int __init timer_init(void)
{
spin_lock_init(&timerdev.lock);
if (timerdev.major) {
timerdev.devid = MKDEV(timerdev.major, 0);
register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
} else {
alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME);
timerdev.major = MAJOR(timerdev.devid);
timerdev.minor = MINOR(timerdev.devid);
}
timerdev.cdev.owner = THIS_MODULE;
cdev_init(&timerdev.cdev, &timer_fops);
cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
if (IS_ERR(timerdev.class)) {
return PTR_ERR(timerdev.class);
}
timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
if (IS_ERR(timerdev.device)) {
return PTR_ERR(timerdev.device);
}
init_timer(&timerdev.timer);
timerdev.timer.function = timer_function;
timerdev.timer.data = (unsigned long)&timerdev;
return 0;
}
static void __exit timer_exit(void)
{
gpio_set_value(timerdev.led_gpio, 1);
del_timer_sync(&timerdev.timer);
#if 0
del_timer(&timerdev.tiemr);
#endif
cdev_del(&timerdev.cdev);
unregister_chrdev_region(timerdev.devid, TIMER_CNT);
device_destroy(timerdev.class, timerdev.devid);
class_destroy(timerdev.class);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ht");