/

Arduino鬼畜电子琴制作

由于某些原因,我又拿出了我的Arduino开发板,这次准备制作一个完整的电子琴。没有意外的话,这应该是我最后一次做Arduino了。

正式开始前的准备

明确需求

  1. 能演奏基本音符
  2. 能演奏高低音
  3. 能通过显示设备输出当前音符
  4. 能够自动演奏

硬件准备

开发板

这次仍然采用经典的 Arduino Uno 开发板,相关说明在我之前的博客中有提及,这里不做描述。

发声设备

为便于研究(lan),这次采用的是无源蜂鸣器作为发声设备,同样,我在之前博客中有提及,这里不描述。

显示设备

我的显示设备采用LCD 1602,使用spi接口,可以大幅降低成本(但会占用大量GPIO)
LCD 1602

其他

  1. 杜邦线若干(众所周知,杜邦线是会自己消失的 /确信)
  2. 适量面包板
  3. 10个10k欧电阻(用于制作下拉电路)
  4. 11个按键

制作

绘制电路图

工具

我使用fritzing制作电路图

LCD1602接线

为节约GPIO,我采用四线接法,为方便接线,我把六根线全挂在An GPIO上面,接线表如下

LCD1602 PIN
VSS GND
VDD 5V
V0 电位器中间引脚
RS A0
RW GND
E A1
D0 /
D1 /
D2 /
D3 /
D4 A2
D5 A3
D6 A4
D7 A5
A 5V
K GND

关于开关按键的电路

经过我的多次实践发现,若按键直接接INPUT接口,且开发板上接有无源蜂鸣器,会出现按键莫名改变状态的问题。
经过约一周的收集资料及咨询大佬,终于发现了问题。
问题关键是INPUT接口的高低电平不确定。目前存在两种解决方案:

  1. 设置芯片INPUT接口内部上拉
  2. 在按键外部部分设置外部下拉电路
方案一 内部上拉

故名思意,内部上拉,就是在芯片内部将这个GPIO的电平调至高电平,先不管芯片内部是怎么实现的这一功能,对于我们来说,这个功能只需要一行代码就能解决,所以这是最简单的解决方案。
代码说明:
原来的INPUT接口初始化代码如下

1
pinMode(11, INPUT);

我们现在要将其改为

1
pinMode(11, INPUT_PULLUP);

即可实现内部上拉。
但因为我可能在某宝买到了假的Uno开发板,我没能实现内部上拉,所以我选择了外部下拉。

方案二 外部下拉

既然是外部,电路自然不同,原本的开关电路将要改成下图样式
外部下拉电路简图
以咱可怜的高中物理水平就不强行解释这个电路原理了(

成果图

成果电路图

采购元件

这里推荐淘宝上的优信电子

代码部分

我需要先定义各个音符及其高低音的发声频率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define Do_l 262
#define re_l 294
#define mi_l 330
#define fa_l 349
#define so_l 392
#define la_l 440
#define si_l 494
#define Do 523
#define re 587
#define mi 659
#define fa 698
#define so 784
#define la 880
#define si 932
#define Do_h 1046
#define re_h 1175
#define mi_h 1318
#define fa_h 1397
#define so_h 1568
#define la_h 1760
#define si_h 1976

然后开始制作标准音及混音发声
目前我找到了一个很难受的土制混音方案,就是每隔一毫秒换一次音,缺点就是声音很沙哑,有点抖。
为实现无限叠加混音,我使用了如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
while (1) {
int normalTones[8] = {};
int num = 0;
//get button state
button0 = digitalRead(2);
button1 = digitalRead(3);
button2 = digitalRead(4);
button3 = digitalRead(5);
button4 = digitalRead(6);
button5 = digitalRead(7);
button6 = digitalRead(8);
button7 = digitalRead(9);
button8 = digitalRead(10);
button9 = digitalRead(11);
if (button0 == HIGH) {
normalTones[num] = Do;
num++;
}
if (button1 == HIGH) {
normalTones[num] = re;
num++;
}
if (button2 == HIGH) {
normalTones[num] = mi;
num++;
}
if (button3 == HIGH) {
normalTones[num] = fa;
num++;
}
if (button4 == HIGH) {
normalTones[num] = so;
num++;
}
if (button5 == HIGH) {
normalTones[num] = la;
num++;
}
if (button6 == HIGH) {
normalTones[num] = si;
num++;
}
normalTones[num] = 0;
if (num == 0) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Welcome! ");
}
else if (num == 1) {
if (normalTones[0] == Do) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Do (C) ");
}
else if (normalTones[0] == re) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Re (D) ");
}
else if (normalTones[0] == mi) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Mi (E) ");
}
else if (normalTones[0] == fa) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Fa (F) ");
}
else if (normalTones[0] == so) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("So (G) ");
}
else if (normalTones[0] == la) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("La (A) ");
}
else if (normalTones[0] == si) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Si (B) ");
}
}
else{
//lcd print
lcd.setCursor(0, 1);
lcd.print("MIX ");
}
for (int i = 0; i < 8; i++) {
if (normalTones[i] == 0) break;
tone(13, normalTones[i]);
delay(1);
}
if (num == 0) break;
}

