❶ 用单片机做电子琴,复音(合音)怎么实现
解析MIDI电子琴的设计用单片机是如何实现的
摘要:用单片机控制通用MIDI音源模块制作制作出的电子琴,结构简单,可靠性高,并且价格低廉,具有实用的价值。这种电子琴能够支持单音和复音弹奏,如果与高品质的音源芯片连接,音质更可与高档电子琴相媲美。我们在实验过程中,也曾采用手机中通用的音乐芯片构成音源模块,效果不错,价格更低廉,如韩国产的QS6400 等,这些芯片的驱动要复杂一些,需要对芯片进行初始化设置,详细内容可参看国防工业出版社出版的《MIDI原理与开发应用》一书中的相关章节。
关键字:电子琴,单片机,音源板,MD2064
1、电子琴的硬件设计方案
本电子琴包含48个按键键盘,即具有4个8度的音域,单片机AT89C51通过对所弹按键的识别,产生相应的MIDI消息。它支持单音弹奏和最多16个复音弹奏。电子琴结构示意图和电路原理图分别如图1和图2所示。AT89C51作为主控芯片,它使得键盘矩阵模块、通道和音色选择以及串口发送等各功能模块协调工作。48按键行列式键盘矩阵构成MIDI电子琴的键盘扫描输入端,由于89C51的P0口内部没有上拉电阻,故这里采用电阻R14——R21将列线拉至高电平,与六条行线组合完成48个琴键的扫描识别,在图1中,单片机与键盘矩阵间的双箭头线表示单片机在扫描键盘矩阵时,P0口和P2口分别作为输入/输出口使用。人机接口电路则利用了单片机P1口的大部分口线,并通过或门向INT0发出中断请求,该部分电路主要完成MIDI电子琴的通道设置和音色选择等人机交互功能。键盘的弹奏信息以及通道、音色信息经CPU处理后,由串口将标准的MIDI数据发送给MIDI音源及放大器,推动扬声器发声。
图1:MIDI电子琴结构示意图
图2:MIDI电子琴电路图
音源模块采用MD2064 套板,如图3所示。它是一种模块化的MIDI音源产品,由得理电子公司开发,具有标准MIDI接口,该板能接受标准GM MIDI命令进行音乐播放,自带3D, REVERB, CHORUS等效果处理。由于该套板的MIDI 接口采用了光耦合器,电流驱动,故设计了由Q1、Q2等器件组成的驱动电路,使单片机串口数据得以正常传输。在模块的耳机输出端取得信号后,经小功率放大即可推动扬声器发声。
2、电子琴的软件设计特点
该电子琴软件采用模块化设计方法,程序也较简单。软件中各功能模块都由相应的子程序完成,主要包含通道选择模块,音色选择模块,48按键键盘扫描模块,串口发送模块等,其中为了及时完成用户命令,音色选择模块采用了中断服务子程序,可以在演奏中快速响应使用者的请求。
主程序在完成串口初始化、相关变量的初始化以及设置通道后,即进入键盘扫描、发送音符消息流程,为了使按键识别准确可靠,还设置了两个缓冲区BUFF1和BUFF2保存键盘扫描值。主程序流程图如图3。
图3:MIDI电子琴程序的流程图
以下是部分功能模块的程序设计介绍。
2.1 音色选择模块的设计
该模块的功能是使MIDI电子琴能按要求快速改变音色,所以采用了中断服务子程序。当某个音色选择按键压下时,通过或门向单片机的INT0发出中断请求,CPU响应后进入该中断服务子程序。MIDI技术规范规定,标准MIDI含有128种音色,它们的编号范围是0~127,为了能够快速找到所需音色,硬件中设置3个按键,其中2个用于音色编号的单步增加和减小,每次增加或减小1个音色编号,另外一个键用于音色快进,当快进键有效时,每次增加8个音色编号,选择增加8个音色的原因是:标准MIDI的128种音色是按每8个音色一组编排的,共包含16个乐器组。电子琴开机时默认的音色编号是0,即大钢琴音色。
单片机的P1.2口线连接着音色增加按键,P1.3则连接音色减小按键,P1.4连接音色快进键。低电平时按键有效,这三个按键通过与门连接外部中断INT0,以便实时响应音色设置。该外部中断0的中断服务子程序流程图见图4,(图中省去了按键延时去抖动部分):
图4:音色改变子程序流程图
在该子程序中,变量TAMBER中存放当前音色,其值可在0~127间循环,当TAMBER是最大值127时,加1后又变为0;而当TAMBER为0时,减1则变为127;在边界范围加8取模后,刚好为其对应的音色值。
2.2 串口发送模块
串口发送模块主要用于发送产生的MIDI消息,串口采用的模式1,发送的波特率是31.25KBPS。串口通过驱动电路连接MIDI音源,发送MIDI消息。通道号存放在变量CHANNEL中,通过与90H相与,所得值就是当前所设置的通道号。
2.3 键盘扫描模块
本电子琴提供了48个MIDI按键,即4个8度音的音域范围,当按下单个键时,产生一条MIDI消息,当按下多个键值时产生对应键值的多条MIDI音符开消息,当某个键值被释放时,发送对应的音符关消息。这些MIDI消息通过串口发送给MIDI音源,产生MIDI音乐。音乐的时值由按键的时间长度控制,当按键被释放,实时产生MIDI消息,关闭被释放的键值音。
由P0口和P2口的P2.0~P2.5构成行列式键盘,也可继续扩展键盘,例如改为常用的49键或64键。因为支持复音按键,键盘扫描程序必须扫描到行列式键盘的每个键值,扫描所得的键值存放在缓冲区BUFF1或BUFF2中。键盘扫描程序获得的键盘编号范围是0~47,还需将这个键盘编号值转换为MIDI设备能够识别的钢琴键盘编号,这个功能由一个子程序来完成,限于篇幅本文不再详述。键盘扫描子程序流程如图5。
图5:键盘扫描子程序
❷ 楂桦垎姹备竴涓鍗旷墖链虹数瀛愮惔c璇瑷绋嫔簭
<p>6锛 鎶ヨ︿骇鐢熷櫒 </p> <p>1锛 瀹为獙浠诲姟 </p> <p>鐢≒1.0杈揿嚭1KHz鍜500Hz镄勯煶棰戜俊鍙烽┍锷ㄦ壃澹板櫒锛屼綔鎶ヨ︿俊鍙凤纴瑕佹眰1KHz淇″彿鍝100ms锛500Hz淇″彿鍝200ms,浜ゆ浛杩涜岋纴P1.7鎺ヤ竴寮鍏宠繘琛屾带鍒讹纴褰揿紑鍏冲悎涓婂搷鎶ヨ︿俊鍙凤纴褰揿紑鍏虫柇寮锻婅︿俊鍙峰仠姝锛岀紪鍑虹▼搴忋 </p> <p>2锛 鐢佃矾铡熺悊锲 </p> <p> </p> <p>锲4.6.1 </p> <p>3锛 绯荤粺𨱒夸笂纭浠惰繛绾 </p> <p>锛1锛 鎶娾滃崟鐗囨満绯荤粺钬濆尯锘熶腑镄凯1.0绔鍙g敤瀵肩嚎杩炴帴鍒扳滈煶棰戞斁澶фā鍧椻濆尯锘熶腑镄凷PK IN绔鍙d笂锛 </p> <p>锛2锛 鍦ㄢ滈煶棰戞斁澶фā鍧椻濆尯锘熶腑镄凷PK OUT绔鍙d笂鎺ヤ笂涓涓8娆х殑鎴栬呮槸16娆х殑锽囧彮锛 </p> <p>锛3锛 鎶娾滃崟鐗囨満绯荤粺钬濆尯锘熶腑镄凯1.7/RD绔鍙g敤瀵肩嚎杩炴帴鍒扳滃洓璺𨰾ㄥ姩寮鍏斥濆尯锘熶腑镄凨1绔鍙d笂锛 </p> <p>4锛 绋嫔簭璁捐″唴瀹 </p> <p>锛1锛 淇″彿浜х敓镄勬柟娉 </p> <p>500Hz淇″彿锻ㄦ湡涓2ms锛屼俊鍙风数骞充负姣1ms鍙桦弽1娆★纴1KHz镄勪俊鍙峰懆链熶负1ms锛屼俊鍙风数骞虫疮500us鍙桦弽1娆★绂 </p> <p> </p> <p>5锛 绋嫔簭妗嗗浘 </p> <p> </p> <p>锲4.6.2 </p> <p>6锛 姹囩紪婧愮▼搴</p> <p>FLAG BIT 00H</p> <p>ORG 00H</p> <p>START: JB P1.7,START</p> <p>JNB FLAG,NEXT</p> <p>MOV R2,#200</p> <p>DV: CPL P1.0</p> <p>LCALL DELY500</p> <p>LCALL DELY500</p> <p>DJNZ R2,DV</p> <p>CPL FLAG</p> <p>NEXT: MOV R2,#200</p> <p>DV1: CPL P1.0</p> <p>LCALL DELY500</p> <p>DJNZ R2,DV1</p> <p>CPL FLAG</p> <p>SJMP START</p> <p>DELY500: MOV R7,#250</p> <p>LOOP: NOP</p> <p>DJNZ R7,LOOP</p> <p>RET</p> <p>END</p> <p>7锛 C璇瑷婧愮▼搴</p> <p>#include <AT89X51.H></p> <p>#include <INTRINS.H></p> <p>bit flag;</p> <p>unsigned char count;</p> <p>void dely500(void)</p> <p>{</p> <p>unsigned char i;</p> <p>for(i=250;i>0;i--)</p> <p>{</p> <p>_nop_();</p> <p>}</p> <p>}</p> <p>void main(void)</p> <p>{</p> <p>while(1)</p> <p>{</p> <p>if(P1_7==0)</p> <p>{</p> <p>for(count=200;count>0;count--)</p> <p>{</p> <p>P1_0=~P1_0;</p> <p>dely500();</p> <p>}</p> <p>for(count=200;count>0;count--)</p> <p>{</p> <p>P1_0=~P1_0;</p> <p>dely500();</p> <p>dely500();</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <p></p>
❸ 用单片机做电子琴的问题
你首先要保证你的单片机外围电路要正常,他们是单片机工作的前提,我给你提点你查查,看看复位电路是否接好,最好用上电复位电路,再观察你的晶振电路是否正常,最好用示波器观察。不知道你用的是哪个型号的单片机,如果是40管脚的51系列,那你就再看看31管脚是否接的是高电平!就那个VPP,这个很多人就忽视了。检查完以后如果一切都正常,那要用软件测试的办法了,你写一个小程序,烧写进去,看看是否运行,如果可以的话,那就不是硬件的问题了,而是你的软件问题了,行了就说这么多了,如果 还有问题可以交流!祝你成功!!
❹ 单片机电子琴的工作原理
众所周知,声音是周围空气的震动,音调取决于震动的频率,频率越高音调越高。
电子琴按下不同的琴键就会发出不同音调的声音,其实就是产生不同频率的震动。
单片机电子琴说白了就是利用单片机产生不同频率的电压波形,推动扬声器或蜂鸣器来发出不同音调的声音。
假设电子琴有八个音阶,就对应8个不同的频率,频率越高音调就越高。单片机很容易输出方波信号,那么只要让它产生不同频率的方波就可以了,然后用这个方波信号驱动扬声器就可以了。单片机的按键可以模拟琴键,按下不同的按键就对应不同的频率的方波,就能发出不同频率的声音了。
❺ 单片机电子琴的发声原理
简单点儿用IO口输出不同频率的方波给喇叭就行
❻ 单片机简易电子琴程序
22. 电子琴
1. 实验任务
(1. 由4X4组成16个按钮矩阵,设计成16个音。
(2. 可随意弹奏想要表达的音乐。
2. 电路原理图
图4.22.1
3. 系统板硬件连线
(1. 把“单片机系统”区域中的P1.0端口用导线连接到“音频放大模块”区域中的SPK IN端口上;
(2. 把“单片机系统“区域中的P3.0-P3.7端口用8芯排线连接到“4X4行列式键盘”区域中的C1-C4 R1-R4端口上;
4. 相关程序内容
(1. 4X4行列式键盘识别;
(2. 音乐产生的方法;
一首音乐是许多不同的音阶组成的,而每个音阶对应着不同的频率,这样我们就可以利用不同的频率的组合,即可构成我们所想要的音乐了,当然对于单片机来产生不同的频率非常方便,我们可以利用单片机的定时/计数器T0来产生这样方波频率信号,因此,我们只要把一首歌曲的音阶对应频率关系弄正确即可。现在以单片机12MHZ晶振为例,例出高中低音符与单片机计数T0相关的计数值如下表所示
音符 频率(HZ) 简谱码(T值) 音符 频率(HZ) 简谱码(T值)
低1 DO 262 63628 # 4 FA# 740 64860
#1 DO# 277 63731 中 5 SO 784 64898
低2 RE 294 63835 # 5 SO# 831 64934
#2 RE# 311 63928 中 6 LA 880 64968
低 3 M 330 64021 # 6 932 64994
低 4 FA 349 64103 中 7 SI 988 65030
# 4 FA# 370 64185 高 1 DO 1046 65058
低 5 SO 392 64260 # 1 DO# 1109 65085
# 5 SO# 415 64331 高 2 RE 1175 65110
低 6 LA 440 64400 # 2 RE# 1245 65134
# 6 466 64463 高 3 M 1318 65157
低 7 SI 494 64524 高 4 FA 1397 65178
中 1 DO 523 64580 # 4 FA# 1480 65198
# 1 DO# 554 64633 高 5 SO 1568 65217
中 2 RE 587 64684 # 5 SO# 1661 65235
# 2 RE# 622 64732 高 6 LA 1760 65252
中 3 M 659 64777 # 6 1865 65268
中 4 FA 698 64820 高 7 SI 1967 65283
下面我们要为这个音符建立一个表格,有助于单片机通过查表的方式来获得相应的数据
低音0-19之间,中音在20-39之间,高音在40-59之间
TABLE: DW 0,63628,63835,64021,64103,64260,64400,64524,0,0
DW 0,63731,63928,0,64185,64331,64463,0,0,0
DW 0,64580,64684,64777,64820,64898,64968,65030,0,0
DW 0,64633,64732,0,64860,64934,64994,0,0,0
DW 0,65058,65110,65157,65178,65217,65252,65283,0,0
DW 0,65085,65134,0,65198,65235,65268,0,0,0
DW 0
2、音乐的音拍,一个节拍为单位(C调)
曲调值 DELAY 曲调值 DELAY
调4/4 125ms 调4/4 62ms
调3/4 187ms 调3/4 94ms
调2/4 250ms 调2/4 125ms
对于不同的曲调我们也可以用单片机的另外一个定时/计数器来完成。
下面就用AT89S51单片机产生一首“生日快乐”歌曲来说明单片机如何产生的。
在这个程序中用到了两个定时/计数器来完成的。其中T0用来产生音符频率,T1用来产生音拍。
5. 程序框图
贴不了.
7. C语言源程序
#include <AT89X51.H>
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
unsigned char temp;
unsigned char key;
unsigned char i,j;
unsigned char STH0;
unsigned char STL0;
unsigned int code tab[]={64021,64103,64260,64400,
64524,64580,64684,64777,
64820,64898,64968,65030,
65058,65110,65157,65178};
void main(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)
{
case 0x0e:
key=0;
break;
case 0x0d:
key=1;
break;
case 0x0b:
key=2;
break;
case 0x07:
key=3;
break;
}
temp=P3;
P1_0=~P1_0;
P0=table[key];
STH0=tab[key]/256;
STL0=tab[key]%256;
TR0=1;
temp=temp & 0x0f;
while(temp!=0x0f)
{
temp=P3;
temp=temp & 0x0f;
}
TR0=0;
}
}
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)
{
case 0x0e:
key=4;
break;
case 0x0d:
key=5;
break;
case 0x0b:
key=6;
break;
case 0x07:
key=7;
break;
}
temp=P3;
P1_0=~P1_0;
P0=table[key];
STH0=tab[key]/256;
STL0=tab[key]%256;
TR0=1;
temp=temp & 0x0f;
while(temp!=0x0f)
{
temp=P3;
temp=temp & 0x0f;
}
TR0=0;
}
}
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)
{
case 0x0e:
key=8;
break;
case 0x0d:
key=9;
break;
case 0x0b:
key=10;
break;
case 0x07:
key=11;
break;
}
temp=P3;
P1_0=~P1_0;
P0=table[key];
STH0=tab[key]/256;
STL0=tab[key]%256;
TR0=1;
temp=temp & 0x0f;
while(temp!=0x0f)
{
temp=P3;
temp=temp & 0x0f;
}
TR0=0;
}
}
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)
{
case 0x0e:
key=12;
break;
case 0x0d:
key=13;
break;
case 0x0b:
key=14;
break;
case 0x07:
key=15;
break;
}
temp=P3;
P1_0=~P1_0;
P0=table[key];
STH0=tab[key]/256;
STL0=tab[key]%256;
TR0=1;
temp=temp & 0x0f;
while(temp!=0x0f)
{
temp=P3;
temp=temp & 0x0f;
}
TR0=0;
}
}
}
}
void t0(void) interrupt 1 using 0
{
TH0=STH0;
TL0=STL0;
P1_0=~P1_0;
}
根据自己的情况稍微改改就好了