STM32歌唱《孤勇者》(标准库)

        先说一下自己玩两年的单片机的想法吧,玩了大概有两年了,从刚刚开始的不知所措,到现在的成熟,终于还是搞明白了一件事,编程思路,远远比源码更重要,所以,我接下来会给大家完整的介绍,完成此项目的心得,太心酸了。最后也会把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;
	}
}

猜你喜欢

转载自blog.csdn.net/m0_58832575/article/details/125287345