C音调与谱曲控制对应表
音调 低1(低音“dao”) 低2 低3 低4 低5 低6 低7
频率 262 294 330 349 392 440 494
音调 中1(中音“dao”) 中2 中3 中4 中5 中6 中7
频率 523 587 659 698 784 880 988
音调 高1(高音“dao”) 高2 高3 高4 高5 高6 高7
频率 1046 1175 1318 1397 1568 1760 1967
实现方法
1.音频控制
要让蜂鸣器发出某音调的声音,只要给蜂鸣器输送该音调频率的电平信号就可以了。
由于单片机I/O口的输出只有高电平“1”和低电平“0”两种状态,因此给蜂鸣器输送的电平信号实际上就是该音频的方波。
例如:中音“1”的频率为523HZ,它的周期为1/523s,即1.91ms,因此,只要给蜂鸣器输送周期为1.91ms的脉冲方波电平信号就能发出
523HZ的音调,该方波的半周期为1.91/2=0.955ms.为此,需要利用定时器的中断,让输送给蜂鸣器的电平信号每0.995ms取反一次即可,
单片机晶振频率为11.0952MHZ,它的一个机器周期为12*(1/11.0592)us,所以需要的机器周期总数为
955us/1.085us=880
即定时器的定时常数应取为880,由上述分析,发出频率为f的音频时,定时常数C计算公式为
C=(10^6 / 2f)/1.085us = 460830/f
因为T0工作方式0,其最大计数值为8192,完全可以满足各音频定时常数设置的需要。可以证明,在已知定时常数C的条件下,13位计数器
的高8位和低5位的初值可由以下公式设定:
TH0=(8192-C)/32 //13位计数器高8位的赋初值方法
TL0=(8192-C)%32 //13位计数器低5位的赋初值方法
2.节拍控制
每分钟72拍,每个节拍所需时间:(1000*60/72)ms=833ms // 具体见单片机扉页
3.音调与节拍的存储
可以将简谱中所有音调的频率及节拍分别存储于两个数组,然后依次从数组中读出频率,再根据频率和定时器延时常数的计算公式
即可由定时器中断控制发出该音调的音频,其发声时间可由节拍控制(1-4个延时单位)。
4.音调的宏定义
直接将频率存入数组,显然不如以“dao,rei,mi,fa,sao……”的形式存储方便,但是为了让单片机认识这些,需要在程序开头处对各音调
的频率进行宏定义。例如,低音6的频率为440Hz,中音6的频率为880Hz,高音6的频率为1760Hz。所以可以对这三个频率进行如下宏定义
#define l_la 440 //将“l_la”宏定义为低音“6”的频率440Hz,以此类推
#define la 880
#define h_la 1760
程序设计
#include<reg52.h>
sbit deep=P2^3;
uint C;
#define uint unsigned int
#define uchar unsigned char
#define l_dao 262
#define l_re 286
#define l_mi 311
#define l_fa 349
#define l_sao 392
#define l_la 440
#define l_xi 494
#define dao 523
#define re 587
#define mi 659
#define fa 698
#define sao 784
#define la 880
#define xi 987
#define h_dao 1046
#define h_re 1174
#define h_mi 1318
#define h_fa 1396
#define h_sao 1567
#define h_la 1760
#define h_xi 1975
void delay(uint xms)
{
uchar i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void main()
{
uchar i.j;
uint code f[]={re,mi,re,dao,l_la,dao,l_la,
l_sao,l_mi,l_sao,l_la,dao,
l_la,dao,sao,la,mi,sao,re,mi,re,mi,sao,mi,
l_sao,l_mi,l_sao,l_la,dao,
l_la,l_la,dao,l_la,l_sao,l_re,l_mi,
l_sao,re,re,sao,la,sao,
fa,mi,sao,mi,la,sao,mi,re,mi,l_la,dao,
re,mi,re,mi,sao,mi,l_sao,l_mi,l_sao,l_la,dao,
l_la,dao,re,l_la,dao,re,mi,re,l_la,dao,re,l_la,dao,
re,mi,re,l_la,dao,re,l_la,dao,re,mi,re,0xff};
uchar code JP[]={4,1,1,4,1,1,2, //每行对应一小节音调的节拍
2,2,2,2,8,
4,2,3,1,2,2,
10,
4,2,2,4,4,
2,2,2,2,4,
2,2,2,2,2,2,2,
10,
4,4,4,2,2,
4,2,4,4,
4,2,2,2,2,2,2,
10,
4,2,2,4,4,
2,2,2,2,6,
4,2,2,4,1,1,4,
10,
4,2,2,4,1,1,4,
10};
EA=1; //开总中断
ET0=1; //定时器T0中断允许
TMOD=0X00; //使用定时器T0的模式0(13位计数器)
while(1) //无限循环
{
i=0;
while(1)
{
i=0; //从第一个音调f[0]开始播放
while(f[i]!=0xff) //只要没有读到结束标志就继续播放
{
C=460830/f[i];
TH0=(8192-C)/32;
TL0=(8192-C)%32;
TR0=1;
for(j=0;j<JP[i];j++) //控制节拍数
delay(); //延时一个节拍单位
TR0=0; //关闭定时器T0
i++; //播放下一个音调
}
}
}
void Time() interrupt 1
{
deep=!deep;
TH0=(8192-C)/32;
TL0=(8192-C)%32;
}
|