STM32F103ZET6使用蜂鸣器播放音乐-新年好

一:

使用无源蜂鸣器进行音乐播放有两种方式,一种是通过PWM调节不同占空比达到音调的不同实现,一种是通过定时器驱动蜂鸣器IO口,利用定时器同样达到输出方波的效果。这里使用PWM进行演示。

首先需要明白使用PWM播放音乐的原理,利用占空比达到对应的音调,然后需要知道每个音调的持续时间。这就需要简单理解歌曲简谱,以新年好为例:

二:

简单简谱知识,左上角看出每小节3/4拍,两个竖线为一小节。音调有中高低区别,单个数字显示中音,数字下方有点代表低音,数字上方有点代表高音。区分节拍,全拍:数字 - - -;半拍:数字 -;四分子一拍:数字;八分之一拍:数字下面一条横线;十六分音符:数字下面两条横线。

如何知道每个音符对应的PWM占空比,利用下面的图:

忽略带#号的,我们需要通过调整PWM输出频率即可控制蜂鸣器发出不同的音调,实现通过蜂鸣器播放音乐。

        例如,分频后频率为1MHz,且T=\frac{1}{f},频率已知,因此可以算出所需的T的值。例如低音1的T=\frac{1000000}{262}

从而我们可以得到下列配置:

const uint16_t tone[21] = {3817,3401,3030,2865,2551,2272,2024,//低音0-6
							1912,1703,1517,1432,1275,1136,1012,//中音7-13
							956,851,758,715,637,568,506};//高音  14-20

根据简谱和上方配置将新年好的曲子按顺序播放:

const uint8_t music_tone[30] = {7,7,7,4,9,9,9,7,7,9,11,11,10,9,8,8,9,10,10,9,8,9,7,7,9,8,4,6,8,7};

例如第一个7代表简谱中的第一个数字1,为中音,指向tone[21]的第八位。

通过上面的说明可以知道这个简谱每个音符需要持续的时间,以四分音符为例,假设四分音符持续时间单位为4,那八分音符持续时间则为2,二分音符持续时间为8等,一一对应可以得到下面配置:

const uint8_t music_timer[30] = {2,2,4,4,2,2,4,4,2,2,4,4,2,2,8,2,2,4,4,2,2,4,4,2,2,4,4,2,2,8};	

完整简谱配置如下:

const uint16_t tone[21] = {3817,3401,3030,2865,2551,2272,2024,//低音0-6
							1912,1703,1517,1432,1275,1136,1012,//中音7-13
							956,851,758,715,637,568,506};//高音  14-20
const uint8_t music_tone[30] = {7,7,7,4,9,9,9,7,7,9,11,11,10,9,8,8,9,10,10,9,8,9,7,7,9,8,4,6,8,7};	
const uint8_t music_timer[30] = {2,2,4,4,2,2,4,4,2,2,4,4,2,2,8,2,2,4,4,2,2,4,4,2,2,4,4,2,2,8};	
// 音符的频率数组(以 Hz 为单位)

接下来配置定时器PWM输出,相关说明不作解释:

void TIM4_PWM_Init(void)  
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
//	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //设置该引脚为复用输出功能,输出TIM4 CH3的PWM脉冲波形	GPIOB.8
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH4----------------------
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM4(根据)
//	TIM_TimeBaseStructure.TIM_Period = 3816-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Period = 1;
	TIM_TimeBaseStructure.TIM_Prescaler =72-1; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;//设置比较值比较直的范围决定了占空比
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC3Init(TIM4, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM4 OC2

	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);  //使能TIM4在CCR2上的预装载寄存器与输出端口有关
 
	TIM_Cmd(TIM4, ENABLE);  //使能TIM4
}


void set_pwm(uint16_t Period,uint16_t Pulse) 
{
	TIM_SetAutoreload(TIM4,Period);
	TIM_SetCompare3(TIM4,Pulse);
	
}

主函数main.c里:除了初始化外,需要添加的代码:

for(i = 0;i < 30; i++)
			{
				set_pwm(tone[music_tone[i]],tone[music_tone[i]]/15);
				delay_ms(music_timer[i]*90.5);
			}
			play_flag = 0;
			TIM_SetCompare3(TIM4,0);
			delay_ms(500);

tone[music_tone[i]]/15,将声音调小也许可以避免声音太大无法分清每个音符,导致声音模糊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值