基于51单片机的电子琴制作(播放+弹奏)
制作的电子琴具有播放一段音乐和自我弹奏两种工作模式,可通过按键来切换模式。
程序如下:
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define LCD1602_DATAPINS P2
typedef unsigned int u16;
typedef unsigned char u8;
sbit beep=P1^4;
sbit LCD1602_E=P1^2;
sbit LCD1602_RW=P1^1;
sbit LCD1602_RS=P1^0;
sbit swtich=P1^3;//切换按键
uchar timer0h,timer0l,time,flag,key,a,b,c;
u8 moshi[11]="bofangmoshi";
u8 moshi1[11]="tanzoumoshi";
u8 yindiao[7]="1234567";
code uchar song[]={0,1,1,
5,1,1,
5,1,1,
5,1,1,
6,1,1,
7,1,1,
6,1,1,
5,1,1,
3,1,1,
3,1,1,
3,1,1,
2,1,1,
2,1,1,
2,1,2,
0,1,2,
5,1,1,
5,1,1,
5,1,1,
3,1,1,
2,1,1,
2,1,1,
1,1,1,
1,1,1,
2,1,1,
1,1,1};
code uchar TH_0[]={0xF2,0xF3,0xF5,0xF5,0xF6,0xF7,0xF8,
0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC,0xFC, //1,2,3,4,5,6,7,8,i
0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,
0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,} ;
code uchar TL_0[]={0x42,0xC1,0x17,0xB6,0xD0,0xD1,0xB6,
0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B,0x8F, //1,2,3,4,5,6,7,8,i
0xEE,0x44, 0x6B,0xB4,0xF4,0x2D,
0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16,};
void delay(uchar t)
{
uchar t1;
unsigned long t2;
for(t1=0;t1<t;t1++)
{
for(t2=0;t2<8000;t2++);
}
}
void Lcd1602_Delay1ms(uint c) //误差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择输入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //开显示
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
void LCD_Display(void)
{
u8 i;
LcdWriteCom(0x00+0x80);
if(flag==1)
{
LcdWriteCom(0x01);
for(i=0;i<11;i++)
{
LcdWriteData(moshi[i]);
}
}
else
{
for(i=0;i<11;i++)
{
LcdWriteData(moshi1[i]);
}
LcdWriteCom(0x40+0x80);
for(i=0;i<1;i++)
{
LcdWriteData(yindiao[c]);
}
}
}
void check_key() //按键函数
{
P3=0xff;//先赋给P2组口高电平
switch(P3)//按下一个键
{
case 0xfe:key=7;c=0;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xfd:key=8;c=1;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xfb:key=9;c=2;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xf7:key=10;c=3;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xef:key=11;c=4;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xdf:key=12;c=5;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
case 0xbf:key=13;c=6;TH0=TH_0[key];
TL0=TL_0[key];
TR0=1;break;
default:beep=1;TR0=0; //没有按键按下关闭喇叭
}
}
void songd() // 音乐处理函数
{
TH0=timer0h;
TL0=timer0l;
TR0=1;
delay(time);
}
void main(void)//主函数
{
uchar k,i;
LcdInit();
TMOD=1; //置CT0定时工作方式1
EA=1;
ET0=1;
flag=0; //IE=0x82 //CPU开中断,CT0开中断
while(1)
{
if(swtich==0)
{
flag=1;
i=0;
LCD_Display();
while(i<75) //音乐数组长度 ,放完循环播放
{
k=song[i]+7*song[i+1]-1;
timer0h=TH_0[k];
timer0l=TL_0[k];
time=song[i+2];
i=i+3;
songd();
if(swtich==1)
break;
}
}
else
{
flag=0;
check_key();
LCD_Display();
}
}
}
void t0int() interrupt 1 //定时器中断函数
{
if(flag==1)
{
TR0=0;
beep=~beep;
TH0=timer0h;
TL0=timer0l;
TR0=1;
}
else
{
beep=~beep;
TH0=TH_0[key];
TL0=TL_0[key];
}
}
数组song[]中存放我们想要播放的歌曲每三个数代表音乐简谱中的一个音的音调,节拍,时间(本人对音乐不是很了解,求指正),可以通过改变数组的长度和内容来存放不同的音乐。
可通过开关切换至弹奏模式,通过按不同的按键来发出不同的音调(长按某个按键会一直发出其对应的音调,轻触按键即可)
proteus仿真
示例图片如下(播放元件选用喇叭播放出来的声音效果要比选用蜂鸣器的效果要好的多)。
亲测可用,如有错误望各位大佬不吝指正。