/********************************************************************************* * 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 Raspberry Pi Board * VCC <-----> 5V * buzzer <-----> #Pin32(BCM GPIO12) * Led <-----> #Pin33(BCM GPIO13) * GND <-----> GND * * /boot/config.txt: * * dtoverlay=pwm,pin=12,func=4 (Buzzer) * dtoverlay=pwm,pin=13,func=4 (Led) * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #define PWMCHIP_PATH "/sys/class/pwm/pwmchip0" #define ENABLE 1 #define DISABLE 0 int init_pwm(int channel, int freq, int duty); int turn_pwm(int channel, int status); int term_pwm(int channel); 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(" -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 -c 0 -f 2750 -d 50 -s 1\n", progname); printf("Example Led : %s -c 1 -f 100 -d 50 -s 1\n", progname); printf("Example disable: %s -c 0 -s 0\n", progname); printf("\n"); banner(progname); return; } int main(int argc, char **argv) { int rv; char *progname=NULL; int channel = -1; int freq = 2500; int duty = 50; int status = ENABLE; struct option long_options[] = { {"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, "c:f:d:s:vh", long_options, NULL)) != -1) { switch (rv) { case 'c': /* Set pwm channel, such as 0,1 */ channel = 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(channel < 0 ) { program_usage(progname); return 0; } if( status ) { if( (rv=init_pwm(channel, freq, duty)) < 0 ) { printf("initial PWM failure, rv=%d\n", rv); return 1; } turn_pwm(channel, ENABLE); } else { term_pwm(channel); } return 0; } /* check PWM $channel export or not */ int check_pwm(int channel) { char path[256]; /* check /sys/class/pwm/pwmchip0/pwmN exist or not */ snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel); return access(path, F_OK) ? 0 : 1; } /* export($export=1)/unexport($export=0) PWM $channel */ int export_pwm(int channel, int export) { int fd; char buf[32]; char path[256]; if( export && check_pwm(channel) ) return 0; /* export already when export */ else if( !export && !check_pwm(channel) ) return 0; /* unexport already when unexport */ /* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */ snprintf(path, sizeof(path), "%s/%s", PWMCHIP_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", channel); write(fd, buf, strlen(buf)); msleep(100); if( export && check_pwm(channel) ) return 0; /* export already when export */ else if( !export && !check_pwm(channel) ) return 0; /* unexport already when unexport */ return -4; } /* configure PWM $channel */ int config_pwm(int channel, int freq, int duty) { int fd; char buf[32]; char path[256]; int period; int duty_cycle; if( !check_pwm(channel) ) return -2; /* open PWM period file and write period in ns */ snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel); 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/pwm%d/duty_cycle", PWMCHIP_PATH, channel); 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 init_pwm(int channel, int freq, int duty) { int rv; char buf[32]; char path[256]; if( (rv=export_pwm(channel, 1)) ) { printf("export PWM channel[%d] failed, rv=%d\n", channel, rv); return rv; } if( (rv=config_pwm(channel, freq, duty)) ) { printf("config PWM channel[%d] failed, rv=%d\n", channel, rv); return rv; } printf("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty); return 0; } int turn_pwm(int channel, int status) { int fd; char buf[32]; char path[256]; if( !check_pwm(channel) ) return -1; /* open PWM enable file and enable(1)/disable(0) it */ snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel); 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] %s\n", channel, status?"enable":"disable"); return 0; } int term_pwm(int channel) { if( !check_pwm(channel) ) return 0; turn_pwm(channel, DISABLE); export_pwm(channel, 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 ; }