23 Linux PWM 驱动
23.1 什么是 PWM?
PWM(Pulse Width Modulation,脉宽调制)是一种通过调节信号的占空比(Duty Cycle)来控制能量输送的技术。它广泛应用于电机速度控制、LED 亮度调节、信号调制、音频生成等领域。PWM 的主要优势是可以有效地控制模拟设备的工作状态,同时保持较高的效率。
23.2 PWM 的用途
电机控制:通过调节 PWM 占空比,可以控制电机的转速。占空比越大,电机转速越高。
LED 亮度调节:通过改变 PWM 占空比可以调节 LED 的亮度。
音频生成:PWM 可以用来生成一定频率的方波,从而实现简单的音频输出。
伺服电机控制:PWM 信号常用于控制 RC 伺服电机的位置。
23.3 Linux Kernel 中的 PWM 支持
Linux 内核提供了一个统一的 PWM 子系统,开发者可以使用它来控制不同硬件平台的 PWM 控制器。通过内核的 PWM 框架,可以将硬件 PWM 抽象成标准的接口,方便用户空间的访问与使用。
23.3.1 配置 Linux Kernel PWM 选项
在使用 Linux 内核的 PWM 功能之前,需要确保内核中已经启用了相应的 PWM 驱动和子系统。下面是配置内核支持 PWM 的步骤:
) 启用 PWM 子系统: 在配置菜单中,找到以下路径并启用 PWM 子系统:
-> Device Drivers
-> Pulse-width modulation (PWM) Support
-> Enable the PWM framework (CONFIG_PWM)
) 选择硬件平台的 PWM 驱动: 在同一个菜单下,可以选择与硬件平台对应的 PWM 驱动。例如,Rockchip的 PWM 驱动位于:
Device Drivers -> PWM Support -> Rockchip PWM support (CONFIG_PWM_ROCKCHIP)
-> GPIO PWM support (CONFIG_PWM_GPIO)
注意, CONFIG_PWM_GPIO需要打上23.4.3所提到的patch。 重新编译Kernel,并将新编译的内核安装并重启系统。
23.4 使用 GPIO 作为 PWM 输出
如果系统没有专用的 PWM 控制器,或是需要在简单场景下使用 PWM,普通的 GPIO 也可以用于软件实现 PWM。
23.4.1 软件 PWM 的实现
普通的 GPIO 可以通过软件控制高低电平切换来模拟 PWM 信号。Linux 内核提供了 gpio-pwm 模块,允许你使用 GPIO 实现类似的 PWM 控制。
可以编写一个简单的 Linux 内核模块来模拟 PWM,具体代码可以参考之前提到的例子(定时器 + GPIO 切换高低电平)。
23.4.2 设备树(Device Tree)配置 GPIO PWM
为了在 Linux 中使用 GPIO 生成 PWM,需要在设备树中为 GPIO 配置 PWM 节点。如下:
pwm0: pwm {
compatible = "pwm-gpio";
pwm-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>; // 使用 GPIO 38
pwm-freq = <1000>; // 1kHz PWM 频率
};
23.4.3 更改kernel pwm代码
1.) kernel/drivers/pwm/Kconfig
diff --git a/kernel/drivers/pwm/Kconfig b/kernel/drivers/pwm/Kconfig
index ca5db4311..b4dcd70bc 100644
--- a/kernel/drivers/pwm/Kconfig
+++ b/kernel/drivers/pwm/Kconfig
@@ -401,6 +401,12 @@ config PWM_RENESAS_TPU
To compile this driver as a module, choose M here: the module
will be called pwm-renesas-tpu.
+config PWM_GPIO
+ tristate "GPIO PWM support"
+ help
+ Generic PWM framework driver for the PWM controller found on
+ GPIO.
+
config PWM_ROCKCHIP
tristate "Rockchip PWM support"
depends on ARCH_ROCKCHIP || COMPILE_TEST
2.) 更改kernel/drivers/pwm/Makefile
diff --git a/kernel/drivers/pwm/Makefile b/kernel/drivers/pwm/Makefile
index cbdcd55d6..106d6bb5d 100644
--- a/kernel/drivers/pwm/Makefile
+++ b/kernel/drivers/pwm/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_PWM_TWL) += pwm-twl.o
obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
obj-$(CONFIG_PWM_ZX) += pwm-zx.o
+obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o
3.) 修改kernel/drivers/pwm/pwm-gpio.c
diff --git a/kernel/drivers/pwm/pwm-gpio.c b/kernel/drivers/pwm/pwm-gpio.c
index 92963771d..8cf3750cf 100644
--- a/kernel/drivers/pwm/pwm-gpio.c
+++ b/kernel/drivers/pwm/pwm-gpio.c
@@ -157,7 +157,7 @@ static int gpio_pwm_probe(struct platform_device *pdev)
pc->chip.npwm = 1;
pc->chip.of_xlate = of_pwm_xlate_with_flags;
pc->chip.of_pwm_n_cells = 3;
- pc->chip.can_sleep = true;
+ //pc->chip.can_sleep = true;
pc->gpiod = devm_gpiod_get(&pdev->dev, "pwm", GPIOD_OUT_LOW);
23.5 测试PWM
内核启动并加载 PWM 驱动后,可以通过用户空间接口(如 sysfs)来控制 PWM。通常,PWM 控制接口位于 /sys/class/pwm/ 目录下。
root@LPA3588:/sys/class/pwm/pwmchip1# ls -l
total 0
lrwxrwxrwx 1 root root 0 Sep 27 17:13 device -> ../../../pwm
--w------- 1 root root 4096 Sep 27 17:14 export
-r--r--r-- 1 root root 4096 Sep 27 17:13 npwm
drwxr-xr-x 2 root root 0 Sep 27 17:13 power
drwxr-xr-x 3 root root 0 Sep 27 17:14 pwm0
lrwxrwxrwx 1 root root 0 Mar 2 2023 subsystem -> ../../../../../class/pwm
-rw-r--r-- 1 root root 4096 Mar 2 2023 uevent
--w------- 1 root root 4096 Sep 27 17:13 unexport
23.5.1 启用 PWM
使用如下命令启用一个 PWM 通道:
root@LPA3588:/sys/class/pwm/pwmchip1# echo 0 > export
root@LPA3588:/sys/class/pwm/pwmchip1# cd pwm0/
root@LPA3588:/sys/class/pwm/pwmchip1/pwm0# ls
capture duty_cycle enable output_type period polarity power uevent
root@LPA3588:/sys/class/pwm/pwmchip1/pwm0# echo 1000000 > period
root@LPA3588:/sys/class/pwm/pwmchip1/pwm0# echo 800000 > duty_cycle
root@LPA3588:/sys/class/pwm/pwmchip1/pwm0# echo 1 > enable
上面是设置周期为 1ms(单位纳秒)。
设置占空比为 80%(800us)
启用 PWM 输出
使用示波器测试波形如下:
23.6 总结
Linux 内核中的 PWM 子系统为开发者提供了强大的硬件控制能力。通过内核支持的 PWM 驱动,用户可以轻松控制不同硬件平台上的 PWM 设备。如果硬件没有专用的 PWM 控制器,普通的 GPIO 也可以通过软件控制来生成 PWM 信号。