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.3. PWM的周期

T=1/f,T是周期,f是频率。

如果频率为50Hz ,也就是说一个周期是20ms,那么一秒钟就有 50次PWM周期。

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控制器会不一样,请对照下表使用

LubanCat-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-RK356x系列-40pin引脚对照图

LubanCat-RK3588系列-40pin引脚对照图

14.2.1. 使能PWM接口功能

PWM接口在默认情况是关闭状态的,需要使能才能使用, 每个板卡都具有四个硬件PWM,下面的使能操作以 PWM8PWM9 为例

14.2.1.1. 方法一

1
2
3
4
5
#进入工具配置
sudo fire-config

#移动光标到下图的位置
#按确认键进入配置
未找到图片

打开PWM8和PWM9

  1. 使用方向键移动光标到 PWM8

  2. “空格键” 选中PWM8(出现 “*” ),如下图

  3. 使用方向键移动光标到 PWM9

  4. “空格键” 选中PWM9(出现 “*” ),如下图

  5. “确认键” 进行设置

  6. “Esc键” 退出到终端,运行 sudo reboot 进行重启应用

未找到图片

14.2.1.2. 方法二

LubanCat各设备的配置文件

板子名称

配置文件名称

说明

当前使用的板卡

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

可以通过打开 /boot/uEnv/board.txt (board是你所用的板子的名称),一般第一次启动已经初始化将板级uEnv.txt软连接到了/boot/uEnv/uEnv.txt,可以直接修改该文件。

查看是否启用了pwm相关设备设备树插件。

编辑文件,将带有 pwm8pwm9 的两行的注释符号去掉 如下图:

未找到图片

然后重启激活设备

注解

如果是直接拔电源的方式重启,会有可能出现文件没能做出修改 (原因:文件未能及时从内存同步到存储设备中,解决方法,在终端上输入 “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控制方式(系统调用)

base_linux/pwm/pwm_test.c
 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