无源蜂鸣器与播放音乐(总结)

时间:2024-04-13 07:21:29

原作者链接:http://blog.sina.com.cn/s/blog_bf63e2650102wibs.html

如若侵犯原作者,请及时联系本人,本人立刻删除


单片机产生音乐的原理:
       
音乐的产生主要是通过单片机的I/O口输出高低不同的脉冲信号来控制蜂鸣器发音,要想产生音频脉冲信号,需要算出某音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。利用单片机定时器计时这个半周期的时间,每当计时到后就输出脉冲的I/O口反相,这样就在此I/O口上得到此脉冲的频率。

通常,利用单片机的内部定时器0,工作在方式一下,改变计数初始值TH0TL0来产生不同的率。

 例如,若单片机采用12MHzs晶振,要产生频率为587Hzs的音频脉冲时,其音频信号的脉冲周期T=1/587=1703.5775us,半周期时间为852us,因此总要令计时器计数=852us/1us=852,在每计数852时将I/O口反相,就得到C大调的中调Re

 由于定时器0的在方式一计数方式为16位计数器,最多只能装载的数为216次方即65536个,也就是说定时器计数到65536就会进入中断函数,而在中断函数中将输出脉冲的I/O口反相即可,故只需设置好定时器0的初值就可以了发出特定的音频了。初值与音频的对应关系见下表所示:

音频

定时器初值

1do

63628

2re

63835

3mi

64021

4fa

64103

5so

64260

6la

64400

7si

64524

1do

64580

2re

64684

3mi

64777

4fa

64820

5so

64898

6la

64968

7si

65030

1do

65058

2re

65110

3mi

65157

4fa

65160

5so

65217

6la

65252

7si

65282

 

#include"STC12C5A.h"

int tab[]={64580,64684,64777,64820,64898,64968,65030,65058,65110,65157,65178,65217,65252,65282}; //网上找到的音符与定时器初值对照表中的数据,用来设定时器0初值

sbit bell=P1^3;//位定义蜂鸣器  

int i; //用于赋定时器0初值

void delay_ms(unsigned int time)

{

  unsigned int t;

  for(;time>0;time--)

  {

    t=1500;

    while(t--);

  }

}

void Timer0_init()

{

  TMOD=0x01;//选择定时器0方式1

  ET0=1;   //开启定时器0中断

  EA=1;   

  TR0=1;

  TL0=tab;

  TH0=tab>>8;

}

void Timer0_Isp() interrupt 1

{

  TL0=tab;

  TH0=tab>>8;

  bell=!bell;

}

//放音符的数组前面记得加个code,延迟时间凭乐感吧!要关掉蜂鸣器貌似蜂鸣器跟定时器都要关哦,歌曲暂停与播放,强烈推荐用外部中断!


void main()

{

  int s;

  code int song[]={6,5,6,8,9,10,7,6,5,3,5,9,8};

  code int song1[]={2,3,5,6,5,3,2,1} ;

  code song2[]={10,9,8,5,9,3,5,9,5};

  code int song3[]={8,9,10,12,13,10,9};

  code int song4[]={9,8,9,10,12,10};


  for(s=0;s<13;s++)

  {

    i=song-1;

    Timer0_init();

    Timer0_init();

    delay_ms(450);

  } 

  delay_ms(500);

  for(s=0;s<8;s++)

  {

    i=song1-1;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  } 

  delay_ms(700);

  for(s=1;s<3;s++)

  { 

i=4;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  i=5;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  i=9;

  Timer0_init();

  Timer0_init();

  delay_ms(700);

  }

  i=7;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  i=8;

  Timer0_init();

  Timer0_init();

  delay_ms(1000);


  for(s=0;s<9;s++)

  {

    i=song2-1;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  }

  delay_ms(100);

  i=7;

  Timer0_init();

  Timer0_init(); 

  delay_ms(1000); 

 

  for(s=0;s<7;s++)

  {

    i=song3-1;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  }

  delay_ms(1000);

 

  for(s=0;s<6;s++)

  {

    i=song4-1;

  Timer0_init();

  Timer0_init();

  delay_ms(450);

  }

  delay_ms(1000);

}


如果我们能够控制好频率和节拍,那就有可能演奏出动听的音乐。因此,我们首先需要搞清楚各音调的频率,具体见下表(分别为低音、中音和高音):

无源蜂鸣器与播放音乐(总结)

