06、软件模拟PWM
软件生成PWM信号通常需要定时器中断。但传统定时器频率在100到1000次/秒之间,精度只有毫秒级别。对于需要更高时间精度的应用(比如模拟PWM),这样的精度就不够用了。
为此,Linux系统开发了高精度定时器(hrtimers)来解决这个问题。高精度定时器使用系统时钟和特殊的时间记录方式作为时间基准,并通过高性能硬件设备(如HPET或LAPIC定时器)实现了纳秒级精度的时间控制。
高精度定时器结构体 struct hrtimer 内容如下所示:
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
u8 state;
u8 is_rel;
u8 is_soft;
u8 is_hard;
ANDROID_KABI_RESERVE(1);
};
2
3
4
5
6
7
8
9
10
11
12
一、高精度定时器工作流程
以下是高分辨率定时器(hrtimer)使用步骤说明:
(1)初始化定时器
用 hrtimer_init()
函数创建一个定时器对象。需要指定两个关键参数:
- 时钟类型(比如实时时钟或单调时钟)
- 运行模式(比如“绝对时间”还是“相对时间”触发)
系统会为这个定时器分配一个所属的“时钟管理器”(每个处理器都有自己的管理器)。
(2)设置定时器
用 hrtimer_start()
或 hrtimer_set()
设置定时器的触发时间:
- 可以指定 具体时间点(绝对时间)或 延迟时间(相对时间)。
- 系统会自动把时间转换为对应的“到期时间标记”,并把这个定时器按时间顺序排到队列里。
(3)定时器触发处理
当系统检测到时间到达时(比如时钟中断触发):
- 系统会检查队列里到期的定时器,
- 自动执行定时器关联的 回调函数(用户自己写的处理逻辑),
- 如果还有未到期的定时器,系统会更新下一个要触发的时间点,保持高效运行。
(4)定时器管理
- 取消定时器:用
hrtimer_cancel()
停止一个正在等待的定时器。 - 重新计算时间:用
hrtimer_forward()
根据当前时间重新设置到期时间。 - 设置精度范围:用
hrtimer_start_range_ns()
为定时器指定一个时间范围,让系统更精准地触发。
:::
总结
整个流程就像设置闹钟:
先买一个闹钟并设定类型(比如“世界时钟”或“固定时间”);
设定响铃时间,放进闹钟队列;
到点后闹铃响(执行你的操作),未响的继续等待;
可以随时关掉闹钟,或重新调整响铃时间。 :::
二、高精度定时器 API 讲解
1. hrtimer_init()
作用:初始化一个高精度定时器(struct hrtimer
)。
参数:
timer
:要初始化的定时器结构体指针。clockid
:选择定时器使用的时钟类型(比如系统时钟、单调时钟等)。mode
:设置定时器的工作模式(如相对时间或绝对时间模式)。
2. hrtimer_set()
作用:设置定时器的到期时间。
参数:
timer
:要设置的定时器结构体指针。time
:定时器触发的时间点(可以是“未来某个绝对时间”或“从现在开始的相对时间”)。mode
:指定时间是相对模式(HRTIMER_MODE_REL
)还是绝对模式(HRTIMER_MODE_ABS
)。
3. hrtimer_start()
作用:启动定时器,将其加入内核的定时器队列中。
参数:
timer
:要启动的定时器结构体指针。time
:定时器触发的时间点(绝对或相对时间)。mode
:时间模式(相对或绝对)。
返回值:- 成功返回
0
,失败返回负数。
4. hrtimer_forward()
作用:将定时器的到期时间往后推一个指定的间隔(常用于周期性任务)。
参数:
timer
:要调整的定时器结构体指针。now
:当前时间(用于计算新的到期时间)。interval
:要增加的时间间隔(例如每秒触发一次)。
返回值:
返回定时器新的到期时间。
5. hrtimer_cancel()
作用:停止并移除正在运行的定时器。
参数:
timer
:要取消的定时器结构体指针。
返回值:- 如果定时器正在运行,返回
1
; - 如果定时器已停止,返回
0
。
:::
总结
初始化:hrtimer_init()
→ 选择时钟和模式。
设置时间:hrtimer_set()
→ 指定触发时间和模式。
启动:hrtimer_start()
→ 加入队列等待触发。
周期调整:hrtimer_forward()
→ 延长到期时间(如循环定时)。
取消:hrtimer_cancel()
→ 立即停止定时器。 :::
三、实验代码
设备树:
leds {
compatible = "pwm-leds";
led-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
};
2
3
4
驱动:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/pwm.h>
#include <linux/gpio/consumer.h>
#include <linux/hrtimer.h>
#include <linux/slab.h>
dev_t dev_num;
struct cdev cdev_test;
struct class *class;
struct device *device;
// 定义 PWM LED 数据结构体
struct pwm_led_data {
int sum_count; // PWM 周期总脉冲数
int high_count; // PWM 高电平持续脉冲数
struct gpio_desc *gpiod; // GPIO 描述符
struct hrtimer pwm_timer; // 高分辨率定时器
ktime_t time; // 定时器时间间隔
};
// 声明 PWM LED 数据结构体指针
struct pwm_led_data *data;
// PWM 定时器回调函数
enum hrtimer_restart pwm_timer_func(struct hrtimer *timer) {
static int timer_count = 0; // 定时器计数器
struct pwm_led_data *mydata = container_of(timer, struct pwm_led_data, pwm_timer);
// 如果计数器达到总脉冲数, 将 GPIO 设为高电平
if (timer_count == mydata->sum_count) {
gpiod_set_value(mydata->gpiod, 1);
timer_count = 0;
}
// 如果计数器达到高电平持续脉冲数, 将 GPIO 设为低电平
if (timer_count == mydata->high_count) {
gpiod_set_value(mydata->gpiod, 0);
}
timer_count++;
// 如果高电平持续脉冲数为 0, 则计数器重置为 0
if (mydata->high_count == 0) {
timer_count = 0;
}
// 将定时器向前移动 time 时间间隔, 并重新启动
hrtimer_forward(timer, hrtimer_cb_get_time(timer), mydata->time);
return HRTIMER_RESTART;
}
// 字符设备打开回调函数
static int cdev_test_open(struct inode *inode, struct file *file) {
printk("This is cdev test open\n");
return 0;
}
// 字符设备写入回调函数
static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off) {
int ret;
int kbuf[2];
printk("This is cdev test write\n");
// 从用户空间拷贝数据到内核空间
ret = copy_from_user(kbuf, buf, size);
if (ret != 0) {
printk("copy_from_user failed\n");
return -EFAULT;
}
// 更新 PWM LED 数据结构体
data->sum_count = kbuf[0];
data->high_count = kbuf[1];
return size;
}
// 字符设备释放回调函数
static int cdev_test_release(struct inode *inode, struct file *file) {
printk("This is cdev test release\n");
return 0;
}
// 字符设备操作函数集
static struct file_operations cdev_test_ops = {
.owner = THIS_MODULE,
.open = cdev_test_open,
.write = cdev_test_write,
.release = cdev_test_release,
};
// 平台设备探测回调函数
static int led_probe(struct platform_device *pdev) {
int ret;
// 分配 PWM LED 数据结构体内存
data = kmalloc(sizeof(struct pwm_led_data), GFP_KERNEL);
if (!data) {
printk("kmalloc failed\n");
return -ENOMEM;
}
// 初始化 PWM LED 数据结构体
data->sum_count = 20;
data->high_count = 10;
// 动态分配设备号
ret = alloc_chrdev_region(&dev_num, 0, 1, "alloc_name");
if (ret < 0) {
printk("alloc_chrdev_region is error\n");
kfree(data);
return ret;
}
printk("alloc_chrdev_region is ok\n");
// 初始化字符设备
cdev_init(&cdev_test, &cdev_test_ops);
cdev_test.owner = THIS_MODULE;
ret = cdev_add(&cdev_test, dev_num, 1); // 向内核注册字符设备
if (ret) {
printk("cdev_add is error\n");
unregister_chrdev_region(dev_num, 1);
kfree(data);
return ret;
}
class = class_create(THIS_MODULE, "test"); // 创建设备类
if (IS_ERR(class)) {
printk("class_create is error\n");
cdev_del(&cdev_test);
unregister_chrdev_region(dev_num, 1);
kfree(data);
return PTR_ERR(class);
}
device = device_create(class, NULL, dev_num, NULL, "pwm-gpio"); // 创建设备节点
if (IS_ERR(device)) {
printk("device_create is error\n");
class_destroy(class);
cdev_del(&cdev_test);
unregister_chrdev_region(dev_num, 1);
kfree(data);
return PTR_ERR(device);
}
data->gpiod = gpiod_get(&pdev->dev, "led", GPIOD_OUT_HIGH); // 获取 GPIO 描述符
if (IS_ERR(data->gpiod)) {
printk("gpiod_get is error\n");
device_destroy(class, dev_num);
class_destroy(class);
cdev_del(&cdev_test);
unregister_chrdev_region(dev_num, 1);
kfree(data);
return PTR_ERR(data->gpiod);
}
gpiod_set_value(data->gpiod, 1); // 将 GPIO 设为高电平
// 初始化高分辨率定时器
data->time = ktime_set(0, 1000000); // 1 ms
hrtimer_init(&data->pwm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->pwm_timer.function = pwm_timer_func;
hrtimer_start(&data->pwm_timer, data->time, HRTIMER_MODE_REL); // 启动高分辨率定时器
printk("led_probe successful\n");
return 0;
}
// 平台设备移除回调函数
static int led_remove(struct platform_device *pdev) {
hrtimer_cancel(&data->pwm_timer); // 停止高分辨率定时器
gpiod_put(data->gpiod); // 释放 GPIO 描述符
device_destroy(class, dev_num); // 删除设备节点
class_destroy(class); // 删除设备类
cdev_del(&cdev_test); // 从内核注销字符设备
unregister_chrdev_region(dev_num, 1); // 释放设备号
kfree(data); // 释放 PWM LED 数据结构体内存
printk("led_remove successful\n");
return 0;
}
// 设备树匹配表
static const struct of_device_id led_of_device_id[] = {
{.compatible = "pwm-leds"},
{},
};
MODULE_DEVICE_TABLE(of, led_of_device_id);
// 平台设备驱动结构体
static struct platform_driver led_platform_driver = {
.driver = {
.name = "pwm-leds",
.of_match_table = led_of_device_id,
},
.probe = led_probe,
.remove = led_remove,
};
// 模块初始化函数
static int __init modulecdev_init(void) {
int ret;
// 注册平台设备驱动
ret = platform_driver_register(&led_platform_driver);
if (ret) {
printk("platform_driver_register is error\n");
return ret;
}
printk("platform_driver_register is ok\n");
return 0;
}
// 模块退出函数
static void __exit modulecdev_exit(void) {
// 注销平台设备驱动
platform_driver_unregister(&led_platform_driver);
printk("bye bye\n");
}
// 模块初始化和退出函数注册
module_init(modulecdev_init);
module_exit(modulecdev_exit);
// 模块许可证
MODULE_LICENSE("GPL");
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228