STM32利用蜂鸣器演奏音乐

前述

        本文在实现蜂鸣器演奏音乐时只使用了基于SysTick定时器的延时函数,最后做出来的音色效果并不是很好,目前推测的原因有很多,有可能是电路设计的问题也有可能是蜂鸣器的问题,也有可能是延时函数并不能及时翻转引脚电平的问题。有兴趣的同志可以试一试利用定时器中断实现蜂鸣器引脚电平翻转,看看效果会不会有所提升。

        本文只为大家提供算法思想。

乐理知识

音阶

        本人对乐理这方面不是很了解,在此只讲述一下我的理解,大家也可以去网上搜索。

        通俗一点,音阶就是Do、Re、Mi、Fa、So、La、Xi即1、2、3、4、5、6、7(C、D、E、F、G、A、B),越往后音调越高即频率越高,所以我们在后续的代码中只需要将乐谱中对应的音阶转换成蜂鸣器所需要的震荡频率就可以发出我们需要的音调了。

音符

        音符就是每一个音调持续的时间,如果假设正常的一个拍子为1s,那么全音符就是1s,二分音符就是0.5s,四分音符就是0.25s以此类推

        OK,现在我们已经可以编程了。

代码实现

延时函数

        首先初始化SysTick定时器,将SysTick定时器的时钟源设置为主频的八分频,如果主频为168那么定时器的时钟频率为eq?%7B%5Ctfrac%7B168%7D%7B8%7D%7D%3D21MHz,那么定时器计数1次的时间为eq?%7B%5Ctfrac%7B1%7D%7B21%7D%7DUs,计时1us就需要21次,所以fac_us=定时器时钟的频率=21

        因为SysTick定时器是24位的倒计时器,所以计数最大值为2^24,所以计时的最大值为eq?2%5E%7B24%7D%5Ccdot%20%5Ctfrac%7B8%7D%7Bf%7D

如果主频超频到240MHz那么计时的最大值就为559240us,这里我们取540000us方便计算


void SysTick_Init(uint8_t SYSCLK)
{
    //将SysTick的时钟频率设置为主频的八分频,如果主频为168MHZ那么将SysTick的时钟频率为21MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
	
	fac_us=SYSCLK/8;    //周期=频率的倒数,频率为21MHz,那么1us就需要震荡21次
	fac_ms=fac_us*1000;  //ms=us*1000
}

void Delay_Nus(uint32_t nus)
{
	uint32_t temp;
	
	SysTick->LOAD=fac_us*nus;    //装载计数值
	SysTick->VAL=0x00;            //当前计数值清零
	SysTick->CTRL|=0x01;        //启动定时器
	do
	{
		temp=SysTick->CTRL;    //获取当前计数值
	}
	while((temp&0x01)&&!(temp&(1<<16)));    //当计时器启动且溢出标志位置1退出循环
	
	SysTick->CTRL&=~0x01;    //关闭计时器
	SysTick->VAL=0x00;        //当前计数值清零
}

/*
SysTick定时器为24位的倒计时器,所以最大计数值为2^24
如果主频超频到240MHz,那么可以计时的最大时间为(2^24)/(240/8)=559240us
这里取540000us方便计算
*/
void Delay_Us(uint32_t us)
{
	uint32_t repeat;
	uint32_t remain;
	
	repeat=(unsigned int)(us/540000);    //如果计时值为1088000=540000*2+8000us
	remain=(unsigned int)(us%540000);    
	
	while(repeat--)
	{
		Delay_Nus(540000);        //540000*2
	}
	if(remain!=0)
	{
		Delay_Nus(remain);        //+8000=1088000us
	}
}

GPIO初始化


#define BUZZER_BUS		GPIOF
#define BUZZER_Pin		GPIO_Pin_8

