找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 18732|回复: 28
打印 上一主题 下一主题
收起左侧

基于STC单片机的触摸式电钢琴制作

  [复制链接]
跳转到指定楼层
楼主
触摸式电钢琴

       这个世界上有很多让人上瘾的事情,除了抽烟,喝酒这些坏毛病外,其实往51黑论坛发稿也是会上瘾的。这不,我又急着来向大家展示我的作品~~话入正题,单片机制作无非声、光、动、传感,这次就来做点跟声音有关的吧。这次带来的是一款触摸式电钢琴,细心的网友可能会注意到,我用的是电钢琴而不是电子琴,这两者有区别么?我的回答是肯定的,因为这正是本作品的亮点所在。单片机演奏音乐大家肯定都不会陌生,用单片机内部的定时器,送入不同的频率,每一个频率对应着一个音调,然后按照事先编排好的顺序驱动蜂鸣器发声,就可以演奏出音乐了。至于电子琴只需要把不同的频率映射到对应按键上即可。之所以说是电子琴,是因为这种方法只能演奏出单调的方波音频。想不想让声音不再单调,而是发出动听的钢琴音色呢?如果想的话,请拿出你的热情并且打开你的烙铁的开关,跟我一起往下制作吧!
          名称        说明       数量

        STC12C5A60S2    单片机主控     1片

        40脚芯片座    与单片机配套使用   1片

         LM386L       功放芯片       1片

       220uf电解电容  功放芯片配套使用    1个

       10uf电解电容   功放芯片配套使用     1个

        万用电路板      大一点的     1张

          LED      任何你喜欢的颜色    1个

        耳机插座       3.5毫米       1个

         蜂鸣器      功放喇叭        1个

         排针         4PIN         1个

        长条贴纸    打印好琴键的图案     1张

         钉书针     作为触摸点使用     72个

        免刮漆包线     飞线连接       1卷

        拨动开关    耳机/功放输出切换    1个

        0.1uf瓷片电容    电源滤波        1个

       制作所需的元器件如上表所示,本着精简制作的原则,用到的都是很普通的元器件,数量也很少,所以硬件制作的难度不是很大。连接部分则是过锡走线加飞线,烙铁温度在350度左右即可。电路图如下所示,值得说明的一点是,图中的矩阵触摸按键这里只画出了一组,其实有三组,共起来的一端分别与  P0.0、   P2.2、P2.1连接。剩下部分的电路都很明了,有创造力的朋友看电路图自行发挥就好,新手可以参考下我的布局。

先把打印好的琴键图片裁好,用双面胶贴在洞洞板上。(如图1、2、3 )








       怎么样,是不是很有电钢琴的样子?什么?你说触摸按键在哪里?别着急,让我请出下面这位特别来宾——钉书针。为了整个制作的美观跟手感,触摸点的选取费了我很大的心思,试了很多种导体都无法达到满意的效果,直到有一天无意中把钉书针掉在了洞洞板上,发现它那细长又导电的身体,长度刚刚好可以插进洞洞板,才有了这个有趣的设计。如图4、5所示,按照琴键的位置插好钉书针并在背面压紧。




       以此类推,完成36个键不会想你想象的那么枯燥,美妙的琴声在等着你。之后用双面胶把扬声器粘在板子正面,并完成扬声器导线、单片机、下载口、拨动开关、耳机插座、LED、功放芯片以及电解电容的焊接。焊接好的样子如图6所示。




       接下来是触摸按键矩阵后与单片机的连接了,我的方法是过锡加飞线。先把单片机正下方的一组矩阵按键与两边矩阵共起来的部分过锡连接到I/O口上(如图7),

