单片机课程设计---模拟电子琴发声控制系统

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《单片机应用与仿真训练》设计报告模拟电子琴发声控制系统
专业:电气工程与自动化
摘要
本次课程设计的主要内容是用AT89C51单片机为核心控制元件,设计一个简单的电子琴并可实现音乐的连续播放。

以单片机作为主控核心,与键盘、扬声器等模块组成核心主控制模块,在主控模块上设有12个按键和扬声器。

定时器按设置的定时参数产生中断,由于定时参数不同,就会发出不同频率的脉冲,不同频率的脉冲经喇叭驱动电路放大滤波后,就会发出不同音调。

本简易电子琴的设计可实现的功能如下:程序中预存了4首音乐:《同一首歌》、《两只蝴蝶》、《祝你快乐》、《Time to say goodbye》并通过一个独立键盘可以实现对四首音乐的“下一曲”控制。

3*4矩阵键盘中的7个按键分别对应着7个不同的音符,另外3个分为高、中、低音的控制,当按下某一按键,会发出相应的音调。

按下按键时,扬声器会发出声音,松开按键后,扬声器停止发声,按键的时间越长,发声时间越久。

连续按下不同的按键,可以实现乐曲的演奏。

此外还有一“模式”按键,负责在电子琴和音乐播放器两种不同模式下的切换。

目录
1 概述 ------------------------------------------------------------------------------------------------- 4
1.1 音乐产生原理---------------------------------------------------------------------------- 4
1.2 结构框图 ---------------------------------------------------------------------------------- 4
2 系统总体方案及硬件设计 -------------------------------------------------------------------- 5
2.1 总体方案 ---------------------------------------------------------------------------------- 5
2.2 按键键盘 ---------------------------------------------------------------------------------- 5
2.3 蜂鸣器电路 ------------------------------------------------------------------------------- 5
2.4 数码管电路 ------------------------------------------------------------------------------- 6
2.5 最小系统 ---------------------------------------------------------------------------------- 7
2.6 设计实现过程---------------------------------------------------------------------------- 7
3 软件设计------------------------------------------------------------------------------------------- 9
3.1 整体设计 ---------------------------------------------------------------------------------- 9
3.2 音乐设计 -------------------------------------------------------------------------------- 10
3.3 按键设计 -------------------------------------------------------------------------------- 11
3.4 显示设计 -------------------------------------------------------------------------------- 13
4 Proteus仿真 ------------------------------------------------------------------------------------ 14
5 课程设计体会 ---------------------------------------------------------------------------------- 15
参考文献----------------------------------------------------------------------------------------- 15 附1:源程序代码----------------------------------------------------------------------------- 16 附2:系统原理图----------------------------------------------------------------------------- 26
1概述
1.1音乐产生原理
一首音乐是许多不同的音阶组成的,而每个音阶对应着不同的频率,这样我们就可以利用不同的频率的组合,即可构成我们所想要的音乐了,当然对于单片机来产生不同的频率非常方便,我们可以利用单片机的定时/计数器T0来产生这样方波频率信号,因此,我们只要把一首歌曲的音阶对应频率关系正确即可。

若要产生音频脉冲,只要算出某一音频的周期(1/频率),再将此周期除以2,即为半周期的时间。

利用定时器计时半周期时间,每当计时终止后就将P1.0反相,然后重复计时再反相。

就可在P1.0引脚上得到此频率的脉冲。

利用STC89C52的内部定时器使其工作计数器模式(MODE1)下,改变计数值TH0及TL0以产生不同频率的方法产生不同音阶,例如,频率为523Hz,其周期T=1/523=1912μs,单片机晶振频率为12MHz,因此只要令计数器计时(956μs/1μs)=478,每计数478次时将I/O反相,就可得到中音DO(523Hz)。

对于AT80C51而言要产生一定频率的方波一般是先将某口线输出高电平,延迟一段时间后再输出低电平。

通过改变延迟时间可以改变单片机的输出频率。

单片机的延时主要有两种方式,即软件延时和使用定时/计数器延时。

其中软件延时不是很精确,而电子琴电路由于每个音符的频率值要求比较严格,因此我们选用定时/计数器延时。

