PWM 应用编程
应用层如何操控 PWM
PWM 控制方式
-
通过 sysfs 进行操作,目录为 /sys/class/pwm
- /sys/class/pwm 目录下的内容
-
包含 8 个 pwmchipX 文件夹,对应 I.MX6U 的 8 个 PWM 控制器
- pwmchip0 目录下的内容
PWM 控制器属性文件
-
npwm:只读,显示该 PWM 控制器下的 PWM 输出路数,I.MX6U 每个控制器只有 1 路 PWM 输出
- 读取 npwm 属性文件
-
export:导出 PWM 控制器,命令示例:echo 0 > export
- 导出 PWM
-
unexport:删除导出的 PWM 控制器,命令示例:echo 0 > unexport
PWM 控制过程
-
导出后会生成 pwm0 目录,包含以下属性文件
- pwm0 目录下的内容
-
enable:控制 PWM 的使能状态,写入 “0” 禁用,写入 “1” 使能
-
polarity:设置 PWM 极性,“normal” 为普通,“inversed” 为反转
- 很多 SoC 的 PWM 外设其硬件上并不支持极性配置,所以对应的驱动程序中并未实现这个接口
-
period:配置 PWM 周期,单位为纳秒,例如:echo 10000 > period 设置周期为 10 微秒
-
duty_cycle:配置 PWM 占空比,单位为纳秒,例如:echo 5000 > duty_cycle 设置占空比为 5 微秒
注意事项
- I.MX6U 的 PWM1 已用于 LCD 背光控制,应用层不能直接控制,其他 PWM 因 I/O 资源限制无法使用
编写应用程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
static char pwm_path[100]; // 静态字符数组,用于存储PWM设备路径
//配置PWM的属性
static int pwm_config(const char *attr, const char *val)
{
char file_path[100]; // 字符数组,用于存储属性文件路径
int len; // 整数变量,用于存储属性值的长度
int fd;
sprintf(file_path, "%s/%s", pwm_path, attr);
if (0 > (fd = open(file_path, O_WRONLY))) {
perror("open error");
return fd;
}
len = strlen(val); // 获取属性值的长度
if (len != write(fd, val, len)) {
perror("write error");
close(fd);
return -1;
}
close(fd); //关闭文件
return 0;
}
int main(int argc, char *argv[])
{
/* 校验传参 */
if (4 != argc) {
fprintf(stderr, "usage: %s <id> <period> <duty>\n",
argv[0]);
exit(-1);
}
/* 打印配置信息 */
printf("PWM config: id<%s>, period<%s>, duty<%s>\n",
argv[1], argv[2],
argv[3]);
/* 导出pwm */
sprintf(pwm_path, "/sys/class/pwm/pwmchip%s/pwm0", argv[1]);
//access 函数用于检查文件是否存在或是否具有某种访问权限
//F_OK:检查文件是否存在
if (access(pwm_path, F_OK)) {//如果pwm0目录不存在, 则导出
char temp[100]; // 临时字符数组,用于存储导出路径
int fd;
sprintf(temp, "/sys/class/pwm/pwmchip%s/export", argv[1]);
if (0 > (fd = open(temp, O_WRONLY))) {
perror("open error");
exit(-1);
}
if (1 != write(fd, "0", 1)) {//导出pwm
perror("write error");
close(fd);
exit(-1);
}
close(fd); //关闭文件
}
/* 配置PWM周期 */
if (pwm_config("period", argv[2]))
exit(-1);
/* 配置占空比 */
if (pwm_config("duty_cycle", argv[3]))
exit(-1);
/* 使能pwm */
pwm_config("enable", "1");
/* 退出程序 */
exit(0);
}
校验传入的参数数量是否为4
- 如果不是则打印使用说明并退出程序
打印PWM配置信息
- 打印PWM的ID、周期和占空比信息
生成PWM设备路径,格式为 /sys/class/pwm/pwmchip{id}/pwm0
使用 access 函数检查PWM设备路径是否存在
- 如果不存在,则生成导出路径并使用 open 和 write 函数导出PWM
调用 pwm_config 函数配置PWM的周期、占空比和使能状态
-
pwm_config 函数
-
使用 sprintf 生成属性文件路径
-
以只写模式打开属性文件,如果失败则打印错误信息并返回错误码
-
获取属性值的长度,并将属性值写入文件。如果写入长度不匹配则打印错误信息并返回错误码
-
关闭文件并返回成功码(0)
-
-
如果失败则退出程序
退出程序
在开发板上测试
正点原子开发板出厂系统无法使用PWM,需要修改内核源码和设备树
修改设备树方法
-
禁用LCD和backlight设备(设置status为disabled)
-
重新编译设备树并替换开发板启动文件中的dtb文件
-
参考《I.MX6U嵌入式Linux驱动开发指南》第七十三章内容自行配置PWM
简单方法
-
不用重新编译设备树,直接在u-boot命令行模式下禁用LCD和backlight设备,腾出PWM1供测试
-
步骤
-
重启开发板,进入u-boot命令行模式
-
修改内核设备树文件,将LCD和backlight设备的status属性改为“disabled”
-
SD/eMMC启动方式
-
setenv disable_lcd ‘fdt addr ${fdt_addr}; fdt set /backlight status disable; fdt set /soc/aips-bus@02100000/lcdif@021c8000 status disable’
-
在u-boot环境中设置一个名为disable_lcd的环境变量,其值是一系列用于操作设备树(Device Tree)的命令
-
setenv disable_lcd
-
setenv 是u-boot中的一个命令,用于设置环境变量
-
disable_lcd 是这个环境变量的名称
-
-
'fdt addr ${fdt_addr};
-
fdt 是u-boot中用于操作设备树的命令
-
addr 是fdt命令的一个子命令,用于设置设备树的地址
-
${fdt_addr} 是一个环境变量,表示设备树(dtb文件)在内存中的地址
-
-
fdt set /backlight status disable;
-
set 是fdt命令的一个子命令,用于修改设备树中的属性
-
/backlight 是设备树中的一个节点路径,表示背光设备
-
status disable 是将/backlight节点的status属性设置为disable,表示禁用这个设备
-
-
fdt set /soc/aips-bus@02100000/lcdif@021c8000 status disable’
-
这是另一个fdt set命令,用于修改设备树中的另一个节点
-
/soc/aips-bus@02100000/lcdif@021c8000 是设备树中的一个节点路径,表示LCD接口设备
-
status disable 是将/soc/aips-bus@02100000/lcdif@021c8000节点的status属性设置为disable,表示禁用这个设备
-
-
-
setenv mmcboot ‘echo Booting from mmc …; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try;
then if run loadfdt; then run disable_lcd; bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz;
else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;’
-
-
NAND启动方式
-
setenv disable_lcd ‘fdt addr ${fdt_addr}; fdt set /backlight status disable; fdt set /soc/aips- bus@02100000/lcdif@021c8000 status disable’
-
setenv bootcmd ‘nand read ${loadaddr} 0x620000 0x800000;nand read ${fdt_addr} ${fdt_offset} 0x20000; run
disable_lcd; bootz ${loadaddr} - ${fdt_addr}’
-
-
-
执行两条命令后,执行boot命令启动开发板
-
-
注意事项
- 这种修改方式只对本次启动生效,重启后恢复原状态