这章学习Linux内核定时器,介绍其API函数和用法。
- 一、时间管理
- 二、内核定时器
- 三、驱动程序
- 四、测试
- 1.测试App
- 2.编译
- 3.测试
系统节拍默认100hz,即每秒100次,定义一个HZ=100
Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动时将jiffies初始化为0,定义在include/linux/jiffies.h中。
所以jiffies/HZ就是系统运行时间。
| 函数 | 描述 |
|---|---|
| time_after(unknow,know) | unknown超过known返回真,否则返回假 |
| time_before(unknow,known) | 没有超过返回真,否则返回假 |
| time_after_eq(unknow,known) | 大于等于返回真,否则返回假 |
| time_before_eq(unknown,known) | 小于等于返回真,否则返回假 |
unknown一般为jiffies,known是对比的值。
下面是jiffies和ms、us、ns之间的转换函数
| 函数 | 描述 |
|---|---|
| int jiffies_to_msecs(const unsigned long j) | jiffies转换成毫秒 |
| int jiffies_to_usecs(const unsigned long j) | jiffies转换成微秒 |
| u64 jiffies_to_nsecs(const unsigned long j) | jiffies转换成纳秒 |
| long msecs_to_jiffies(const unsigned int m) | 毫秒转换成jiffies |
| long usecs_to_jiffies(const unsigned int u) | 微妙转换成jiffes |
| unsigned long nsecs_to_jiffies(u64 n) | 纳秒转换成jiffies |
提供超时时间和需要处理的函数即可。
struct timer_list {
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function) (unsigned long);
unsigned long data;
int slack;
};
比如定义一个周期2s的定时器,超时时间定义为expires = jiffies+(2*HZ)。
下面是定时器的API函数
void init_timer (struct timer_list *timer) void add_timer (struct timer_list *timer) int del_timer (struct timer_list * timer) int del_timer_sync (struct timer_list *timer) int mod_timer (struct timer_list *timer, unsigned long expires)
Linux内核短延迟函数
| 函数 | 描述 |
|---|---|
| void ndelay(unsigned long nsecs) | 纳秒延迟 |
| void udelay(unsignd long usces) | 微秒延迟 |
| void mdelay(unsigned long msecs) | 毫秒延迟 |
使用定时器让led闪,输入时间控制闪烁频率。
#include#define TIMER_CNT 1 #define KEY_NAME "timer" struct timer_device { struct device_node *nd; int timer_gpio; struct timer_list timer; int period; } struct timer_device timer; static int timer_open(struct inode *inode, struct file *filp) { filp->private = &timer; timer.period = 1000; } static long timer_unlocked_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) { struct timer_device *dev = filp->private; int ret; switch(cmd) { case CLOSE: ret = del_timer_sync(&dev->timer); break; case OPEN: mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period)); break; case SET: dev->period = arg; mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period)); break; default: break; } return 0; } static struct file_operations timer_fops = { .owner = THIS_MODULE, .open = timer_open, .unlocked_ioctl = timer_unlocked_ioctl, }; void function(unsigned long arg) { struct timer_device *t = (struct timer_device *)arg; static int temp = 0; if(temp % 2) gpio_set_value(t->timer_gpio, 1); else gpio_set_value(t->timer_gpio, 0); temp++; mod_timer (&t->limer, jiffies + msecs_to_jiffies(t->period)); } static int __init timer_init(void) { init_timer(&timer.timer); timer.timer.function = function; timer.timer.expires = jiffies + msecs_to_jiffies(1000); timer.timer.data = (unsigned long) &timer; return 0; } static void __exit timer_exit(void) { del_timer_sync(&timer.timer); cdev_del(&timer.cdev); unreginser_chrdev_region(timer.devid, TIMER_CNT); device_destroy(timer.class, timer.devid); class_destroy(timer.class); }
重复代码略写
这fops函数加了unlocked_ioctl函数,用于传递参数。
在del定时器后,可以不必初始化-注册定时器,因为只是从内核删除,本身还存在,所以再激活,只要add或者mod函数激活既可以使用。
定时器fun函数输入参数是dev结构体的地址(unsigned long),因此可以在fun函数继续使用结构体内的变量。
最后exit函数,记得删除定时器,删除建议使用sync函数。
int main(int argc, char *argv[])
{
int fd, ret;
char *filename;
unsigned int cmd;
unsigned int arg;
unsigned char str[100];
filename = argv[1];
fd = open(filename, O_RDWR);
while (1) {
printf("Input CMD:");
ret = scanf("%d", &cmd);
if (ret != 1) {
gets(str);
}
if(cmd == 1)
cmd = CLOSE;
else if(cmd == 2)
cmd = OPEN;
else if(cmd == 3) {
cmd = SET;
printf("Input Timer Period:");
ret = scanf("%d", &arg);
if (ret != 1) {
gets(str);
}
}
ioctl(fd, cmd, arg);
}
close(fd);
}
2.编译
和前面一样
3.测试参考App代码吧。



