蜂鸣器君的发声练习
最近搞到一个51单片机, 看了下有个控制蜂鸣器发声的实验, 挺有意思, 嗯, 就做个记录吧.
如何让蜂鸣器响起来就不多说了, 上图, 硬件上利用了三极管开关, 用51一个管脚就能控制了.
关键难题是如何让蜂鸣器君演奏出旋律, 仔细看了例程, 所幸不是很难. 只要知道曲子中的每个音(即知道音的频率)和每个音的拍子(即持续的时间)就能简单实现了. 下面是找到的例程框架. 可以看到这个例程是利用m(发音频率的参考变量)和一个延迟函数配合, 发出曲谱上的每个音;利用n(发音时间的参考变量)和一个计时器timer0配合, 组成曲谱的拍子.
1 |
|
先把主函数放上来看看
1 | void main() |
下面是配合演出的函数们
1 | void timer0() interrupt 1 //timer0中断函数 |
最有(dan)趣(teng)的是编曲部分, 从网上找到了音阶对应频率的表
比如要蜂鸣器发低音Do, 频率为262Hz, 也就是要在1s内开关蜂鸣器262次, 大概3.8ms(3800us)变换状态, 对于中音与高音变换时间更短, 得使用us级的延时函数delay(). 然后为了让unsigned char型的m(最大装值255)能表示所有音阶, 配上一个系数. 取延时最长的低音Do除以16差不多239, 接近255, 就这么愉快地决定了. 因为delay()函数中while(—i)大概8us执行一次, 所以把传过来的m的值乘2, 就能平衡前面除以16的影响发出正确的音. 好了, 所有音都处理后得到了一张表
然后再稍微补充下学习乐理基础的知识吧, 既然是拿简谱开刀, 没有基础的乐理也是不够的, 其实这里只需关注每个音的延时, 然后利用50ms的计时器与变量n配合即可.
首先必须知道一个标准的拍子(比如四分音符为一拍)是多少时间, 这个我查了下与曲子有关, 不同曲子所要求的弹奏速度是不一样的, 有时还要加入弹奏者的理解自行发挥. 所幸在比较标准的简谱左上角会标出弹奏的标准速度. 比如♩=100, 也就是说一分钟弹奏100个四分音符, 600ms是标准一拍的时间, 此时简谱中纯数字的n写为12就行了(12*50ms=600ms), 然后数字下有一横的表示半拍(n=6), 两横的表示四分之一拍(n=3), 如果是数字后的短横表示延音, 也类似, 一条横线延长一拍(n=24), 以此类推就行了. 事实是有明确标明弹奏速度的简谱并不常见, 一般就以400/500ms一拍的速度就好了.
嗯, 首先找来了一个《小星星》简谱来试试, 顺便记一下, 左上角的1=D表示D调, 简谱可能还得辨识大小调, 从而重新确定音调. 不过实在没必要搞得太麻烦, 又没什么大的区别(我是音痴, 学音乐的不要打我ˋ( ° ▽、° )
4/4表示四分音符为一拍, 每小节四拍, 两条竖线之间为一节吧
这就是编的小星星(一闪一闪亮晶晶那个)
1 | unsigned char code music_tab[] ={ |
好像不错再编个天空之城主题曲《君をのせて》
不过我打算把音调与拍子分开编, 也挺容易的就是有点…
1 | unsigned char code music_f[] = //音调谱 |
编了一半我傻了, 简单的十进制放着不用, 干嘛非用麻烦的十六进制, 果然是被例程带坏了 ಥ_ಥ
既然明白了原理, 就再改改放一个最终方案作为结束, 上代码
1 |
|
这里做了点改进
1.宏定义了一些符号常量, 便于编谱
2.改变了休止符表示方法, 并把休止符也看成一个音调, 不像原来那样goto语句特殊处理. 可以通过n来控制音长(虽然没有声音)
作为初学者就折腾到这里吧, 不知道结合键盘能不能自己演奏呢. 被蜂鸣器的声音折磨了几个小时, 赶紧听下原版获得救赎吧XD
.
.
.
.
.
.
.
.
.
終わり