guowenxue
2024-07-26 9cf87703eb60e121b47e6d577ea2bc6b63bd4ac4
commit | author | age
1e563e 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2021 LingYun IoT System Studio
3  *                  All rights reserved.
4  *
5  *       Filename:  pwm.c
6  *    Description:  This file is used to control PWM buzzer/Led
7  *
8  * Pin connection:
9  *               PWM Module              IGKBoard
10  *                  VCC       <----->      5V
11  *                  Led       <----->      #Pin28(PWM8)
12  *                  GND       <----->      GND
13  *
14  *
15  *
16  ********************************************************************************/
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <dirent.h>
23 #include <string.h>
24 #include <time.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include <getopt.h>
28 #include <libgen.h>
29
30 #define PWM_SYS_PATH      "/sys/class/pwm/pwmchip0"
31
32 #define ENABLE            1
33 #define DISABLE           0
34
35
36 typedef struct pwm_s 
37 {
38     char                pwm_path[64]; /* PWM path, such as /sys/class/pwm/pwmchip0 */
39     char                chn_path[64]; /* channel path, such as /sys/class/pwm/pwmchip0/pwm0 */
40     int                 pwm_num;      /* pwm number */
41     int                 chn_num;      /* channel number */
42 } pwm_t; 
43
44
45 int init_pwm(pwm_t *pwm, int pwm_num, int chn_num);
46 int conf_pwm(pwm_t *pwm, int freq, int duty);
47 int turn_pwm(pwm_t *pwm, int status);
48 int term_pwm(pwm_t *pwm);
49
50 static inline void msleep(unsigned long ms);
51
52 static inline void banner(const char *progname)
53 {
54     printf("%s program Version v1.0.0\n", progname);
55     printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
56 }
57
58 static void program_usage(const char *progname)
59 {
60
61     printf("Usage: %s [OPTION]...\n", progname);
62     printf(" This is pwm control program. \n");
63
64     printf(" -p[pwm     ]  Specify PWM chip, such as 1 for pwmchip1\n");
65     printf(" -c[channel ]  Specify PWM channel, such as 0\n");
66     printf(" -f[freq    ]  Specify PWM frequency, default 2500(Hz)\n");
67     printf(" -d[duty    ]  Specify PWM duty, default 50(50%%)\n");
68     printf(" -s[status  ]  Specify PWM status: 1->on(default), 0->off\n");
69     printf(" -h[help    ]  Display this help information\n");
70     printf(" -v[version ]  Display the program version\n");
71     printf("\n");
72
73     printf("Example buzzer : %s -p 1 -c 0 -f 10000 -d 50 -s 1\n", progname);
74     printf("Example Led    : %s -p 1 -c 1 -f 100 -d 50 -s 1\n", progname);
75     printf("Example disable: %s -p 1 -c 0 -s 0\n", progname);
76
77     printf("\n");
78     banner(progname);
79     return;
80 }
81
82 int main(int argc, char **argv)
83 {
84     int             rv;
85     char           *progname=NULL;
86     int             chn_num = -1;
87     int             pwm_num = -1;
88     int             freq = 2500;
89     int             duty = 50;
90     int             status = ENABLE;
91     pwm_t           pwm;
92
93     struct option long_options[] = {
94         {"pwm", required_argument, NULL, 'p'},
95         {"channel", required_argument, NULL, 'c'},
96         {"freq", required_argument, NULL, 'f'},
97         {"duty", required_argument, NULL, 'd'},
98         {"status", required_argument, NULL, 's'},
99         {"version", no_argument, NULL, 'v'},
100         {"help", no_argument, NULL, 'h'},
101         {NULL, 0, NULL, 0}
102     };
103
104     progname = basename(argv[0]);
105
106     /* Parser the command line parameters */
107     while ((rv = getopt_long(argc, argv, "p:c:f:d:s:vh", long_options, NULL)) != -1)
108     {
109         switch (rv)
110         {
111             case 'p': /*  Set pwm chip, such as 1 for pwmchip1 */
112                 pwm_num = atoi(optarg);
113                 break;
114
115             case 'c': /*  Set pwm channel, such as 0,1 */
116                 chn_num = atoi(optarg);
117                 break;
118
119             case 'f': /*  Set pwm frequency */
120                 freq = atoi(optarg);
121                 break;
122
123             case 'd': /*  Set pwm duty cycle */
124                 duty = atoi(optarg);
125                 break;
126
127             case 's': /*  Set pwm status, 0 for disable and 1 for enable */
128                 status = atoi(optarg);
129                 break;
130
131             case 'v':  /* Get software version */
132                 banner(progname);
133                 return EXIT_SUCCESS;
134
135             case 'h':  /* Get help information */
136                 program_usage(progname);
137                 return 0;
138
139             default:
140                 break;
141         }
142     }
143
144     if( pwm_num<0 || chn_num<0 )
145     {
146         program_usage(progname);
147         return 0;
148     }
149
150     init_pwm(&pwm, pwm_num, chn_num);
151
152
153     if( status )
154     {
155         if( (rv=conf_pwm(&pwm, freq, duty)) < 0 )
156         {
157             printf("Configure PWM failure, rv=%d\n", rv);
158             return 1;
159         }
160
161         turn_pwm(&pwm, ENABLE);
162     }
163     else
164     {
165         term_pwm(&pwm);
166     }
167
168     return 0;
169 }
170
171 int init_pwm(pwm_t *pwm, int pwm_num, int chn_num)
172 {
173     if( !pwm || pwm_num<0 || chn_num<0 )
174     {
175         printf("Invalid input arguments\n");
176         return -1;
177     }
178
179     snprintf(pwm->pwm_path, sizeof(pwm->pwm_path), "/sys/class/pwm/pwmchip%d", pwm_num);
180     snprintf(pwm->chn_path, sizeof(pwm->chn_path), "/sys/class/pwm/pwmchip%d/pwm%d", pwm_num, chn_num);
181     pwm->pwm_num = pwm_num;
182     pwm->chn_num = chn_num;
183
184     return 0;
185 }
186
187 /* check PWM $channel export or not */
188 int check_pwm(pwm_t *pwm)
189 {
190     /* check /sys/class/pwm/pwmchipX/pwmY exist or not */
191     return access(pwm->chn_path, F_OK) ? 0 : 1;
192 }
193
194 /* export($export=1)/unexport($export=0) PWM $channel */
195 int export_pwm(pwm_t *pwm, int export)
196 {
197     int           fd;
198     char          buf[32];
199     char          path[256];
200
201     if( export && check_pwm(pwm) )
202         return 0; /* export already when export  */
203     else if( !export && !check_pwm(pwm) )
204         return 0; /* unexport already when unexport  */
205
206     /* export PWM channel by echo Y > /sys/class/pwm/pwmchipX/export */
207     snprintf(path, sizeof(path), "%s/%s", pwm->pwm_path, export?"export":"unexport");
208     if( (fd=open(path, O_WRONLY)) < 0 )
209     {
210         printf("open '%s' failed: %s\n", path, strerror(errno));
211         return -3;
212     }
213
214     snprintf(buf, sizeof(buf), "%d", pwm->chn_num);
215     write(fd, buf, strlen(buf));
216
217     msleep(100);
218
219     if( export && check_pwm(pwm) )
220         return 0; /* export already when export  */
221     else if( !export && !check_pwm(pwm) )
222         return 0; /* unexport already when unexport  */
223
224     return -4;
225 }
226
227 /* set PWM $channel */
228 int set_pwm(pwm_t *pwm, int freq, int duty) 
229 {
230     int           fd;
231     char          buf[32];
232     char          path[256];
233     int           period;
234     int           duty_cycle;
235
236     if( !check_pwm(pwm) )
237         return -2;
238
239     /* open PWM period file and write period in ns */
240     snprintf(path, sizeof(path), "%s/period", pwm->chn_path);
241     if( (fd=open(path, O_WRONLY)) < 0 )
242     {
243         printf("open '%s' failed: %s\n", path, strerror(errno));
244         return -3;
245     }
246     period = 1000000000/freq;
247     snprintf(buf, sizeof(buf), "%d", period);
248     write(fd, buf, strlen(buf));
249
250     /* open PWM duty_cycle file and write duty */
251     snprintf(path, sizeof(path), "%s/duty_cycle", pwm->chn_path);
252     if( (fd=open(path, O_WRONLY)) < 0 )
253     {
254         printf("open '%s' failed: %s\n", path, strerror(errno));
255         return -3;
256     }
257     duty_cycle = (period*duty) / 100;
258     snprintf(buf, sizeof(buf), "%d", duty_cycle);
259     write(fd, buf, strlen(buf));
260
261     return 0;
262 }
263
264 int conf_pwm(pwm_t *pwm, int freq, int duty)
265 {
266     int           rv;
267     char          buf[32];
268     char          path[256];
269
270     if( (rv=export_pwm(pwm, 1)) )
271     {
272         printf("export PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv);
273         return rv; 
274     }
275
276     if( (rv=set_pwm(pwm, freq, duty)) )
277     {
278         printf("config PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv);
279         return rv;
280     }
281
282     printf("config pwm%d channel%d with freq[%d] duty[%d] okay\n", pwm->pwm_num, pwm->chn_num, freq, duty);
283
284     return 0;
285 }
286
287 int turn_pwm(pwm_t *pwm, int status)
288 {
289     int           fd;
290     char          buf[32];
291     char          path[256];
292
293     if( !check_pwm(pwm) )
294         return -1;
295
296     /* open PWM enable file and enable(1)/disable(0) it */
297     snprintf(path, sizeof(path), "%s/enable", pwm->chn_path);
298     if( (fd=open(path, O_WRONLY)) < 0 )
299     {
300         printf("open '%s' failed: %s\n", path, strerror(errno));
301         return -3;
302     }
303     snprintf(buf, sizeof(buf), "%d", status?1:0);
304     write(fd, buf, strlen(buf));
305
306     printf("pwm[%d] channel[%d]%s\n", pwm->pwm_num, pwm->chn_num, status?"enable":"disable");
307
308     return 0;
309 }
310
311 int term_pwm(pwm_t *pwm)
312 {
313     if( !check_pwm(pwm) )
314         return 0;
315
316     turn_pwm(pwm, DISABLE);
317     export_pwm(pwm, 0);
318
319     return 0;
320 }
321
322 static inline void msleep(unsigned long ms)
323 {
324     struct timespec cSleep;
325     unsigned long ulTmp;
326
327     cSleep.tv_sec = ms / 1000;
328     if (cSleep.tv_sec == 0)
329     {
330         ulTmp = ms * 10000;
331         cSleep.tv_nsec = ulTmp * 100;
332     }
333     else
334     {
335         cSleep.tv_nsec = 0;
336     }
337
338     nanosleep(&cSleep, 0);
339
340     return ;
341 }