我们知道了音调的频率后,下一步就是控制音符的演奏时间。每个音符都会播放一定的时间,这样才能构成一首优美的曲子,而不是生硬的一个调的把所有的音符一股脑的都播放出来。音符节奏分为一拍、半拍、1/4拍、1/8拍,我们规定一拍音符的时间为1;半拍为0.51/4拍为0.251/8拍为0.125……,所以我们可以为每个音符赋予这样的拍子播放出来,音乐就成了。

这里我们具体以《欢乐颂》为例:

无源蜂鸣器与播放音乐(总结)



从简谱看,该音乐是D调的,这里的各音符对应的频率对应的是上表中D调的部分。另外,该音乐为四分之四拍,每个对应为1拍。(我们知道,音符节奏分为一拍、半拍、1/4拍、1/8拍,我们规定一拍音符的时间为1;半拍为0.5;1/4拍为0.25;1/8拍为0.125……,)几个特殊音符说明如下:

第一,普通音符。如第一个音符3,对应频率350,占1拍。

第二,带下划线音符,表示0.5拍;两个下划线是四分之一拍(0.25)。

第三,有的音符后带一个点,表示多加0.5拍,即1+0.5。

第四,有的音符后带一个,表示多加1拍,即1+1。

第五,有的两个连续的音符上面带弧线,表示连音,可以稍微改下连音后面那个音的频率,比如减少或增加一些数值(需自己调试),这样表现会更流畅,其实不做处理,影响也不大。

 

1.  #define NTD0 -1

2.  #define NTD1 294

3.  #define NTD2 330

4.  #define NTD3 350

5.  #define NTD4 393

6.  #define NTD5 441

7.  #define NTD6 495

8.  #define NTD7 556

9.   

10. #define NTDL1 147

11. #define NTDL2 165

12. #define NTDL3 175

13. #define NTDL4 196

14. #define NTDL5 221

15. #define NTDL6 248

16. #define NTDL7 278

17.  

18. #define NTDH1 589

19. #define NTDH2 661

20. #define NTDH3 700

21. #define NTDH4 786

22. #define NTDH5 882

23. #define NTDH6 990

24. #define NTDH7 112

25. //列出全部D调的频率

26.  

27. #define WHOLE      1

28. #define HALF       0.5

29. #define QUARTER    0.25

30. #define EIGHTH     0.125

31. #define SIXTEENTH  0.625

32. //列出所有节拍


33. int tune[]=                 //根据简谱列出各频率

34. {

35.   NTD3,NTD3,NTD4,NTD5,

36.   NTD5,NTD4,NTD3,NTD2,

37.   NTD1,NTD1,NTD2,NTD3,

38.   NTD3,NTD2,NTD2,

39.   NTD3,NTD3,NTD4,NTD5,

40.   NTD5,NTD4,NTD3,NTD2,

41.   NTD1,NTD1,NTD2,NTD3,

42.   NTD2,NTD1,NTD1,

43.   NTD2,NTD2,NTD3,NTD1,

44.   NTD2,NTD3,NTD4,NTD3,NTD1,

45.   NTD2,NTD3,NTD4,NTD3,NTD2,

46.   NTD1,NTD2,NTDL5,NTD0,

47.   NTD3,NTD3,NTD4,NTD5,

48.   NTD5,NTD4,NTD3,NTD4,NTD2,

49.   NTD1,NTD1,NTD2,NTD3,

50.   NTD2,NTD1,NTD1

51. };


52. float durt[]=                   //根据简谱列出各节拍

53. {

54.   1,1,1,1,

55.   1,1,1,1,

56.   1,1,1,1,

57.   1+0.5,0.5,1+1,

58.   1,1,1,1,

59.   1,1,1,1,

60.   1,1,1,1,

61.   1+0.5,0.5,1+1,

62.   1,1,1,1,

63.   1,0.5,0.5,1,1,

64.   1,0.5,0.5,1,1,

65.   1,1,1,1,

66.   1,1,1,1,

67.   1,1,1,0.5,0.5,

68.   1,1,1,1,

69.   1+0.5,0.5,1+1,

70. };


71. int length;

72. int tonepin=6;   //得用6号接口


73. void setup()

74. {

75.   pinMode(tonepin,OUTPUT);

76.   length=sizeof(tune)/sizeof(tune[0]);   //计算长度

77. }


78. void loop()

79. {

80.   for(int x=0;x)

81.   {

82.     tone(tonepin,tune[x]);

83.     delay(500*durt[x]);   //这里用来根据节拍调节延时,500这个指数可以自己调整,在该音乐中,我发现用500比较合适。

84.     noTone(tonepin);

85.   }

86.   delay(2000);

87. }