STC学习:可振动感应的电子音乐

程序设计目标及程序运行效果说明
程序设计目标:通过无源蜂鸣器与振动传感器外接模块实现电子音乐的切换功能。
程序运行效果说明:通过振动可以切换正在播放的歌曲。

程序相关电路及原理说明
1.原理说明
本实验板采用的是无源蜂鸣器,无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用2K~5K的方波去驱动它。相比与有源蜂鸣器,无源蜂鸣器的优点在于价格便宜,可以通过控制其振动频率来改变发出的声音,做出“多来米发索拉西”的效果。因此,无源蜂鸣器可以用于音乐的播放。而有源蜂鸣器的优点在于使用简单,不需要编写“乐谱”。本实验板使用的无源蜂鸣器是电磁式蜂鸣器,电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,接收到的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。

每一个音符的发声频率是不同的,我们需要用计时器来精确计时,用以产生方波,这样才能发出不用的音符声音。C调各音符频率与计数值如图1所示,以下的简谱码是在晶振为12MHz的情况下计算的,换算为16进制的简谱码如程序中quzi[]数组所示。

本程序中,数组music[]即是要播放的音乐,格式为音符,节拍,音符,节拍,如此循环下去。音符为要发出的音调,而节拍则是声音的持续时间。如图1,在数组music[]中,音符表示的格式为:十位代表是低八度,中八度还是高八度,1代表高八度,2代表中八度,3代表高八度,个位代表简谱的音符,例如,0x15代表低八度的S0,如图即是低5S0,0x21代表中八度的DO,如图即是中1 D0。音符中,0x00代表结束符,表示整首歌曲演唱完毕,而0xff代表休止符,表示要休止100ms。遇到这两种情况,都应该重新执行循环中的第一步。其余情况则是正常播放。在定时器中断中进行振动/倒置传感器的检测,因为每播放完一个音节(产生一个方波),都重新启动了定时器。相当于在每个音节播放完就检测一次振动/倒置传感器的状态。程序烧入单片机后,会自动进行演奏。注意:不要一直振动/倒置,这样会使歌曲一直处于切换状态而无法正常播放。
在这里插入图片描述
2.程序相关电路
(1)无源蜂鸣器电路原理图
在这里插入图片描述
(2)按键控制电路
在这里插入图片描述
芯片相关引脚:Beep——P3^4;

代码如下:

#include <STC15F2K60S2.H>	
#define uint unsigned int
#define uchar unsigned char
sbit Key1=P3^2;	   //Key1对应触发外部中断0
sbit beep=P3^4;			  //蜂鸣器
sbit vibrate=P2^4;		  //振动传感器
uchar timeh,timel;		  //定义定时器中断的重装值
uchar jindu=0;			  //播放每首音乐时的进度值
uchar gequ=0;			  //播放第几首音乐,默认为“0”--同一首歌
uchar zanting=0;		  //暂停标志位
bit flag;		  //播放标志位
uchar code music1[] ={    //音乐代码,歌曲为《同一首歌》,格式为: 音符, 节拍, 音符, 节拍,    
0x15,0x20,0x21,0x10,   	 //音符的十位代表是低八度,中八度还是高八度,1代表高八度,2代表中八度,3代表高八度
0x22,0x10,0x23,0x15,	 //个位代表简谱的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
0x24,0x05,0x23,0x10,	 //节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x05代表1/2拍
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x05,
0x24,0x05,0x25,0x10,
0x21,0x10,0x24,0x15,
0x23,0x05,0x25,0x10,
0x22,0x05,0x23,0x05,
0x23,0x05,0x22,0x05,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x15,0x26,0x05,
0x26,0x20,0x25,0x10,
0x25,0x05,0x26,0x05,
0x27,0x10,0x26,0x05,
0x25,0x05,0x23,0x40,
0x24,0x15,0x24,0x05,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x05,
0x23,0x05,0x22,0x20,
0x17,0x10,0x17,0x05,
0x16,0x05,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
};
uchar code music2[] ={			   //《小毛驴》的音乐代码
0x21,0x05,0x21,0x05,
0x21,0x05,0x23,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x26,0x05,0x26,0x05,
0x26,0x05,0x31,0x05,
0x25,0x20,0x24,0x05,
0x24,0x05,0x24,0x05,
0x26,0x05,0x23,0x05,
0x23,0x05,0x23,0x05,
0x23,0x05,0x22,0x05,
0x22,0x05,0x22,0x05,
0x22,0x05,0x25,0x15,
0x25,0x05,0x21,0x05,
0x21,0x05,0x21,0x05,
0x23,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x26,0x05,
0x26,0x05,0x26,0x05,
0x31,0x05,0x25,0x20,
0x24,0x05,0x24,0x05,
0x24,0x05,0x26,0x05,
0x21,0x05,0x21,0x05,
0x21,0x05,0x23,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x26,0x05,0x26,0x05,
0x26,0x05,0x31,0x05,
0x25,0x20,0x24,0x05,
0x24,0x05,0x24,0x05,
0x23,0x03,0x23,0x03,
0x23,0x03,0x23,0x03,
0x23,0x05,0x23,0x05,
0x22,0x05,0x22,0x05,
0x22,0x05,0x23,0x05,
0x21,0x20,0x00,0x00
};
uchar code music3[] ={			   //《天空之城》的音乐代码

0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x23,0x10,
0x16,0x15,0x15,0x05,
0x16,0x10,0x21,0x10,
0x15,0x30,0x13,0x10,
0x14,0x15,0x13,0x05,
0x14,0x05,0x21,0x05,
0x21,0x10,0x13,0x30,
0x21,0x10,0x17,0x10,
0x14,0x05,0x14,0x10,
0x17,0x10,0x17,0x20,
0xff,
0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x13,0x05,
0x13,0x05,0x16,0x15,
0x15,0x05,0x16,0x10,
0x21,0x10,0x15,0x30,
0x13,0x10,0x14,0x10,
0x21,0x05,0x17,0x05,
0x17,0x10,0x21,0x10,
0x22,0x10,0x23,0x05,
0x21,0x20,0xff,
0x21,0x05,0x17,0x05,
0x16,0x10,0x17,0x10,
0x15,0x10,0x16,0x20,
0xff,
0x21,0x05,0x22,0x05,
0x23,0x15,0x22,0x10,
0x23,0x10,0x24,0x10,
0x25,0x05,0x22,0x30,
0x15,0x10,0x22,0x03,
0x21,0x03,0x17,0x03,
0x21,0x03,0x21,0x10,
0x21,0x05,0x22,0x10,
0x23,0x05,0x23,0x40,
0x16,0x05,0x17,0x05,
0x21,0x10,0x17,0x05,
0x21,0x05,0x22,0x10,
0x21,0x15,0x15,0x05,
0x15,0x20,0x24,0x10,
0x23,0x10,0x22,0x10,
0x21,0x10,0x23,0x30,
0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x13,0x10,
0x16,0x15,0x15,0x05,
0x16,0x10,0x21,0x10,
0x15,0x30,0x13,0x10,
0x14,0x10,0x21,0x05,
0x17,0x05,0x17,0x10,
0x21,0x10,0x22,0x10,
0x23,0x05,0x21,0x05,
0x21,0x20,0x21,0x05,
0x17,0x05,0x16,0x10,
0x17,0x10,0x15,0x10,
0x16,0x40,0x00,0x00
};
uchar code quzi[] ={	  //此数组数据为各个音符在定时器中的重装值,第一列是高位,第二列是低位
0xf8,0x8c,	  //低八度,低1
0xf9,0x5b,	  
0xfa,0x15,	  //低3
0xfa,0x67,
0xfb,0x04,	  //低5
0xfb,0x90,
0xfc,0x0c,	  //低7	
0xfc,0x44,	  //中央C调
0xfc,0xac,	  //中2
0xfd,0x09,
0xfd,0x34,	  //中4
0xfd,0x82,
0xfd,0xc8,	  //中6
0xfe,0x06,
0xfe,0x22,	  //高八度,高1	
0xfe,0x56,
0xfe,0x6e,	  //高3
0xfe,0x9a,
0xfe,0xc1,	  //高5
0xfe,0xe4,
0xff,0x03	  //高7
};
void delay(unsigned int xms)
{
	uint i,j;						   
	 for(i=xms;i>0;i--)
	 	for(j=124;j>0;j--);
}
/**********************
功能描述:在quzi数组中,找到music数组定义的简谱音符的重装值,并返回其在quzi数组中的位置
入口参数:tem:music数组中定义的简谱音符
出口参数:返回的是tem音符在quzi数组中的位置值
***********************/
uchar quyin(uchar tem)
{
	uchar qudiao,jp,weizhi;		  //定义曲调,音符和位置
	qudiao=tem/16;				  //高4位是曲调值
	jp=tem%16;					  //低4位是音符
	if(qudiao==1)				  //当曲调值为1时,即是低八度,低八度在quzi数组中基址为0
		qudiao=0;
	else if(qudiao==2)			  //当曲调值为2时,即是中八度,中八度在quzi数组中基址为14
		qudiao=14;
	else if(qudiao==3)			  //当曲调值为3时,即是高八度,高八度在quzi数组中,基址为28
		qudiao=28;
	weizhi=qudiao+(jp-1)*2;		  //通过基址加上音符作为偏移量,即可定位此音符在quzi数组中的位置
	return weizhi;				  //返回这一个位置值
}
//播放音乐
void playmusic()
{
	uchar p,m,tem;   //m为节拍      
	while(1)   
	{ 	  while(zanting)			   //暂停选择位
	    	  beep=0;
			if(gequ==0)				  //歌曲选择位为0,则播放同一首歌
			{	
				p=music1[jindu];   	 
				if(p==0x00)		    //如果碰到结束符,延时1秒,回到开始再来一遍 
				{
					jindu=0;
					delay(1000);
					break;
				}        
				else if(p==0xff)   //若碰到休止符,延时100ms,继续取下一音符
				{
					jindu=jindu+1;
					delay(100);
					TR0=0;
					break;
				}      
				else			   //正常情况下取音符和节拍 
				{
					tem=quyin(music1[jindu]);		//取出当前音符在quzi数组中的位置值
					timeh=quzi[tem];			//把音符相应的计时器重装载值赋予timeh和timel
					timel=quzi[tem+1];
					jindu++;
					TH0=timeh;					//把timeh和timel赋予计时器
					TL0=timel;
					m=music1[jindu];					 //取得节拍
					jindu++;
				}  		   
		        TR0=1;                    //开定时器1    
			 	delay(m*180);             //等待节拍完成, 通过P3^4口输出音频    		    
				TR0=0;                    //关定时器1
				beep=0;				      //将beep置0,以保护蜂鸣器
			}										
			else if(gequ==1)		   //歌曲选择位为1,则播放小毛驴
			{	
				p=music2[jindu];   	
				if(p==0x00)			 //如果碰到结束符,延时1秒,回到开始再来一遍 
				{
					jindu=0;
					delay(1000);
					break;
				}        
				else if(p==0xff)   //若碰到休止符,延时100ms,继续取下一音符
				{
					jindu=jindu+1;
					delay(100);
					TR0=0;
					break;
				}      
				else			   //正常情况下取音符和节拍 
				{
					tem=quyin(music2[jindu]);		//取出当前音符在quzi数组中的位置值
					timeh=quzi[tem];			//把音符相应的计时器重装载值赋予timeh和timel
					timel=quzi[tem+1];
					jindu++;
					TH0=timeh;					//把timeh和timel赋予计时器
					TL0=timel;
					m=music2[jindu];					 //取得节拍
					jindu++;
				}  		   
		        TR0=1;                    //开定时器1    
				delay(m*300);             //等待节拍完成, 通过P3^4口输出音频    			
				TR0=0;                    //关定时器1
				beep=0;	 					 //将beep置0,以保护蜂鸣器
			}
			else if(gequ==2)		  //歌曲选择位为2,则播放天空之城
			{	
				p=music3[jindu];   	 
				if(p==0x00)			 //如果碰到结束符,延时1秒,回到开始再来一遍 
				{
					jindu=0;
					delay(1000);
					break;
				}        
				else if(p==0xff)    //若碰到休止符,延时100ms,继续取下一音符
				{
					jindu=jindu+1;
					delay(100);
					TR0=0;
					break;
				}      
				else			   //正常情况下取音符和节拍 
				{
					tem=quyin(music3[jindu]);		//取出当前音符在quzi数组中的位置值
					timeh=quzi[tem];			//把音符相应的计时器重装载值赋予timeh和timel
					timel=quzi[tem+1];
					jindu++;
					TH0=timeh;					//把timeh和timel赋予计时器
					TL0=timel;
					m=music3[jindu];					 //取得节拍
					jindu++;
				}  		   
		        TR0=1;                    //开定时器1    
			    delay(m*200);             //等待节拍完成, 通过P3^4口输出音频    				
				TR0=0;                    //关定时器1
				beep=0;	 					 //将beep置0,以保护蜂鸣器
			}
		}
}
//系统初始化,功能是配置IO口
void init_sys()
{
	P0M0=0xff;						 //设置推挽模式
	P0M1=0x00;
	P2M0=0x08;
	P2M1=0x00;
	P3M0=0x10;
}
//定时器和外部中断的初始化
void init()						  
{
	TMOD=0x01;					  //设置定时器0,工作方式1,16位手动重装
	TH0=0xD8;					  //定时器初值
	TL0=0xEF;
	IE=0x87;					  // 1000 0111   EA=1,EX0=1,ET0=1,EX1=1
	IP=0x01;					  // 0000 0001   PX0=1	  外部中断0优先级高
	TR0=0;						  //启动定时器0
	beep=0;
}
//Key1对应的外部中断0的中断响应函数
void Exint0()interrupt 0	  
{
   if(Key1==0)
   {
      delay(10);			   	//延时消抖
      if(Key1==0)
		zanting=~zanting;    //zanting==1 暂停播放
   }	   
}
void main()
{
	init_sys();			 //系统初始化
	init();
	P0=0x00;
	vibrate=1;			
	flag=0;				//播放标志位清零
	while(1)
		playmusic();	//播放音乐函数
}
//定时器0中断处理,重新装值,并把beep值取反,产生方波
void time1()interrupt 1						//计时器控制频率
{
	TH0=timeh;								   //手动重装初值
	TL0=timel;
    if(zanting==1)							   //判断是否暂停
	  beep=0;
	else 
	{
	  beep=~beep;
	  if(vibrate==0)						   //判断是否振动
   	  {
		jindu=0;			   //歌曲的进度清零,从头开始演唱
		gequ++;				   //切换歌曲
		if(gequ==3)			   //如果到达最后一首,从第一首开始
		gequ=0;
	  }  
	}
}
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值