/*********************************************************************************
|
* 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 <stdio.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
#include <fcntl.h>
|
#include <dirent.h>
|
#include <string.h>
|
#include <time.h>
|
#include <errno.h>
|
#include <signal.h>
|
#include <getopt.h>
|
#include <libgen.h>
|
|
#include "logger.h"
|
#include "util_proc.h"
|
#include "pwm.h"
|
|
/* 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 )
|
{
|
log_error("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 )
|
{
|
log_error("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 )
|
{
|
log_error("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)) )
|
{
|
log_error("export PWM channel[%d] failed, rv=%d\n", channel, rv);
|
return rv;
|
}
|
|
if( (rv=config_pwm(channel, freq, duty)) )
|
{
|
log_error("config PWM channel[%d] failed, rv=%d\n", channel, rv);
|
return rv;
|
}
|
|
log_debug("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 )
|
{
|
log_error("open '%s' failed: %s\n", path, strerror(errno));
|
return -3;
|
}
|
snprintf(buf, sizeof(buf), "%d", status?1:0);
|
write(fd, buf, strlen(buf));
|
|
log_debug("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;
|
}
|
|
int turn_beep(int times)
|
{
|
int rv;
|
|
/* stop beeper beep */
|
if(times == 0)
|
{
|
log_debug("Stop beeper\n");
|
term_pwm(CHN_BEEPER);
|
return 0;
|
}
|
|
rv = init_pwm(CHN_BEEPER, FRQ_BEEPER, 50);
|
if(rv < 0)
|
{
|
log_error("Initial beeper pwm[%d] failed, rv=%d\n", CHN_BEEPER);
|
return -2;
|
}
|
|
/* turn beeper beep ceaselessly */
|
if( times < 0)
|
{
|
turn_pwm(CHN_BEEPER, ENABLE);
|
return 0;
|
}
|
|
while( times-- )
|
{
|
turn_pwm(CHN_BEEPER, ENABLE);
|
msleep(800);
|
|
turn_pwm(CHN_BEEPER, DISABLE);
|
msleep(800);
|
}
|
|
term_pwm(CHN_BEEPER);
|
return 0;
|
}
|