然后用免刮漆包线把两侧矩阵要与I/O连接的地方向中间过锡部分连接(如图8)

       按道理制作到这里硬件的部分就大功告成了,但是在后期测试的过程中,总会有上电时乱响的问题。仔细思考后发现问题所在:因为触摸按键是基于增强型51单片机的I/O口高阻状态(下文会详细介绍),这种状态对电流的波动很敏感,会受到电源杂波的干扰。所以在正极跟地之间加了一个104电容,效果有很大好转。顺便一提,使用电池供电效果最好。到此就剩下载程序了,在最下方可以下载到源程序跟编译好的HEX文件,然后用STC-ISP软件下载到单片机里。注意,如果单片机是新的或者上一次使用连接了晶振的话,要先连接好晶振才能下载进程序。软件设置如图9。

到此,制作的部分全部完成了。


       如果我现在结尾,肯定有人说我不地道,因为大家最想听的软件原理我还没有说明。别急,听我娓娓道来。先说触摸吧,前面提到I/O口的高阻状态,说到了在这种状态下I/O口对电流很敏感,那是不是只要接触I/O口,就能用人体的生物电完成触摸了?哎,好事多磨,虽然它很敏感,但是生物电的强度还是不能稳定的被感应。怎么办呢?冷静下来想一想,只要再叫一个强推状态的I/O口来帮忙就好办了,所谓强推状态,就是比普通准双向I/O口上拉能力强很多的一种状态。这样,在我们同时触碰这两个I/O时,强推I/O口的电流就通过皮肤流入高阻状态的I/O口,从而读到电平变化,从而实现触摸操作。这么好用的功能怎么设置呢,请看我从STC数据手册里截下来的设置方法。


如图所示,当我们用C语言设置I/O状态时,只需向P*M1 P*M0赋值(0x开头的十六进制格式)即可,如图10

       接下来到了发音部分,说到这里要感谢杜洋老师,因为这里的原理跟程序参考了他的SPEAKER32语音盒子。两者同样都是用电脑把要播放的音频先在电脑上用专业的音频软件转换成ASCII Test数据,稍加改造变成数组后放入单片机60K的ROM里,配合PWM解码程序,一个在你进门时甜美的说出“你好,欢迎光临”,一个则是在你触摸琴键后发出钢琴的音色。专业的音频软件有很多,我用的是一款名为“Adobe Audition 3.0”的软件,简洁的界面,强大的功能,都是我选择它的原因。接下来是使用方法,准备好从网上下载到的音频,你可以用鼠标拖入音轨中,也可以对着一条空音轨点击“右键→插入→音频”来把你的音频放入音轨中。这时音轨可能没有紧贴前端,这样会制造出一段空白音频,这部分不但影响正常工作还很占空间,用右键点住它向前拖动,直到与前端紧贴。如图11

       之后双击音频,进入编辑模式。在这个模式下我们要做的是删除空白跟扩大音量。向上滚动滑轮让时间间隔变小,前端的空白就会变得明显,去除它的原因跟上面一样。我们用左键框选出空白部分,然后点击“右键→剪切”来删除空白。如图12

       随后按Ctrl+A全选波形,点击左边效果栏里的放大,调整好合适的放大倍数后点击确认以完成放大操作。如图13

       然后点击左上角的“文件→另存为”,这时会弹出一个选择格式跟保存路径的窗口,下方的保存类型我们选择“ACW波形(*.wav)”,然后点击下方的选项,滤波器处改为“PCM”,属性处改为“8.000kHz,8位,单声道”,然后“确定→保存”如果弹出窗口点击确定即可。如图14


        这样就把音频转成了8位单声道,为后面的音频表做好准备。接下来软件会自动载入刚刚保存的音频,再点击左上角的“文件→另存为”在弹出的窗口里选择“ASCII 文本数据(*.txt)”然后点击下方的选项,把两个勾选框的对钩都点掉,再点击“确定→保存”就完成了ASCII Test数据的转换。如图15

       这样重复36此之后,我们就得到了36个音调的8位音频数据了。打开电钢琴的工程文件,在程序的C语言文件下方有36个用音调名命名的数据表文件。这时只要把刚刚生成的数据复制到对应数据表中就大功告成了,不过要注意开始处数组的定义跟每个数据后的逗号,编译器可不会被你的急切所打动。我这里只是抛砖引玉,感兴趣的话可以在网上找到更多乐器的音色,让它变得更好玩,更强大。在茶余饭后悄悄的拿出它,为家人弹奏一曲。简单的儿歌,或者经典的旋律,不管你音乐水平的高低,总能在家人欢乐的笑声中感受到电子制作带给你的幸福!




