STC硬件主板--电子乐谱展示的设计

摘要:本设计基于我们所学习的单片机课程讲座、实例以及自学,掌握单片机技术是一门不可或缺的技术,对我们将来的工作以及生活都有密切的联系。单片机是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域的广泛应用。从上世纪80年代,由当时的4位、8位单片机,发展到现在的32300M的高速单片机。

 

关键词:IAP15F2K61S2芯片  STC89C51单片机  蜂鸣器 数码管 led灯 中断 定时器 嵌入式系统

 

前言:近年来,随着电子技术和微机计算机的迅速发展,单片机的档次不断提高,其运用领域也在不断的扩大,已在工业控制,尖端科学,智能仪器仪表,日用家电,汽车电子系统,办公自动化,个人信息终端及通信产品中得到了广泛的运用,成为现代电子系统中最重要的智能化的核心部件,在嵌入式系统中充当着人体头脑的最重要的作用。

 

一、课程设计目的,任务要求

设计概述:该设计的目的是设计出一个可以显示电子音乐的乐谱的工程。我们经常可以在酷狗,虾米音乐等等音乐app里使用听歌识曲的功能,哼着一个没有歌词的调子就可以搜出这个歌曲,对于喜欢音乐的人来说极其便捷。显然,通过一个曲调就识别然后匹配出相应的歌曲,这里必可以通过识别这首歌的一段乐谱,然后在乐曲库里寻找和这段曲调相似的歌曲。另一方面,有些人的确在音乐方面比较懵懂,播放一个乐曲直接学,很多调都听不准所以学不准,如果只有音乐没有乐谱,对于学习音乐就比较困难。所有如果能识别出音乐的乐谱并输出给人们看,会大大减小学习音乐的困难,是音乐感不强的人学音乐的福利。

 任务要求:播放一段音乐,通过不同按键控制不同显示乐谱的方式,在数码管上显示简谱,或者用led灯显示乐谱。

 二、程序的设计流程图

 

播放音乐子程序设计流程图如下:

STC硬件主板--电子乐谱展示的设计

整个程序设计流程图如下:

 STC硬件主板--电子乐谱展示的设计


三、程序相关电路及工作原理说明

1.IAP15F2K61S2芯片40个引脚如下:

sbit beep=P3^4;                //蜂鸣器引脚

sbit sel_led=P2^3;           //控制数码管和led灯转换的引脚

sbit sel_0=P2^0;

sbit sel_1=P2^1;

sbit sel_2=P2^2;              //控制三个进位的引脚

sbit key1=P3^2;

sbit key2=P3^3;

sbit key3=P1^7;                    //3个按键引脚

STC硬件主板--电子乐谱展示的设计

2.共阴极数码管电路图如下: P0口输入高电压led灯亮

STC硬件主板--电子乐谱展示的设计

3.按键电路示意图:按下是低电平, 高电平时常态

STC硬件主板--电子乐谱展示的设计

4.无源蜂鸣器电路示意图如下:输入方波,发出声音

STC硬件主板--电子乐谱展示的设计

四、程序设计框架

1.宏定义

#defineuint unsigned int

#defineuchar unsigned char

2.变量定义

bit k1=0;

bit k2=0;

bit k3=0;                               //控制三种乐谱显示方式的标志位

uchar qudiao,jp,weizhi;          //曲调 音符 位置

uchar duanxuan[]                  //定义段选 数码管从0-7

uchar led[]                   //定义0-8led灯亮的情况

uchar timeh,timel;            //定义定时器的重装值 表示不同的频率


3.程序函数说明

      voiddelay(unsigned int xms)  //延时程序

      ucharquyin(uchar tem)              //找到tem音符在quzi数组中的位置值,识别输入的音符,并根据不同的按键按下来控制不同的乐谱输出方式

      voidplaymusic()                      //播放音乐函数

      voidinit()                           //定时器初始化配置io口(系统初始化)

