guowenxue
2024-09-27 10a1a347aa6fa60aa0dd667a4d892aaced108121
commit | author | age
1e563e 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
3  *                  All rights reserved.
4  *
5  *       Filename:  pwm_music.c
6  *    Description:  This file used to test PWM music
7  *                 
8  *        Version:  1.0.0(10/21/2022~)
9  *         Author:  Guo Wenxue <guowenxue@gmail.com>
10  *      ChangeLog:  1, Release initial version on "10/21/2022 17:46:18 PM"
11  *                 
12  ********************************************************************************/
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <time.h>
19 #include <unistd.h>
20 #include <string.h>
21
22 #define PWM_CHIP            1 /* buzzer on pwmchip1 */
23
24 typedef struct pwm_note_s
25 {
26     unsigned int    msec; //持续时间,毫秒
27     unsigned int    freq;//频率
28     unsigned char   duty;//相对占空比,百分比 * 100
29 }pwm_note_t;
30
31 /* 使用宏确定使用中音还是高、低音 */
32 #define CX                      CM
33
34 /* 低、中、高音频率*/
35 static const unsigned short CL[8] = {0, 262, 294, 330, 349, 392, 440, 494};
36 static const unsigned short CM[8] = {0, 523, 587, 659, 698, 784, 880, 988};
37 static const unsigned short CH[8] = {0, 1046, 1175, 1318, 1397, 1568, 1760, 1976};
38
39 /* 小星星曲子*/
40 static unsigned short melody[] = {
41     CX[1], CX[1], CX[5], CX[5], CX[6], CX[6], CX[5], CX[0],
42     CX[4], CX[4], CX[3], CX[3], CX[2], CX[2], CX[1], CX[0],
43     CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0],
44     CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0],
45 };
46
47 static char pwm_path[64]; /* pwm file path buffer */
48
49 static int pwm_export(int chip);
50 static int pwm_config(const char *attr, const char *val);
51 static int pwm_ring(pwm_note_t *note);
52 static inline void msleep(unsigned long ms);
53
54 int main(int argc, char *argv[])
55 {
56     pwm_note_t    note;
57     int           i;
58
59     if( pwm_export(PWM_CHIP) < 0 ) 
60         return 1;
61
62     pwm_config("enable", "1");
63
64     for(i=0; i<sizeof(melody)/sizeof(melody[0]); i++)
65     {
66         if(melody[i] == 0)
67         {
68             note.duty = 0; 
69         }
70         else
71         {
72             note.duty = 15; //越大音量越大
73             note.freq = melody[i];
74         }
75         note.msec = 300;
76
77         pwm_ring(&note);
78     }
79
80     pwm_config("enable", "0");
81
82     return 0;
83 }
84
85 static int pwm_export(int chip)
86 {
87     char file_path[100];
88     int  fd, rv=0;
89
90     /* 导出pwm 首先确定最终导出的文件夹路径*/
91     memset(pwm_path, 0, sizeof(pwm_path));
92     snprintf(pwm_path, sizeof(pwm_path), "/sys/class/pwm/pwmchip%d/pwm0", PWM_CHIP);
93     
94     //如果pwm0 目录已经存在了, 则直接返回
95     if ( !access(pwm_path, F_OK)) 
96         return 0;
97
98     //如果pwm0 目录不存在, 则开始导出
99     
100     memset(file_path, 0, sizeof(file_path));
101     snprintf(file_path, sizeof(file_path) , "/sys/class/pwm/pwmchip%d/export", PWM_CHIP);
102
103     if ( (fd = open(file_path, O_WRONLY) < 0))
104     {
105         printf("ERROR: open pwmchip%d error\n", PWM_CHIP);
106         return 1;
107     }
108
109     if ( write(fd, "0", 1) < 0 )
110     {
111         printf("write '0' to  pwmchip%d/export error\n", PWM_CHIP);
112         rv = 2;
113     }
114
115     close(fd); 
116     return rv;
117 }
118
119 /*pwm 配置函数 attr:属性文件名字 
120  *  val:属性的值(字符串)
121 */
122 static int pwm_config(const char *attr, const char *val)
123 {
124     char file_path[100];
125     int fd;
126
127     if( !attr || !val )
128     {
129         printf("[%s] argument error\n", __FUNCTION__);
130         return -1;
131     }
132
133     memset(file_path, 0, sizeof(file_path));
134     snprintf(file_path, sizeof(file_path), "%s/%s", pwm_path, attr);
135     if( (fd=open(file_path, O_WRONLY)) < 0 ) 
136     {
137         printf("[%s] open %s error\n", __FUNCTION__, file_path);
138         return fd;
139     }
140
141     if ( write(fd, val, strlen(val)) < 0) {
142         printf("[%s] write %s to %s error\n", __FUNCTION__, val, file_path);
143         close(fd);
144         return -2;
145     }
146
147     close(fd);  //关闭文件
148     return 0;
149 }
150
151 /* pwm蜂鸣器响一次声音 */
152 static int pwm_ring(pwm_note_t *note)
153 {
154     unsigned long period = 0;
155     unsigned long duty_cycle = 0;
156     char period_str[20] = {};
157     char duty_cycle_str[20] = {}; 
158
159     if( !note || note->duty > 100 )
160     {
161         printf("[INFO] %s argument error.\n", __FUNCTION__);
162         return -1;
163     }
164
165     period = (unsigned long)((1.f / (double)note->freq) * 1e9);//ns单位
166     duty_cycle = (unsigned long)(((double)note->duty / 100.f) * (double)period);//ns单位
167
168     snprintf(period_str, sizeof(period_str), "%lu", period);
169     snprintf(duty_cycle_str, sizeof(duty_cycle_str), "%lu", duty_cycle);
170
171     //设置pwm频率和周期
172     if (pwm_config("period", period_str))
173     {
174         printf("pwm_config period failure.\n");
175         return -1;
176     }
177     if (pwm_config("duty_cycle", duty_cycle_str))
178     {
179         printf("pwm_config duty_cycle failure.\n");
180         return -2;
181     }
182
183     msleep(note->msec);
184
185     /* 设置占空比为0 蜂鸣器无声 */
186     if (pwm_config("duty_cycle", "0"))
187     {
188         printf("pwm_config duty_cycle failure.\n");
189         return -3;
190     }
191     msleep(20);
192
193     return 0;
194 }
195
196 /* ms级休眠函数 */
197 static inline void msleep(unsigned long ms)
198 {
199     struct timespec cSleep;
200     unsigned long ulTmp;
201
202     cSleep.tv_sec = ms / 1000;
203     if (cSleep.tv_sec == 0)
204     {
205         ulTmp = ms * 10000;
206         cSleep.tv_nsec = ulTmp * 100;
207     }
208     else
209     {
210         cSleep.tv_nsec = 0;
211     }
212
213     nanosleep(&cSleep, 0);
214 }