由于本课程设计是由用户通过键盘输入弹奏乐曲的,所以节拍由用户掌握,不由程序控制。

因此,我们只需弄清楚音乐中的音符和对应的频率,利用单片机的定时/计数器来产生方波频率信号即可。

1.2结构框图
2系统总体方案及硬件设计
2.1总体方案
本设计采用3*4键盘控制通过程序产生高、中、低七度音律,要考虑到软件和硬件的匹配以及硬件电路焊接时的排版问题,否则建会带来不必要的焊接麻烦,在编程序时要注意仿真与实际电路中可能的不符。

在此基础上,另外设计数码管显示电路,蜂鸣器驱动电路。

2.2按键键盘
设计中采用AT89S51的并行口P0接3×4矩阵键盘,以P0.0-P0.2作输入线,以P0.4-P0.7作输出线。

每个按键有它的行值和列值,行值和列值的组合就是识别这个按键的编码。

矩阵的行线和列线分别通过两并行接口和CPU通信。

每个按键的状态同样需变成数字量“0”和“1”,开关的一端(列线)通过电阻接VCC,而接地是通过程序输出数字“0”实现的。

键盘处理程序的任务是:确定有无键按下,判断哪一个键按下,键的功能是什么;还要消除按键在闭合或断开时的抖动。

两个并行口中,一个输出扫描码,使按键逐行动态接地,另一个并行口输入按键状态,由行扫描值和回馈信号共同形成键编码而识别按键,通过软件查表,查出该键的功能,在本程序中,当程序检测到有键按下时,将查表使按键值与预存数据表中数据对应,并调用发声程序。

另单独焊接第三行第四列按键为独立按键,用以控制歌曲的切换。

2.3蜂鸣器电路
蜂鸣器一阳极接三极管的集电极,另一端接地,三极管的集电极节电源
VCC,基极接P3.6口。

当P3.6口为低电平时三极管导通,蜂鸣器有电流流过,即发声。

本次设计采用的是9015(NPN型)三极管作为蜂鸣器的驱动电路。

2.4数码管电路
本次单片机课程设计的显示电路采用共阳极LED数码管显示,由于LED 是属于电流控制器件,使用时必须加限流电阻。

本次显示使用的是数码管的静态显示功能,主要用以显示高、中、低音的状态以及电子琴、播放器状态的指示。

2.5最小系统
本部分分为印刷电路板,主要包含复位电路、时钟电路、P0-P3口插槽。

2.6设计实现过程
本次硬件设计的核心部分就是按键,组成键盘的按键有机械式、电容式、导电橡胶式、薄膜式多种,但不管什么形式,其作用都是一个使电路接通与断开的开关。

目前微机系统中使用的键盘按其功能不同,通常可分为编码键盘和非编码键盘两种基本类型。

编码键盘:键盘本身带有实现接口主要功能所需的硬件电路。

不仅能自动检测被按下的键,并完成去抖动、防串键等功能,而且能提供与被按键功能对应的键码(如ASCII码)送往CPU。

所以,编码键盘接口简单、使用方便。

但由于硬件电路较复杂,因而价格较贵。

非编码键盘:键盘只简单地提供按键开关的行列矩阵。

有关按键的识别、键码的确定与输入、去抖动等功能均由软件完成。

目前微机系统中,一般为了降低成本大多数采用非编码键盘。

键盘接口必须具有去抖动、防串键、按键识别和键码产生4个基本功能。

(1)去抖动:每个按键在按下或松开时,都会产生短时间的抖动。

抖动的持续时间与键的质量相关,一般为5—20mm。

所谓抖动是指在识别被按键是必须避开抖动状态,只有处在稳定接通或稳定断开状态才能保证识别正确无误。

去抖问题可通过软件延时或硬件电路解决。

(2)防串键:防串键是为了解决多个键同时按下或者前一按键没有释放又有新的按键按下时产生的问题。

常用的方法有双键锁定和N键轮回两种方法。

双键锁定,是当有两个或两个以上的按键按下时,只把最后释放的键当作有效键并产生相应的键码。

