21音电子琴程序仿真
所有资料打包下载:
21音电子琴程序【含仿真】.zip
(205.26 KB, 下载次数: 35)
单片机源程序:
- #include <reg52.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- #define PSMG P0 //数码管IO口
- #define PKEY P1 //定义4x4按键接的IO口 行扫描
- #define PLED P2 //LED接的IO口
- sbit PLAY = P3^1;
- sbit SPK = P3^0;
- #define ALLSONG 3 //歌曲总数 按实际写
- #define CODEMAX 30 //最大音符数
- uchar tone_h;
- uchar tone_l;
- uchar t1_flag = 0; //用于记录定时器1进入中断的次数
- uchar PressTime = 0; //按键按下的时间(节拍)
- uchar code chuzhi[3][16]={ //音调对应的计数初值
- 0xff,0xff, //用任意值占0位,因为音调从1开始
- 0xf8,0x8c,//低1
- 0xf9,0x5b,// 2
- 0xfa,0x15,// 3
- 0xfa,0x67,// 4
- 0xfb,0x04,// 5
- 0xfb,0x90,// 6
- 0xfc,0x0c,//低7
-
- 0xff,0xff,//占0位
- 0xfc,0x44,//中1
- 0xfc,0xac,// 2
- 0xfd,0x09,// 3
- 0xfd,0x34,// 4
- 0xfd,0x82,// 5
- 0xfd,0xc8,// 6
- 0xfe,0x06,//中7
-
- 0xff,0xff,//占0位
- 0xfe,0x22,//高1
- 0xfe,0x56,// 2
- 0xfe,0x85,// 3
- 0xfe,0x9a,// 4
- 0xfe,0xc1,// 5
- 0xfe,0xe4,// 6
- 0xff,0x03 //高7
- };
- //共阴数码管段码表
- uchar code YDTAB[23]={
- 0x00, //各段全灭 【0】
- 0x77,0x7c,0x39,0x5e,0x79,0x71,0x3d, //a - g 【1~7】
- 0x3f, //0 【8】
- 0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, //1 - 7 【9~15】
- 0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87 //1. - 7. 【16~22】
- };
- //发光二极管闪烁表
- uchar code LEDTAB[9]={0xff,0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x01,0x00};
- //****** 生日快乐 ******
- uint code srkl[] = {
- 205,205,406,405,411,807,
- 205,205,406,405,412,811,
- 205,205,415,413,411,407,406,
- 314,114,413,411,413,812,
- 305,105,406,405,411,807,
- 305,105,406,405,412,811,
- 305,105,415,413,411,
- 407,406,314,114,413,411,412,811,410,
- 0xffff
- };
- //*******恋曲1990*******
- uint code lq1990[] = {
- 613,213,412,411,613,213,412,411,613,213,412,411,1213, 110, //前奏
- 215,215,215,215,413,412, //乌溜溜的黑眼珠
- 613,211,211,212,413,1206, //和你的笑脸
- 212,213,212,213,415,213,212, //怎么也难忘记
- 612,211,211,206,405,1213,110, //你 容颜的转变
- 215,215,215,215,213,212, //轻飘飘的旧时
- 613,211,211,212,213,1206, //光 就这么溜走
- 212,213,212,213,415,213,212, //转头回去看看
- 612,205,213,212,413,1211,110, //时 已匆匆数年
-
- 215,215,215,215,413,412, //苍茫茫的天涯
- 613,211,211,212,413,1206, //路 是你的漂泊
- 212,213,212,213,415,213,212, //寻寻觅觅长相
- 612,211,211,206,405,1213,110, //守 是我的脚步
- 215,215,215,215,213,212, //黑漆漆的孤枕
- 613,211,211,212,213,1206, //边 是你的温柔
- 212,213,212,213,415,213,212, //醒来时的清晨
- 612,205,213,212,413,1211,110, //里 是我的哀愁
-
- 215,215,215,215,413,412, //轰隆隆的雷雨
- 613,211,211,212,413,1206, //声 在我的窗前
- 212,213,212,213,415,213,212, //怎么也难忘记
- 612,211,211,206,405,1213,110, //你 离去的转变
- 215,215,215,215,213,212, //孤单单的身影
- 613,211,211,212,213,1206, //后 寂寥的心情
- 212,213,212,213,415,213,212, //永远无怨
- 612,205,213,212,413,1211,110, //的 是我的双眼
-
- 615,213,415,416, //或许明日
- 621,216,421,416, //太阳西下
- 415,415,415,416,1213, //倦鸟已归时
- 212,213,212,213,415,413, //你将已经踏上
- 612,211,411,413,1212,210, //旧时的归途
- 613,213,412,413, //人生难得
- 615,213,415,416, //再次寻觅
- 421,421,421,422,1216, //相知的伴侣
- 221,221,221,221,416,415, //生命终究难舍
- 212,412,212,412,413,1215,1610, 1610, //蓝蓝的白云天
-
- 0xFFFF,
- };
-
- //==========================
- //粗略延时函数
- //==========================
- void delayms(uint ms)//延时?个 ms
- {
- uchar a,b,c;
- while(ms--)
- {
- for(c=1;c>0;c--)
- for(b=142;b>0;b--)
- for(a=2;a>0;a--);
- }
- }
- //======================
- //定时器1 测量按键持续的节拍数
- //======================
- void Timer1_Init(void)
- {
- EA = 1;
- ET1 = 1;
- TMOD &= 0x0F;
- TMOD |= 0x10;
- TH1 = (65536-25000)/256;
- TL1 = (65536-25000)%256; //25ms中断一次
- }
- void timer1() interrupt 3
- {
- TH1 = (65536-25000)/256;
- TL1 = (65536-25000)%256; //25ms中断一次
- t1_flag++;
- if(t1_flag == 5) //125ms
- {
- t1_flag = 0;
- if(PressTime < 16) //最多16 即最长4秒
- PressTime++;
-
- //8个LED显示节拍
- if(PressTime <= 8)
- PLED = LEDTAB[PressTime];
- else PLED = LEDTAB[8];
- }
- }
- //======================
- //按键扫描函数 【行扫描】
- //低4位接行,高4位接列
- /*键值分布
- 0 1 2 3
- 4 5 6 7
- 8 9 10 11
- 12 13 14 15
- */
- //======================
- uchar keyScan(void)
- {
- uchar hang;
- uchar key;
- uchar temp;
- for(hang = 0;hang < 4;hang++)
- {
- PKEY = ~(1<<hang);
- temp = PKEY&0xF0; //取高4位的值
- temp >>= 4; //将高四位右移到低四位
- if(temp != 0x0F) //有按键按下
- {
- switch(temp)
- {
- case 14: key = 4*hang+0;break; //temp:1110
- case 13: key = 4*hang+1;break; //temp:1101
- case 11: key = 4*hang+2;break; //temp:1011
- case 7: key = 4*hang+3;break; //temp:0111
- }
- break; //有键按下,获取键值后,终止扫描
- }
- else //没有按键按下 返回255
- key = 255;
- }
- return key;
- }
- //======================
- //节拍延时
- //======================
- void delay125ms(uint pai) //延时 ?*125ms 即?个节拍
- {
- uchar a,b,c;
- while(pai--)
- {
- for(c=239;c>0;c--)
- for(b=104;b>0;b--)
- for(a=1;a>0;a--);
- }
- }
- //======================
- //定时器0 产生音调
- //======================
- void Timer0_Init(void)
- {
- EA = 1;
- ET0 = 1;
- TMOD &= 0xF0;
- TMOD |= 0x01;
- PT0 = 1;
- TH0 = 255;
- TL0 = 255;
- }
- //======================
- //定时器0中断 每进入一次,SPK取反
- //======================
- void timer0() interrupt 1
- {
- TH0 = tone_h;
- TL0 = tone_l;
- SPK = ~SPK;
- }
- //======================
- //存储弹奏的歌曲
- //0xffff代表歌曲结束
- //千位与百位表示节拍
- //十位:低中高音[分别是0,1,2]
- //个位:音调[0~7] [0代表不发声]
- //======================
- uint music[CODEMAX] =
- {
- 215,215,215,215,413,412, //乌溜溜的黑眼珠
- 613,211,211,212,413,1206, //和你的笑脸
- 212,213,212,213,415,213,212, //怎么也难忘记
- 612,211,211,206,405,1213,110, //你 容颜的转变
- 0xffff,
- };
- //====================================
- //播放函数
- //播放完毕后返回1,否则返回0
- //music:歌曲数组,note:发第几个音
- //====================================
- uchar PlayMusic(uint *music,uint note)
- {
- uchar yin1; //低中高音 0,1,2
- uchar yin2; //音调 0~7 0代表不发声,但有节拍
- uchar jiepai;
-
- if(music[note] == 0xffff)
- return 1;
- else
- {
- if(music[note]%10 != 0) //音调不为0 【音调为0时表示不发声,但有节拍】
- {
- yin1 = music[note]%100/10;
- yin2 = music[note]%10;
- tone_h = TH0 = chuzhi[yin1][yin2*2 ]; //音调高位 【二维数组 第1维表示低中高音,第二维表示音调】
- tone_l = TL0 = chuzhi[yin1][yin2*2 + 1]; //音调低位
- TR0 = 1; //开启定时器0 开始发声
- //======数码管显示音调==================
- if(yin1 == 0) //低音
- { PSMG = YDTAB[yin2]; }
- else if(yin1 == 1) //中音
- { PSMG = YDTAB[8+yin2];}
- else if(yin1 ==2) //高音
- { PSMG = YDTAB[15+yin2];}
- //==================================
- }
- else{ PSMG = YDTAB[8]; }
-
- jiepai = music[note]/100;
- //===此处利用定时器1中断中的节拍显示功能
- t1_flag = 0;
- PressTime = 0;
- TH1 = (65536-25000)/256;
- TL1 = (65536-25000)%256; //25ms中断一次
- TR1 = 1; //开启定时器1
-
- delay125ms(jiepai); //节拍
-
- TR0 = 0; //已经响够节拍数,停止发声。
- TR1 = 0; //停止显示节拍
-
- PSMG = YDTAB[0]; //数码管不显示
- PLED = LEDTAB[0]; //8位LED全灭
-
- delayms(30); //每个节拍结束后,停一段时间
- return 0;
- }
- }
- //======================
- //主函数
- //======================
- void main(void)
- {
- uchar key = 255;
- uchar save = 0;
- uint note = 0; //音符
- uchar note2 = 0;
- uchar play_flag = 0;
- uchar yin1 = 0; //低中高音 0,1,2
- uchar yin2 = 1; //音调 0~7 0代表不发声,但有节拍
-
- uchar PlayID = 0; //播放歌曲的序号
- uchar PlayIDb = 0; //上次播放的歌曲序号
-
- music[CODEMAX-1] = 0xffff; //防止歌曲数组无0xffff出现错误
-
- PSMG = YDTAB[0];
- Timer0_Init();
- Timer1_Init();
-
- while(1)
- {
- if(PLAY == 0) //按下PLAY键
- {
- PLAY = 0; //引脚置0 进入播放模式
-
- //切换了播放歌曲,从第0个音符开始播放,
- if(PlayID != PlayIDb)
- {
- PlayIDb = PlayID;
- note = 0;
- }
- //===============================================================
- //==== 重点 =====
- if(play_flag %2 == 0) //play_flag为偶数时播放,奇数时暂停播放
- {
- if(PlayID == 0)
- {
- if(PlayMusic(music,note) == 0) //播放音符
- note++; //为结束播放下一个音符
- else
- {
- if(PlayID < ALLSONG-1) PlayID++; //播放下一首
- else PlayID = 0;
- }
- }
- else if(PlayID == 1)
- {
- if(PlayMusic(srkl,note) == 0) //播放音符
- note++; //为结束播放下一个音符
- else
- {
- if(PlayID < ALLSONG-1) PlayID++; //播放下一首
- else PlayID = 0;
- }
- }
- else if(PlayID == 2)
- {
- if(PlayMusic(lq1990,note) == 0) //播放音符
- note++; //为结束播放下一个音符
- else
- {
- if(PlayID < ALLSONG-1) PlayID++; //播放下一首
- else PlayID = 0;
- }
- }
- }
- //===================================================================
- key = keyScan(); //要稍微按久一点才有反应
- if(key == 15) //按下15号按键,停止播放
- {
- PLAY = 1;
- note = 0; //停止后再按播放,从头开始播放。
- play_flag = 0;
-
- TR0 = 0;
- tone_h = TH0 = chuzhi[2][2*2];
- tone_l = TL0 = chuzhi[2][2*2+1];
- TR0 = 1;
- delayms(100); //按键后响一声
- TR0 = 0;
-
- while(keyScan() != 255); //放开按键时(255时) 跳出循环
- }
- else if(key == 13) //按下13号键 暂停与暂停后播放
- {
- play_flag++;
-
- TR0 = 0;
- tone_h = TH0 = chuzhi[2][2*2];
- tone_l = TL0 = chuzhi[2][2*2+1];
- TR0 = 1;
- delayms(100); //开始或者暂停都响一声
- TR0 = 0;
- while(keyScan() != 255); //放开按键时(255时) 跳出循环
- }
- else if(key == 12) //播放上一首
- {
- if(PlayID > 0) PlayID--;
- else PlayID = ALLSONG-1;
-
- TR0 = 0;
- tone_h = TH0 = chuzhi[2][2*2];
- tone_l = TL0 = chuzhi[2][2*2+1];
- TR0 = 1;
- delayms(100); //按键后响一声
- TR0 = 0;
-
- while(keyScan() != 255); //放开按键时(255时) 跳出循环
- }
- else if(key == 14) //播放下一首
- {
- if(PlayID < ALLSONG-1) PlayID++;
- else PlayID = 0;
-
- TR0 = 0;
- tone_h = TH0 = chuzhi[2][2*2];
- tone_l = TL0 = chuzhi[2][2*2+1];
- TR0 = 1;
- delayms(100); //按键后响一声
- TR0 = 0;
-
- while(keyScan() != 255); //放开按键时(255时) 跳出循环
- }
- }
- else if(PLAY == 1) //弹奏模式
- {
- PlayID = 0;
- note = 0;
-
- key = keyScan();
-
- if(key == 0) //按下0键开始记录
- {
- if(save == 0) //第一次按 表示开始弹奏
- {
- save = 1; //标记,存储
- note2 = 0;
- tone_h = TH0 = chuzhi[0][1*2];
- tone_l = TL0 = chuzhi[0][1*2+1];
- TR0 = 1;
- delayms(100); //开始录制 响一短低音
- TR0 = 0;
- }
- else //第二次按 表示弹奏结束 写入结束符0xffff
- {
- save = 0;
- music[note2] = 0xffff;
-
- tone_h = TH0 = chuzhi[1][3*2];
- tone_l = TL0 = chuzhi[1][3*2+1];
- TR0 = 1;
- delayms(100); //录制完成 响一短高音
- TR0 = 0;
- }
- while(keyScan() != 255); //放开按键时(255时) 跳出循环
- }
- else if(key != 255) //按下1~15键
- {
- t1_flag = 0;
- PressTime = 0;
- TH1 = (65536-25000)/256;
- TL1 = (65536-25000)%256; //25ms中断一次
- TR1 = 1; //开启定时器1,开始计算按键按下时间
-
- //判断被按下按键的键值
- if(key >= 1 && key <= 3) //低音 5~7
- { yin1 = 0; yin2 = key+4; } //音调对应的定时器初值,在二维数组中的位置
- else if(key >= 4 && key <= 11) //0音 与中音1~7
- { yin1 = 1; yin2 = key - 4; }
- else if(key >= 12 && key <= 15) //高音1~4
- { yin1 = 2; yin2 = key - 11; }
-
- tone_h = TH0 = chuzhi[yin1][yin2*2]; //音调高位 【二维数组 第1维表示低中高音,第二维表示音调】
- tone_l = TL0 = chuzhi[yin1][yin2*2+1]; //音调低位
- if(yin2 != 0) //非0音时,开启定时器0,发声
- TR0 = 1;
-
- //======数码管显示音调==================
- if(yin2 == 0) //0音
- PSMG = YDTAB[8];
- else if(yin1 == 0) //低音
- { PSMG = YDTAB[yin2]; }
- else if(yin1 == 1) //中音
- { PSMG = YDTAB[8+yin2];}
- else if(yin1 ==2) //高音
- { PSMG = YDTAB[15+yin2];}
- //==================================
-
- while(1) //释放按键时 跳出循环
- {
- key = keyScan();
- if(key == 255) //放开按键
- {
- PSMG = YDTAB[0];
- PLED = LEDTAB[0];
-
- TR1 = 0;
-
- TR0 = 0; //停止发声
- if(save == 1) //save变量为1时,将弹奏的信息保存
- {
- TR1 = 0; //关闭定时器1,停止
- music[note2] = (uint)PressTime*100 + yin1*10 + yin2;
- if(note2 < CODEMAX - 2) //长度有限制
- note2++;
- }
- break;
- }
- }
- }
- }
- }
- }
- //===================================
- //单片机STC89C52RC
- //晶振12MHz
- //最后编辑2016/7/3
- //仅供学习交流,不得用于商业用途,版权归 李资鹏 所有
- //===================================
复制代码
|