最近学单片机,要求编一个简单的电子琴程序,要求是能够弹奏出C调和G调并且有储存回放功能,但是程序编出来了,用PROTEUS仿真的时候发生两个情况,一个是选取C调或者G调后,蜂鸣器发出的声音有时正常有时只会有一些哒哒哒的声音(频率很低的时候的声音),还有个情况就是无法储存和回放,但又找不到错误。求助希望有人指点下,万分感谢啊。 代码如下
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit c=P0^0;//定义C调切换键
sbit g=P0^1;//定义G调切换键
sbit sa=P0^2;//定义触发储存音调键
sbit r=P0^3;//定义回放触发键
sbit sound=P1^5;//发声口
uint tl,th,i,s=0;//tl储存定时器低八位 th储存定时器高八位 i数组循环变量 s存储标志
uchar key=20,chc=0,chg=0;//key 键值 chc C调选择标志 chg G调选择标志
uchar save[50];//储存音符最大长度为50
uchar lev[50];//level 储存该音符是C调还是G调
uchar code freqc[]={0x00,//音符的初值数组,注意两两一组标识音符,
相应的仿真图.zip
(15.65 KB)
第一个0x00的目的是让音符从freq【1】开始,方便计算
0xF8,0x8B,
0xF8,0xF2,
0xF9,0x5B,
0xF9,0xB7,
0xFA,0x14,
0xFA,0x66,
0xFA,0xB9,
0xFB,0x03,
0xFB,0x4A,
0xFB,0x8F,
0xFB,0xCF,
0xFC,0x0B,
0xFC,0x43};
uchar code freqg[]={0x00,
0xfe,0x78,
0xfe,0x61,
0xfe,0x48,
0xfe,0x2e,
0xfe,0x13,
0xfd,0xf5,
0xfd,0xd6,
0xfd,0xb5,
0xfd,0x92,
0xfd,0x6d,
0xfd,0x46,
0xfd,0x1c,
0xfc,0xf0};
void init();// 定时器初始函数
void songc(); // C调发声函数
void songg();// g调发声函数
void delay(uchar t);//延时
void keyg(uchar ke);//G调初值赋值函数
void keyc(uchar ke);//c调初值赋值函数
void replay();//回放
void keyscan();//键盘扫描
void savep();//储存函数
void inits();//储存初始化
/*****主函数****/
void main(){
init();
while(1){
songc();
songg();
}
}
/*****定时器初始*****/
void init()
{
TMOD=0x01;//模式一工作
EA=1;//总中断打开
ET0=1;//关外部中断
TR0=0;//定时器暂时不工作(定时器要到发声时才工作)
}
/****中断函数****/
void timer() interrupt 1{
TL0=tl;//低八
TH0=th;//高八
sound=~sound;//依据发声原理要产生震荡,所以到半周期取反 使发生器按照一定频率发声
}
/********C调赋初值函数**********/
void keyc(uchar ke){
if(ke!=20)
{
th=freqc[ke*2-1]; //置对应音符初值产生相应频率的震荡
tl=freqc[ke*2];
TR0 = 1;//开启定时器
delay(100000); //延时
}
}
/********G调赋初值函数**********/
void keyg(uchar ke){
if(ke!=20)
{
th=freqg[ke*2-1]; //置对应音符初值产生相应频率的震荡
tl=freqg[ke*2];
TR0 = 1;//开启定时器
delay(100000); //延时
}
}
/********延时函数**********/
void delay(uchar t){
uchar n;
for(n=0;n<t;n++);
}
/********键盘扫描**********/
void keyscan(void)
{ uchar temp;
temp = 0;
P2=0xF0; //高四位输入 列为高电平 行为低电平
delay(1); //延时
temp=P2; //读P1口
temp=temp&0xF0; //屏蔽低四位
temp=~((temp>>4)|0xF0);//此处是将高四位的值移到低四位,因为按下标识为0,如11111011 为了之后能方便读数 故取反,变为00000100 此时该值为十进制的4
if(temp==1)
key=1; //第1个按键键值
else if(temp==2)
key=2; //第2个按键键值
else if(temp==4)
key=3; //第3个按键键值
else if(temp==8)
key=4; //第4个按键键值
else
key =20;
P2=0x0F; //低四位输入 行为高电平 列为低电平
delay(1); //延时
temp=P2; //读P1口
temp=temp&0x0F;
temp=~(temp|0xF0); //原理同上,此时不移位因标志位即按下键的标识0本身就在低四位
if(temp==2) //第一行 p1.1 被拉低(直接temp==2 是因为我们P1.0空的,是3*4的矩阵键盘)
key=key+0;
else if(temp==4) //第二行 p1.2 被拉低
key=key+4;
else if(temp==8) //第三行 p1.3 被拉低
key=key+8;
else
key =20; //没有按下
}
/********储存函数**********/
void savep(){
if(key!=20&&s==1) //如果键按下且储存标识置一,储存音符
{save=key;
if(chc==1) //C调lev为0
lev=0;
if(chg==1) //g调为1
lev=1;
i++; //储存位置+1
}
if(i>49||(!sa&&s==1))//若储存超过最大值或者储存人为按键则停止
{ delay(50);
if(i>49||(!sa&&s==1)){
s=0;
while(!sa); //松手检测
}
}
}
/********储存数组初始化**********/
void inits(){
if(!sa&&!s) //在储存触发键被按下且标志位为0时,储存数组初始化置0,并将标志位置1
{
delay(50);
if(!sa&&s==0){
s=1;
for(i=0;i<50;i++)
{
save=0;
lev=0;
}
i=0;
while(!sa);
}
}
}
/*******回放函数**********/
void replay(){
uint j;
if(!r){ //回放键按下后将SAVE数组中的值取出,并将每一个值对应的音符取出赋给相应音调的初值赋值函数进行赋值发声
delay(50);
if(!r){
while(!r);
for(j=0;j<50;j++)
{
if(lev[j]==0)
keyc(save[j]);
if(lev[j]==1)
keyg(save[j]);
if(save[j+1]==20)//如果后面一位为20则停止回放跳出
j=50;
}
}
}
}
/********C调发声**********/
void songc(){
if(!c){ //选择C调
delay(50);
if(!c){
chc=1;
while(1){
keyscan();
replay();
inits();
savep();
keyc(key);
while(key!=20)
{
keyscan();
}
TR0 = 0;
if(!g) //如果按下G调选择键则跳出C调循环
{
chc=0;
break;
}
}
}
}
}
/********G调发声**********/
void songg(){
if(!g){ //选择C调
delay(50);
if(!g){
chg=1;
while(1){
keyscan();
replay();
inits();
savep();
keyg(key);
while(key!=20)
{
keyscan();
}
TR0=0;
if(!c) //如果按C调选择键则跳出C调循环
{
chg=0;
break;
}
}
}
}
} |