N键轮回,是当检测到有多个键被按下时,能根据发现它们的顺序依次产生相应键的键码。

(3)被按键识别:如何识别被按键是接口解决的主要问题,一般可通过软硬结合的方法完成。

常用的方法有行扫描法和线反转法两种。

行扫描法的基本思想是,由程序对键盘逐行扫描,通过检测到的列输出状态来确定闭合键,为此,需要设置入口、输出口一个,该方法在微机系统中被广泛使用。

线反转法的基本思想是通过行列颠倒两次扫描来识别闭合键,为此需要提供两个可编程的双向输入/输出端口。

(4)键码产生:为了从键的行列坐标编码得到反映键功能的键码,一般在
内存区中建立一个键盘编码表,通过查表获得被按键的键码。

用STC89C52的并行口P0接3×4矩阵键盘,以P0.0-P0.2作输入线,以P0.4-P0.7作输出线。

除核心电路外,还包括辅助的发音电路和显示电路。

发音电路主要部件是蜂鸣器,用三极管作为可控器件辅以驱动电路,以保证音量适中,音色较真。

显示电路由两位共阳极数码管作为显示器件,同样辅以三极管放大电路,并串接限流电阻,通过静态显示的方法作为高、中、低音状态的显示和电子琴模式与音乐播放器模式的状态显示。

3软件设计
3.1整体设计
本设计使用Protues仿真软件进行仿真,在程序设计部分,包括键盘扫描程序,音乐发声程序(程序见附录1)。

3*4矩阵键盘是由单片机控制I/O口来实现扫描的。

具体程序见附录二主程序键盘扫描部分。

程序首先给P0口赋值,然后读取P0的状态,将读取的状态值同所赋值相比较,如果二者不相同,则说明有键按下,子程序将按键值返回主程序;否则,说明没有键按。

程序继续进行下一次扫描。

本系统的软件主程序流程图如下:
3.2音乐设计
对于不同的曲调我们也可以用单片机的另外一个定时/计数器来完成。

琴键处理程序,根据检测到得按键值,查询音律表,给计时器赋值,发出相应频率的声音。

对音调的控制:根据不同的按键,对定时器T1送入不同的初值,调节T1的溢出时间,这样就可以输出不同音调频率的方波。

不同音调下各个音阶的定时器。

在这个程序中用到了两个定时/计数器来完成的。

其中T0用来产生音符频率,T1用来产生音拍。

要产生相应的音频脉冲,只需要计算出某音频的周期,再除以2。

利用计数
器计时半周期,计满时使P2.0反向,然后重复计时再反向。

本例中,单片机
工作在12MHz时钟,使用定时器/计数器T0,工作模式为1,改变计数初值
TH0、TL0就可产生不同频率的脉冲信号。

例如低3MI音,频率为330Hz,其周期T=1/f=1/330=3030us,计数值N=3030/2=1515,所以每计数1515次P2.0反向。

计数初值T=65536-N=64021。

计数脉冲值与频率的关系式是:
N=fi÷2÷fr
式中,N是计数值;fi是机器频率(晶体振荡器为12MHz时,其频率为1MHz);fr是想要产生的频率。

其计数初值T的求法如下:
T=65536-N=65536-fi÷2÷fr
例如:设K=65536,fi=1MHz,求低音DO(261Hz)、中音DO(523Hz)、高音DO (1046Hz)的计数值。

T=65536-N=65536-fi÷2÷fr
=65536-1000000÷2÷fr
=65536-500000/fr
计算可得高中低音分别为:
低音DO的T=65536-500000/262=63627
中音DO的T=65536-500000/523=64580
高音DO的T=65536-500000/1046=65059
单片机12MHZ晶振,高中低音符与计数T0相关的计数值如表所示:
音符频率表
我们要为这个音符建立一个表格,单片机通过查表的方式来获得相应的数据,低音0-19之间,中音在20-39之间,高音在40-59之间
TABLE1: DW 64021,64103,64260,64400
DW 64524,64580,64684,64777
DW 64820,64898,64968,65030
DW 65058,65110,65157,65178
音乐的音拍,一个节拍为单位(C调)如下表:
曲调值表
音乐播放子程序
对于不同的曲调我们也可以用单片机的另外一个定时/计数器来完成。

