由高音階的七個音和低音階的七個音組成的14鍵的簡易電子琴。
帶有錄音回放,演奏歌曲功能。
Altium Designer畫的單片機電子琴原理圖和PCB圖如下:(51hei附件中可下載工程文件)
電路PCB(洞洞板)
電路PCB(工業制板)
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載).
元件清單:
元件名稱 參考數量
STC89C52RC 1
IC底座 1
排阻 4.7K/10K 1
11.0592M/12M晶振 1
30pf瓷片電容 2
10uf電解電容 1
33Ω電阻 1 根據實際情況選擇
10K電阻 2
按鍵開關 18
排針 3
S8050 2
跳線 10 根據實際情況選擇
喇叭 1
線路板/洞洞板 1
電子琴程序源碼(含固定曲)
源程序如下:
- #include <STC89C5xRC.H>
- #include <intrins.h>
- #define PKEY P1 //定義4x4按鍵接的IO口 行掃描
- sbit PLAY = P2^0; //播放按鍵
- sbit SPK = P2^2; //音頻信號輸出
- #define ALLSONG 3 //歌曲總數 按實際寫
- #define CODEMAX 30 //最大音符數
- unsigned char tone_h;
- unsigned char tone_l;
- unsigned char t1_flag = 0; //用于記錄定時器1進入中斷的次數
- unsigned char PressTime = 0; //按鍵按下的時間(節拍)
- unsigned char 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
- };
- unsigned int 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
- };
- unsigned int code lq1990[] = { //戀曲1990歌詞
- 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(unsigned int a) //延時函數
- {
- unsigned char i, j;
- unsigned int b=0;
- for(b=0;b<a;b++)
- {
- _nop_();
- _nop_();
- _nop_();
- i = 11;
- j = 190;
- do
- {
- while (--j);
- } while (--i);
- }
- }
- void Timer1_Init(void) //定時器1,測量按鍵持續的節拍數
- {
- 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++;
- }
- }
- /***************************
- 按鍵掃描函數 【行掃描】
- 低4位接行,高4位接列
- 鍵值分布
- 0 1 2 3
- 4 5 6 7
- 8 9 10 11
- 12 13 14 15
- ****************************/
- unsigned char keyScan(void)
- {
- unsigned char hang; //行
- unsigned char key;
- unsigned char 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 RhythmDelay(unsigned int pai) //節拍延時
- {
- while(pai--)
- {
- delayms(20);
- }
- }
- //======================
- //定時器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代表不發聲]
- //======================
- unsigned int 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:發第幾個音
- //====================================
- unsigned char PlayMusic(unsigned int *music,unsigned int note)
- {
- unsigned char yin1; //低中高音 0,1,2
- unsigned char yin2; //音調 0~7 0代表不發聲,但有節拍
- unsigned char jiepai;//節拍
-
- if(music[note] == 0xffff) //如果歌曲結束
- return 1; //返回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 開始發聲
- }
- jiepai = music[note]/100;
- RhythmDelay(jiepai); //節拍
-
- TR0 = 0; //已經響夠節拍數,停止發聲。
- TR1 = 0; //停止顯示節拍
-
- delayms(30); //每個節拍結束后,停一段時間
- return 0;
- }
- }
- //======================
- //主函數
- //======================
- void main(void)
- {
- unsigned char key = 255;
- unsigned char save = 0;
- unsigned int note = 0; //音符
- unsigned char note2 = 0;
- unsigned char play_flag = 0;
- unsigned char yin1 = 0; //低中高音 0,1,2
- unsigned char yin2 = 1; //音調 0~7,0代表不發聲,但有節拍
-
- unsigned char PlayID = 0; //播放歌曲的序號
- unsigned char PlayIDb = 0; //上次播放的歌曲序號
-
- music[CODEMAX-1] = 0xffff; //防止歌曲數組無0xffff出現錯誤
-
- Timer0_Init();//定時器0 產生音調
- Timer1_Init();//定時器1 測量按鍵持續的節拍數
-
-
-
- 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++;
-
- if(play_flag %2 == 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 == 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 <= 7) //按鍵 1~7
- { yin1 = 0; yin2 = key+0; } //低音
-
- else if(key >= 8 && key <= 11) //按鍵 8~11
- { yin1 = 1; yin2 = key - 4; } //中音
-
- else if(key >= 12 && key <= 15) //按鍵 12~15
- { yin1 = 2; yin2 = key - 8; } //高音
-
-
- tone_h = TH0 = chuzhi[yin1][yin2*2]; //音調高位 【二維數組 第1維表示低中高音,第二維表示音調】
- tone_l = TL0 = chuzhi[yin1][yin2*2+1]; //音調低位
- if(yin2 != 0) //非0音時,開啟定時器0,發聲
- TR0 = 1;
-
- while(1) //釋放按鍵時 跳出循環
- {
- key = keyScan();
- if(key == 255) //放開按鍵
- {
- TR1 = 0;
- TR0 = 0; //停止發聲
- if(save == 1) //save變量為1時,將彈奏的信息保存
- {
- TR1 = 0; //關閉定時器1,停止
- music[note2] = (unsigned int)PressTime*100 + yin1*10 + yin2;
- if(note2 < CODEMAX - 2) //長度有限制
- note2++;
- }
- break;
- }
- }
- }
-
- }
- }
- }
復制代碼
所有資料51hei提供下載:
基于51單片機的電子琴設計2019-6-25 133408.7z
(1.9 MB, 下載次數: 111)
2019-10-26 16:59 上傳
點擊文件名下載附件
|