原作者链接:http://blog.sina.com.cn/s/blog_bf63e2650102wibs.html
如若侵犯原作者,请及时联系本人,本人立刻删除
单片机产生音乐的原理:
音乐的产生主要是通过单片机的I/O口输出高低不同的脉冲信号来控制蜂鸣器发音,要想产生音频脉冲信号,需要算出某音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。利用单片机定时器计时这个半周期的时间,每当计时到后就输出脉冲的I/O口反相,这样就在此I/O口上得到此脉冲的频率。
通常,利用单片机的内部定时器0,工作在方式一下,改变计数初始值TH0和TL0来产生不同的率。
例如,若单片机采用12MHzs晶振,要产生频率为587Hzs的音频脉冲时,其音频信号的脉冲周期T=1/587=1703.5775us,半周期时间为852us,因此总要令计时器计数=852us/1us=852,在每计数852时将I/O口反相,就得到C大调的中调Re。
由于定时器0的在方式一计数方式为16位计数器,最多只能装载的数为2的16次方即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);
}
如果我们能够控制好频率和节拍,那就有可能演奏出动听的音乐。因此,我们首先需要搞清楚各音调的频率,具体见下表(分别为低音、中音和高音):
这里我们具体以《欢乐颂》为例:
第一,普通音符。如第一个音符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. }