PWM - 脉冲宽度调制
脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法。 通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。
PWM模块属于Linux PWM子系统,会调用PWM子系统的相关接口。
不同平台上拥有不同个数的PWM通道,其中两个为一个PWM对。其中PWM具有以下特点:
- 支持脉冲,周期和互补对输出;
- 支出捕捉输入;
- 带可编程死区发生器,死区时间可控;
- 0-100M输出频率范围。0%-100%占空比可调,最小分辨率1/65536;
- 支持PWM输出和捕捉输入产生中断;
- 组模式下各通道输出波形相对相位可配置。
模块配置
驱动配置
驱动位于
Allwinner BSP --->
Device Drivers --->
PWM Drivers ->
<*> PWM Support for Allwinner SoCs
< > Soft-PWM Support for Allwinner SoCs

设备树配置
PWM 控制器节点
这里以 PWM0 控制器作为示例说明
pwm0: pwm@42000c00 {
#pwm-cells = <0x3>;
compatible = "allwinner,sunxi-pwm-v205";
reg = <0x0 0x42000c00 0x0 0x400>;
clocks = <&ccu CLK_PWM>, <&aon_ccu CLK_DCXO>;
clock-names = "bus", "clk_hosc";
resets = <&ccu RST_BUS_PWM>;
interrupts-extended = <&plic0 19 IRQ_TYPE_LEVEL_HIGH>;
pwm-number = <12>;
pwm-base = <0x0>;
sunxi-pwms = <&pwm0_0>, <&pwm0_1>, <&pwm0_2>, <&pwm0_3>, <&pwm0_4>,
<&pwm0_5>, <&pwm0_6>, <&pwm0_7>, <&pwm0_8>,
<&pwm0_9>, <&pwm0_10>, <&pwm0_11>;
status = "okay";
};
compatible表示用哪套设备和驱动绑定reg寄存器的基地址clocks时钟配置interrupts中断号的配置resets复位配置pwm-number控制器的pwm通道个数pwm-basepwm的起始基数sunxi-pwms控制器的具体通道
PWM 通道配置
pwm0_0: pwm0@42000c10 {
compatible = "allwinner,sunxi-pwm0";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c10 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_1: pwm0@42000c11 {
compatible = "allwinner,sunxi-pwm1";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c11 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_2: pwm0@42000c12 {
compatible = "allwinner,sunxi-pwm2";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c12 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_3: pwm0@42000c13 {
compatible = "allwinner,sunxi-pwm3";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c13 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_4: pwm0@42000c14 {
compatible = "allwinner,sunxi-pwm4";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c14 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_5: pwm0@42000c15 {
compatible = "allwinner,sunxi-pwm5";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c15 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_6: pwm0@42000c16 {
compatible = "allwinner,sunxi-pwm6";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c16 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_7: pwm0@42000c17 {
compatible = "allwinner,sunxi-pwm7";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c17 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_8: pwm0@42000c18 {
compatible = "allwinner,sunxi-pwm8";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c18 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_9: pwm0@42000c19 {
compatible = "allwinner,sunxi-pwm9";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c19 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_10: pwm0@42000c1a {
compatible = "allwinner,sunxi-pwm10";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c1a 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
pwm0_11: pwm0@42000c1b {
compatible = "allwinner,sunxi-pwm11";
pinctrl-names = "active", "sleep";
reg = <0x0 0x42000c1b 0x0 0x4>;
reg_base = <0x42000c00>;
status = "disabled";
};
具体通道配置按照需求进行配置。
pinctrl-names:分别表示pwm的io口的两种工作状态;pinctrl-0、pinctrl-1:pwm两种工作状态的引脚参数设置;status:模块设备的打开或关闭,当编译该模块时,status="okay";不编译时,status="disabled";。
设备端配置
设备端主要配置 PWM 的引脚,配置每个 PWM 是否启用功能。
示例配置,具体引脚复用请参照芯片手册
&pio {
pwm8_pins_active: pwm8@0 {
pins = "PD18";
function = "pwm0_8";
};
pwm8_pins_sleep: pwm8@1 {
pins = "PD18";
function = "gpio_in";
bias-pull-down;
};
};
&pwm0_8 {
pinctrl-names = "active", "sleep";
pinctrl-0 = <&pwm8_pins_active>;
pinctrl-1 = <&pwm8_pins_sleep>;
status = "okay";
};
模块驱动
PWM驱动的源代码位于BSP独立仓库的 drivers/pwm 目录下,具体的路径如下所示:
.
├── Kconfig
├── Makefile
├── pwm-sunxi.c
├── pwm-sunxi.h
└── sunxi-soft-pwm.c
用户层接口
可以直接在linux内核中调试pwm模块,具体如下:
- 进入
/sys/class/pwm目录。该目录是 linux 内核为 pwm 子系统提供的类目录,遍历该目录。可看到很多的 pwmchipX,其中X表示很多能够被控制的通道,下面以 pwmchip0 为例进行说明。
/sys/class/pwm # ls
pwmchip0
- 可以看到,上述 pwmchip0 就是我们注册的 pwm 控制器,进入该目录,然后遍历该目录。
/sys/class/pwm # cd pwmchip0/
/pwm/pwmchip0 # ls
device export npwm subsystem uevent unexport
- 其中
npwm文件储存了该pwm控制器的pwm个数,而export和unexport是导出和删除某个pwm设备的文件,下面演示导出pwm1。
/pwm/pwmchip0 # cat npwm
2
/pwm/pwmchip0 # echo 1 > export
/pwm/pwmchip0 # ls
device export npwm pwm1 subsystem uevent unexport
可以看到目录中多出pwm1目录,进入该目录显示出不同的节点。不同节点的参数如下:
- enable:使能pwm,其中1代表使能,0代表不使能;
- duty_cycle:pwm信号的占空比,单位为(ns);
- period:pwm信号的频率,单位为(ns);
- polarity:是否翻转极性,其中1表示翻转极性,0表示不翻转极性。
/pwm/pwmchip0 # cd pwm1/
/pwm/pwmchip0/pwm1 # ls
capture duty_cycle enable period polarity uevent
可通过以上节点来对pwm的状态进行改变:
/pwm/pwmchip0/pwm1 # echo 1000000000 > period
/pwm/pwmchip0/pwm1 # echo 500000000 > duty_cycle
/pwm/pwmchip0/pwm1 # echo normal > polarity
/pwm/pwmchip0/pwm1 # echo 1 > enable
如果相关引脚接上了示波器等,可以看到波形。最后返回上层目录,删除该pwm设备。
/pwm/pwmchip0/pwm1 # cd ..
/pwm/pwmchip0 # ls
device export npwm pwm1 subsystem uevent unexport
/pwm/pwmchip0 # echo 1 > unexport
/pwm/pwmchip0 # ls
device export npwm subsystem uevent unexport
内核层接口
内核调用的接口如下
devm_pwm_get
- 函数原型:
struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)。
-
作用: 请求pwm设备。
-
参数:
- dev: 指向当前PWM设备的实例;
- con_id: 使用方绑定的名称;
-
返回值:
- 成功:返回请求成功的pwm设备;
- 失败:返回错误码
pwm_config
- 函数原型
static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-
作用: 更改PWM设备配置。
-
参数:
- dev: 指向当前PWM设备的实例;
- duty_ns: 占空比;
- period_ns:周期
-
返回值:
- 成功:0;
- 失败:负值错误码
pwm_enable
- 函数原型:
static inline int pwm_enable(struct pwm_device *pwm)
- 作用: 启动PWM输出。
- 参数: pwm: 指向当前PWM设备的实例;
- 返回值:
- 成功:0;
- 失败:负值错误码