先说一下自己玩两年的单片机的想法吧,玩了大概有两年了,从刚刚开始的不知所措,到现在的成熟,终于还是搞明白了一件事,编程思路,远远比源码更重要,所以,我接下来会给大家完整的介绍,完成此项目的心得,太心酸了。最后也会把main函数里面完整的代码粘出来。
此项目是哔哩哔哩上面看到的一个小玩意,B站上面的利用的51单片机,而我移植他们的一部分程序,加载到32单片机上了。
实验现象:
https://www.bilibili.com/video/BV1j34y1W7RU/?vd_source=f6d73e725f237ac544554b57b2c58f16
首先,有一块带无源蜂鸣器的板子,一定是无源蜂鸣器,无源蜂鸣器,会根据加载不同频率的信号,发出不同的声音。
然后,就是乐理知识,每一个音符的频率大小是多少,我也找出来了,这里有低音、中音、高音三种。最基础也就7个:哆来咪发唆啦西;但是这里面又多出来带#号的音符:例:低音4#:表示,比音调4高半个音,比音调5低半个音,就记住比前一个高,比后一个低就行了,咱们有不学音乐,大致了解就行了。
还有就是对于音调的区分:在简谱中,不带点的基本符号叫做中音;在基本符号上面加上一个点叫高音;加两个点叫倍高音;加三个点叫超高音;在基本符号下面加一个点叫低音;加两个点叫倍低音;加三个点叫超低音。
高音的音乐很少很少,而且《孤勇者》也没有用到高音,所以在程序中定义一个数组来存放这些基础音调的频率,我们演奏的歌曲的时候,需要哪个,直接调用,就可以完成曲子了。
基础符号后面的点表示的意思:
那个点是附点,他的时值是他前面时值的一半。也就是说。如果附点前面音符是一拍,那么那个附点就是半拍。如果前面音符是2拍,那附点就是一拍时值。如果前面是四拍的音符,那附点就是2拍。如果附点前面时值是半拍,那么附点就是四分之一拍。
基础音符的频率用一个数组来表示出来。有低音,中音,高音。大部分歌曲都是有这三种音符组成,大家也可以找一下别的歌曲的频率和音符。
我的程序中定义数组:
unsigned int music_table[] =
{
130,139,147,156,165,175,185,196,208,220,233,247, //0-11 超低音
262,277,294,311,329,349,370,392,415,440,466,494, //12-23 低音,正常音符下面有一个点
523,554,587,622,659,698,740,784,830,880,932,988, //24-35 中音,正常音符
0, //0:不发声音,停止播放,在音乐中叫休止符
1046,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976// 高音,正常音符上面有一个点
};
先解释,数组中数据怎么演奏出来吧,定义了两个数组,第一个数组表示基础音符的频率,第二个数组存放《孤勇者》乐谱的频率组合所对应第一个数组的地址。利用数组嵌套的方式去调用。
j=music_table[music[x]];
最基础的源码贴出来了,该拿出来,《孤勇者》的乐谱了,我也找了出来;演奏的是我用红色框起来的那段高潮部分,我也用一个数组来保存这段乐谱:
有人看见就蒙圈了:我来解释一下数组里面数据什么意思;下面还有一个宏定义,来表示这些数据
B6N:表示音符6的低音;
B6H:表示音符6的高音;
36:表示第一个数组的第36的数据0;也就是刚刚上面代码中的休止符,表示停顿。
unsigned char music[] =
{
B6N,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 孤 身 走 暗 巷
B1H,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 不 跪 的 模 样
B1H,B2H,B3H,B2H,B3H,B2H,B3H,36,
//爱 你 对 峙 过 绝 望
B3H,B2H,B3H,36,B5H,B5H,B3H,36,
//不 肯 哭 一 场
B6N,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 破 烂 的 衣 裳
B1H,B7N,B1H,B2H,B7N,B1H,B1H,36,
//却 敢 赌 命 运 的 枪
B1H,B2H,B3H,B2H,B3H,B2H,B3H,36,
//爱 你 和 我 那 么 像
B3H,B2H,B3H,36,B5H,B5H,B3H,36,36,
//缺 口 都 一 样
B5H,B3H,B3H,36,36,B5H,B3H,36,36,
//去 吗 配 吗
B5H,B3H,B5H,B6H,B3H,B5H,36,
//这 褴 褛 的 披 风
B5H,B3H,B5H,B3H, 36,
//战 马 斩 啊
B5H,B3H,B5H,B6H,B3H,B5H,36,
//以 最 卑 微 的 梦
B5H,B5H,B3H,B2H,B2H,B2H,B1H,B3H,B3H,B2H,B2H,B2H,B1H,B1H,B6N,36,36,
//掷 那 黑 夜 中 的 呜 咽 与 怒 吼
B5H,B5H,B3H,B2H,B2H,B2H,B1H,B3H,B3H,B2H,B2H,B2H,B1H,B1H,B6N,36,
//谁 说 站 在 光 里 的 才 算 英 雄
};
还有一个宏定义:
#define B6N 19
#define B7N 21
#define B1H 23
#define B2H 24
#define B3H 26
#define B4H 28
#define B5H 29
#define B6H 31
《孤勇者》对应的音符组合,每个音符的频率在第一个数组的位置。
接下来就是控制频率了,频率就是在一秒钟跳变了几次,所以我就直接利用延迟函数,来控制每次跳变的时间,也就是周期的一半:
j:表示频率,控制每个音符持续多长时间,控制每的音符的周期
i:也表示频率,控制while(i--)控制周期
delay_us(500000/j):1/262等于周期T(s),换成一半周期的,单位us的周期
i=j*0.6;//半节拍(控制每个音符持续多少时间)
while(i--)//控制周期(1s跳变多少次)
{
GPIO_Trgger();
delay_us(500000/j);// 1/262等于周期T(s),换成一半周期的,单位us的周期
}
main函数的程序:没有多少新东西,就是那个时间控制的有点微妙,多看几遍;
#include "stm32f10x.h"
#include "SysTick.h"
#include "usart.h"
#include "gpio.h"
#define B6N 19
#define B7N 21
#define B1H 23
#define B2H 24
#define B3H 26
#define B4H 28
#define B5H 29
#define B6H 31
unsigned int music_table[] = {130,139,147,156,165,175,185,196,208,220,233,247,//0-11半音
262,277,294,311,329,349,370,392,415,440,466,494,//12-23 低音
523,554,587,622,659,698,740,784,830,880,932,988,0};//24-35 中音
unsigned char music[] = {B6N,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 孤 身 走 暗 巷
B1H,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 不 跪 的 模 样
B1H,B2H,B3H,B2H,B3H,B2H,B3H,36,
//爱 你 对 峙 过 绝 望
B3H,B2H,B3H,36,B5H,B5H,B3H,36,
//不 肯 哭 一 场
B6N,B7N,B1H,B2H,B7N,B1H,B1H,36,
//爱 你 破 烂 的 衣 裳
B1H,B7N,B1H,B2H,B7N,B1H,B1H,36,
//却 敢 赌 命 运 的 枪
B1H,B2H,B3H,B2H,B3H,B2H,B3H,36,
//爱 你 和 我 那 么 像
B3H,B2H,B3H,36,B5H,B5H,B3H,36,36,
//缺 口 都 一 样
B5H,B3H,B3H,36,36,B5H,B3H,36,36,
//去 吗 配 吗
B5H,B3H,B5H,B6H,B3H,B5H,36,
//这 褴 褛 的 披 风
B5H,B3H,B5H,B3H, 36,
//战 马 斩 啊
B5H,B3H,B5H,B6H,B3H,B5H,36,
//以 最 卑 微 的 梦
B5H,B5H,B3H,B2H,B2H,B2H,B1H,B3H,B3H,B2H,B2H,B2H,B1H,B1H,B6N,36,36,
//掷 那 黑 夜 中 的 呜 咽 与 怒 吼
B5H,B5H,B3H,B2H,B2H,B2H,B1H,B3H,B3H,B2H,B2H,B2H,B1H,B1H,B6N,36,
//谁 说 站 在 光 里 的 才 算 英 雄
};
int main(void)
{
uint16_t i=0,j=0;
unsigned int x=0,endx=0;
My_USART_Init();
SysTick_Init(72);
My_GPIOInit();
endx = sizeof(music);
while(1)
{
j=music_table[music[x]];
i=j*0.6;//半节拍(控制每个音符持续多少时间)
while(i--)//控制周期(1s跳变多少次)
{
GPIO_Trgger();
delay_us(500000/j);// 1/262等于周期T(s),换成一半周期的,单位us的周期
}
if(j==0)//休止符
{
delay_ms(200);
GPIO_Trgger_JQD();
}
x++;
if(x>=endx)x=0;
}
}