这里说明一下几个函数

1
2
3
4
5
lcd.setCursor(0, 1);//设置打印字符光标位置为第二行第一个
lcd.print();//打印括号内的字符
delay(1);//延时1ms
tone();//发声函数,需要传递两个参数,一个参数为发声脚,一个为频率
digitalRead();//获取按键状态,当被按下时为HIGH

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
#include <LiquidCrystal.h>
/*
This programme was produced by Lakphy.
It is prohibited to use these code for illegal purposes.
Welcome to visit the private site of Lakphy:
International channel: lakphy.me
China channel: lakphy.gitee.io
Email: [email protected]
Thanks for the help of the seniors of SAST !
Thanks for open source !
*/
#define Do_l 262
#define re_l 294
#define mi_l 330
#define fa_l 349
#define so_l 392
#define la_l 440
#define si_l 494
#define Do 523
#define re 587
#define mi 659
#define fa 698
#define so 784
#define la 880
#define si 932
#define Do_h 1046
#define re_h 1175
#define mi_h 1318
#define fa_h 1397
#define so_h 1568
#define la_h 1760
#define si_h 1976
// vss vcc v0 rs rw e db0 db1 db2 db3 db4 db5 db6 db7 led+ led-
// a0 a1 \ \ \ \ A2 A3 A4 A5
int button0 = LOW;
int button1 = LOW;
int button2 = LOW;
int button3 = LOW;
int button4 = LOW;
int button5 = LOW;
int button6 = LOW;
int button7 = LOW;
int button8 = LOW;
int button9 = LOW;
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);
int music[] = {
so_l, so_l, la_l, so_l, Do , si_l,
so_l, so_l, la_l, so_l, re , Do ,
so_l, so_l, so , mi , Do , si_l, so_l,
fa , fa , mi , Do , re , Do
};
int times[] = {
200, 200, 400, 400, 400, 800,
200, 200, 400, 400, 400, 800,
400, 400, 400, 400, 400, 400, 400,
400, 200, 400, 400, 400, 800
};
void setup() {
// setup lcd
lcd.begin(16, 2);
lcd.print("Lakphy Singer :)");
lcd.setCursor(0, 1);
lcd.print("Hello! ");
// setup keyboard
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, INPUT);
pinMode(7, INPUT);
pinMode(8, INPUT);
pinMode(9, INPUT);
pinMode(10, INPUT);
pinMode(11, INPUT);
//setup tones
pinMode(13, OUTPUT);
noTone(13);
}

