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;

代码如下:

1:

#include<STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit sbtBeep=P3^4;    //蜂鸣器
uchar ucTimerH,ucTimerL;   //定义定时器的重装值
uchar code arrMusic[]=     //音乐代码,歌曲为《同一首歌》,格式为: 音符, 节拍
{
//音符的十位代表是低中高八度,1代表高八度,2代表中八度,3代表高八度
//个位代表简谱的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
//节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x08代表1/2拍
0x15,0x20,0x21,0x10,	 //音符的十位代表是低八度,中八度还是高八度,1代表低八度,2代表中八度,3代表高八度
0x22,0x10,0x23,0x18,	 //个位代表简谱的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
0x24,0x08,0x23,0x10,	 //节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x08代表1/2拍
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x08,
0x24,0x08,0x25,0x10,
0x21,0x10,0x24,0x18,
0x23,0x08,0x25,0x10,
0x22,0x08,0x23,0x08,
0x23,0x08,0x22,0x08,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x18,0x26,0x08,
0x26,0x20,0x25,0x10,
0x25,0x08,0x26,0x08,
0x27,0x10,0x26,0x08,
0x25,0x08,0x23,0x40,
0x24,0x18,0x24,0x08,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x08,
0x23,0x08,0x22,0x20,
0x17,0x10,0x17,0x08,
0x16,0x08,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
};
uchar code arrMusicToTimerNum[]=
{
    //此数组数据为各个音符在定时器中的重装值,第一列是高位,第二列是低位
    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 DelayMs(unsigned int xms)
{
    uint i,j;
    for(i=xms;i>0;i--)
        for(j=124;j>0;j--);
}
//取址函数
//取出tem音符在arrMusicToTimerNum数组中的位置值
uchar GetPosition(uchar tem) 
{
    uchar ucBase,ucOffset,ucPosition;     //定义曲调,音符和位置
    ucBase=tem/16;            //高4位是曲调值,基址
    ucOffset=tem%16;          //低4位是音符,偏移量
    if(ucBase==1)              //当曲调值为1时,即是低八度,基址为0
        ucBase=0;
    else if(ucBase==2)          //当曲调值为2时,即是中八度,基址为14
        ucBase=14;
    else if(ucBase==3)          //当曲调值为3时,即是高八度,基址为28
        ucBase=28;
    //通过基址加上偏移量,即可定位此音符在arrMusicToTimerNum数组中的位置
ucPosition=ucBase+(ucOffset-1)*2; 
    return ucPosition;            //返回这一个位置值
}
//播放音乐功能函数
void PlayMusic()
{
    uchar ucNoteTmp,ucRhythmTmp,tem; // ucNoteTmp为音符,ucRhythmTmp为节拍
    uchar i=0;
    while(1)
    {
        ucNoteTmp=arrMusic[i];    //如果碰到结束符,延时1秒,回到开始再来一遍
        if(ucNoteTmp==0x00)
        {
            i=0;
            DelayMs(1000);
        }
        else if(ucNoteTmp==0xff)  //若碰到休止符,延时100ms,继续取下一音符
        {
            i=i+2;
            DelayMs(100);
            TR0=0;
        }
        else                     //正常情况下取音符和节拍
        {
            //取出当前音符在arrMusicToTimerNum数组中的位置值
            tem=GetPosition(arrMusic[i]);              
            //把音符相应的计时器重装载值赋予ucTimerH和ucTimerL
            ucTimerH=arrMusicToTimerNum[tem];  
            ucTimerL=arrMusicToTimerNum[tem+1];
            i++;
            TH0=ucTimerH;           //把ucTimerH和ucTimerL赋予计时器
            TL0=ucTimerL;
            ucRhythmTmp=arrMusic[i];      //取得节拍
            i++;
        }
        TR0=1;                          //开定时器1
        DelayMs(ucRhythmTmp*180);      //等待节拍完成, 通过P3^4口输出音频
        TR0=0;                          //关定时器1

    }
}
//初始化函数
//功能是配置IO口
void InitSys()
{
    P0M0=0xff;
    P0M1=0x00;
    P2M0=0x08;
    P2M1=0x00;
    P3M0=0x10;
    P3M1=0x00;
}
//定时器0初始化函数
void InitT0()
{
    TMOD=0x01;
    TH0=0xD8;
    TL0=0xEF;
    EA=1;
    ET0=1;
    TR0=0;
}
//主函数
void main()
{
    InitSys();
    InitT0();
    P0=0x00;
    PlayMusic();
    while(1);
}
/*---------定时器0中断处理函数---------*/
//重新装值,并把sbtBeep值取反,产生方波
void T0_Process() interrupt 1                     //计时器控制频率
{
    TH0=ucTimerH;
    TL0=ucTimerL;
    sbtBeep=~sbtBeep;
}

2:

#include "STC15F2K60S2.H"   
#define uchar unsigned char
#define uint  unsigned int
uint j=0;
sbit beep=P3^4;
uint reset[15]={0,1908,1701,1515,1433,1276,1136,1012,956,852,759,716,638,568,506};	     // c调低音与中音1-7 对应的定时器重装值 
              //0   1    2    3    4    5    6    7    1  2   3   4   5   6   7  
xdata uchar music[204]={0,2,10,1,10,1,10,1,10,1,12,1,12,3,0,4,0,2,9,1,9,1,10,1,9,1.5,0,7.5,                 0,2,8,1,8,1,8,1,8,1,8,1,10,2,10,1,10,1,11,2,10,3,
                      0,8, 8,1, 8,1, 9,1, 8,1, 9,2, 0,2, 8,1, 8,2, 6,1, 6,1, 5,3, 0,8, 9,1, 9,1,10,1,11,1,11,2,0,2,9,1,8,1,10,1,9,0.5,9,2,0,6,
					  10,1,10,1,10,1,10,1,12,1,12,3,0,4,10,1,10,1,12,1,12,2, 10,1.5,0,6,8,1, 8,1,8,1,8,1,8,1,10,2,10,1,0,1,10,1,10,1,10,2,10,1,
					  10,1,11,1,10,4,0,6,8,1,8,1,9,1,8,1,8,2,6,1.5,8,1,8,2,6,1,5,3,0,5,9,1, 9,1,10,1,11,1,11,2,0,1,10,1,9,1,8,1,10,1,9,1};
			                             //乐谱 格式为一个音调加持续的时长的组合
void timer0() interrupt 1
{
	beep=~beep;
}
void delay(uint t)		                //延时函数	延时 t ms
{									   
	uint z;  
	for(;t>0;t--)   
    	for(z=800;z>0;z--) 
		;									
}

void play()
{
	if(reset[music[j]]!=0)			     //读取到不为0的某音调时,改变定时器0的初值,从而发出对应频率的方波给蜂鸣器
	{
	   TR0=1;								 
	   TH0=(65536-reset[music[j]])/256;
	   TL0=(65536-reset[music[j]])%256;
	}
	else TR0=0;						 
	delay(180*music[++j]);               //延迟某音调对应的时长
	TR0=0;
	delay(60*music[j]);					 //乐谱中组合之间的小延迟
	j++;
    if(j==204)							 //唱完歌后延迟5s 继续唱该歌曲
	{
	   j=0;
	   delay(5000);
	}
}

main()
{	
    P3M0=0x10;							  //推挽输出
	P3M1=0x00;
    EA=1;								  //开启定时器中断
    ET0=1;
	TR0=1;
	beep=0;
	while(1){play();}
}
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

布布要成为最负责的男人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值