琴键处理程序,根据检测到得按键值,查询音律表,给计时器赋值,发出相应频率的声音。

对音调的控制:根据不同的按键,对定时器T1送入不同的初值,调节T1的溢出时间,这样就可以输出不同音调频率的方波。

不同音调下各个音阶的定时器。

读取键值函数getkey:说明:
显示部分使用共阳极数码管,用NPN型三极管驱动电路进行静态显示,分别显示高中低音状态和音乐播放器及电子琴的模拟切换状态。

数码管由P1.0-P1.7控制段显示,P2.0和P2.1分别控制住位选,流程图如下:显示函数display():
4Proteus仿真
软件仿真调试主要是针对单片机部分进行调试。

在软件运行前,先确保电路中连线正确,这一工作是整个调试工作的第一步,也是非常重要的一个步骤。

在这部分调试中主要通过目测,用来完成检测电路中是否存在断路或者短路情况等。

5课程设计体会
本次课程设计制作简易电子琴,虽然花费了我们很多精力,但收获颇丰,一方面,将自己的理论知识与实践相结合起来,进一步巩固了专业基础知识和相关专业课程知识;另一方面,也培养了自己独立自主、综合分析的思维与创新能力,更让我知道了团队的力量是巨大的。

在实际制作时,我们将程序烧入芯片,调试成功后,可任意弹奏自己想要的旋律。

本设计通过制作电子琴,将几个模块很好的融合,对使用单片机设计简易电子琴进行了分析,并了解了基于单片机电子琴统硬件组成。

利用单片机产生不同频率来获得我们要求的音阶,最终可随意弹奏想要表达的音乐。

说明一首音乐是许多不同的音阶组成的,而每个音阶对应着不同的频率,这样我们就可以利用不同的频率的组合,即可构成我们所想要的音乐了,于是我们可以利用单片机的定时/计数器T0来产生这样方波频率信号,因此,我们只要把一首歌曲的音阶对应频率关系弄正确即可,然后我们利用功放电路来将音乐声音放大。

设计的过程不是一帆风顺,遇到过各种各样的问题。

特别是设计软件时,一些很细小的问题都可能导致功能性的错误,修改了多次才通过仿真,例如:1、在刚刚焊接好后,程序烧入芯片后,发现并不发声,经检查才发现是对键盘没看清楚,将键盘接乱了;2、在程序编写时发现程序正确但电路并不工作,后来才发现是所用三极管为NPN型,而程序编写时则按PNP型进行编写,在修改程序后,程序运行正常实现了预想的功能;3、在硬件电路修改好后,发现每次按键时会引起蜂鸣器一直响,再按键时有可能会消失,经检查后知道是在每按键件程序后蜂鸣器对应参值没有直接置1(置1关蜂鸣器,由电路连接方式决定)。

总之,这是一次动手能力的锻炼,也是一次单片机知识的再回顾,还是对自己细心以及耐心能力的考察。

同时在具体的制作过程中我们发现一些书本上的知识与实际的应用存在着一定的差距,书本上的知识很多都是理想化后的结论,忽略了很多实际的因素,或者涉及的不全面,可在实际的应用时这些是不能被忽略的,我们不得不考虑这方面的问题,这也让我更深刻地体会到在今后的学习工作中也要注重理论联系实际。

