/*********************************************************************************
|
* Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
|
* All rights reserved.
|
*
|
* Filename: pwm_music.c
|
* Description: This file used to test PWM music
|
*
|
* Version: 1.0.0(10/21/2022~)
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "10/21/2022 17:46:18 PM"
|
*
|
********************************************************************************/
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#include <fcntl.h>
|
#include <time.h>
|
#include <unistd.h>
|
#include <string.h>
|
|
#define PWM_CHIP 1 /* buzzer on pwmchip1 */
|
|
typedef struct pwm_note_s
|
{
|
unsigned int msec; //持续时间,毫秒
|
unsigned int freq;//频率
|
unsigned char duty;//相对占空比,百分比 * 100
|
}pwm_note_t;
|
|
/* 使用宏确定使用中音还是高、低音 */
|
#define CX CM
|
|
/* 低、中、高音频率*/
|
static const unsigned short CL[8] = {0, 262, 294, 330, 349, 392, 440, 494};
|
static const unsigned short CM[8] = {0, 523, 587, 659, 698, 784, 880, 988};
|
static const unsigned short CH[8] = {0, 1046, 1175, 1318, 1397, 1568, 1760, 1976};
|
|
/* 小星星曲子*/
|
static unsigned short melody[] = {
|
CX[1], CX[1], CX[5], CX[5], CX[6], CX[6], CX[5], CX[0],
|
CX[4], CX[4], CX[3], CX[3], CX[2], CX[2], CX[1], CX[0],
|
CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0],
|
CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0],
|
};
|
|
static char pwm_path[64]; /* pwm file path buffer */
|
|
static int pwm_export(int chip);
|
static int pwm_config(const char *attr, const char *val);
|
static int pwm_ring(pwm_note_t *note);
|
static inline void msleep(unsigned long ms);
|
|
int main(int argc, char *argv[])
|
{
|
pwm_note_t note;
|
int i;
|
|
if( pwm_export(PWM_CHIP) < 0 )
|
return 1;
|
|
pwm_config("enable", "1");
|
|
for(i=0; i<sizeof(melody)/sizeof(melody[0]); i++)
|
{
|
if(melody[i] == 0)
|
{
|
note.duty = 0;
|
}
|
else
|
{
|
note.duty = 15; //越大音量越大
|
note.freq = melody[i];
|
}
|
note.msec = 300;
|
|
pwm_ring(¬e);
|
}
|
|
pwm_config("enable", "0");
|
|
return 0;
|
}
|
|
static int pwm_export(int chip)
|
{
|
char file_path[100];
|
int fd, rv=0;
|
|
/* 导出pwm 首先确定最终导出的文件夹路径*/
|
memset(pwm_path, 0, sizeof(pwm_path));
|
snprintf(pwm_path, sizeof(pwm_path), "/sys/class/pwm/pwmchip%d/pwm0", PWM_CHIP);
|
|
//如果pwm0 目录已经存在了, 则直接返回
|
if ( !access(pwm_path, F_OK))
|
return 0;
|
|
//如果pwm0 目录不存在, 则开始导出
|
|
memset(file_path, 0, sizeof(file_path));
|
snprintf(file_path, sizeof(file_path) , "/sys/class/pwm/pwmchip%d/export", PWM_CHIP);
|
|
if ( (fd = open(file_path, O_WRONLY) < 0))
|
{
|
printf("ERROR: open pwmchip%d error\n", PWM_CHIP);
|
return 1;
|
}
|
|
if ( write(fd, "0", 1) < 0 )
|
{
|
printf("write '0' to pwmchip%d/export error\n", PWM_CHIP);
|
rv = 2;
|
}
|
|
close(fd);
|
return rv;
|
}
|
|
/*pwm 配置函数 attr:属性文件名字
|
* val:属性的值(字符串)
|
*/
|
static int pwm_config(const char *attr, const char *val)
|
{
|
char file_path[100];
|
int fd;
|
|
if( !attr || !val )
|
{
|
printf("[%s] argument error\n", __FUNCTION__);
|
return -1;
|
}
|
|
memset(file_path, 0, sizeof(file_path));
|
snprintf(file_path, sizeof(file_path), "%s/%s", pwm_path, attr);
|
if( (fd=open(file_path, O_WRONLY)) < 0 )
|
{
|
printf("[%s] open %s error\n", __FUNCTION__, file_path);
|
return fd;
|
}
|
|
if ( write(fd, val, strlen(val)) < 0) {
|
printf("[%s] write %s to %s error\n", __FUNCTION__, val, file_path);
|
close(fd);
|
return -2;
|
}
|
|
close(fd); //关闭文件
|
return 0;
|
}
|
|
/* pwm蜂鸣器响一次声音 */
|
static int pwm_ring(pwm_note_t *note)
|
{
|
unsigned long period = 0;
|
unsigned long duty_cycle = 0;
|
char period_str[20] = {};
|
char duty_cycle_str[20] = {};
|
|
if( !note || note->duty > 100 )
|
{
|
printf("[INFO] %s argument error.\n", __FUNCTION__);
|
return -1;
|
}
|
|
period = (unsigned long)((1.f / (double)note->freq) * 1e9);//ns单位
|
duty_cycle = (unsigned long)(((double)note->duty / 100.f) * (double)period);//ns单位
|
|
snprintf(period_str, sizeof(period_str), "%lu", period);
|
snprintf(duty_cycle_str, sizeof(duty_cycle_str), "%lu", duty_cycle);
|
|
//设置pwm频率和周期
|
if (pwm_config("period", period_str))
|
{
|
printf("pwm_config period failure.\n");
|
return -1;
|
}
|
if (pwm_config("duty_cycle", duty_cycle_str))
|
{
|
printf("pwm_config duty_cycle failure.\n");
|
return -2;
|
}
|
|
msleep(note->msec);
|
|
/* 设置占空比为0 蜂鸣器无声 */
|
if (pwm_config("duty_cycle", "0"))
|
{
|
printf("pwm_config duty_cycle failure.\n");
|
return -3;
|
}
|
msleep(20);
|
|
return 0;
|
}
|
|
/* ms级休眠函数 */
|
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);
|
}
|