void tim1()interrupt 1          //定时器0中断服务程序,重新装值,产生方波,对应蜂鸣器发声

 

五、下载与调试(实验日志)

1.   2016.8.22  问题:程序下载到板子上没有反映,没有显示

解决思路:显示电路主要有usb口 、晶振片和iap15f2k61s2芯片组成,检测到usb口电路没有焊错,应该就是晶振片坏了。

解决过程:换了晶振,果然可以了。

已解决。

 

2.  2016.8.30 霍尔实例下载验证不了

解决思路:磁铁靠近与远离,霍尔元器件没有任何反应,检查霍尔元器件,发现正负极焊反了。

解决过程:把霍尔元器件再重新焊对一遍。

已解决。

 

3.  2016.9.2 设计乐谱显示工程:

2016.9.2:刚开始只设计出一种在数码管上显示乐谱的显示方式,也没有按键控制功能。

多次修改程序,下载验证成功。

2016.9.3:加一个用led灯来输出乐谱的方式,没有按键功能。

多次修改程序,下载验证成功。

2016.9.4:在原来的基础上,用按键来控制选择哪种方式来输出乐谱。

多次修改程序,下载验证成功。

 

六、总结与心得体会

学习c51单片机,我觉得最重要也最基础应该要搞懂中断和定时器这两个问题。中断就是cpu在处理A事件,B事件发生(中断产生)了并要求cpu马上处理(发出中断请求),cpu转去处理B事件(中断响应),并执行中断处理程序(中断服务),处理完后返回处理A事件(中断返回)。画个流程图,就是:中断产生-->中断响应-->中断服务-->中断返回c51单片机里有五种中断源,分别是外部中断0,定时器中断0,外部中断1,定时器中断1与串行口。搞明白了中断源,还要明白中断响应是有条件的,要符合三个条件:一个是cpu要开中断开关(EA=1),一个是中断源有请求,一个是中断允许位为1。定时器就要明白一个机械周期等于12个振荡周期,如果是12MHZ的晶振片,那么一个计数周期T=1/(12*10^6) *12=10^(-6)=1us16位计数器总的就是65536us,最多了可以定时65ms。软件程序的调试一般可以把重点放在分模块调试上,统调是最后一环,通过各模块的单步或跟踪调试,使程序逐步趋于正确,最后统调程序。

 

七、参考文献

1.51单片机c语言应用程序设计实例精讲》        戴佳 戴卫恒编著             电子工业出版社

2.《计算机硬件技术基础实验教程》     方恺晴主编  清华大学出版社

3.《单片机原理与运用及c51程序设计》 杨家国 清华大学出版社

4.51单片机课程设计》               周向红 华中科技大学出版社

5.10天学会51单片机视频             郭天祥主讲   

6.C15单片机及c语言知识点必备秘籍

7.51单片机学习论坛

8.百度百科      

 

附录:源程序

/**********************

说明:直接播放音乐key1键就显示在led灯上显示乐谱

                                                                      key2键就在数码管7上显示乐谱

                                                                      key3键就在数码管0上显示音调

 

***********************/

 

#include <STC15F2K60S2.h>

#define uint unsigned int

#define uchar unsigned char

sbit beep=P3^4;                     //蜂鸣器引脚

sbit sel_led=P2^3;         //数码管和led转换引脚

sbit sel_0=P2^0;

sbit sel_1=P2^1;

sbit sel_2=P2^2;                   //控制3个进位的管脚

sbit key1=P3^2;

sbit key2=P3^3;

sbit key3=P1^7;                          //控制三个按键k1 k2 k3 的引脚

 

bit k1=0;

bit k2=0;

bit k3=0;                                    //控制3个乐谱显示方式的标志位

 

uchar qudiao,jp,weizhi;          //定义曲调,音符和位置

ucharduanxuan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07}; //数码管显示0-7

ucharled[]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //led灯从左到右分别亮

uchar timeh,timel;           //定义定时器的重装值