/*
@brief:Configure the GPIO which is connected to Buzzer.
@param:None.
@retern:None.
*/
void Buzzer_GPIO_Init(void)
{
	GPIO_InitTypeDef	Buzzer_GPIO_Init;
	
	Buzzer_GPIO_Init.GPIO_Mode=GPIO_Mode_OUT;
	Buzzer_GPIO_Init.GPIO_OType=GPIO_OType_PP;
	Buzzer_GPIO_Init.GPIO_Pin=BUZZER_Pin;
	Buzzer_GPIO_Init.GPIO_PuPd=GPIO_PuPd_DOWN;
	Buzzer_GPIO_Init.GPIO_Speed=GPIO_Speed_100MHz;
	
	GPIO_Init(BUZZER_BUS,&Buzzer_GPIO_Init);
}

蜂鸣器播放

        下面函数的形参用的都是float型变量,这是因为形参的单位是Hz和Ms,函数内部处理时会将频率转换为周期并放大一百万倍进行处理,也就是以Us为单位进行处理。假设频率转换为周期后为5.123456……S

在函数内部处理时将转换成512 3456Us进行处理,这样产生的频率更加精准

/*
@brief:Play music with buzzer.
@param:
	scale->音阶.Unit->Hz
	note ->音符. Unit->ms
@retern:None.
*/
void Music_Play(float scale,float note)    //这里用float型是因为形参是以Hz和Ms为单位,但我们
{                                          //后续用的是MHz和Us为单位,也就是将其放大了                                                                        
	uint32_t temp;                         //一百万倍,这样就产生的频率和时长就更加精确
	uint32_t T;
	uint32_t repeat;
	
	T=(unsigned int)((1/scale)*1000000);    //将频率转换为周期,单位为us
	repeat=(unsigned int)(note*(scale/1000));    //一个周期的时间为T,那么只需要重复
	                                           //T*repeat=note即repeat=note/T*1000,单位Ms
	if(scale!=0)
	{
		while(repeat--)
		{
			GPIO_SetBits(BUZZER_BUS,BUZZER_Pin);
			Delay_Us(T/2);
			GPIO_ResetBits(BUZZER_BUS,BUZZER_Pin);
			Delay_Us(T/2);
		}
	}
	else
	{
		Delay_Us(note*1000);
	}
}

/*
float(*)[2]表明这个指针指向的对象是一个包含两个float类型元素的数组,MusicNotation是一个指针变量名。也就是将二维数组转换成了一位数组传递,这个一位数组每个单元有两个float型元素
*/
void Music_Player(float(* MusicNotation)[2])    
{
	uint32_t i=0;
	
	while((MusicNotation[i][0])||(MusicNotation[i][1]))
	{
		Music_Play(MusicNotation[i][0],MusicNotation[i][1]);
		i++;
	}
}

音阶转换

        接下来就是网上搜索,将音阶转换成对应的频率,这不是什么技术活,大家可以直接copy

/* 
Quicker search :
	@Music Scale
		Low-pitched 	Range->C1	C2	 C3
		Middle-pitched 	Range->C4
		High-pitched	Range->C5	C6	 C7	  C8
	@Music Note
*/


/************************* Music Scale ****************************/
/*	Unit->Hz   */
	
//Low-pitched Range
#define	C1	33		//Dou
#define	D1	37		//Ri
#define E1	41		//Mi
#define	F1	44		//Fa
#define	G1	49		//Sou
#define A1	55		//La
#define	B1	62		//Xi
	
#define	C2	65
#define	C_2	69
#define	D2	73
#define D_2	78
#define E2	82
#define F2	87
#define	F_2	93
#define	G2	98
#define	G_2	104
#define A2	110
#define A_2	117
#define B2	123

#define	C3	131
#define	C_3	139
#define	D3	147
#define D_3	156
#define E3	165
#define F3	175
#define	F_3	185
#define	G3	196
#define	G_3	208
#define A3	220
#define A_3	233
#define B3	247

//Middle-pitched Range
#define	C4	262
#define	C_4	277
#define	D4	294
#define D_4	311
#define E4	330
#define F4	349
#define	F_4	370
#define	G4	392
#define	G_4	415
#define A4	440
#define A_4	466
#define B4	494

