一款简单的单片机DIY小制作
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
视频教程:
制作出来的实物图如下:
单片机源程序如下:
- #include <reg51.h>
- #include <absacc.h>
- #include <stdio.h>
- #include <math.h> //头文件
- #define uchar unsigned char
- #define uint unsigned int //宏定义
- uchar STH0; //定时器变量
- uchar STL0; //定时器变量
- bit FY=0; //模式变量,为0时弹奏模式,为1时播放模式
- uchar Song_Index=0,Tone_Index=0;//单首歌曲音符数
- uchar k, key; //k:按键数值变量。key:按键的键值(也就是有按键按下时的P0口状态)
- sbit SPK=P3^7 ; //定义喇叭的接口
- sbit LED1=P3^5;
- sbit LED2=P3^4; //定义两个LED的接口
- uchar code DSY_CODE[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,//数码管显示的数组(1 2 3 4 5 6 7 8 9)
- 0x88,0x83,0xc6,0xa1,0x86,0x8e,0x89,0xa3,0x8c,0xc8}; //(A B C D E F H O P N)
- unsigned char code num1[]= " Music: ";
- uchar code Song[][100]= //内置音乐数组,song【歌曲序号】【音符顺序】
- {
- // 1 2 3 4 5 6 7
- // 8 9 10 11 12 13 14
- // 15 16 17 18 19 20 21
- {12,10,9,9,10,8,9,10,
- 12,10,9,9,5,10,11,10,
- 10,10,14,10,9,8,7,8,
- 9,10,13,6,8,10,9,6,
- 8,7,5,6,12,10,9,9,
- 9,10,8,9,10,12,10,9,
- 9,9,5,10,11,10,10,10,
- 14,10,9,8,7,8,9,10,
- 13,6,8,10,9,6,8,7,
- 5,6,-1}, //烟花易冷
- {13,12,10,12,15,13,
- 12,13,10,12,13,12,10,8,6,12,
- 10,9,9,10,12,12,13,10,9,
- 8,12,10,9,8,6,8,5,-1},//世上只有妈妈好
- {10,10,11,10,9,8,9,12,9,9,
- 8,8,9,8,7,6,7,10,7,7,
- 6,9,10,9,8,6,5,9,10,9,
- 8,6,6,9,10,9,8,6,7,8,-1}, //当你孤单你会想起谁
-
- {5,3,5,8,6,8,
- 5,5,1,2,3,2,1,2,5,
- 3,5,8,7,6,8,5,5,2,
- 3,4,0,1,6,8,8,7,6,
- 7,8,6,7,8,6,6,5,3,
- 1,2,5,3,5,8,7,6,8,
- 5,5,2,3,4,0,1,-1}, //送别
- {5,6,8,6,6,5,6,5,3,5,
- 5,6,8,6,6,5,6,5,6,1,
- 1,2,3,2,2,2,1,2,1,6,
- 3,2,5,6,8,6,6,5,6,5,
- 6,1,1,2,3,4,4,5,6,6,
- 5,6,8,6,8,6,5,5,1,6,
- 5,5,6,8,3,2,3,1,-1}, //最浪漫的事
- {5,9,10,9,10,12,13,12,8,9,
- 10,13,12,10,12,12,13,15,13,12,
- 10,12,10,8,9,10,8,6,10,9,
- 12,9,10,9,10,12,13,12,8,9,
- 10,13,12,12,13,15,13,
- 12,10,12,10,8,6,10,9,8,6,
- 8,9,9,8,-1}, //发如雪
-
- {5,8,9,10,9,10,11,12,12,12,
- 11,10,9,5,8,9,10,11,12,12,
- 12,13,12,9,10,8,8,6,9,9,
- 10,10,8,12,8,12,8,7,8,8,6,
- 9,9,10,10,12,12,
- 12,13,12,9,10,8,
- 5,8,9,10,9,10,11,12,12,12,
- 11,10,9,5,8,9,10,9,10,11,
- 12,12,12,9,10,8,8,6,9,9,
- 10,10,8,12,8,12,12,7,8,-1}, //简单爱
- {8,9,10,8,8,9,10,8,10,11,12,10,11,12,
- 12,13,12,11,10,8,12,13,12,11,10,8,8,5,8,8,5,8,-1}, //两只老虎
-
- {5,5,6,5,8,7,
- 5,5,6,5,9,8,
- 5,5,12,10,8,7,6,
- 11,11,10,8,9,8,-1}, //生日快乐
- {6,8,9,10,12,10,8,9,6,8,9,10,
- 12,12,13,9,10,10,12,13,
- 12,13,15,14,13,12,13,10,8,9,10,12,8,6,
- 8,9,10,13,12,10,13,13,
- 12,11,10,9,10,12,6,8,9,8,9,
- 10,12,13,15,14,13,12,10,13,-1}, //让我们荡起双桨
- {8,8,12,12,13,13,12,
- 11,11,10,10,9,9,8,
- 12,12,11,11,10,10,9,
- 12,12,11,11,10,10,9,
- 8,8,12,12,13,13,12,
- 11,11,10,10,9,9,8,-1}, //小星星
- {12,10,12,10,12,10,8,9,11,10,9,12,
- 12,10,12,10,12,10,8,9,11,10,9,8,
- 9,9,11,11,10,8,12,9,11,10,9,12,
- 12,10,12,10,12,10,8,9,11,10,9,8,-1}, //粉刷匠
- {8,9,10,11,12,12,12,11,10,
- 11,11,11,10,9,8,10,12,
- 8,9,10,11,12,12,12,11,10,
- 11,11,11,10,9,8,10,8,
- 13,13,13,12,11,12,12,12,11,10,
- 11,11,11,10,9,8,10,12,
- 13,13,13,12,11,12,12,12,11,10,
- 11,11,11,10,9,8,10,8,-1}, //洋娃娃和小熊跳舞
- {12,10,12,13,15,16,17,16,15,13,12,15,
- 17,16,15,12,17,16,15,12,
- 10,12,13,15,16,17,15,13,15,16,
- 13,13,14,13,13,17,15,14,15,13,
- 12,10,12,13,15,16,17,16,15,13,12,15,
- 17,17,17,17,15,16,16,16,16,12,
- 12,10,12,13,15,16,17,16,15,13,12,
- 15,15,-1}, //小红花
- {8,9,10,11,12,10,8,15,13,11,12,12,10,
- 8,9,10,11,12,10,9,8,9,10,9,12,
- 8,9,10,11,12,10,8,15,13,11,12,10,
- 8,9,10,11,12,10,9,8,9,10,8,8,
- 15,13,11,12,12,8,15,13,11,12,10,
- 8,9,10,11,12,10,9,8,9,10,8,8,
- 15,13,11,12,12,8,15,13,11,12,10,
- 8,9,10,11,12,10,9,8,9,10,8,8,-1}, //小红帽
-
- {10,12,16,15,12,11,10,10,10,11,12,
- 13,12,10,12,16,15,12,11,
- 10,12,12,13,14,15,15,16,12,12,14,13,12,
- 10,12,15,13,15,16,15,14,12,10,12,
- 16,15,12,11,10,12,12,13,14,15,15,-1}, //雪绒花
- };
- uchar code Len[][100]= //内置音乐对应的节拍(音符持续时间)
- {
- {1,1,2,4,1,1,1,4,
- 1,1,2,2,1,1,1,4,
- 1,1,1,1,2,1,1,1,
- 1,1,3,1,1,1,1,1,
- 1,1,1,6,1,1,2,1,
- 1,1,1,1,4,1,1,2,
- 1,1,1,1,1,4,1,1,
- 3,1,2,1,1,1,1,1,
- 3,1,1,1,1,1,1,2,
- 2,6,-1},
- {3,1,2,2,2,1,
- 1,4,2,1,1,2,2,1,1,1,
- 1,4,3,1,2,1,1,2,2,
- 4,3,1,1,1,1,1,6,-1},
- {2,1,1,2,1,1,2,2,2,3,
- 2,1,1,2,1,1,2,2,2,3,
- 2,1,1,1,1,2,2,1,1,1,
- 1,2,2,1,1,1,1,1,1,6,-1},
-
- {2,3,1,3,2,2,
- 4,2,1,1,2,1,1,4,2,
- 1,1,2,1,2,2,4,2,1,
- 1,2,1,4,2,2,4,2,1,
- 1,4,1,1,1,1,1,1,1,
- 1,8,2,1,1,2,1,2,2,
- 4,2,1,1,2,2,4,-1},
-
- {1,1,1,2,1,1,1,1,1,5,
- 1,1,1,2,1,1,1,1,1,5,
- 1,1,1,1,1,1,1,1,1,1,
- 2,3,1,1,1,2,1,1,1,1,
- 1,5,1,1,1,2,1,1,1,1,
- 1,2,1,3,1,1,1,4,1,1,
- 5,1,1,1,1,1,1,6,-1},
- {1,1,2,1,1,1,1,3,1,1,
- 1,1,1,1,3,1,1,2,1,1,
- 1,1,2,1,1,1,1,1,1,1,
- 1,1,2,1,1,1,1,3,1,1,
- 1,1,3,1,1,2,1,
- 1,1,1,2,1,1,1,1,1,1,
- 1,1,1,4,-1},
- {1,1,1,1,1,1,1,1,1,1,
- 1,1,3,1,1,1,1,1,1,1,
- 1,1,2,1,1,2,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,
- 1,1,2,1,1,2,
- 1,1,2,1,1,1,1,1,1,1,
- 1,1,3,1,1,1,1,1,1,1,
- 1,1,2,1,1,2,1,1,1,1,
- 1,1,2,1,1,1,1,1,1,-1},
-
- {2,2,2,2,2,2,2,2,2,2,4,2,2,4,
- 1,1,1,1,2,2,1,1,1,1,2,2,2,2,4,2,2,4,-1},
- {1,1,2,2,2,4,
- 1,1,2,2,2,4,
- 1,1,2,2,2,2,2,
- 1,1,2,2,2,4,-1},
- {1,1,1,3,1,1,1,2,4,1,1,1,
- 3,1,2,2,4,1,1,4,
- 3,1,1,1,1,1,1,2,1,1,3,1,2,2,
- 1,1,1,1,5,4,3,1,
- 1,1,2,4,3,1,1,1,3,1,1,
- 2,2,2,2,1,1,1,1,4,-1},
- {2,2,2,2,2,2,3,
- 2,2,2,2,2,2,3,
- 2,2,2,2,2,2,3,
- 2,2,2,2,2,2,3,
- 2,2,2,2,2,2,3,
- 2,2,2,2,2,2,4,-1},
- {1,1,1,1,1,1,2,1,1,1,1,2,
- 1,1,1,1,1,1,2,1,1,1,1,2,
- 1,1,1,1,1,1,2,1,1,1,1,2,
- 1,1,1,1,1,1,2,1,1,1,1,3,-1},
- {1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,2,
- 1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,2,
- 1,1,1,1,1,1,1,1,1,2,
- 1,1,1,1,1,1,1,2,
- 1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,3,-1},
- {1,1,1,1,1,1,2,1,1,1,1,4,
- 2,1,1,4,2,1,1,4,
- 2,1,1,2,2,1,1,1,1,4,
- 2,1,1,3,1,1,1,1,1,4,
- 1,1,1,1,1,1,2,1,1,1,1,4,
- 1,1,1,1,4,1,1,1,1,4,
- 1,1,1,1,1,1,2,1,1,1,1,
- 4,4,-1},
- {1,1,1,1,2,1,1,2,1,1,1,1,2,
- 1,1,1,1,1,1,1,1,2,2,2,2,
- 1,1,1,1,2,1,1,2,1,1,2,2,
- 1,1,1,1,1,1,1,1,2,2,2,2,
- 2,1,1,1,1,2,2,1,1,2,2,
- 1,1,1,1,1,1,1,1,2,2,2,2,
- 2,1,1,1,1,2,2,1,1,2,2,
- 1,1,1,1,1,1,1,1,2,2,2,2,-1},
- {2,2,4,2,2,4,2,2,1,1,2,
- 4,4,2,2,4,2,2,4,
- 2,2,1,1,2,4,4,4,1,2,1,1,2,
- 2,2,4,2,2,2,2,4,4,2,2,
- 4,2,2,4,2,2,1,1,1,4,4,-1}
- };
- uint code tab[]={ //内置16个音符弹奏和播放时都是调用此数组内的数据用于定时器初值
- 63500, //超低音 si
- 63628,63835,64021,64103,64260,64400,64524, //低音do-si
- 64580,64684,64777,64820,64898,64968,65030, // do-si
- 65058,65110,65157,65178,65217,65252,65283, //高音do-si
- 65313 }; //超高音do
- void delay1 (uint ms) //粗略1ms延时函数,不精确
- {
- uchar t;
- while (ms--)
- for(t=0;t<120;t++);
- }
- void delay(void)
- {
- uchar i;
- for (i=300;i>0;i--);
- }
- uchar getkey(void) //矩阵按键扫描函数
- {
- uchar scancode,tmpcode;
- if((P1&0xf0)==0xf0) //无按键按下时此表达式成立
- return (0); //此函数返回值为0
- scancode = 0xfe; //如果上面的if表达式不成立,此语句才可以执行
- while((scancode&0x10)!=0) //有按键按下时,此表达式成立
- {
- P1=scancode; //将P0口赋值
- if((P1&0xf0)!=0xf0) //判断P0口的状态
- {
- tmpcode = (P1&0xf0)|0x0f; //P0口的状态与上0xf0,然后或上0x0f
- return((~scancode)+(~tmpcode)); //将两个变量的值取反相加后返回此函数
- }
- else scancode=(scancode<<1)|0x01; //如果上面的if语句不成立,将scancode左移一位
- }
- return(0xff);
- }
- void anjian() //按键键值识别
- {
- P1=0xf0; //P1口赋值
- if((P1&0xf0)!=0xf0) //判断是否有按键按下
- {
- delay(); //去抖
- if((P1&0xf0)!=0xf0) //再次判断有无按键按下
- {
- key=getkey(); //扫描按键
- Tone_Index=0; //播放音符顺序清零
- switch(key) //根据扫描的按键编码将k赋值
- {
- case 0x88: //按键编码为0x88
- k = 0; //k赋值0
- break; //已经确定键值后提前跳出switch
- case 0x48: //如果不满足上一个case则继续向下判断,直到有符合
- k = 1; //k赋值1
- break; //下同,略
- case 0x28:
- k = 2 ;
- break;
- case 0x18:
- k = 3 ;
- break;
- case 0x84:
- k = 4 ;
- break;
- case 0x44:
- k = 5 ;
- break;
- case 0x24:
- k = 6 ;
- break;
- case 0x14:
- k = 7 ;
- break;
- case 0x82:
- k = 8 ;
- break;
- case 0x42:
- k = 9 ;
- break;
- case 0x22:
- k = 10 ;
- break;
- case 0x12:
- k = 11 ;
- break;
- case 0x81:
- k = 12 ;
- break;
- case 0x41:
- k = 13 ;
- break;
- case 0x21:
- k = 14 ;
- break;
- case 0x11:
- k = 15 ;
- break;
- default : //如果以上都不符合,直接跳出,无键值输出
- break;
- }
- }
- }
- }
- void main(void) //主函数
- {
- SPK=0;
- LED1=1;
- LED2=0; //开机默认弹奏模式
- P0=0xc0; //数码管显示0
- IE=0x87; //定义外部中断控制器
- TMOD=0x01; //定义定时器0的工作方式
- IT0=1; //外部中断0为下降沿触发
- IT1=1; //外部中断1为下降沿触发
- while(1) //进入死循环
- {
- P1=0xf0; //P1口赋值
- if((P1&0xf0)!=0xf0) //判断P0口是否有变化
- {
- anjian(); //读取键值
- P0=DSY_CODE[k]; //显示键值,也就是显示音符
- if(FY==0) //如果是弹奏模式
- {
- STH0 = tab[k]/256;
- STL0 = tab[k]%256; //根据k的值赋初值给T0
- TR0 = 1; //打开定时器用于定时产生频率发生
- while ((P1&0xf0)!=0xf0); //按键不松开的话,T0就一直产生频率
- TR0=0; //按键松开后关闭T0计时,频率停止
- }
- else //如果是播放模式(上面的if语句不成立就执行else)
- {
- while (FY==1) //进入播放模式
- {
- if(Song[k][Tone_Index]==-1) //一首播放完退出
- {
- Tone_Index=0;
- SPK=0;
- break;
- }
- STH0=(tab[Song[k][Tone_Index]])/256;
- STL0=(tab[Song[k][Tone_Index]])%256; //将内置音乐数组的数据赋给定时器做为初值计时
- // P0=DSY_CODE[Song[k][Tone_Index]]; //显示播放的音符
- TR0 = 1; //打开定时器定时开关
- delay1(300*Len[k][Tone_Index]); //节拍数组延时
- Tone_Index++; //变量加准备播放下一个音符
- TR0=0; //停止定时器
- anjian(); //扫描按键
- P0=DSY_CODE[k]; //显示音乐序号
- while((P1&0xf0)!=0xf0);
- }
- }
- }
- }
- }
- void EXO_IXT() interrupt 0 //外部中断0
- {
- FY=0; //弹奏模式
- LED1=1;
- LED2=0; //点亮弹奏模式指示灯
- Tone_Index=0; //歌曲音符序号清零,以便于下次播放内置音乐时从头播放
- }
- void EX1_INT() interrupt 2 //外部中断1
- {
- FY=1; //播放模式
- LED1=0; //点亮播放模式指示灯
- LED2=1;
- }
- void time0_int(void) interrupt 1 using 0 //定时器0
- {
- TH0 = STH0; //定时器赋初值
- TL0 = STL0;
- SPK=!SPK; //喇叭引脚取反,产生频率的音乐
- }
复制代码
所有资料51hei提供下载:
电子琴.7z
(6.46 MB, 下载次数: 389)
限于篇幅其余资料在压缩包中,含视频等
|