uchar code music[] ={ 

0x33,0x10,0x32,0x10,0x33,0x10,0x32,0x10,0x33,0x10,  //音乐代码:《致爱丽丝》

0x27,0x10,0x32,0x10,0x31,0x10,0x26,0x20,

0x23,0x10,0x26,0x10,0x31,0x10,0x27,0x20,

0x23,0x10,0x31,0x10,0x27,0x10,0x26,0x20,

0x31,0x10,0x31,0x10,0x32,0x10,0x33,0x20,

0x25,0x10,0x34,0x10,0x33,0x10,0x32,0x20,

0x25,0x10,0x33,0x10,0x32,0x10,0x31,0x20,

0x25,0x10,0x32,0x10,0x31,0x10,0x27,0x20,

0x23,0x10,0x31,0x10,0x27,0x10,0x26,0x20,

  //音乐代码,歌曲为《同一首歌》,格式为: 音符, 节拍, 音符, 节拍,   

0x15,0x20,0x21,0x10,   //音符的十位代表是低八度,中八度还是高八度,1代表高八度,2代表中八度,3代表高八度

0x22,0x10,0x23,0x15,   //个位代表简谱的音符,例如0x15代表低八度的S00x21代表中八度的DO

0x24,0x05,0x23,0x10,   //节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x05代表1/2

0x21,0x10,0x22,0x20,

0x21,0x10,0x16,0x10,

0x21,0x40,0x15,0x20,

0x21,0x10,0x22,0x10,

0x23,0x10,0x23,0x05,

0x24,0x05,0x25,0x10,

0x21,0x10,0x24,0x15,

0x23,0x05,0x25,0x10,

0x22,0x05,0x23,0x05,

0x23,0x05,0x22,0x05,

0x22,0x30,0x23,0x20,

0x25,0x10,0x31,0x10,

0x27,0x15,0x26,0x05,

0x26,0x20,0x25,0x10,

0x25,0x05,0x26,0x05,

0x27,0x10,0x26,0x05,

0x25,0x05,0x23,0x40,

0x24,0x15,0x24,0x05,

0x25,0x10,0x26,0x10,

0x25,0x10,0x24,0x05,

0x23,0x05,0x22,0x20,

0x17,0x10,0x17,0x05,

0x16,0x05,0x15,0x10,

0x16,0x10,0x21,0x40,

0x00,0x00

};

uchar code quzi[] ={        //此数组数据为各个音符在定时器中的重装值,第一列是高位,第二列是低位

0xf8,0x8c,        //低八度,低1

0xf9,0x5b,       

0xfa,0x15,        //3

0xfa,0x67,

0xfb,0x04,        //5

0xfb,0x90,

0xfc,0x0c,        //7  

0xfc,0x44,        //中央C

0xfc,0xac,        //2

0xfd,0x09,

0xfd,0x34,        //4

0xfd,0x82,

0xfd,0xc8,        //6

0xfe,0x06,

0xfe,0x22,        //高八度,高1  

0xfe,0x56,

0xfe,0x6e,        //3

0xfe,0x9a,

0xfe,0xc1,        //5

0xfe,0xe4,

0xff,0x03  //7

};

/**********************

函数名称:voiddelay(unsigned int xms)

功能描述:延时

入口参数:xms:输入需要延时的毫秒值

出口参数:无

备注:

 

***********************/

void delay(unsigned int xms)

{

       uinti,j;                                         

        for(i=xms;i>0;i--)

             for(j=124;j>0;j--);

}

 

/**********************

函数名称:voidinit()

功能描述:系统初始化,功能是配置IO  定时器的初始化

入口参数:无

出口参数:无

备注:

 

//P2^0,P2^1,P2^2,P2^3都设为推挽输出

***********************/

void init()

