一、实验目的以及要求
1、了解电子音乐的产生与频率的关系。
2、编写一个电子音乐发生器。
3、编制一个程序,可自动演奏电子音乐。
二、实验原理
音名与频率的关系:
音乐的十二平均率规定:每两个八度音(如简谱中的中音1与高音1)之间的频率相差一倍。在两个八度音之间,又可分为十二个半音,每两个半音的频率比为12根号2。另外,音名A(简谱中的低音6)的频率为440HZ,音名B到C之间、E到F之间为半音,其余为全音。由此可以计算出简谱中从低度音1至高音1之间每个音名的频率,如下表所示:
简谱中音名与频率的关系表
音名 | 频率(HZ) | 音名 | 频率(HZ) | 音名 | 频率(HZ) |
低音1 | 261.63 | 中音1 | 523.25 | 高音1 | 1046.50 |
低音2 | 293.67 | 中音2 | 587.33 | 高音2 | 1174.66 |
低音3 | 329.63 | 中音3 | 659.25 | 高音3 | 1318.51 |
低音4 | 349.23 | 中音4 | 698.46 | 高音4 | 1396.92 |
低音5 | 391.99 | 中音5 | 783.99 | 高音5 | 1567.98 |
低音6 | 440 | 中音6 | 880 | 高音6 | 1760 |
低音7 | 493.88 | 中音7 | 987.76 | 高音7 | 1975.52 |
由于音阶频率多为非整数,而分频系数又不能为小数,故必须将计算得到的分频数四舍五入取整。若基准频率过低,则由于分频系数过小,四舍五入取整后的误差较大。若基准频率过高,虽然误码差变小,但分频结构将变大。实际的设计应综合考虑两方面的因素,在尽量减小频率误差的前提下取合适的基准频率。
三、实验内容和步骤
P1.5接蜂鸣器SP孔。运行程序music1或music2,蜂鸣器将演奏音乐。
四、实验代码
/****************************** (C) COPYRIGHT 2013 **************************
* 主 控 MCU : AT89S52
* 主 频 : 11.0592MHz
* 编 译 器 : Keil C51 V4.22
* 描 述 : 驱动蜂鸣器演奏歌曲演示程序.
*********************************************************************************/
#include"Reg52.H" /* 调用MCS51寄存器头文件 */
#include"stdio.H"
//*********************************************
//MON51必须用到的
code unsigned char stop[3] _at_ 0x3b;
//*********************************************
typedef unsigned char UINT8; /* 类型定义 */
typedef unsigned int UINT16;
/******************************** 引脚定义 **************************************
单片机 蜂鸣器
P15 U16-N1
*********************************************************************************/
sbit Speaker = P1^5; /* 定义蜂鸣器端口 */
UINT8 Sound_Temp_TH0,Sound_Temp_TL0; /* 定时器初值 */
UINT8 SpeedTime; /* 一拍时间 */
/******************************** 音阶频率表 ************************************/
/* 四个八度的28个频率数据 */
UINT8 code FREQH[ ] = { /* 音阶频率表 高八位 */
0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC, /* 低音 1,2,3,4,5,6,7 */
0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE, /* 中音 1,2,3,4,5,6,7 */
0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF /* 高音 1,2,3,4,5,6,7 */
} ;
UINT8 code FREQL[ ] = { /* 音阶频率表 低八位 */
0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B, /* 低音 1,2,3,4,5,6,7 */
0x8F,0xEE,0x44,0x6B,0xB4,0xF4,0x2D, /* 中音 1,2,3,4,5,6,7 */
0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16 /* 高音 1,2,3,4,5,6,7 */
};
/********************************** 歌曲表1 *************************************/
/* 两只老虎数据表 */
UINT8 code MUSIC1[ ] = {
1,2,2, 2,2,2, 3,2,2, 1,2,2, 1,2,2, 2,2,2, 3,2,2, 1,2,2, 3,2,2, 4,2,2, 5,2,4, 3,2,2, 4,2,2, 5,2,4, 0,0,0
};
/* 爱如火数据表 */
UINT8 code MUSIC2[ ] = {
1,3,2, 7,2,2, 3,2,2, 3,2,2, 5,2,2, 5,2,2, 3,2,2, 6,2,2, 5,2,2,
1,3,2, 7,2,2, 3,2,2, 3,2,2, 5,2,2, 5,2,2, 3,2,2, 6,2,2, 5,2,2,
1,3,2, 2,3,2, 7,2,2, 6,2,2, 7,2,2, 7,2,2, 6,2,2, 2,3,2, 7,2,2,
1,3,2, 7,2,2, 3,2,2, 3,2,2, 5,2,2, 5,2,2, 3,2,2, 6,2,2, 5,2,2,
};
//一个音符有三个数字。前为第几个音、中为第几个八度、后为时长(以拍为单位)
//6,2,3 分别代表: 6,中音,3个拍;
//5,2,1 分别代表: 5,中音,1个拍;
/*******************************************************************************
* 函 数 名 : DelayMs
* 描 述 : 毫秒延时.
* 输 入 : UINT16 Us:
* 要延时的Ms时间.
* 返 回 : 无.
*******************************************************************************/
void DelayMs( UINT16 Ms )
{
UINT16 i;
while( Ms-- )
{
for ( i = 0; i < 114; i++ ); /* 延时1毫秒 */
}
}
/*******************************************************************************
* 函 数 名 : BeepTimer0
* 描 述 : 定时器T0,蜂鸣器中断.
* 输 入 : 无.
* 返 回 : 无.
*******************************************************************************/
void BeepTimer0() interrupt 1
{
Speaker = !Speaker;
TH0=Sound_Temp_TH0;
TL0=Sound_Temp_TL0; /* 重新赋定时器初值 */
}
/*******************************************************************************
* 函 数 名 : PlayMusic
* 描 述 : 播放音乐.
* 输 入 : UINT8 *Sound:
* 指向音乐缓冲区.
* 返 回 : 无.
*******************************************************************************/
void PlayMusic( UINT8 *Sound )
{
UINT16 i = 0,j;
UINT8 ByteCnt;
UINT16 SoundLength; /* 歌曲长度 */
SoundLength = 0;
while( Sound[ SoundLength++ ] != 0x00 ); /* 计算歌曲长度 */
while( i < SoundLength )
{
j = Sound[ i ] + 7 * ( Sound[ i + 1 ] - 1 ) - 1; //
Sound_Temp_TH0 = FREQH[ j ]; /* 音符频率 */
Sound_Temp_TL0 = FREQL[ j ];
SpeedTime = Sound[ i + 2 ]; /* 该音符时常 */
TH0 = Sound_Temp_TH0; /* 赋定时器初值 */
TL0 = Sound_Temp_TL0;
TR0=1; /* 启动定时器 */
i += 3; /* 指向下一组数据 */
while( SpeedTime-- ) /* 延时达到该音符符时间间隔 */
{
for( ByteCnt = 0; ByteCnt < 200; ByteCnt++ )
{
DelayMs(1);
}
}
TR0=0; /* 关闭定时器 */
Speaker = 0; /* 关闭蜂鸣器 */
}
}
/*******************************************************************************
* 函 数 名 : main
* 描 述 : 主函数.
* 输 入 : 无.
* 返 回 : 无.
*******************************************************************************/
void main( )
{
TMOD = 0x01; /* 定时器T0工作在方式1 */
ET0=1; /* 开TR0中断 */
EA=1; /* 开中断 */
while(1)
{
PlayMusic( MUSIC1 ); /* 播放第一首歌曲 */
DelayMs(500);
PlayMusic( MUSIC2 ); /* 播放第二首歌曲 */
DelayMs(500);
}
}