//High-pitched Range
#define	C5	535
#define	C_5	554
#define	D5	587
#define D_5	622
#define E5	659
#define F5	698
#define	F_5	740
#define	G5	784
#define	G_5	831
#define A5	880
#define A_5	932
#define B5	988
	
#define	C6	1047
#define	C_6	1109
#define	D6	1175
#define D_6	1245
#define E6	1319
#define F6	1397
#define	F_6	1480
#define	G6	1568
#define	G_6	1661
#define A6	1760
#define A_6	1865
#define B6	1976

#define	C7	2093
#define	C_7	2217
#define	D7	2349
#define D_7	2489
#define E7	2580
#define F7	2890
#define	F_7	2960
#define	G7	3136
#define	G_7	3322
#define A7	3520
#define A_7	3729
#define B7	3951

#define	C8	4186.0
#define	C_8	4434.9
#define	D8	4698.6
#define D_8	4978.0
#define E8	5274.0
#define F8	5587.7
#define	F_8	5919.9
#define	G8	6271.9
#define	G_8	6644.9
#define A8	7040.0
#define A_8	7458.6
#define B8	7902.1
	
/************************* Music Note ****************************/	
/*	Unit->ms   */
#define FullNote	1500
#define BinaryNote	750
#define QuarterNote	325
#define	EighthNote	162.5
#define SixteenNote	81.25

 乐谱转换

        网上搜索相关音乐的乐谱,抄上去就行了,二维数组中第一个元素存放音阶即频率,第二个元素存放音符即时长

/*
Music name:两只老虎
Author	  :未知
*/
float TwoTigers[][2]=
{
	{C4,QuarterNote},	
	{D4,QuarterNote},	
	{E4,QuarterNote},
	{C4,QuarterNote},
	
	{C4,QuarterNote},
	{D4,QuarterNote},
	{E4,QuarterNote},
	{C4,QuarterNote},
	
	{E4,QuarterNote},
	{F4,QuarterNote},
	{G4,QuarterNote},
	{G4,QuarterNote},
	
	{E4,QuarterNote},
	{F4,QuarterNote},
	{G4,QuarterNote},
	{G4,QuarterNote},
	
	{G4,QuarterNote},
	{A4,QuarterNote},
	{G4,QuarterNote},
	{F4,QuarterNote},
	
	{E4,QuarterNote},
	{E4,QuarterNote},
	{C4,QuarterNote},
	{C4,QuarterNote},
	
	{G4,QuarterNote},
	{A4,QuarterNote},
	{G4,QuarterNote},
	{F4,QuarterNote},
	
	{E4,QuarterNote},
	{E4,QuarterNote},
	{C4,QuarterNote},
	{C4,QuarterNote},
	
	{D4,QuarterNote},
	{D4,QuarterNote},
	{G3,QuarterNote},
	{G3,QuarterNote},
	
	{C4,QuarterNote},
	{C4,QuarterNote},
	{0,QuarterNote},
	{0,QuarterNote},
	
	{D4,QuarterNote},
	{D4,QuarterNote},
	{G3,QuarterNote},
	{G3,QuarterNote},
	
	{C4,QuarterNote},
	{C4,QuarterNote},
	{0,QuarterNote},
	{0,QuarterNote},
	
	{0,0}			//Stop signal.
};

        如果是变量定义在外部文件,别忘记在头文件声明变量。

extern float TwoTigers[][2];

音乐演奏

        在主函数里调用之前写的那些代码。

#define TARGET_GLOBAL
#define UseHappyBirthday
#define UseTwoTigers
#include "main.h"

int main(void)
{
	System_Init();
	SysTick_Init(168);
	Buzzer_GPIO_Init();
	
	Music_Player(TwoTigers);
	while(1)
	{

	}
} 

源码链接

链接:https://pan.quark.cn/s/e0a803203bbb
提取码:gGDr

效果演示

        可能是延时函数的原因最后得到的声音效果并不是很好,大家也可以用定时器中断试一试。

当然也有可能是其他原因

STM32蜂鸣器演奏音乐

作者:CmeHY

物联沃分享整理
物联沃-IOTWORD物联网 » STM32利用蜂鸣器演奏音乐

发表回复