小橙出品,必属精品。
请大家多多支持宅晓橙!

单片机源程序:

  1. #define SPEED         72//语速(在12MHz时设置为70最佳,值越小语速越快)
  2. #define X2                 2 //音频播放倍速(默认值2,增加/减少时音频速度整倍加/减)
  3. #define A_DA         1600 //音频数据字节的数量,即音频文件的行数。最大值不得大于65535        我的音频是1600
  4. //(如果有较长的音频需要播放可以分段处理,每段字节数不大于65535)//
  5. #define DL_F         1 //音频播放前的延时(单位:mS)。最大值不得大于65535
  6. #define DL_A         1 //音频播放后的延时(单位:mS)。最大值不得大于65535

  7. /*********************************************************************************************/

  8. #include<STC12C5A60S2.H> //头文件
  9. sbit PWM  = P1 ^ 3;//音频输出接口(只适用PWM接口)
  10. sbit LED  = P3 ^ 6;//状态指示LED灯 正极
  11. sbit LEDG  = P3 ^ 7;//状态指示LED灯 负极
  12. unsigned char code  wav1[];//音频数据表声明                 
  13. unsigned char code  wav2[];
  14. unsigned char code  wav3[];
  15. unsigned char code  wav4[];
  16. unsigned char code  wav5[];
  17. unsigned char code  wav6[];
  18. unsigned char code  wav7[];
  19. unsigned char code  wav8[];
  20. unsigned char code  wav9[];
  21. unsigned char code wav10[];
  22. unsigned char code wav11[];
  23. unsigned char code wav12[];
  24. unsigned char code wav13[];
  25. unsigned char code wav14[];
  26. unsigned char code wav15[];
  27. unsigned char code wav16[];
  28. unsigned char code wav17[];
  29. unsigned char code wav18[];
  30. unsigned char code wav19[];
  31. unsigned char code wav20[];
  32. unsigned char code wav21[];
  33. unsigned char code wav22[];
  34. unsigned char code wav23[];
  35. unsigned char code wav24[];
  36. unsigned char code wav25[];
  37. unsigned char code wav26[];
  38. unsigned char code wav27[];
  39. unsigned char code wav28[];
  40. unsigned char code wav29[];
  41. unsigned char code wav30[];
  42. unsigned char code wav31[];
  43. unsigned char code wav32[];
  44. unsigned char code wav33[];
  45. unsigned char code wav34[];
  46. unsigned char code wav35[];
  47. unsigned char code wav36[];
  48. sbit  TUI1  =  P2 ^ 1 ;
  49. sbit  TUI2  =  P2 ^ 2 ;
  50. sbit  TUI3  =  P0 ^ 0 ;
  51. sbit  KEY1  =  P2 ^ 3 ;
  52. sbit  KEY2  =  P2 ^ 4 ;
  53. sbit  KEY3  =  P2 ^ 5 ;
  54. sbit  KEY4  =  P2 ^ 6 ;
  55. sbit  KEY5  =  P2 ^ 7 ;
  56. sbit  KEY6  =  P0 ^ 7 ;
  57. sbit  KEY7  =  P0 ^ 6 ;
  58. sbit  KEY8  =  P0 ^ 5 ;
  59. sbit  KEY9  =  P0 ^ 4 ;
  60. sbit  KEY10 =  P0 ^ 3 ;
  61. sbit  KEY11 =  P0 ^ 2 ;
  62. sbit  KEY12 =  P0 ^ 1 ;

  63. unsigned char         xiang           ;
  64. /***********************************************************************/
  65. void DELAY_MS (unsigned int a){//毫秒级CPU延时函数
  66.         unsigned int i;
  67.         while( --a != 0){
  68.                 for(i = 0; i < 600; i++);
  69.         }
  70. }  
  71. /*********************************************************************************************/
  72. void DELAY_NS (unsigned char a){//用于音频延时
  73.         unsigned int i;
  74.         while( --a != 0){
  75.                 for(i = 0; i < SPEED; i++);
  76.         }
  77. }  
  78. /**********************************************************************************************/
  79. void PWM_init (void){//PWM初始化函数
  80.         CMOD=0x02; //设置PCA定时器
  81.          CL=0x00;
  82.          CH=0x00;
  83.         CCAPM0=0x42; //PWM0设置PCA工作方式为PWM方式(0100 0010)
  84.          CCAP0L=0x00; //设置PWM0初始值与CCAP0H相同
  85.          CCAP0H=0x00; // PWM0初始时为0
  86.          CR=1; //启动PCA定时器
  87. }  
  88. /**********************************************************************************************/
  89. void PWM0_set (unsigned char a){//PWM0占空比设置函数
  90.         CCAP0L= a; //设置值直接写入CCAP0L
  91.          CCAP0H= a; //设置值直接写入CCAP0H
  92. }  

  93. /**********************************************************************************************/
  94. void SP1 (void){//音频播放函数
  95.         unsigned int a;
  96.     LEDG = 0;//LED灯开
  97.         DELAY_MS (DL_F);//播放前延时
  98.         for(a=0;a<A_DA;a++){ //音频播放数据长度
  99.                
  100.                 DELAY_NS (X2);//播放数据间隔,即可调整音频速度和音调
  101.                 if(xiang == 1) {
  102.                 PWM0_set(wav1[a]);}
  103.                 if(xiang == 2) {
  104.                 PWM0_set(wav2[a]);}
  105.                 if(xiang == 3) {
  106.                 PWM0_set(wav3[a]);}
  107.                 if(xiang == 4) {
  108.                 PWM0_set(wav4[a]);}
  109.                 if(xiang == 5) {
  110.                 PWM0_set(wav5[a]);}
  111.                 if(xiang == 6) {
  112.                 PWM0_set(wav6[a]);}
  113.                 if(xiang == 7) {
  114.                 PWM0_set(wav7[a]);}
  115.                 if(xiang == 8) {
  116.                 PWM0_set(wav8[a]);}
  117.                 if(xiang == 9) {
  118.                 PWM0_set(wav9[a]);}
  119.                 if(xiang ==10) {
  120.                 PWM0_set(wav10[a]);}
  121.                 if(xiang ==11) {
  122.                 PWM0_set(wav11[a]);}
  123.                 if(xiang ==12) {
  124.                 PWM0_set(wav12[a]);}
  125.                 if(xiang ==13) {
  126.                 PWM0_set(wav13[a]);}
  127.                 if(xiang ==14) {
  128.                 PWM0_set(wav14[a]);}
  129.                 if(xiang ==15) {
  130.                 PWM0_set(wav15[a]);}
  131.                 if(xiang ==16) {
  132.                 PWM0_set(wav16[a]);}
  133.                 if(xiang ==17) {
  134.                 PWM0_set(wav17[a]);}
  135.                 if(xiang ==18) {
  136.                 PWM0_set(wav18[a]);}
  137.                 if(xiang ==19) {
  138.                 PWM0_set(wav19[a]);}
  139.                 if(xiang ==20) {
  140.                 PWM0_set(wav20[a]);}
  141.                 if(xiang ==21) {
  142.                 PWM0_set(wav21[a]);}
  143.                 if(xiang ==22) {
  144.                 PWM0_set(wav22[a]);}
  145.                 if(xiang ==23) {
  146.                 PWM0_set(wav23[a]);}
  147.                 if(xiang ==24) {
  148.                 PWM0_set(wav24[a]);}
  149.                 if(xiang ==25) {
  150.                 PWM0_set(wav25[a]);}
  151.                 if(xiang ==26) {
  152.                 PWM0_set(wav26[a]);}
  153.                 if(xiang ==27) {
  154.                 PWM0_set(wav27[a]);}
  155.                 if(xiang ==28) {
  156.                 PWM0_set(wav28[a]);}
  157.                 if(xiang ==29) {
  158.                 PWM0_set(wav29[a]);}
  159.                 if(xiang ==30) {
  160.                 PWM0_set(wav30[a]);}
  161.                 if(xiang ==31) {
  162.                 PWM0_set(wav31[a]);}
  163.                 if(xiang ==32) {
  164.                 PWM0_set(wav32[a]);}
  165.                 if(xiang ==33) {
  166.                 PWM0_set(wav33[a]);}
  167.                 if(xiang ==34) {
  168.                 PWM0_set(wav34[a]);}
  169.                 if(xiang ==35) {
  170.                 PWM0_set(wav35[a]);}
  171.                 if(xiang ==36) {
  172.                 PWM0_set(wav36[a]);}

  173.         
  174.         }
  175.         
  176.         xiang = 0;
  177.     DELAY_MS (DL_A);//播放后延时
  178.         LEDG=1;//LED灯关
  179.         PWM0_set(0); //设置PWM占空比

  180.         PWM = 0;//PWM关闭前清0
  181. }   
  182. /**********************************************************************************************/
  183. void main (void){//主函数
  184.     P0M1 = 0xFE;// (11111110)*P0.0(低音)P2.2(中音)P2.1(高音)强推
  185.         P0M0 = 0x01;// (00000001)*P2.3 P2.4 P2.5 P2.6 P2.7 P0.7  
  186.         P2M1 = 0xF8;// (11111000)*P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 高阻
  187.         P2M0 = 0x06;// (00000110)

  188.         PWM0_set(128);//设置PWM到中间值,即音频波形的基础电平。
  189.         CR=1; //启动PCA定时器
  190.         P3M0 = 0xFF;//状态指示LED接口强推设置
  191.         PWM = 0;//PWM启动前准备
  192.         PWM_init(); //PWM初始化
  193.          while(1){
  194.    TUI1 = 1;         
  195.              if(KEY1||KEY2||KEY3||KEY4||KEY5||KEY6||KEY7||KEY8||KEY9||KEY10||KEY11||KEY12 == 1){
  196.                    DELAY_MS (20);
  197.                        if(KEY1 == 1){xiang = 1 ;SP1();while(KEY1  == 1);}
  198.                              if(KEY2 == 1){xiang = 2 ;SP1();while(KEY2  == 1);}
  199.                            if(KEY3 == 1){xiang = 3 ;SP1();while(KEY3  == 1);}

  200. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码


程序以及HEX文件:
PIANO.zip (556.77 KB, 下载次数: 199)



评分

参与人数 3黑币 +19 收起 理由
鲸橙 + 2
练氏 + 12
jianwei + 5 很给力!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏22 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:231071 发表于 2017-10-27 12:17 | 只看该作者
我要学
回复

使用道具 举报

板凳
ID:232585 发表于 2017-10-31 17:14 | 只看该作者
只能说楼主很牛B,需要膜拜
回复

使用道具 举报

地板
ID:248323 发表于 2017-11-11 13:02 | 只看该作者
膜拜大佬
回复

使用道具 举报

5#
ID:163950 发表于 2017-11-25 08:37 | 只看该作者
看起来不错啊!
回复

使用道具 举报

6#
ID:238726 发表于 2017-11-25 11:37 | 只看该作者
这个感觉好难
回复

使用道具 举报

7#
ID:249545 发表于 2017-11-25 20:51 | 只看该作者
老师对音乐真的是热爱,连钢琴都想到DIY.
回复

使用道具 举报

8#
ID:217947 发表于 2018-1-6 14:59 | 只看该作者
学习学习,
回复

使用道具 举报

9#
ID:125648 发表于 2018-1-8 10:16 | 只看该作者
您好,请问,我在keil里无法生成.hex文件,在您的提供的压缩包里也没有看到.hex文件,我刚触单片机,请麻烦您能给我一份.hex文件吧,谢谢1580808688@qq.com
回复

使用道具 举报

10#
ID:125648 发表于 2018-1-8 10:19 | 只看该作者
您好,请问,我在keil里无法生成.hex文件,在您的提供的压缩包里也没有看到.hex文件,我刚触单片机,请麻烦您能给我一份.hex文件吧,谢谢1580808688@qq.com
回复

使用道具 举报

11#
ID:104477 发表于 2018-3-15 18:25 来自手机 | 只看该作者
可以扩展到88个键盘吗
回复

使用道具 举报

12#
ID:292689 发表于 2018-3-16 16:16 | 只看该作者
不错不错
回复

使用道具 举报

13#
ID:303961 发表于 2018-4-8 20:33 | 只看该作者
谢谢 我要学
回复

使用道具 举报

14#
ID:187056 发表于 2018-11-30 13:10 | 只看该作者
看起来不错啊!
回复

使用道具 举报

15#
ID:302325 发表于 2018-11-30 13:19 | 只看该作者
不得不说   这是一个相当完美的设计            
回复

使用道具 举报

16#
ID:146045 发表于 2018-11-30 14:16 来自手机 | 只看该作者
硬件做得不错
回复

使用道具 举报

17#
ID:326450 发表于 2018-11-30 16:07 | 只看该作者
感谢楼主的分享
回复

使用道具 举报

18#
ID:440205 发表于 2018-12-6 10:40 | 只看该作者
为什么会提示这个啊?PIANO.c(221): error C202: 'KEY1': undefined identifier 好像所有定义都找不到
回复

使用道具 举报

19#
ID:384554 发表于 2018-12-6 14:44 | 只看该作者
我早三个月在别的论坛找到了一模一样的帖子,不知道是不是楼主本人发的,然后内容和文件一模一样,然后我问了有没有hex文件,也没理我,芯片都买了,一直不知道怎么弄,(有热心的贴友给我导出了hex文件,但是超过60K肯定放不进去)我想问下有人按这个教程做出来了吗?
回复

使用道具 举报

20#
ID:382826 发表于 2018-12-9 18:14 | 只看该作者
厉害
回复

使用道具 举报

21#
ID:399179 发表于 2019-4-10 11:03 来自手机 | 只看该作者
下载了,编译后108处警告,怎么回事?也不发声
回复

使用道具 举报

22#
ID:604009 发表于 2019-8-26 19:15 | 只看该作者
不知道文件大小如何还有需不需要调用啊
回复

使用道具 举报

23#
ID:649923 发表于 2019-11-26 14:33 | 只看该作者
学习了,不错的教程
回复

使用道具 举报

24#
ID:399179 发表于 2020-3-3 22:18 来自手机 | 只看该作者
这个有人做成功了吗?
回复

使用道具 举报

25#
ID:407089 发表于 2020-3-4 11:14 | 只看该作者
挺好!留个记号。
回复

使用道具 举报

26#
ID:561633 发表于 2020-6-28 22:32 来自手机 | 只看该作者
请问有人做出来了吗。我按这种写的声音不正确。
回复

使用道具 举报

27#
ID:561633 发表于 2020-6-29 18:12 | 只看该作者
请问有没有61键的音效的ASCII Test数据啊。自己转换的没有up主转换出来的声音好听
回复

使用道具 举报

28#
ID:774631 发表于 2020-7-1 08:49 | 只看该作者
还能DIY钢琴,长见识了。谢谢分享
回复

使用道具 举报

29#
ID:976767 发表于 2021-12-15 15:03 | 只看该作者
终于找到了。。。之前一直搜电子琴加音色,原来在这里,泪目.......
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表