02、驱动 PWM 使用
PWM的使用主要有两种方式:
- 通过设备驱动调用系统接口:用编程方式调用操作系统提供的PWM功能接口,设置参数并开启PWM输出
- 通过系统文件控制:直接操作系统文件(如/sys目录下的文件),用命令或应用程序调整PWM参数
配置PWM设备通常需要三个步骤:
- 驱动配置:在操作系统中设置PWM驱动的相关参数
- 硬件配置:在设备树(硬件描述文件)中定义PWM控制器的连接关系
- 输出控制:通过编程或系统命令实际设置PWM的频率、占空比并启动输出
简单来说:先让系统认识PWM硬件,再告诉硬件如何工作,最后控制它实际输出需要的波形。整个过程就像先安装设备驱动,再设置硬件参数,最后启动设备工作一样。
一、配置设备树
DTS 配置参考文档 Documentation/devicetree/bindings/pwm/pwm.txt, 设备树配置参考如下所示:
生产者: (PWM控制器)
C
&pwm9 {
status = "okay";
pinctrl-0 = <&pwm9m0_pins>;
};
1
2
3
4
2
3
4
消费者:(驱动模块)
C
bl: backlight {
pwms = <&pwm9 0 25000 PWM_POLARITY_INVERTED>;
pwm-names = "backlight";
};
1
2
3
4
2
3
4
关于代码中pwms的三个主要参数:
- 第一个参数:PWM编号,通常填0 (因为这种芯片一般只有一个PWM输出通道)
- 第二个参数:波形周期时长,单位是纳秒 (比如设置25000就是40千赫兹,因为25000ns=0.000025秒,1/0.000025=40000次/秒)
- 第三个参数:波形方向(可选参数) (设置为负极性,即波形从低电平开始上升)
二、配置内核驱动
使用PWM驱动的简单步骤
1. 包含头文件
在驱动代码开头添加:
C
#include <linux/pwm.h>
1
2. 申请PWM设备
通过设备编号获取PWM控制权:
C
struct pwm_device *pwm0 = pwm_request(1, "my_pwm");
// 参数1:PWM编号(如1代表PWM1)
// 参数2:设备名称(用于标识,可自定义)
1
2
3
2
3
3. 配置PWM参数
设置PWM的周期和占空比(单位:纳秒):
C
pwm_config(pwm0, 500000, 1000000);
// 第一个参数:高电平持续时间(如500000ns = 0.5ms)
// 第二参数:总周期时间(如1000000ns = 1ms)
// 占空比 = (500000 / 1000000)*100% = 50%
1
2
3
4
2
3
4
4. 启用PWM输出
开始生成PWM信号:
C
pwm_enable(pwm0);
1
5. 禁用PWM输出(临时停止)
C
pwm_disable(pwm0);
1
6. 释放PWM资源
设备不再使用时,释放控制权:
C
pwm_free(pwm0);
1
:::
注意事项
顺序必须正确:必须先配置再启用,禁用后再释放
单位统一:所有时间参数都以纳秒(ns)为单位
占空比计算:占空比 = (duty_ns / period_ns)*100% :::
完整示例代码
C
#include <linux/pwm.h>
struct pwm_device *pwm0;
// 初始化PWM
pwm0 = pwm_request(1, "led_pwm");
pwm_config(pwm0, 1000000, 2000000); // 50%占空比(1ms/2ms)
pwm_enable(pwm0);
// 关闭PWM时
pwm_disable(pwm0);
pwm_free(pwm0);
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
三、接口原型
pwm.h
C
#if IS_ENABLED(CONFIG_PWM)
/* PWM user APIs */
struct pwm_device *pwm_request(int pwm_id, const char *label);
void pwm_free(struct pwm_device *pwm);
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
int pwm_adjust_config(struct pwm_device *pwm);
/**
* pwm_output_type_support()
* @pwm: PWM device
*
* Returns: output types supported by the PWM device
*/
static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
{
if (pwm->chip->ops->get_output_type_supported != NULL)
return pwm->chip->ops->get_output_type_supported(pwm->chip,
pwm);
else
return PWM_OUTPUT_FIXED;
}
/**
* pwm_config() - change a PWM device configuration
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
* @period_ns: duration (in nanoseconds) of one cycle
*
* Returns: 0 on success or a negative error code on failure.
*/
static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
{
struct pwm_state state;
if (!pwm)
return -EINVAL;
if (duty_ns < 0 || period_ns < 0)
return -EINVAL;
pwm_get_state(pwm, &state);
if (state.duty_cycle == duty_ns && state.period == period_ns)
return 0;
state.duty_cycle = duty_ns;
state.period = period_ns;
return pwm_apply_state(pwm, &state);
}
/**
* pwm_config_extend() - change PWM period and duty length with u64 data type
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
* @period_ns: duration (in nanoseconds) of one cycle
*
* Returns: 0 on success or a negative error code on failure.
*/
static inline int pwm_config_extend(struct pwm_device *pwm, u64 duty_ns,
u64 period_ns)
{
struct pwm_state state;
if (!pwm)
return -EINVAL;
pwm_get_state(pwm, &state);
if (state.duty_cycle == duty_ns && state.period == period_ns)
return 0;
state.duty_cycle = duty_ns;
state.period = period_ns;
return pwm_apply_state(pwm, &state);
}
/**
* pwm_set_polarity() - configure the polarity of a PWM signal
* @pwm: PWM device
* @polarity: new polarity of the PWM signal
*
* Note that the polarity cannot be configured while the PWM device is
* enabled.
*
* Returns: 0 on success or a negative error code on failure.
*/
static inline int pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
{
struct pwm_state state;
if (!pwm)
return -EINVAL;
pwm_get_state(pwm, &state);
if (state.polarity == polarity)
return 0;
/*
* Changing the polarity of a running PWM without adjusting the
* dutycycle/period value is a bit risky (can introduce glitches).
* Return -EBUSY in this case.
* Note that this is allowed when using pwm_apply_state() because
* the user specifies all the parameters.
*/
if (state.enabled)
return -EBUSY;
state.polarity = polarity;
return pwm_apply_state(pwm, &state);
}
/**
* pwm_enable() - start a PWM output toggling
* @pwm: PWM device
*
* Returns: 0 on success or a negative error code on failure.
*/
static inline int pwm_enable(struct pwm_device *pwm)
{
struct pwm_state state;
if (!pwm)
return -EINVAL;
pwm_get_state(pwm, &state);
if (state.enabled)
return 0;
state.enabled = true;
return pwm_apply_state(pwm, &state);
}
/**
* pwm_disable() - stop a PWM output toggling
* @pwm: PWM device
*/
static inline void pwm_disable(struct pwm_device *pwm)
{
struct pwm_state state;
if (!pwm)
return;
pwm_get_state(pwm, &state);
if (!state.enabled)
return;
state.enabled = false;
pwm_apply_state(pwm, &state);
}
/* PWM provider APIs */
int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
unsigned long timeout);
int pwm_set_chip_data(struct pwm_device *pwm, void *data);
void *pwm_get_chip_data(struct pwm_device *pwm);
int pwmchip_add_with_polarity(struct pwm_chip *chip,
enum pwm_polarity polarity);
int pwmchip_add(struct pwm_chip *chip);
int pwmchip_remove(struct pwm_chip *chip);
struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
unsigned int index,
const char *label);
struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args);
struct pwm_device *pwm_get(struct device *dev, const char *con_id);
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id);
void pwm_put(struct pwm_device *pwm);
struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
const char *con_id);
void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
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
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