/********************************************************************************* * Copyright: (C) 2021 LingYun IoT System Studio * All rights reserved. * * Filename: pwm.c * Description: This file is used to control PWM buzzer/Led * * Pin connection: * PWM Module IGKBoard * VCC <-----> 5V * Led <-----> #Pin28(PWM8) * GND <-----> GND * * * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #define PWM_SYS_PATH "/sys/class/pwm/pwmchip0" #define ENABLE 1 #define DISABLE 0 typedef struct pwm_s { char pwm_path[64]; /* PWM path, such as /sys/class/pwm/pwmchip0 */ char chn_path[64]; /* channel path, such as /sys/class/pwm/pwmchip0/pwm0 */ int pwm_num; /* pwm number */ int chn_num; /* channel number */ } pwm_t; int init_pwm(pwm_t *pwm, int pwm_num, int chn_num); int conf_pwm(pwm_t *pwm, int freq, int duty); int turn_pwm(pwm_t *pwm, int status); int term_pwm(pwm_t *pwm); static inline void msleep(unsigned long ms); static inline void banner(const char *progname) { printf("%s program Version v1.0.0\n", progname); printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); } static void program_usage(const char *progname) { printf("Usage: %s [OPTION]...\n", progname); printf(" This is pwm control program. \n"); printf(" -p[pwm ] Specify PWM chip, such as 1 for pwmchip1\n"); printf(" -c[channel ] Specify PWM channel, such as 0\n"); printf(" -f[freq ] Specify PWM frequency, default 2500(Hz)\n"); printf(" -d[duty ] Specify PWM duty, default 50(50%%)\n"); printf(" -s[status ] Specify PWM status: 1->on(default), 0->off\n"); printf(" -h[help ] Display this help information\n"); printf(" -v[version ] Display the program version\n"); printf("\n"); printf("Example buzzer : %s -p 1 -c 0 -f 10000 -d 50 -s 1\n", progname); printf("Example Led : %s -p 1 -c 1 -f 100 -d 50 -s 1\n", progname); printf("Example disable: %s -p 1 -c 0 -s 0\n", progname); printf("\n"); banner(progname); return; } int main(int argc, char **argv) { int rv; char *progname=NULL; int chn_num = -1; int pwm_num = -1; int freq = 2500; int duty = 50; int status = ENABLE; pwm_t pwm; struct option long_options[] = { {"pwm", required_argument, NULL, 'p'}, {"channel", required_argument, NULL, 'c'}, {"freq", required_argument, NULL, 'f'}, {"duty", required_argument, NULL, 'd'}, {"status", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; progname = basename(argv[0]); /* Parser the command line parameters */ while ((rv = getopt_long(argc, argv, "p:c:f:d:s:vh", long_options, NULL)) != -1) { switch (rv) { case 'p': /* Set pwm chip, such as 1 for pwmchip1 */ pwm_num = atoi(optarg); break; case 'c': /* Set pwm channel, such as 0,1 */ chn_num = atoi(optarg); break; case 'f': /* Set pwm frequency */ freq = atoi(optarg); break; case 'd': /* Set pwm duty cycle */ duty = atoi(optarg); break; case 's': /* Set pwm status, 0 for disable and 1 for enable */ status = atoi(optarg); break; case 'v': /* Get software version */ banner(progname); return EXIT_SUCCESS; case 'h': /* Get help information */ program_usage(progname); return 0; default: break; } } if( pwm_num<0 || chn_num<0 ) { program_usage(progname); return 0; } init_pwm(&pwm, pwm_num, chn_num); if( status ) { if( (rv=conf_pwm(&pwm, freq, duty)) < 0 ) { printf("Configure PWM failure, rv=%d\n", rv); return 1; } turn_pwm(&pwm, ENABLE); } else { term_pwm(&pwm); } return 0; } int init_pwm(pwm_t *pwm, int pwm_num, int chn_num) { if( !pwm || pwm_num<0 || chn_num<0 ) { printf("Invalid input arguments\n"); return -1; } snprintf(pwm->pwm_path, sizeof(pwm->pwm_path), "/sys/class/pwm/pwmchip%d", pwm_num); snprintf(pwm->chn_path, sizeof(pwm->chn_path), "/sys/class/pwm/pwmchip%d/pwm%d", pwm_num, chn_num); pwm->pwm_num = pwm_num; pwm->chn_num = chn_num; return 0; } /* check PWM $channel export or not */ int check_pwm(pwm_t *pwm) { /* check /sys/class/pwm/pwmchipX/pwmY exist or not */ return access(pwm->chn_path, F_OK) ? 0 : 1; } /* export($export=1)/unexport($export=0) PWM $channel */ int export_pwm(pwm_t *pwm, int export) { int fd; char buf[32]; char path[256]; if( export && check_pwm(pwm) ) return 0; /* export already when export */ else if( !export && !check_pwm(pwm) ) return 0; /* unexport already when unexport */ /* export PWM channel by echo Y > /sys/class/pwm/pwmchipX/export */ snprintf(path, sizeof(path), "%s/%s", pwm->pwm_path, export?"export":"unexport"); if( (fd=open(path, O_WRONLY)) < 0 ) { printf("open '%s' failed: %s\n", path, strerror(errno)); return -3; } snprintf(buf, sizeof(buf), "%d", pwm->chn_num); write(fd, buf, strlen(buf)); msleep(100); if( export && check_pwm(pwm) ) return 0; /* export already when export */ else if( !export && !check_pwm(pwm) ) return 0; /* unexport already when unexport */ return -4; } /* set PWM $channel */ int set_pwm(pwm_t *pwm, int freq, int duty) { int fd; char buf[32]; char path[256]; int period; int duty_cycle; if( !check_pwm(pwm) ) return -2; /* open PWM period file and write period in ns */ snprintf(path, sizeof(path), "%s/period", pwm->chn_path); if( (fd=open(path, O_WRONLY)) < 0 ) { printf("open '%s' failed: %s\n", path, strerror(errno)); return -3; } period = 1000000000/freq; snprintf(buf, sizeof(buf), "%d", period); write(fd, buf, strlen(buf)); /* open PWM duty_cycle file and write duty */ snprintf(path, sizeof(path), "%s/duty_cycle", pwm->chn_path); if( (fd=open(path, O_WRONLY)) < 0 ) { printf("open '%s' failed: %s\n", path, strerror(errno)); return -3; } duty_cycle = (period*duty) / 100; snprintf(buf, sizeof(buf), "%d", duty_cycle); write(fd, buf, strlen(buf)); return 0; } int conf_pwm(pwm_t *pwm, int freq, int duty) { int rv; char buf[32]; char path[256]; if( (rv=export_pwm(pwm, 1)) ) { printf("export PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv); return rv; } if( (rv=set_pwm(pwm, freq, duty)) ) { printf("config PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv); return rv; } printf("config pwm%d channel%d with freq[%d] duty[%d] okay\n", pwm->pwm_num, pwm->chn_num, freq, duty); return 0; } int turn_pwm(pwm_t *pwm, int status) { int fd; char buf[32]; char path[256]; if( !check_pwm(pwm) ) return -1; /* open PWM enable file and enable(1)/disable(0) it */ snprintf(path, sizeof(path), "%s/enable", pwm->chn_path); if( (fd=open(path, O_WRONLY)) < 0 ) { printf("open '%s' failed: %s\n", path, strerror(errno)); return -3; } snprintf(buf, sizeof(buf), "%d", status?1:0); write(fd, buf, strlen(buf)); printf("pwm[%d] channel[%d]%s\n", pwm->pwm_num, pwm->chn_num, status?"enable":"disable"); return 0; } int term_pwm(pwm_t *pwm) { if( !check_pwm(pwm) ) return 0; turn_pwm(pwm, DISABLE); export_pwm(pwm, 0); return 0; } static inline void msleep(unsigned long ms) { struct timespec cSleep; unsigned long ulTmp; cSleep.tv_sec = ms / 1000; if (cSleep.tv_sec == 0) { ulTmp = ms * 10000; cSleep.tv_nsec = ulTmp * 100; } else { cSleep.tv_nsec = 0; } nanosleep(&cSleep, 0); return ; }