要求:
1、设置三种音阶:低音、中音、高音控制按键;
2、设计1、2、3、4、5、6、7音(哆来咪法搜拉稀);
3、能弹奏一首简单的儿歌。
获取该程序的方式:
1、CSDN下载:
https://download.csdn.net/download/qq_38351824/11391320
2、关注微信公众号下载:
① 关注微信公众号:Tech云
②
3、可以关注点赞并在下方评论,我给你邮箱发过去。
一、Protues仿真图:
二、程序源码:
因为注释非常的全,这里就不再进行讲解了。
/*******************************************************************************
================================================================================
【平 台】STC89C51_sumjess平台
【编 写】sumjess
【E-mail 】1371129880@qq.com
【软件版本】V2.0
【最后更新】2019年06月10日
【相关信息参考下列地址】
【网 站】
https://blog.csdn.net/qq_38351824
http://www.51hei.com/bbs/mcu-2-1.html
---------------------------------------------------------------------------------
【dev.env.】MDK4.02及以上版本
【Target 】STC89C51
第一次修订:2019/05/09
第二次修订:2019/05/21
第三次修订:2019/06/10
【problem 】
(1)库内补充的不全面;
(2)库内解释部分不全面;
(3)库内还存在一定的bug;
【direction】
下一步的目标就是把库继续集成!
【explain 】
为了方便使用,我也自己写了很多的库,和优化了算法和表示方式!
【warning】
目前程序中暂无错误 !
---------------------------------------------------------------------------------
没有完美的代码,只有不断的奉献,大家一起努力;
赠人玫瑰手留余香,欢迎大家反馈bug!
================================================================================
********************************************************************************/
#include<reg51.h>
#include<intrins.h>
#define uchar unsigned char //无符号字符型 宏定义 变量范围0~255
#define uint unsigned int //无符号整型 宏定义 变量范围0~65535
sbit SDA1=P0^2;//串行数据输入,对应595的14脚SER
sbit SCL1=P0^1;//移位寄存器时钟输入,对应595的11脚SCK
sbit SCL2=P0^0;//存储寄存器时钟输入,对应595的12脚RCK
sbit W1=P0^3;
sbit W2=P0^4;
sbit k1=P1^0; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k2=P1^1; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k3=P1^2; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k4=P1^3; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k5=P1^4; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k6=P1^5; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit k7=P1^6; //哆1 啦2 咪3 发4 嗦5 啦6 西7 哆
sbit D1=P2^6; //播放音乐模式
sbit D2=P2^7; //弹奏模式
sbit k8=P3^2; //功能切换键
sbit k9=P3^3; //低音
sbit k10=P3^4; //中音
sbit k11=P3^5; //高音
sbit beep=P2^2;//蜂鸣器接口
uchar code table1[]={ //共阴极数码管
0x3F,/*0*/
0x06,/*1*/
0x5B,/*2*/
0x4F,/*3*/
0x66,/*4*/
0x6D,/*5*/
0x7D,/*6*/
0x07,/*7*/
0x7F,/*8*/
0x6F,/*9*/
0x37,/*N*///中音
0x38,/*L*///低音
0x76,/*H*///高音
0x79 /*E*/
};
/格式为: 频率常数, 节拍常数, 频率常数, 节拍常数///
uchar code table2[]={ //音阶频率表 低八位
0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,//中音
0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC,//低音的高8位
0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,
};
uchar code table3[]={
0x8E,0xED,0x44,0x6B,0xB4,0xF4,0x2D,//中音
0x21,0xDB,0x87,0xD7,0x68,0xE8,0x5B,//低音的低8位
0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16,
};
uchar code table4[]={
1,2,3,1,
1,2,3,1,
3,4,5,
3,4,5,
5,6,5,4,3,1,
5,6,5,4,3,1,
1,12/*低音5*/,1,
1,12,1
};
uchar code table5[]={ //演奏歌曲延时表
4,4,4,4,
4,4,4,4,
4,4,8,
4,4,8,
2,1,2,1,4,4,
2,1,2,1,4,4,
4,4,6,
4,4,6
};
uchar i;
uchar key,aa=0;
uchar bb,cc;
bit flag=0;
//延时子函数
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=340;y>0;y--);
}
void in(uchar Data)
{
uchar i;
for(i=0;i<8;i++) //循环8次,刚好移完8位
{
Data<<=1;
SCL1=CY;
SDA1=1; //先将移位寄存器控制引脚置为低
_nop_();
_nop_();
SDA1=0;
}
}
void out()
{
SCL2=0; //先将存储寄存器引脚置为低
_nop_();
SCL2=1; //再置为高,产生移位时钟上升沿,上升沿时移位寄存器的数据进入数据存储寄存器,更新显示数据。
_nop_();
SCL2=0;
}
//初始化子函数
void init()
{
beep=0; //蜂鸣器关闭
D1=1; //开始演奏音乐
D2=0; //关闭弹奏功能
EA=1; //开总中断
TCON=0x01; //外部中断0设置为边沿触发
/* 以下是D7~D0 ,0x01=0000 0001 所以该函数相当于 IT0=1
TF1:定时器T1溢出标志位。当定时器T1溢出时,由硬件自动使TF1置1,并向CPU申请中断。CPU响应中断后,自动对TF1清零。TF1也可以用软件清零。
TR1:定时器T 1运行控制位。可由软件置1(或清零)来启动(或关闭)定时器T1,使定时器T1开始计数。用指令SETB TR1(或CLR TR1)使TR1置1(或清零)。
TF0:定时器T0溢出标志位。其功能与TF1相同。
TR0:定时器T0运行控制位。其功能与TR1相同。
IE1:外部中断1请求标志位。
IT1:外部中断1触发方式控制位。
IE0:外部中断0请求标志位。
IT0:外部中断0触发方式控制位。
*/
EX0=1; //开外部中断0
ET0=1; //定时器0的中断打开
ET1=1; //定时器1的中断打开
TMOD=0x11; //定时器0,1工作在定时状态,均为方式1
}
//数码管显示子函数
void display1()
{
in(table1[aa]); //再传段码
out();
W1=0;;
delay(1); //延迟时间2ms以内
W1=1;
in(table1[cc+1]); //再传段码
out();
W2=0;
delay(1); //延迟时间2ms以内
W2=1;
}
//高低音选择子函数
void yinjie()
{
if(k10==0)
{
delay(5);
if(k10==0)
{
aa=10;
bb=0;//返回10为seg[10]显示C
}
}
if(k9==0)
{
delay(5);
if(k9==0)
{
aa=11;
bb=1;//返回11为seg[11]显示L
}
}
if(k11==0)
{
delay(5);
if(k11==0)
{
aa=12;
bb=2;//返回12为seg[12]显示H
}
}
if(aa==0)
{
aa=13;
}
}
//播放音乐子函数
void display_music()
{
TH0=table2[table4[i]-1]; //取频率常数 和 节拍常数
TL0=table3[table4[i]-1];
while(flag==0)
{
if(i<32)
{
TR0=1;
delay(57*table5[i]);
i++;
}
if(i==32) i=0;
}
}
//演奏模式子函数
void display_play()
{
TR0=0;
TR1=0;
yinjie();
in(table1[aa]); //再传段码
out();
W1=0;;
delay(1); //延迟时间2ms以内
W1=1;
if(aa!=13&&flag==1)
{
if(k1==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k1==0)
{
cc=0;
display1();
}
}
if(k2==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k2==0)
{
cc=1;
display1();
}
}
if(k3==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k3==0)
{
cc=2;
display1();
}
}
if(k4==0)
{ //取频率常数 和 节拍常数
TH1=table2[7*bb+cc];
TL1=table3[7*bb+cc];
TR1=1;
while(k4==0)
{
cc=3;
display1();
}
}
if(k5==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k5==0)
{
cc=4;
display1();
}
}
if(k6==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k6==0)
{
cc=5;
display1();
}
}
if(k7==0)
{
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
TR1=1;
while(k7==0)
{
cc=6;
display1();
}
}
}
}
//主函数
void main()
{
init(); //初始化子函数
while(1)
{
if(flag==0) display_music(); //演奏音乐
else display_play(); //弹奏音乐
}
}
//外部0中断子函数
void wb0() interrupt 0
{
if(k8==0)
{
delay(5);
while(k8==0);
flag=~flag;
D1=~D1;
D2=~D2;
}
}
//定时器0中断子函数
void t0() interrupt 1
{
TR0=0;
TH0=table2[table4[i]-1]; //取频率常数 和 节拍常数
TL0=table3[table4[i]-1];
beep=~beep;
TR0=1;
}
//定时器1中断子函数
void t1() interrupt 3
{
TR1=0;
TH1=table2[7*bb+cc]; //取频率常数 和 节拍常数
TL1=table3[7*bb+cc];
beep=~beep;
TR1=1;
}