基于单片机的简易电子琴设计.docx
《基于单片机的简易电子琴设计.docx》由会员分享,可在线阅读,更多相关《基于单片机的简易电子琴设计.docx(21页珍藏版)》请在冰豆网上搜索。
基于单片机的简易电子琴设计
1方案论证
1.1原理图
图1-1 电子琴电路图
Figure1-1Keyboardcircuitdiagram
1.2系统板硬件连线
系统板硬件连线如图1-1所示,发生模块,键盘模块,及LED显示模块连接如下1.把“单片机系统”区域中的P1.0端口用导线连接到“音频放大模块”区域中的SPKIN端口上;
2.把“单片机系统”区域中的P3.0-P3.7端口用8芯排线连接到“4X4行列式键盘”区域中的C1-C4 R1-R4端口上;3.把“单片机系统”区域中的P0.0/AD0-P0.7/AD7端口用8芯排线连接到“四路静态数码显示模块”区域中的任一个a-h端口上;要求:
P0.0/AD0对应着a,P0.1/AD1对应着b,……,P0.7/AD7对应着h。
图1-2管脚图
Figure1-2pinmap
1.3.1.1主要功能特性
·兼容MCS-51指令系统
·32个双向I/O口
·2个16位可编程定时/计数器
·全双工UART串行中断口线
·2个外部中断源
·中断唤醒省电模式
·看门狗(WDT)电路
·灵活的ISP字节和分页编程
·4k可反复擦写ISPFlashROM
·4.5-5.5V工作电压
·时钟频率0-33MHz
·128*8bit内部RAM
·低功耗空闲和省电模式
·3级加密位
·软件设置空闲和省电功能
·双数据寄存器指针
1.3.2LM386
1.3.2.1LM386内部电路
LM386是美国国家半导体公司生产的音频功率放大器,主要应用于低电压消费类产品。
LM386特性:
静态功耗低,约为4mA,可用于电池供电;
工作电压X围宽,4V-12V或5V-18V;
外围元件少;
电压增益可调,20-200;
低失真度。
LM386是一种音频集成功放,具有自身功耗低、电压增益可调整、电源电压X围大、外接元件少和总谐波失真小等优点,广泛应用于录音机和收音机之中。
图1-3 LM386内部电路原理图
Fig1-3LM386internalcircuitdiagram
LM386内部电路原理图如图1-3所示。
与通用型集成运放相类似,它是一个三级放大电路.第一级为差分放大电路,T1和T3、T2和T4分别构成复合管,作为差分放大电路的放大管;T5和T6组成镜像电流源作为T1和T2的有源负载;T3和T4信号从管的基极输入,从T2管的集电极输出,为双端输入单端输出差分电路。
使用镜像电流源作为差分放大电路有源负载,可使单端输出电路的增益近似等于双端输出电容的增益
第二级为共射放大电路,T7为放大管,恒流源作有源负载,以增大放大倍数。
第三级中的T8和T9管复合成PNP型管,与NPN型管T10构成准互补输出级。
二极管D1和D2为输出级提供合适的偏置电压,可以消除交越失真。
引脚2为反相输入端,引脚3为同相输入端。
电路由单电源供电,故为OTL电路。
输出端(引脚5)应外接输出电容后再接负载。
电阻R7从输出端连接到T2的发射极,形成反馈通路,并与R5和R6构成反馈网络,从而引入了深度电压串联负反馈,使整个电路具有稳定的电压增益。
图2.3音频放大器电路
Figure2.3audioamplifiercircuit
1.3.3LED数码管
图1-4 7段数码管
Figure1-47segmentdigitaltube
数码管(如图1-4所示)使用条件:
a、段及小数点上加限流电阻b、使用电压:
段:
根据发光颜色决定;小数点:
根据发光颜色决定c、使用电流:
静态:
总电流80mA(每段10mA);动态:
平均电流4-5mA峰值电流100mA上面这只是七段数码管引脚图,其中共阳极数码管引脚图和共阴极的是一样的。
数码管使用注意事项说明:
(1)数码管表面不要用手触摸,不要用手去弄引角;
(2)焊接温度:
260度;焊接时间:
5S(3)表面有保护膜的产品,可以在使用前撕下来。
2实现过程
2.14X4行列式键盘识别及显示
组成键盘的按键有机械式、电容式、导电橡胶式、薄膜式多种,但不管什么形式,其作用都是一个使电路接通与断开的开关。
目前微机系统中使用的键盘按其功能不同,通常可分为编码键盘和非编码键盘两种基本类型。
编码键盘:
键盘本身带有实现接口主要功能所需的硬件电路。
不仅能自动检测被按下的键,并完成去抖动、防串键等功能,而且能提供与被按键功能对应的键码(如ASCII码)送往CPU。
所以,编码键盘接口简单、使用方便。
但由于硬件电路较复杂,因而价格较贵。
非编码键盘:
键盘只简单地提供按键开关的行列矩阵。
有关按键的识别、键码的确定与输入、去抖动等功能均由软件完成。
目前微机系统中,一般为了降低成本大多数采用非编码键盘。
键盘接口必须具有去抖动、防串键、按键识别和键码产生4个基本功能。
(1)去抖动:
每个按键在按下或松开时,都会产生短时间的抖动。
抖动的持续时间与键的质量相关,一般为5—20mm。
所谓抖动是指在识别被按键是必须避开抖动状态,只有处在稳定接通或稳定断开状态才能保证识别正确无误。
去抖问
题可通过软件延时或硬件电路解决。
(2)防串键:
防串键是为了解决多个键同时按下或者前一按键没有释放又有新的按键按下时产生的问题。
常用的方法有双键锁定和N键轮回两种方法。
双键锁定,是当有两个或两个以上的按键按下时,只把最后释放的键当作有效键并产生相应的键码。
N键轮回,是当检测到有多个键被按下时,能根据发现它们的顺序依次产生相应键的键码。
(3)被按键识别:
如何识别被按键是接口解决的主要问题,一般可通过软硬结合的方法完成。
常用的方法有行扫描法和线反转法两种。
行扫描法的基本思想是,由程序对键盘逐行扫描,通过检测到的列输出状态来确定闭合键,为此,需要设置入口、输出口一个,该方法在微机系统中被广泛使用。
线反转法的基本思想是通过行列颠倒两次扫描来识别闭合键,为此需要提供两个可编程的双向输入/输出端口。
(4)键码产生:
为了从键的行列坐标编码得到反映键功能的键码,一般在内存区中建立一个键盘编码表,通过查表获得被按键的键码。
用AT89S51的并行口P1接4×4矩阵键盘,以P1.0-P1.3作输入线,以P1.4-P1.7作输出线;在数码管上显示每个按键的“0-F”序号。
图2-1键盘识别模块
Fig2-1Keyboardrecognitionmodule
2.1.1系统板上硬件连线设计
键盘模块硬件连线如图2-1所示:
(1)把“单片机系统”区域中的P3.0-P3.7端口用8芯排线连接到“4X4行列式键盘”区域中的C1-C4 R1-R4端口上;
(2)把“单片机系统”区域中的P0.0/AD0-P0.7/AD7端口用8芯排线连接到“7段数码显示模块”区域中的任一个a-h端口上;要求:
P0.0/AD0对应着a,P0.1/AD1对应着b,……,P0.7/AD7对应着h。
2.1.2程序设计内容
(1)4×4矩阵键盘识别处理,每个按键有它的行值和列值,行值和列值的组合就是识别这个按键的编码。
矩阵的行线和列线分别通过两并行接口和CPU通信。
每个按键的状态同样需变成数字量“0”和“1”,开关的一端(列线)通过电阻接VCC,而接地是通过程序输出数字“0”实现的。
(2)键盘处理程序的任务是:
确定有无键按下,判断哪一个键按下,键的功能是什么;还要消除按键在闭合或断开时的抖动。
两个并行口中,一个输出扫描码,使按键逐行动态接地,另一个并行口输入按键状态,由行扫描值和回馈信号共同形成键编码而识别按键,通过软件查表,查出该键的功能。
电路图如下
图2.1.1行列式键盘电路
Figure2.1.1determinantkeyboardcircuit
2.1.3I/O并行口直接驱动LED显示
LED显示模块如图2-2所示,利用AT89S51单片机的P0端口的P0.0-P0.7连接到一个七段数码管的a-h的笔段上,数码管的公共端接地。
在数码管上循环显示0-7数字,时间间隔0.2秒。
图2-2电路原理图
Fig2-2Circuitdiagram
系统板上硬件连线:
(如图2-2所示)
把“单片机系统”区域中的P0.0/AD0-P0.7/AD7端口用8芯排线连接到“七段数码显示模块”区域中的任一个数码管的a-h端口上;要求:
P0.0/AD0与a相连,P0.1/AD1与b相连,P0.2/AD2与c相连,……,P0.7/AD7与h相连。
程序设计内容
(1)LED数码显示原理:
七段LED显示器内部由七个条形发光二极管和一个小圆点发光二极管组成,根据各管的极管的接线形式,可分成共阴极型和共阳极型。
LED数码管的g~a七个发光二极管因加正电压而发亮,因加零电压而不以发亮,不同亮暗的组合就能形成不同的字形,这种组合称之为字形码,下面给出共阴极的字形码表(如表2-1所示)
表2-1 字形码表
Table2-1fontcodetable
“0”
3FH
“8”
7FH
“1”
06H
“9”
6FH
“2”
5BH
“A”
77H
“3”
4FH
“b”
7CH
“4”
66H
“C”
39H
“5”
6DH
“d”
5EH
“6”
7DH
“E”
79H
“7”
07H
“F”
71H
(2)由于显示的数字0-9的字形码没有规律可循,只能采用查表的方式来完成我们所需的要求了。
这样我们按着数字0-9的顺序,把每个数字的笔段代码按顺序排好!
建立的表格如下所示:
TABLEDB3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH。
2.2音乐产生的方法
2.2.1原理
一首音乐是许多不同的音阶组成的,而每个音阶对应着不同的频率,这样我们就可以利用不同的频率的组合,即可构成我们所想要的音乐了,当然对于单片机来产生不同的频率非常方便,我们可以利用单片机的定时/计数器T0来产生这
样方波频率信号,因此,我们只要把一首歌曲的音阶对应频率关系正确即可。
若要产生音频脉冲,只要算出某一音频的周期(1/频率),再将此周期除以2,
即为半周期的时间。
利用定时器计时半周期时间,每当计时终止后就将P1.0反相,然后重复计时再反相。
就可在P1.0引脚上得到此频率的脉冲。
利用AT89C51的内部定时器使其工作计数器模式(MODE1)下,改变计数值TH0及TL0以产生不同频率的方法产生不同音阶,例如,频率为523Hz,其周期T=1/523=1912μs,因此只要令计数器计时956μs/1μs=956,每计数956次时将I/O反相,就可得到中音DO(523Hz)。
计数脉冲值与频率的关系式(如式2-1所示)是:
N=fi÷2÷fr 2-1
式中,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相关的计数值如表2-2所示
表2-2音符频率表
1.发声程序
表4 音符频率表
音符
频率
简谱码
中fa
698
64820
低dao
262
63628
中sao
784
64898
低re
294
63835
中la
880
64968
低mi
330
64021
中xi
988
65030
低fa
349
64103
高dao
1046
65058
低sao
392
64260
高re
1175
65110
低la
440
6440
高mi
1318
65157
低xi
494
64524
高fa
1397
65178
中dao
523
64580
高sao
1568
65217
中re
587
64684
高la
1760
65252
中mi
659
64777
高xi
1967
65283
我们要为这个音符建立一个表格,单片机通过查表的方式来获得相应的数据
低音0-19之间,中音在20-39之间,高音在40-59之间
TABLE:
DW0,63628,63835,64021,64103,64260,64400,64524,0,0
DW0,63731,63928,0,64185,64331,64463,0,0,0
DW0,64580,64684,64777,64820,64898,64968,65030,0,0
DW0,64633,64732,0,64860,64934,64994,0,0,0
DW0,65058,65110,65157,65178,65217,65252,65283,0,0
DW0,65085,65134,0,65198,65235,65268,0,0,0
DW0
音乐的音拍,一个节拍为单位(C调)(如表2-3所示)
表2-3曲调值表
Table2-3Tunethevalueofthetable
曲调值
DELAY
曲调值
DELAY
调4/4
125ms
调4/4
62ms
调3/4
187ms
调3/4
94ms
调2/4
250ms
调2/4
125ms
对于不同的曲调我们也可以用单片机的另外一个定时/计数器来完成。
琴键处理程序,根据检测到得按键值,查询音律表,给计时器赋值,发出相应频率的声音。
对音调的控制:
根据不同的按键,对定时器T1送入不同的初值,调节T1的溢出时间,这样就可以输出不同音调频率的方波。
不同音调下各个音阶的定时器。
在这个程序中用到了两个定时/计数器来完成的。
其中T0用来产生音符频率,T1用来产生音拍。
2.2.2程序框图
音乐发声程序框图如图2-4所示:
图2-4音乐发声程序框图
电路原理图:
程序框图
程序框图如图所示:
C语言源程序
#include
unsignedcharcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};
unsignedchardiscount;
voiddelay02s(void)
{
unsignedchari,j,k;
for(i=20;i>0;i--)
for(j=20;j>0;j--)
for(k=248;k>0;k--);
}
voidmain(void)
{
while
(1)
{
for(discount=0;dispcount<10;dispcount++)
{
P0=table[discount];
delay02s();
}
}
}
键盘识别程序框图如图所示:
语言源程序
#include
unsignedcharcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
unsignedchartemp;unsignedcharkey;unsignedchari,j;voidmain(void){while
(1){P3=0xff;P3_4=0;temp=P3;temp=temp&0x0f;if(temp!
=0x0f){for(i=50;i>0;i--)for(j=200;j>0;j--);temp=P3;temp=temp&0x0f;if(temp!
=0x0f){temp=P3;temp=temp&0x0f;switch(temp){case0x0e:
key=7;break;case0x0d:
key=8;break;case0x0b:
key=9;break;case0x07:
key=10;break;}temp=P3;P1_0=~P1_0;P0=table[key];temp=temp&0x0f;while(temp!
=0x0f){temp=P3;temp=temp&0x0f;}}}P3=0xff;P3_5=0;temp=P3;temp=temp&0x0f;if(temp!
=0x0f){for(i=50;i>0;i--)for(j=200;j>0;j--);temp=P3;temp=temp&0x0f;if(temp!
=0x0f){temp=P3;temp=temp&0x0f;switch(temp){case0x0e:
key=4;break;case0x0d:
key=5;break;case0x0b:
key=6;break;case0x07:
key=11;break;}temp=P3;P1_0=~P1_0;P0=table[key];temp=temp&0x0f;while(temp!
=0x0f){temp=P3;temp=temp&0x0f;}}}P3=0xff;P3_6=0;temp=P3;temp=temp&0x0f;if(temp!
=0x0f){for(i=50;i>0;i--)for(j=200;j>0;j--);temp=P3;temp=temp&0x0f;if(temp!
=0x0f){temp=P3;temp=temp&0x0f;switch(temp){case0x0e:
key=1;break;case0x0d:
key=2;break;case0x0b:
key=3;break;case0x07:
key=12;break;}temp=P3;P1_0=~P1_0;P0=table[key];temp=temp&0x0f;while(temp!
=0x0f){temp=P3;temp=temp&0x0f;}}}P3=0xff;P3_7=0;temp=P3;temp=temp&0x0f;if(temp!
=0x0f){for(i=50;i>0;i--)for(j=200;j>0;j--);temp=P3;temp=temp&0x0f;if(temp!
=0x0f){temp=P3;temp=temp&0x0f;switch(temp){case0x0e:
key=0;break;case0x0d:
key=13;break;case0x0b:
key=14;break;case0x07:
key=15;break;}temp=P3;P1_0=~P1_0;P0=table[key];temp=temp&0x0f;while(temp!
=0x0f){temp=P3;temp=temp&0x0f;}
程序框图
音乐发声程序框图如图所示:
程序
#include
unsignedcharcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
unsignedchartemp;
unsignedcharkey;
unsignedchari,j;
unsignedcharSTH0;
unsignedcharSTL0;
unsignedintcodetab[]={64021,64103,64260,64400,64524,64580,64684,64777,
64820,64898,64968,65030,65058,65110,65157,65178};
voidmain(void)
{
TMOD=0x01;
ET0=1;
EA=1;
while
(1)
{
P3=0xff;
P3_4=0;
temp=P3;
temp=temp&0x0f;
if(temp!
=0x0f)
{
for(i=50;i>0;i--)
for(j=200;j>0;j--);
temp=P3;
temp=temp&0x0f;
if(temp!
=0x0f)
{
temp=P3;
temp=temp&0x0f;
switch(temp)
{
case0x0e:
key=0;
break;
case0x0d:
key=1;
break;
case0x0b:
key=2;
break;
case0x07:
key=3;
break;
}
temp=P3;
P1_0=~P1_0;
P0=table[key];
STH0=tab[key]/256;
STL0=tab[key]%6;
TR0=1;
temp=temp&0x0f;
while(temp!
=0x0f)
{
temp=P3;