一、任务需求
具体要求:利用板上的蜂鸣器,播放一首音乐
实现方式:利用PWM生成不同频率,持续时间不同的音调,驱动蜂鸣器播放音乐,程序中制作一个标准的音符库,播放音乐的时候通过查找表的方式把该音符对应的频率的信号播放出来。
二、硬件介绍
【基于树莓派Pico的嵌入式系统学习平台】专为嵌入式系统学习而设计,其可以通过C/C++以及MicroPython编程来学习嵌入式系统的工作原理和应用。
板卡硬件:
2个按键输入
4个单色LED
12个WS2812B RGB三色灯
1个姿态传感器
1个128*64 OLED显示屏
1个蜂鸣器
1个可调电位计(用于电压表)
1路音频信号输入(用于示波器)
8位R-2R电阻网络构成的DAC(用于DDS信号发生器)
三、框图
四、设计思路及代码说明
设计思路:比如标准音库符,然后写主循环和调整各项参数,最后通过thonny这个软件的强大功能调整代码,把代码程序载入到板卡中运行,然后慢慢调试直到达到目标。
1.tones是音符库,它包含了音乐中的音符和对应的频率。音符库中的键是以科学音高记名法表示的音符吗,如‘C0“等,后面的数字是音符的频率,以HZ为单位,如d0对应的频率就是19HZ.
tones = {
'C0':16,
'C#0':17,
'D0':18,
'D#0':19,
'E0':21,
'F0':22,
'F#0':23,
'G0':24,
'G#0':26,
'A0':28,
'A#0':29,
'B0':31,
'C1':33,
'C#1':35,
'D1':37,
'D#1':39,
'E1':41,
'F1':44,
'F#1':46,
'G1':49,
'G#1':52,
'A1':55,
'A#1':58,
'B1':62,
'C2':65,
'C#2':69,
'D2':73,
'D#2':78,
'E2':82,
'F2':87,
'F#2':92,
'G2':98,
'G#2':104,
'A2':110,
'A#2':117,
'B2':123,
'C3':131,
'C#3':139,
'D3':147,
'D#3':156,
'E3':165,
'F3':175,
'F#3':185,
'G3':196,
'G#3':208,
'A3':220,
'A#3':233,
'B3':247,
'C4':262,
'C#4':277,
'D4':294,
'D#4':311,
'E4':330,
'F4':349,
'F#4':370,
'G4':392,
'G#4':415,
'A4':440,
'A#4':466,
'B4':494,
'C5':523,
'C#5':554,
'D5':587,
'D#5':622,
'E5':659,
'F5':698,
'F#5':740,
'G5':784,
'G#5':831,
'A5':880,
'A#5':932,
'B5':988,
'C6':1047,
'C#6':1109,
'D6':1175,
'D#6':1245,
'E6':1319,
'F6':1397,
'F#6':1480,
'G6':1568,
'G#6':1661,
'A6':1760,
'A#6':1865,
'B6':1976,
'C7':2093,
'C#7':2217,
'D7':2349,
'D#7':2489,
'E7':2637,
'F7':2794,
'F#7':2960,
'G7':3136,
'G#7':3322,
'A7':3520,
'A#7':3729,
'B7':3951,
'C8':4186,
'C#8':4435,
'D8':4699,
'D#8':4978,
'E8':5274,
'F8':5588,
'F#8':5920,
'G8':6272,
'G#8':6645,
'A8':7040,
'A#8':7459,
'B8':7902,
'C9':8372,
'C#9':8870,
'D9':9397,
'D#9':9956,
'E9':10548,
'F9':11175,
'F#9':11840,
'G9':12544,
'G#9':13290,
'A9':14080,
'A#9':14917,
'B9':15804
}
2.设置节奏“tempo",设置歌曲,设置是否循环,设置占空比”duty",设置停止标志。初始化计时器,初始化节拍计数器,初始化音符计数器。
from board import pin_cfg
import time
from buzzer_music import music
from machine import Pin, PWM
from math import ceil
from my_people_my_country import song
from tone import tones
class music:
def __init__(self, songString=song, looping=True, tempo=2, duty=1000, pin=None, pins=[Pin(pin_cfg.buzzer)]):
self.tempo = tempo
self.song = songString
self.looping = looping
self.duty = duty
self.stopped = False
self.timer = -1
self.beat = -1
self.arpnote = 0
3.def stop停止方法,停用所有pwm实例并设置停止标志。def tick计时器递增方法,循环中设置了若到达歌曲结束,循环将停止。self beat表示节拍,当音符播放后,会从播放列表中移除过期的音符,并将新音符添加到播放列表。
def stop(self):
for pwm in self.pwms:
pwm.deinit()
self.stopped = True
def tick(self):
if (not self.stopped):
self.timer = self.timer + 1
if (self.timer % (self.tempo * self.end) == 0 and (not (self.timer == 0))):
if (not self.looping):
self.stop()
return False
self.beat = -1
self.timer = 0
print('tick')
if (self.timer % self.tempo == 0):
self.beat = self.beat + 1
i = 0
while (i < len(self.playingDurations)):
self.playingDurations[i] = self.playingDurations[i] - 1
if (self.playingDurations[i] <= 0):
self.playingNotes.pop(i)
self.playingDurations.pop(i)
else:
i = i + 1
if (self.beat < len(self.notes)):
if (self.notes[self.beat] != None):
for note in self.notes[self.beat]:
self.playingNotes.append(note[0])
self.playingDurations.append(note[1])
4.在节拍上运行以上的检查并播放音符。
i = 0
for pwm in self.pwms:
if (i >= len(self.playingNotes)):
pwm.duty_u16(0)
else:
#Play note
pwm.duty_u16(self.duty)
pwm.freq(tones[self.playingNotes[i]])
i = i + 1
if (len(self.playingNotes) > len(self.pwms)):
self.pwms[len(self.pwms)-1].duty_u16(self.duty)
if (self.arpnote > len(self.playingNotes)-len(self.pwms)):
self.arpnote = 0
self.pwms[len(self.pwms)-1].freq(tones[self.playingNotes[self.arpnote+(len(self.pwms)-1)]])
self.arpnote = self.arpnote + 1
5.实例化一个Music对象,主循环,让音乐方向的tick方法运行并休眠一段时间。
singer = music()
singer.tick()
while True:
singer.tick()
time.sleep(0.04)
6.我和我的祖国歌曲文件,包含音符等信息。这里由于后续信息过多因而只截了一部分。
song ="0 D#5 4 43 0.787401556968689;0
五、遇到的主要难题及解决方法
一开始其实我想做项目7,定时报警的时钟,但是我的板子可能有点问题,电路接触不良或者别的什么问题,有三个LED灯不能亮起,于是改变思路,试图将音乐播放与剩余LED灯匹配,播放一些音符的时候灯会随着改变,包括不同颜色的灯在不同音符亮起。但是在调试代码的过程中没有成功调试出来,于是改变想法做了音乐播放器。在代码书写中我有个比较不好的习惯,就是写代码不加注释,如果只是像平常上课二三十行代码不成问题,但是第一次做这个项目要写百行以上代码就出现问题,经常有函数被我遗忘是什么作用,还要重读一遍才能回想起来,在项目的开始我还不能灵活运用thonny这个软件,比如有个查找一些特定词语的功能我也是到后期调试代码才发现,才提高了检查代码的效率。最后通过不懈努力终于完成了代码部分。接下来是板子的调试,就这个板子播放歌曲一开始是放一个音很久才播放下一个音,导致我根本听不出来是我和我的祖国这首歌曲,最后通过调试节拍才试出比较适合的数值。
六、我的收获
通过这个项目我学到了很多东西,主要是写代码习惯的养成,其实各种代码语言还是有相通的地方,学会了这个,那么对其他语言的学习也会大有益处。此外我有真正上手使用了板卡,这与课程还是有比较大的差别。在学校上课时主要以理论为主,跟你讲这个芯片的那个引脚有什么用,怎么接,然而实际上手时就完全不一样,理论学习给的帮助比较有限,很感谢电子森林的这个寒假在家一起练的这个项目,让我有机会实际上手使用板卡,对我以后学习非常有益。以及我也是学会了thonny这个软件,其实在平常学习中用到的软件还是比较多的,这个课程又让我多学会了新的软件,多多益善,如果以后要用到这个软件,那么这次项目将会为我打下良好的基础。
七、未来的计划和建议
通过本次学习,我进一步学会python这门语言的使用。在今后,我想继续参加电子森林的各项活动,特别是有关硬件的项目。在职业规划上,我也想走硬件开发工程师和硬件测试工程师这条路。我希望电子森林能推出更多活动,继续帮助学生在课余时间学习,使自己的能力更进一步。或许电子森林可以推出面包板的活动,目前看来电子森林主要还是偏软件编程的活动较多,实际上搭建电路在学校的课程中还主要使用面包板来实际搭建电路,在学校初步上手面包板的时候还是比较一头雾水,如果电子森林能够推出一些活动来帮助学生适应面包板,那对于我们电子这个专业的学生学习将会有很大益处。