参考文献
[1]余发山.单片机原理及应用技术[M],中国矿业大学出版社,2003年12月版;
[2] 马淑华、王凤文.单片机原理与接口技术[M],北京邮电大学出版社,2005年10月版;
[3]郭天祥.新概念51单片机C语言教程,电子工业出版社,2009年版
附1:源程序代码
#include "MusInstru.h"
#include <reg52.h>
#include <stdio.h>
#include <math.h>
#include <absacc.h>
#include "SoundPlay.h"
unsigned k,h=1;
void Delay1ms(unsigned int count) {
unsigned int i,j;
for(i=0;i<count;i++)
for(j=0;j<120;j++);
}
void music()
{
InitialSound();
P2=0;
P1=0x00;
while(1)
{
Play(Music_One,0,3,360);
Delay1ms(600);
Play(Music_Two,0,3,360);
Delay1ms(600);
Play(Music_Three,0,3,360);
Delay1ms(600);
Play(Music_Four,0,3,360);
Delay1ms(600);
//Play(Music_Five,0,3,360);
//Delay1ms(600);
}
}
/* 键扫描函数*/
uchar getkey(void)
{
uchar scancode,tmpcode;
if ((P0&0xf0)==0xf0)
return(0);
scancode = 0xfe;
while((scancode&0x10)!=0) // 逐行扫描
{
P0 = scancode; // 输出行扫描码
if ((P0&0xf0)!=0xf0) // 本行有键按下
{
tmpcode = (P0&0xf0)|0x0f;
/* 返回特征字节码,为1的位即对应于行和列*/
return((~scancode)+(~tmpcode));
}
else scancode = (scancode<<1)|0x01; // 行扫描码左移一位}
return(0);
}
void display()
{
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,
0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,
0xc6,0xa1,0x86,0x8e};
unsigned char code hl[]={0xf7,0xbf,0xfe};
unsigned int i,m=0;
for(i=0;i<9;i++)
{
m=m%2;
if(m==0)
{
P2=1;
P1=hl[h];
Delay1ms(5);
m+=1;
}
else
{
P2=2;
P1=table[k+1];
Delay1ms(5);
m+=1;
}
}
}
/*MUSINSTRU头文件*/
#ifndef _MUSINSTRU_H // 防止MusInstru.h被重复引用
#define _MUSINSTRU_H
#include <reg52.h>
#include <absacc.h>
#include <stdio.h>
#include <math.h>
#define uchar unsigned char
#define uint unsigned int
/* 音符与计数值对应表*/
uint code tab[3][7]={{0xf88b,0xf95b,0xfa14,0xfa03,0xfb8f,0xfbcf,0xfc0b},
{0xfc43,0xfcab,0xfd08,0xfd33,0xfd81,0xfdc7,0xfe05},
{0xfe21,0xfe55,0xfe84,0xfe99,0xfec0,0xfee3,0xff02}};
/*4首音乐代码*/
unsigned char code Music_One[]={ 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x66, 0x18,0x03,
0x17,0x02, 0x15,0x02, 0x16,0x01, 0x15,0x02, 0x10,0x02,
0x15,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x02,
0x17,0x03, 0x18,0x03, 0x19,0x02, 0x15,0x02, 0x18,0x66,
0x17,0x03, 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x16,0x00,
0x17,0x01, 0x19,0x02, 0x1B,0x02, 0x1B,0x70, 0x1A,0x03,
0x1A,0x01, 0x19,0x02, 0x19,0x03, 0x1A,0x03, 0x1B,0x02,
0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x18,0x66, 0x18,0x03,
0x19,0x02, 0x1A,0x02, 0x19,0x0C, 0x18,0x0D, 0x17,0x03,
0x16,0x01, 0x11,0x02, 0x11,0x03, 0x10,0x03, 0x0F,0x0C,
0x10,0x02, 0x15,0x00, 0x1F,0x01, 0x1A,0x01, 0x18,0x66,
0x19,0x03, 0x1A,0x01, 0x1B,0x02, 0x1B,0x03, 0x1B,0x03,
0x1B,0x0C, 0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x1F,0x01,
0x1A,0x01, 0x18,0x66, 0x19,0x03, 0x1A,0x01, 0x10,0x02,
0x10,0x03, 0x10,0x03, 0x1A,0x0C, 0x18,0x0D, 0x17,0x03,
0x16,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x70,
0x18,0x03, 0x17,0x02, 0x15,0x03, 0x15,0x03, 0x16,0x66,
0x16,0x03, 0x16,0x02, 0x16,0x03, 0x15,0x03, 0x10,0x02,
0x10,0x01, 0x11,0x01, 0x11,0x66, 0x10,0x03, 0x0F,0x0C,
0x1A,0x02, 0x19,0x02, 0x16,0x03, 0x16,0x03, 0x18,0x66,
0x00,0x00 }; // 两只蝴蝶
unsigned char code Music_Two[] ={ 0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03,
0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02,
0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03,
0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x16,0x03,
0x17,0x01, 0x16,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x01,
0x10,0x03, 0x15,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03,
0x16,0x03, 0x15,0x03, 0x10,0x03, 0x15,0x03, 0x16,0x01,
0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03,
0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02,
0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03,
0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x19,0x03,
0x19,0x01, 0x19,0x03, 0x1A,0x03, 0x19,0x03, 0x17,0x01,
0x16,0x03, 0x16,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03,
0x16,0x03, 0x15,0x03, 0x10,0x03, 0x10,0x0D, 0x15,0x00,
0x19,0x03, 0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03,
0x1B,0x03, 0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03,
0x16,0x0D, 0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03,
0x1A,0x02, 0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03,
0x16,0x01, 0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03,
0x19,0x02, 0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E,
0x1B,0x04, 0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E,
0x1B,0x04, 0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03,
0x17,0x0D, 0x16,0x03, 0x17,0x03, 0x19,0x01, 0x19,0x03,
0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03, 0x1B,0x03,
0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03, 0x16,0x03,
0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03, 0x1A,0x02,
0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x01,
0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03, 0x19,0x03,
0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04,
0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04,
0x17,0x16, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04,
0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03, 0x0F,0x02,
0x10,0x03, 0x15,0x00, 0x00,0x00 }; //同一首歌unsigned char code Music_Three[]={ 0x17,0x02, 0x10,0x03, 0x15,0x03, 0x19,0x03, 0x0D,0x02,
0x19,0x03, 0x15,0x02, 0x16,0x03, 0x15,0x03, 0x10,0x01,
0x1A,0x02, 0x1A,0x03, 0x19,0x03, 0x15,0x02, 0x16,0x02,
0x10,0x02, 0x17,0x03, 0x16,0x03, 0x17,0x03, 0x17,0x03,
0x15,0x03, 0x16,0x01, 0x17,0x02, 0x17,0x03, 0x19,0x03,
0x16,0x03, 0x15,0x02, 0x0F,0x03, 0x10,0x00, 0x17,0x02,
0x17,0x03, 0x19,0x03, 0x1B,0x03, 0x1A,0x03, 0x19,0x02,
0x1A,0x00, 0x1F,0x02, 0x1F,0x03, 0x1B,0x02, 0x1A,0x02,
0x1F,0x00, 0x11,0x02, 0x17,0x03, 0x19,0x03, 0x1A,0x03,
0x1B,0x03, 0x1A,0x03, 0x1A,0x00, 0x11,0x02, 0x1B,0x03,
0x1A,0x02, 0x1B,0x02, 0x16,0x00, 0x16,0x03, 0x16,0x03,
0x16,0x03, 0x10,0x03, 0x15,0x02, 0x16,0x02, 0x17,0x00,
0x19,0x03, 0x19,0x03, 0x19,0x03, 0x1A,0x03, 0x1B,0x02,
0x1A,0x03, 0x19,0x03, 0x1A,0x00, 0x00,0x00 };//祝你快乐unsigned char code Music_Four[]={ 0x0F,0x03, 0x15,0x03, 0x15,0x01, 0x11,0x03, 0x16,0x03,
0x15,0x01, 0x15,0x66, 0x16,0x03, 0x17,0x03, 0x17,0x66,
0x17,0x02, 0x16,0x03, 0x18,0x03, 0x17,0x03, 0x15,0x66,
0x1F,0x03, 0x1B,0x03, 0x19,0x03, 0x15,0x03, 0x1B,0x03,
0x19,0x03, 0x17,0x03, 0x19,0x01, 0x16,0x03, 0x18,0x03,
0x17,0x03, 0x16,0x03, 0x15,0x03, 0x11,0x03, 0x0F,0x03,
0x15,0x03, 0x15,0x01, 0x11,0x03, 0x16,0x03, 0x15,0x01,
0x15,0x66, 0x16,0x03, 0x17,0x03, 0x17,0x03, 0x17,0x03,
0x17,0x03, 0x17,0x17, 0x16,0x03, 0x18,0x03, 0x17,0x03,
0x15,0x66, 0x1F,0x03, 0x1B,0x03, 0x19,0x03, 0x15,0x03,
0x1B,0x03, 0x19,0x03, 0x17,0x03, 0x19,0x01, 0x16,0x03,
0x18,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x11,0x03,
0x11,0x01, 0x0E,0x04, 0x0F,0x04, 0x10,0x04, 0x0F,0x04,
0x10,0x04, 0x11,0x04, 0x10,0x04, 0x11,0x04, 0x15,0x04,
0x11,0x04, 0x15,0x04, 0x16,0x04, 0x15,0x04, 0x10,0x04,
0x16,0x04, 0x17,0x04, 0x18,0x02, 0x17,0x02, 0x0E,0x04,
0x0F,0x04, 0x10,0x04, 0x0F,0x04, 0x10,0x04, 0x11,0x04,
0x10,0x04, 0x11,0x04, 0x16,0x67, 0x15,0x04, 0x17,0x02,
0x0E,0x04, 0x0F,0x04, 0x10,0x04, 0x0F,0x04, 0x10,0x04,
0x11,0x04, 0x10,0x04, 0x11,0x04, 0x15,0x16, 0x0E,0x04,
0x0F,0x04, 0x10,0x04, 0x0F,0x04, 0x10,0x04, 0x11,0x04,
0x10,0x04, 0x11,0x04, 0x16,0x67, 0x15,0x04, 0x16,0x67,
0x15,0x04, 0x18,0x67, 0x17,0x04, 0x0F,0x15, 0x00,0x00 };
//time to say goodbye
#endif
/*SoundPlay头文件*/
#ifndef __SOUNDPLAY_H_REVISION_FIRST__
#define __SOUNDPLAY_H_REVISION_FIRST__
#define SYSTEM_OSC 11059200 //定义晶振频率12000000HZ
#define SOUND_SPACE 4/5 //定义普通音符演奏的长度分率,//每4分音符间隔
sbit BeepIO = P3^6; //定义输出管脚
uint Point,SoundLength;
uint code FreTab[12] = { 262,277,294,311,330,349,369,392,415,440,466,494 }; //原始频率表uchar code SignTab[7] = { 0,2,4,5,7,9,11 }; //1~7在频率表中的位置
uchar code LengthTab[7]= { 1,2,4,8,16,32,64 };
uchar Sound_Temp_TH0,Sound_Temp_TL0; //音符定时器初值暂存
uchar Sound_Temp_TH1,Sound_Temp_TL1; //音长定时器初值暂存
void InitialSound(void)
{
BeepIO = 0;
Sound_Temp_TH1 = (65535-(1/1200)*SYSTEM_OSC)/256; // 计算TL1应装入的初值(10ms的初装值)
Sound_Temp_TL1 = (65535-(1/1200)*SYSTEM_OSC)%256; // 计算TH1应装入的初值TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TMOD |= 0x11;
ET0 = 1;
ET1 = 0;
TR0 = 0;
TR1 = 0;
EA = 1;
}
void BeepTimer0(void) interrupt 1 //音符发生中断
{
BeepIO = !BeepIO;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0;
}
void int0(void) interrupt 0
{
Point =SoundLength ;
}
void Play(unsigned char *Sound,unsigned char Signature,unsigned Octachord,unsigned int Speed)
{
uint NewFreTab[12]; //新的频率表
uchar i,j;
uint LDiv,LDiv0,LDiv1,LDiv2,LDiv4,CurrentFre,Temp_T;
uchar Tone,Length,SL,SH,SM,SLen,XG,FD;
for(i=0;i<12;i++) // 根据调号及升降八度来生成新的频率表{
EX0=1;
j = i + Signature;
if(j > 11)
{
j = j-12;
NewFreTab[i] = FreTab[j]*2;
}
else
NewFreTab[i] = FreTab[j];
if(Octachord == 1)
NewFreTab[i]>>=2;
else if(Octachord == 3)
NewFreTab[i]<<=2;
}
SoundLength = 0;
while(Sound[SoundLength] != 0x00) //计算歌曲长度
{
SoundLength+=2;
}
Point = 0;
Tone = Sound[Point];
Length = Sound[Point+1]; // 读出第一个音符和它时时值
LDiv0 = 12000/Speed; // 算出1分音符的长度(几个10ms) LDiv4 = LDiv0/4; // 算出4分音符的长度
LDiv4 = LDiv4-LDiv4*SOUND_SPACE; // 普通音最长间隔标准
TR0 = 0;
TR1 = 1;
while(Point < SoundLength)
{
SL=Tone%10; //计算出音符
SM=Tone/10%10; //计算出高低音
SH=Tone/100; //计算出是否升半CurrentFre = NewFreTab[SignTab[SL-1]+SH]; //查出对应音符的频率
if(SL!=0)
{
if (SM==1) CurrentFre >>= 2; //低音
if (SM==3) CurrentFre <<= 2; //高音
Temp_T = 65536-(50000/CurrentFre)*10/(12000000/SYSTEM_OSC);
//计算计数器初值Sound_Temp_TH0 = Temp_T/256;
Sound_Temp_TL0 = Temp_T%256;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0 + 12; //加12是对中断延时的补偿
}
SLen=LengthTab[Length%10]; //算出是几分音符
XG=Length/10%10; //算出音符类型(0普通1连音2顿音) FD=Length/100;
LDiv=LDiv0/SLen; //算出连音音符演奏的长度(多少个10ms) if (FD==1)
LDiv=LDiv+LDiv/2;
if(XG!=1)
if(XG==0) //算出普通音符的演奏长度
if (SLen<=4)
LDiv1=LDiv-LDiv4;
else
LDiv1=LDiv*SOUND_SPACE;
else
LDiv1=LDiv/2; //算出顿音的演奏长度
else
LDiv1=LDiv;
if(SL==0) LDiv1=0;
LDiv2=LDiv-LDiv1; //算出不发音的长度
if (SL!=0)
{
TR0=1;
for(i=LDiv1;i>0;i--) //发规定长度的音
{
while(TF1==0);
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TF1=0;
}
}
if(LDiv2!=0)
{
TR0=0; BeepIO=0;
for(i=LDiv2;i>0;i--) //音符间的间隔
{
while(TF1==0);
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TF1=0;
}
}
Point+=2;
Tone=Sound[Point];
Length=Sound[Point+1];
}
BeepIO = 1;
EX0=0;
}
#endif
/* 主程序*/
void main()
{
uchar key;
TMOD = 0x01; // T0,工作方式1
ET0 = 1; EA = 1;
while(1)
{ k=7;
display();
P0 = 0xf0; // 发全0行扫描码
if ((P0&0xf0)!=0xf0) // 若有键按下
{
Delay1ms(2); // 延时去抖动
if ((P0&0xf0)!=0xf0) // 延时后再判断一次,去除抖动影响
{
key = getkey(); // 调用键盘扫描函数switch(key) /*根据获取的按键位置得到k值*/
{
case 0x11: k = 0; break; // 1行1列
case 0x21: k = 1; break; // 1行2列
case 0x41: k = 2; break; // 1行3列
case 0x81: k = 3; break; // 1行4列
case 0x12: k = 4; break; // 2行1列
case 0x22: k = 5; break; // 2行2列
case 0x42: k = 6; break; // 2行3列
case 0x82: music(); // 2行4列
case 0x14: h=0; continue; // 3行1列
case 0x24: h=1; continue; // 3行2列
case 0x44: h=2; continue; // 3行3列
default:
continue;
}
/* 根据所得的k值设定计数器1的计数初值*/
Sound_Temp_TH0= tab[h][k]/256;
Sound_Temp_TL0= tab[h][k]%256;
TR0 = 1; // 开始计数
display();
P0 = 0xf0; // 发全0行扫描码
while ((P0&0xf0)!=0xf0) // 若没有松开按键
{
P0 = 0xf0;
display();
}
TR0= 0; // 若按键松开,则停止计数,不产生脉冲输出}
}
BeepIO =1;
}
}
附2:系统原理图。

相关文档
最新文档