{

       P0M0=0x00;

       P0M1=0x00;

       P2M0=0x0f; 

       P2M1=0x00;

       P3M0=0x10;

       P3M1=0x00;

       P4M0=0x00;

       P4M1=0x00;

       P5M0=0x00;

       P5M1=0x00;

      

       TMOD=0x01;

       TH0=0xD8;

       TL0=0xEF;

       EA=1;

       ET0=1;

       TR0=0;

}

 

/**********************

函数名称:ucharquyin(uchar tem)

 

功能描述:识别输入的音符并且控制3个不同key键的按下来控制不同的乐谱输出方式

quzi数组中,找到music数组定义的简谱音符的重装值,并返回其在quzi数组中的位置

入口参数:temmusic数组中定义的简谱音符

出口参数:返回的是tem音符在quzi数组中的位置值

备注:

 

***********************/

uchar quyin(uchar tem)

{

              qudiao=tem/16;                             //4位是曲调值

       jp=tem%16;                           //4位是音符

      

       if(key1==0)

       {k3=0;

              k2=0;

              k1=1;

}

       if(k1==1)

       {

                     sel_led=1;   //选择led灯亮来显示乐谱

                P0=led[jp];

}

                    

       if(key2==0)

       {

              k2=1;

              k1=0;

              k3=0;

}

if(k2==1)

{

              sel_led=0;//先选数码管亮

              sel_0=1;

              sel_1=1;

              sel_2=1;

              P0=duanxuan[jp];  //选数码管输出音符乐谱

              delay(1);

}

      

       if(key3==0)

       {

              k1=0;

              k2=0;

              k3=1;

      

}

 

if(k3==1)

{

       sel_led=0;

              sel_0=0;

              sel_1=0;

              sel_2=0;

              P0=duanxuan[qudiao];//用数码管输出曲调

              delay(1);

}

 

      

      

      

       if(qudiao==1)                        //当曲调值为1时,即是低八度,低八度在quzi数组中基址为0

              qudiao=0;

       elseif(qudiao==2)                  //当曲调值为2时,即是中八度,中八度在quzi数组中基址为14

              qudiao=14;

       elseif(qudiao==3)                  //当曲调值为3时,即是高八度,高八度在quzi数组中,基址为28

              qudiao=28;

       weizhi=qudiao+(jp-1)*2;               //通过基址加上音符作为偏移量,即可定位此音符在quzi数组中的位置

       returnweizhi;                         //返回这一个位置值

}

/**********************

函数名称:voidplaymusic()

功能描述:播放音乐

入口参数:无

出口参数:无

备注:

 

***********************/

void playmusic()

{

       ucharp,m,tem;   //m为节拍  

       uchari=0;   

       while(1)  

       {  

              p=music[i];           //如果碰到结束符,延时1,回到开始再来一遍

              if(p==0x00)

              {

                     i=0;

                     delay(1000);

              }       

              elseif(p==0xff)   //若碰到休止符,延时100ms,继续取下一音符

              {

                     i=i+2;

                     delay(100);

                     TR0=0;

              }     

              else                   //正常情况下取音符和节拍

              {

                     tem=quyin(music[i]);            //取出当前音符在quzi数组中的位置值

                     timeh=quzi[tem];                 //把音符相应的计时器重装载值赋予timehtimel

                     timel=quzi[tem+1];

                     i++;

                     TH0=timeh;                                //timehtimel赋予计时器

                     TL0=timel;

                     m=music[i];                                //取得节拍

                     i++;

              }

              TR0=1;                                             //开定时器1   

              delay(m*180);             //等待节拍完成, 通过P3^4口输出音频   

              TR0=0;                                            //关定时器1

                       

   }

}

 

 

 

//主函数入口

void main()

{

       init();

       P0=0x00;

       playmusic();

       while(1);

}

 

 

 

/**********************

函数名称:voidtim1() interrupt 1

功能描述:定时器0中断处理,重新装值,并把beep值取反,产生方波

入口参数:无

出口参数:无

备注:

 

***********************/

void tim1() interrupt 1                                       //计时器控制频率

{

       TH0=timeh;

       TL0=timel;

       beep=~beep;

}