17. pwm控制

本章讲解Linux pwm相关应用层程序控制。

17.1. 开启pwm设备树插件

野火imx6ull提供了很多的设备树插件,用于控制板子上外设的开启,默认状态下pwm相关插件是没有开启的, 因此我们先需要开启pwm相关的设备树插件。修改 /boot/uEnv.txt 文件内容,使能pwm设备树相关插件内容, 并注释原来LED设备树插件

1
2
 #dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-led.dtbo
 dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-pwm.dtbo

野火提供的pwm设备树插件引脚为RGB控制所在的三盏灯。其引脚关系如下

pwm3

用户空间

对应LED

pwm3

/sys/class/pwm/pwmchip2

LED_R

pwm7

/sys/class/pwm/pwmchip6

LED_B

pwm8

/sys/class/pwm/pwmchip7

LED_G

开启pwm设备树插件之后RGB所在引脚为电平,也就是说RGB灯默认为点亮状态。

17.2. pwm控制方式(shell)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 #将pwm3导出到用户空间
 echo 0 > /sys/class/pwm/pwmchip2/export

 #设置pwm周期 单位为ns
 echo 1000000 > /sys/class/pwm/pwmchip2/pwm0/period

 #设置占空比
 echo 500000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle

 #设置pwm极性
 echo "normal" > /sys/class/pwm/pwmchip2/pwm0/polarity

 #使能pwm
 echo 1 > /sys/class/pwm/pwmchip2/pwm0/enable

 #取消将pwm3导出到用户空间
 echo 0 > /sys/class/pwm/pwmchip2/unexport

提示

当设置period与duty_cycle值的时候需要注意在任何的情况下都得保证period的值大于等于duty_cycle的值。

17.3. pwm控制方式(系统调用)

ebf_6ull_quick_start_code/Source/pwm/pwm.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
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>

 #include <unistd.h>

 #define PWM_PATH "/sys/class/pwm/pwmchip2/"

 int main(int argc,char * argv[])
 {
     int fd;
     ssize_t ret;
     long period,duty_cycle;

     //判断参数合法性
     if( argc < 3)
     {
         printf("Please enter period and duty_cycle!\n ");
         exit(-1);
     }
     period = atol(argv[1]);
     duty_cycle = atol(argv[2]);
     if ( (period < duty_cycle)  ||
         ( strspn(argv[1], "0123456789")!= strlen(argv[1]) ) ||
         ( strspn(argv[2], "0123456789")!= strlen(argv[2]) )
         )
     {
         printf("Period or duty_cycle parameter error!\n ");
         exit(-1);
     }

     //导出用户空间
     fd = open(PWM_PATH"export",O_WRONLY);
     if( fd < 0 )
     {
         perror(PWM_PATH"export");
         goto err1;
     }

     ret = write( fd,"0",1);
     if( ret < 1 )
     {
         perror(PWM_PATH"export");
         goto err1;
     }
     close(fd);

     //设置pwm周期
     fd = open(PWM_PATH"/pwm0/period",O_RDWR);
     if( fd < 0 )
     {
         perror(PWM_PATH"/pwm0/period");
         goto err1;
     }
     ret = write( fd,argv[1],strlen(argv[1]));
     if( ret < strlen(argv[1]) )
     {
         perror(PWM_PATH"/pwm0/period");
         goto err1;
     }
     close(fd);

     //设置占空比
     fd = open(PWM_PATH"/pwm0/duty_cycle",O_RDWR);
     if( fd < 0 )
     {
         perror(PWM_PATH"/pwm0/duty_cycle");
         goto err1;
     }
     ret = write( fd,argv[2],strlen(argv[2]));
     if( ret < strlen(argv[2]) )
     {
         perror(PWM_PATH"/pwm0/duty_cycle");
         goto err1;
     }
     close(fd);

     //设置极性
     fd = open(PWM_PATH"/pwm0/polarity",O_RDWR);
     if( fd < 0 )
     {
         perror(PWM_PATH"/pwm0/polarity");
         goto err1;
     }
     ret = write( fd,"normal",strlen("normal"));
     if( ret < strlen("normal") )
     {
         perror(PWM_PATH"polarity");
         goto err1;
     }
     close(fd);

     //使能pwm
     fd = open(PWM_PATH"/pwm0/enable",O_RDWR);
     if( fd < 0 )
     {
         perror(PWM_PATH"/pwm0/enable");
         goto err1;
     }
     ret = write( fd,"1",1);
     if( ret < 1 )
     {
         perror(PWM_PATH"/pwm0/enable");
         goto err1;
     }
     close(fd);


     printf("Set pwm success! \n");

     //从用户空间删除
     fd = open(PWM_PATH"unexport",O_WRONLY);
     if( fd < 0 )
     {
         perror(PWM_PATH"unexport");
         goto err1;
     }

     ret = write( fd,"0",1);
     if( ret < 1 )
     {
         perror(PWM_PATH"unexport");
         goto err1;
     }
     close(fd);

     return 0;

 err1:

     close(fd);
     return -1;
 }

17.4. 修改pwm相关设备树

imx6ull提供了pwm1~pwm8总的8个,每个pwm能选择不同的IO引脚(具有PWM复用的引脚)作为pwm引脚。 imx-fire-pwm.dts源码如下所示

/arch/arm/boot/dts/overlays/ebf/imx-fire-pwm.dts
 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
 #include "imx6ul-pinfunc.h"
 #include "imx6ull-pinfunc-snvs.h"
 #include "dt-bindings/input/linux-event-codes.h"
 #include "dt-bindings/gpio/gpio.h"
 #include <dt-bindings/clock/imx6ul-clock.h>
 #include "dt-bindings/interrupt-controller/irq.h"
 #include "dt-bindings/interrupt-controller/arm-gic.h"
 /dts-v1/;
 /plugin/;

 /{

     fragment@0 {
         target = <&iomuxc>;
         __overlay__ {
             pinctrl_pwm3: pwm3grp {
                 fsl,pins = <
                     MX6UL_PAD_GPIO1_IO04__pwm3_OUT   0x000010B1
                 >;
             };
             pinctrl_pwm7: pwm7grp {
                 fsl,pins = <
                     MX6UL_PAD_CSI_VSYNC__pwm7_OUT    0x000010B1
                 >;
             };
             pinctrl_pwm8: pwm8grp {
                 fsl,pins = <
                     MX6UL_PAD_CSI_HSYNC__pwm8_OUT    0x000010B1
                 >;
             };
         };
     };

     fragment@1 {
         target = <&pwm3>;
         __overlay__ {
             pinctrl-names = "default";
             pinctrl-0 = <&pinctrl_pwm3>;
             status = "okay";
         };
     };

     fragment@2 {
         target = <&pwm7>;
         __overlay__ {
             pinctrl-names = "default";
             pinctrl-0 = <&pinctrl_pwm7>;
             clocks = <&clks IMX6UL_CLK_pwm7>,
                             <&clks IMX6UL_CLK_pwm7>;
             status = "okay";
         };
     };


     fragment@3 {
         target = <&pwm8>;
         __overlay__ {
             pinctrl-names = "default";
             pinctrl-0 = <&pinctrl_pwm8>;
             clocks = <&clks IMX6UL_CLK_pwm8>,
                             <&clks IMX6UL_CLK_pwm8>;
             status = "okay";
         };
     };

 };

pwm设备树插件的源码也是相对简单,若想要修改PWM3的引脚只需要修改第18行的内容即可。 想要使能新的pwm也只需要根据代码黄色部分改写即可。