目录
一、时间管理和内核定时器
1.1、时间管理
1.2、内核定时器
1.3、定时器API
1.3.1、init_timer
1.3.2、add_timer
1.3.3、del_timer
1.3.4、del_timer_sync
1.3.5、mod_timer
1.3.6、内核短延时函数
二、驱动程序
三、应用程序
四、测试
一、时间管理和内核定时器
1.1、时间管理
linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动的时候会将jiffies初始化为0。
内核提供了几个jiffies和ms、us、ns之间的转换函数
1.2、内核定时器
内核定时器并不是周期性运行的,超时以后会自动关闭,因此如果想要实现周期性定时,那么需要在定时处理函数里重新开启定时器。内核定义如下:
1
2
3
4
5
6
7
8struct timer_list { struct list_head entry; unsigned long expires; /* 定时器超时时间,单位是节拍数 */ struct tvec_base *base; void (*function)(unsigned long); /* 定时处理函数 */ unsigned long data; /* 要传递给 function 函数的参数 */ int slack; };
expires表示超时时间,单位为节拍数,比如现在需要定义一个周期为2秒的定时器,那么这个定时器超时时间为jiffies+(2*HZ),因此expires=jiffies+(2*HZ)。
1.3、定时器API
1.3.1、init_timer
定义一个time_list变量后要用init_timer初始化一下。
1
2
3
4
5/* @Decription:初始化定时器 @timer:要初始化的定时器 */ void init_timer(struct timer_list *timer)
1.3.2、add_timer
用于向内核注册一个定时器,注册成功以后,定时器就会开始运行。
1
2
3
4
5/* @Decription:注册定时器 @timer:要注册的定时器 */ void add_timer(struct timer_list *timer)
1.3.3、del_timer
用于删除定时器,不管定时器有没有被激活,都可以用来删除定时器
在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数删除定时器之前要先等待其他处理器的定时处理器函数退出
1
2
3
4
5
6
7/* @Decription:删除定时器 @timer:要删除的定时器 @return:0,定时器还没被激活 1,定时器已经激活 */ int del_timer(struct timer_list *timer)
1.3.4、del_timer_sync
del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。
1
2
3
4
5
6
7/* @Decription:删除定时器 @timer:要删除的定时器 @return:0,定时器还没被激活 1,定时器已经激活 */ int del_timer_sync(struct timer_list *timer)
1.3.5、mod_timer
用于修改定时值,如果定时器还没被激活,mod_timer会激活定时器!
1
2
3
4
5
6
7@Decription:修改定时值 @timer:要修改定时值的定时器 @expires:修改后的超时时间 @return:0,调用mod_timer前定时器还没被激活 1,调用mod_timer前定时器已经激活 int mod_timer(struct timer_list *timer, unsigned long expires)
使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37//定义定时器 struct timer_list timer; //定时器回调函数 void function(unsigned long arg) { //user code //如果需要周期运行 mod_timer(&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(&timer); #if 0 del_timer_sync(&timer); #endif }
1.3.6、内核短延时函数
二、驱动程序
使用内核定时器周期性的闪烁LED灯,应用程序可以修改周期值,具体步骤如下:
①、timer设备中定义一个定时器
②、timer设备中定义一个定时周期,并定义一个自旋锁进行保护
③、打开定时器设备时候,只设置默认定时周期,默认开始定时器是关闭的
④、应用程序利用iotcl控制定时器设备,关闭调用del_timer_sync,打开就是取默认定时周期调用mod_timer,修改定时器就是接收应用程序传递的参数,调用mod_timer重新设置定时器周期并启动定时器。读取参数的时候用自旋锁进行保护
⑤、在定时回调函数中对LED状态取反,并调用mod_timer重新开启定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #define TIMER_CNT 1 #define TIMER_NAME "timerdev" #define CLOSE_CMD 1 #define OPEN_CMD 2 #define SETPERIOD_CMD 3 #define timer_ON 1 #define timer_OFF 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; //led所使用的GPIO编号 struct timer_list timer; //定时器 unsigned long perpiod; //定时器周期 spinlock_t lock; //自旋锁 }; struct timer_dev timerdev; static int led_init(void) { int ret = 0; //通过节点名字查找节点 timerdev.nd = of_find_node_by_name(NULL, "gpioled"); if (timerdev.nd == NULL) { printk("find %s failrn", "gpioled"); return -EINVAL; } //获取GPIO编号 timerdev.led_gpio = of_get_named_gpio(timerdev.nd, "gpio", 0); if (timerdev.led_gpio < 0) { printk("get gpio failrn"); return -EINVAL; } //初始化led gpio_request(timerdev.led_gpio, "led"); ret = gpio_direction_output(timerdev.led_gpio, 0); if (ret < 0) { printk("set led status failrn"); return -EINVAL; } return 0; } static int timer_open(struct inode *inode, struct file *filp) { led_init(); filp->private_data = &timerdev; printk(" timer open!n"); return 0; } static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct timer_dev *dev = filp->private_data; unsigned long perpiod = 0; unsigned long flags; switch(cmd) { case CLOSE_CMD: //关闭定时器 del_timer_sync(&dev->timer); break; case OPEN_CMD: //打开定时器 add_timer(&dev->timer); break; case SETPERIOD_CMD: //设置定时器周期 spin_lock_irqsave(&dev->lock, flags); perpiod = arg; dev->perpiod = arg; spin_unlock_irqrestore(&dev->lock, flags); mod_timer(&dev->timer, jiffies + msecs_to_jiffies(perpiod)); break; default: break; } return 0; } static const struct file_operations timer_fops = { .owner = THIS_MODULE, .open = timer_open, .unlocked_ioctl = timer_unlocked_ioctl, }; //定时器回调函数 static void timer_function(unsigned long arg) { static uint8_t sta = 0; struct timer_dev *dev = (struct timer_dev *)arg; sta = !sta; gpio_set_value(dev->led_gpio, sta); //重新启动定时器 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->perpiod)); } static int __init timer_init(void) { int ret = 0; int val = 0; //初始化自旋锁 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); } //初始化cdev timerdev.cdev.owner = THIS_MODULE; cdev_init(&timerdev.cdev, &timer_fops); //添加cdev cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT); //创建类 timerdev.class = class_create(THIS_MODULE, TIMER_NAME); //创建设备 timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME); //初始化定时器 init_timer(&timerdev.timer); timerdev.timer.function = timer_function; timerdev.timer.expires = jiffies +msecs_to_jiffies(1000); timerdev.timer.data = (unsigned long)&timerdev; return 0; } static void __exit timer_exit(void) { //删除定时器 del_timer_sync(&timerdev.timer); //删除设备的类 device_destroy(timerdev.class, timerdev.devid); //删除类 class_destroy(timerdev.class); //删除设备 cdev_del(&timerdev.cdev); //注销设备号 unregister_chrdev_region(timerdev.devid, TIMER_CNT); printk(" dev exitn"); } module_init( timer_init); module_exit( timer_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZK");
三、应用程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" #define CLOSE_CMD 1 #define OPEN_CMD 2 #define SETPERIOD_CMD 3 int main(int argc, char *argv[]) { int fd, ret; unsigned int cmd; unsigned long arg; char *filename; unsigned char writebuf[1]; if (argc != 2) { printf("Usage:n"); printf("n"); return -1; } filename = argv[1]; fd = open(filename, O_RDWR); if (fd < 0) { printf("open file %s failn", filename); } while (1) { printf("please input cmd!rn"); scanf("%d", &cmd); if (cmd == 1) cmd = CLOSE_CMD; else if (cmd == 2) cmd = OPEN_CMD; else if (cmd == 3) { cmd = SETPERIOD_CMD; printf("please input timer period:"); scanf("%d", &arg); } ioctl(fd, cmd, arg); } close(fd); }
四、测试
最后
以上就是微笑蜜粉最近收集整理的关于一、linux驱动开发-5.1-内核定时器一、时间管理和内核定时器二、驱动程序三、应用程序四、测试的全部内容,更多相关一、linux驱动开发-5内容请搜索靠谱客的其他文章。
发表评论 取消回复