【北京迅为】嵌入式学习之Linux系统编程篇 https://www.bilibili.com/video/BV1zV411e7Cy/ 个人学习笔记
文章目录
- 在设备树打开 PWM
- sysfs 方式控制 PWM
- PWM 应用编程
在设备树打开 PWM
RK3568 有 16 个 PWM 控制器,本文件将以 PWM0 为例进行实验,为什么一定要用 PWM0 呢?之前 LED 和 GPIO 我们都是用 GPIO0_B7 作为实验对象,其实这个 IO 还有 PWM 功能:
iTOP-3568 开发板的 PWM0 默认是关闭的,需要修改设备树,将其打开(将 pwm0 节点的 status
属性改为 “okay”),
在 pwm0 节点中,通过 pinctrl 指定其 pin 脚为 pwm0m0_pins
,pwm0m0_pins 定义在 rk3568-pinctrl.dtsi 中,我们可以看到 pwm0m0_pins 对应的 pin 就是 GPIO0_B7,
最后,还需要屏蔽设备树中其他用到 GPIO0_B7 的配置,比如 leds(前面 GPIO 编程时已经进行了屏蔽)
sysfs 方式控制 PWM
进入 /sys/class/pwm 目录,可以看到多个 pwmchipx,x 为编号数字,我的开发板上有 4个 pwmchipx,说明设备树中打开了 4 个 PWM,
系统会将已经打开的 PWM 按照 PWM 编号顺序放置在 /sys/class/pwm 目录,所以 PWM0 对应的是 pwmchip0(但 pwmchip1 不一定对应 PWM1,因为 PWM1 可能没有被开启),进入 pwmchip0,
该目录下有七个文件,比较重要的是 export,npwm,和 unexport 三个文件,
npwm 是一个只读文件,可以通过该文件读到 PWM 控制器下有多少路 PWM 输出,当前我的系统读出来的值是 1
export 在 GPIO 编程中有学到这个文件,用于导出一个新的可操作文件,比如 echo 0 > export
会在 pwmchip0 目录下新建一个 pwm0 文件夹(由于上面已经知晓 npwm 为 1,所以只能导出 pwm0,不存在 pwm1…)
unexport 是 export 的反向操作,用于删除已经导出的 pwm 设备,它们都是只写文件,
进入 pwm0 目录,又能看到七个文件,比较重要的是 polarity,period,duty_cycle 和 enable,
duty_cycle 用于配置 PWM 占空比,单位为 ns,
enable 为 1 时使能 PWM 输出,为 0 时关闭 PWM 输出,
period 用于配置 PWM 周期,单位为 ns,
polarity 用于配置 PWM 极性,“normal” (普通输出),“inversed” (反转输出)。
Tip:必须先设置 period 周期后才可以设置其他属性,
PWM 应用编程
实验代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define PWM_PATH "/sys/class/pwm/pwmchip0/pwm0"
int main(int argc, char** argv)
{
int fd1, fd2, fd3;
int ret = 0;
char tmp_path[60] = {0};
// 参数个数判断
if(argc != 4)
{
printf("The right format: ./app period duty enable.\n");
return 0;
}
// PWM_PATH 不存在
if(access(PWM_PATH, F_OK))
{
int fd, ret;
fd = open("/sys/class/pwm/pwmchip0/export", O_WRONLY);
if(fd < 0)
{
printf("/sys/class/pwm/pwmchip0/export open failed.\n");
return 0;
}
printf("/sys/class/pwm/pwmchip0/export open successfully.\n");
ret = write(fd, "0", 1);
if(ret < 0)
{
printf("/sys/class/pwm/pwmchip0/export write failed.\n");
return 0;
}
close(fd);
}
// 打开 /sys/class/pwm/pwmchip0/pwm0/period
sprintf(tmp_path, "%s/period", PWM_PATH);
fd1 = open(tmp_path, O_WRONLY);
if(fd1 < 0)
{
printf("%s open failed.\n", tmp_path);
return 0;
}
printf("%s open successfully.\n", tmp_path);
// 打开 /sys/class/pwm/pwmchip0/pwm0/duty_cycle
sprintf(tmp_path, "%s/duty_cycle", PWM_PATH);
fd2 = open(tmp_path, O_WRONLY);
if(fd2 < 0)
{
printf("%s open failed.\n", tmp_path);
return 0;
}
printf("%s open successfully.\n", tmp_path);
// 打开 /sys/class/pwm/pwmchip0/pwm0/enable
sprintf(tmp_path, "%s/enable", PWM_PATH);
fd3 = open(tmp_path, O_WRONLY);
if(fd3 < 0)
{
printf("%s open failed.\n", tmp_path);
return 0;
}
printf("%s open successfully.\n", tmp_path);
// 设置 period
ret = write(fd1, argv[1], strlen(argv[1]));
if(ret < 0)
{
printf("%s/period write failed.\n", PWM_PATH);
return 0;
}
printf("%s/period write successfully.\n", PWM_PATH);
// 设置 duty_cycle
ret = write(fd2, argv[2], strlen(argv[2]));
if(ret < 0)
{
printf("%s/duty_cycle write failed.\n", PWM_PATH);
return 0;
}
printf("%s/duty_cycle write successfully.\n", PWM_PATH);
// 设置 enable
ret = write(fd3, argv[3], strlen(argv[3]));
if(ret < 0)
{
printf("%s/enable write failed.\n", PWM_PATH);
return 0;
}
printf("%s/enable write successfully.\n", PWM_PATH);
// 关闭设备文件
close(fd1);
close(fd2);
close(fd3);
return 0;
}
实验结果:
上面三种情况下 LED 现象:(/sys/class/pwm/pwmchip0/pwm0/polarity 为 “normal” 的前提下测试)
第一种情况:LED 微亮,第二种情况:LED 高亮,第三种情况:LED 灭。
发现一件奇怪的事,polarity 默认为 inversed(反向),这种情况下,先控制 PWM 让 LED 点亮,然后将 enable 置 0 或 置 1,LED 都不会灭,且后续再操作 pwm 会出现失灵现象。