void loop() {
bool canRun = 1;
//get button state
button0 = digitalRead(2);
button1 = digitalRead(3);
button2 = digitalRead(4);
button3 = digitalRead(5);
button4 = digitalRead(6);
button5 = digitalRead(7);
button6 = digitalRead(8);
button7 = digitalRead(9);
button8 = digitalRead(10);
button9 = digitalRead(11);

//high
if (button7 == HIGH) {

if (button0 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Do_h (C) ");
//sing
tone(13, Do_h);
delay(50);
while (1) {
if (digitalRead(2) != HIGH) break;
delay(10);
}
noTone(13);
}
//re
if (button1 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Re_h (D) ");
tone(13, re_h);
delay(50);
while (1) {
if (digitalRead(3) != HIGH) break;
delay(10);
}
noTone(13);
}
//mi
if (button2 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Mi_h (E) ");
tone(13, mi_h);
delay(50);
while (1) {
if (digitalRead(4) != HIGH) break;
delay(10);
}
noTone(13);
}
//fa
if (button3 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Fa_h (F) ");
tone(13, fa_h);
delay(50);
while (1) {
if (digitalRead(5) != HIGH) break;
delay(10);
}
noTone(13);
}
//so
if (button4 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("So_h (G) ");
tone(13, so_h);
delay(50);
while (1) {
if (digitalRead(6) != HIGH) break;
delay(10);
}
noTone(13);
}
//la
if (button5 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("La_h (A) ");
tone(13, la_h);
delay(50);
while (1) {
if (digitalRead(7) != HIGH) break;
delay(10);
}
noTone(13);
}
//si
if (button6 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Si_h (B) ");
tone(13, si_h);
delay(50);
while (1) {
if (digitalRead(8) != HIGH) break;
delay(10);
}
noTone(13);
}
}

//low
else if (button8 == HIGH) {

if (button0 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Do_l (C) ");
//sing
tone(13, Do_l);
delay(50);
while (1) {
if (digitalRead(2) != HIGH) break;
delay(10);
}
noTone(13);
}
//re
if (button1 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Re_l (D) ");
tone(13, re_l);
delay(50);
while (1) {
if (digitalRead(3) != HIGH) break;
delay(10);
}
noTone(13);
}
//mi
if (button2 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Mi_l (E) ");
tone(13, mi_l);
delay(50);
while (1) {
if (digitalRead(4) != HIGH) break;
delay(10);
}
noTone(13);
}
//fa
if (button3 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Fa_l (F) ");
tone(13, fa_l);
delay(50);
while (1) {
if (digitalRead(5) != HIGH) break;
delay(10);
}
noTone(13);
}
//so
if (button4 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("So_l (G) ");
tone(13, so_l);
delay(50);
while (1) {
if (digitalRead(6) != HIGH) break;
delay(10);
}
noTone(13);
}
//la
if (button5 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("La_l (A) ");
tone(13, la_l);
delay(50);
while (1) {
if (digitalRead(7) != HIGH) break;
delay(10);
}
noTone(13);
}
//si
if (button6 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Si_l (B) ");
tone(13, si_l);
delay(50);
while (1) {
if (digitalRead(8) != HIGH) break;
delay(10);
}
noTone(13);
}
}
else if (button9 == HIGH) {
if (canRun) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Auto Singing....");
for (int x = 0; x < 25; x++)
{
if (!canRun) break;
if (music[x] == 0) {
noTone(13);
}
else {
tone(13, music[x]);
}
delay(times[x]);//节拍延时
//digitalWrite(ledpin,LOW);
noTone(13);
}
canRun = 0;
}
//lcd print
lcd.setCursor(0, 1);
lcd.print("Singing END ! ");
}
//
//
//
//normal tone
/*
//Do
else if (button0 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Do (C) ");
//sing
tone(13, Do);
delay(50);
while (1) {
if (digitalRead(2) != HIGH) break;
delay(10);
}
noTone(13);
}
//re
else if (button1 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Re (D) ");
tone(13, re);
delay(50);
while (1) {
if (digitalRead(3) != HIGH) break;
delay(10);
}
noTone(13);
}
//mi
else if (button2 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Mi (E) ");
tone(13, mi);
delay(50);
while (1) {
if (digitalRead(4) != HIGH) break;
delay(10);
}
noTone(13);
}
//fa
else if (button3 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Fa (F) ");
tone(13, fa);
delay(50);
while (1) {
if (digitalRead(5) != HIGH) break;
delay(10);
}
noTone(13);
}
//so
else if (button4 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("So (G) ");
tone(13, so);
delay(50);
while (1) {
if (digitalRead(6) != HIGH) break;
delay(10);
}
noTone(13);
}
//la
else if (button5 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("La (A) ");
tone(13, la);
delay(50);
while (1) {
if (digitalRead(7) != HIGH) break;
delay(10);
}
noTone(13);
}
//si
else if (button6 == HIGH) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Si (B) ");
tone(13, si);
delay(50);
while (1) {
if (digitalRead(8) != HIGH) break;
delay(10);
}
noTone(13);
}
*/
else {
while (1) {
int normalTones[8] = {};
int num = 0;
//get button state
button0 = digitalRead(2);
button1 = digitalRead(3);
button2 = digitalRead(4);
button3 = digitalRead(5);
button4 = digitalRead(6);
button5 = digitalRead(7);
button6 = digitalRead(8);
button7 = digitalRead(9);
button8 = digitalRead(10);
button9 = digitalRead(11);
if (button0 == HIGH) {
normalTones[num] = Do;
num++;
}
if (button1 == HIGH) {
normalTones[num] = re;
num++;
}
if (button2 == HIGH) {
normalTones[num] = mi;
num++;
}
if (button3 == HIGH) {
normalTones[num] = fa;
num++;
}
if (button4 == HIGH) {
normalTones[num] = so;
num++;
}
if (button5 == HIGH) {
normalTones[num] = la;
num++;
}
if (button6 == HIGH) {
normalTones[num] = si;
num++;
}
normalTones[num] = 0;
if (num == 0) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Welcome! ");
}
else if (num == 1) {
if (normalTones[0] == Do) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Do (C) ");
}
else if (normalTones[0] == re) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Re (D) ");
}
else if (normalTones[0] == mi) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Mi (E) ");
}
else if (normalTones[0] == fa) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Fa (F) ");
}
else if (normalTones[0] == so) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("So (G) ");
}
else if (normalTones[0] == la) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("La (A) ");
}
else if (normalTones[0] == si) {
//lcd print
lcd.setCursor(0, 1);
lcd.print("Si (B) ");
}
}
else{
//lcd print
lcd.setCursor(0, 1);
lcd.print("MIX ");
}
for (int i = 0; i < 8; i++) {
if (normalTones[i] == 0) break;
tone(13, normalTones[i]);
delay(1);
}
if (num == 0) break;
}
}

//delay
noTone(13);
}

成品图

成品图