14. pwm(脉宽调制)¶
本章讲解Linux pwm相关应用层程序控制。
本章的示例代码目录为:base_linux/pwm
14.1. 脉宽调制¶
14.1.1. 什么是PWM¶
PWM(Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术, 广泛应用在测量、通信、工控等方面。
14.1.2. PWM的频率¶
是指在1秒钟内,信号从高电平到低电平再回到高电平的次数,也就是说一秒钟PWM有多少个周期,单位Hz。
14.1.4. 占空比¶
是一个脉冲周期内,高电平的时间与整个周期时间的比例,单位是% (0%-100%)
一个周期的长度,如下图所示
其中,周期是一个脉冲信号的时间,1s内的周期T次数等于频率f,脉宽时间是指高电平时间。
上图中,脉宽时间占总周期时间的比例,就是占空比。
比方说,周期的时间是10ms,脉宽时间是8ms,那么占空比是8/10= 80%,这就是占空比为80%的脉冲信号。
PWM就是脉冲宽度调制,通过调节占空比就可以调节脉冲宽度。
14.1.5. PWM原理¶
假设高电平为5V、低电平则为0V,那么要输出不同的模拟电压就要用到PWM。
通过改变IO口输出的方波的占空比,从而获得使用数字信号模拟成的模拟电压信号。
电压是以一种脉冲序列被加到模拟负载上去的,接通时是高电平1,断开时是低电平0。
接通时直流供电输出,断开时直流供电断开。通过对接通和断开时间的控制,理论上来讲,可以输出任意不大于最大电压值5V的模拟电压。
比方说,占空比为50%那就是高电平时间一半,低电平时间一半。在一定的频率下,就可以得到模拟的2.5V输出电压。那么75%的占空比,得到的电压就是3.75V,如下图所示。
14.2. pwm引脚¶
LubanCat板卡上集成了四个具有pwm功能的GPIO
由于LubanCat-RK系列的板子的PWM控制器会不一样,请对照下表使用
板卡 |
Pin12 |
Pin32 |
Pin33 |
Pin35 |
---|---|---|---|---|
LubanCat-Zero系列 |
pwm3 |
pwm11 |
pwm8 |
pwm9 |
LubanCat-1系列 |
pwm8 |
pwm9 |
pwm10 |
pwm14 |
LubanCat-2系列 |
pwm8 |
pwm9 |
pwm10 |
pwm14 |
LubanCat-4 |
pwm14 |
pwm15 |
pwm10 |
pwm11 |
具体引脚可以查看快速使用手册:
LubanCat-Q1系列的板子的PWM控制器与LubanCat-RK系列板子的PWM控制器不一样,请对照下表使用
板卡 |
Pin3 |
Pin5 |
Pin12 |
Pin29 |
---|---|---|---|---|
LubanCat-Q1系列 |
pwm5_M1 |
pwm6_M2 |
pwm0_M0 |
pwm6_M0 |
14.2.1. 使能PWM接口功能¶
PWM接口在默认情况是关闭状态的,需要使能才能使用, 每个板卡都具有四个硬件PWM,下面的使能操作以 PWM8 和 PWM9 为例
14.2.1.1. 方法一¶
1 2 3 4 5 | #进入工具配置
sudo fire-config
#移动光标到下图的位置
#按确认键进入配置
|
打开PWM8和PWM9
使用方向键移动光标到
PWM8
按 “空格键” 选中PWM8(出现 “*” ),如下图
使用方向键移动光标到
PWM9
按 “空格键” 选中PWM9(出现 “*” ),如下图
按 “确认键” 进行设置
按 “Esc键” 退出到终端,运行 sudo reboot 进行重启应用
14.2.1.2. 方法二¶
板子名称 |
配置文件名称 |
说明 |
---|---|---|
当前使用的板卡 |
uEnv.txt |
系统会自动把板卡的配置文件链接到该文件 |
LubanCat-RK |
uEnvLubanCat-series.txt |
通用板卡的配置文件,用于第一次启动配置系统 |
Lubancat-Zero-N |
uEnvLubanCatZN.txt |
适用于EBF410068 EBF410068V1 |
Lubancat-Zero-W |
uEnvLubanCatZW.txt |
适用于EBF410067 EBF410067V1 |
Lubancat-1 |
uEnvLubanCat1.txt |
适用于EBF410077 EBF410077V1 EBF410077V2 |
Lubancat-1N |
uEnvLubanCat1N.txt |
适用于EBF410052 EBF410052V1 |
Lubancat-1金手指/BTB |
uEnvLubanCat1IO.txt |
适用于EBF410090 EBF410132 |
Lubancat-2 |
uEnvLubanCat2.txt |
适用于EBF410044 |
Lubancat-2V1 |
uEnvLubanCat2-V1.txt |
适用于EBF410044V1 |
Lubancat-2V2 |
uEnvLubanCat2-V2.txt |
适用于EBF410044V2 |
Lubancat-2-n |
uEnvLubanCat2N.txt |
适用于EBF410076 EBF410076V1 |
Lubancat-2金手指/BTB |
uEnvLubanCat2IO.txt |
适用于EBF410298 EBF410297 |
Lubancat-4 |
uEnvLubanCat4.txt |
适用于EBF410116 |
Lubancat-Q1 |
uEnvLubanCatQ1.txt |
适用于EBF410434 |
可以通过打开 /boot/uEnv/board.txt (board是你所用的板子的名称),一般第一次启动已经初始化将板级uEnv.txt软连接到了/boot/uEnv/uEnv.txt,可以直接修改该文件。
查看是否启用了pwm相关设备设备树插件。
编辑文件,将带有 pwm8 和 pwm9 的两行的注释符号去掉 如下图:
然后重启激活设备
注解
如果是直接拔电源的方式重启,会有可能出现文件没能做出修改 (原因:文件未能及时从内存同步到存储设备中,解决方法,在终端上输入 “sync” 再拔电关机)
14.3. 检查PWM设备¶
使能pwm通信接口后,可以用以下命令查看pwm是否开启
1 2 3 | ls /sys/class/pwm/
cat /sys/kernel/debug/pwm
|
pwmchip0为屏幕的背光,系统默认开启,当开启多个pwm设备树插件时,pwm控制器值越小,系统分配的pwmchip越小。
1 2 3 4 5 6 | 比如我同时开启了pwm8,pwm9,pwm14,那么会出现以下对应关系
pwm4->pwmchip0(屏幕背光)
pwm8->pwmchip1
pwm9->pwmchip2
pwm14->pwmchip3
|
如果是鲁班猫2-V2,因为支持双mipi,系统默认开启了两个背光,分别是PWM4以及PWM5。
如果是鲁班猫4,系统默认开启风扇的PWM,已经两个mipi屏幕的背光PWM,如果打开PWM10和PWM14设备树插件后,效果如下图。
1 2 3 4 5 | pwm0->pwmchip0(风扇)
pwm2->pwmchip1(屏幕背光1)
pwm6->pwmchip2(屏幕背光2)
pwm10->pwmchip3
pwm14->pwmchip4
|
14.4. pwm控制方式(shell)¶
下面操作以pwm8为例
注意
在操作前必须先打开设备树插件,重启使能pwm8引脚
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #将pwm3导出到用户空间
echo 0 > /sys/class/pwm/pwmchip1/export
#设置pwm周期 单位为ns
echo 1000000 > /sys/class/pwm/pwmchip1/pwm0/period
#设置占空比
echo 500000 > /sys/class/pwm/pwmchip1/pwm0/duty_cycle
#设置pwm极性
echo "normal" > /sys/class/pwm/pwmchip1/pwm0/polarity
#使能pwm
echo 1 > /sys/class/pwm/pwmchip1/pwm0/enable
#如果需要关闭PWM则将pwm3导出到用户空间
echo 0 > /sys/class/pwm/pwmchip1/unexport
|
提示
当设置period与duty_cycle值的时候需要注意在任何的情况下都得保证period的值大于等于duty_cycle的值。
14.5. 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 | #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[75];
static int pwm_config(const char *attr, const char *val) //配置PWM
{
char file_path[100];
int len;
int fd;
sprintf(file_path, "%s/%s", pwm_path, attr);
if ((fd = open(file_path, O_WRONLY))< 0) {
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]);
//如果 pwm0 目录不存在, 则导出
if (access(pwm_path, F_OK)) {
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);
}
//导出 pwm
if (1 != write(fd, "0", 1)) {
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");
getchar();
/* 退出程序 */
exit(0);
}
|
方法1:
1 | make
|
方法2:
1 | gcc pwm_test.c -o pwm_test
|
1 2 3 4 | #设置pwm周期 , pwm占空比
#./pwm_test pwmchip2 周期 占空比
sudo ./pwm_test 2 1000000 500000
|