登录  | 立即注册

游客您好!登录后享受更多精彩

扫一扫,访问微社区

QQ登录

只需一步,快速开始

开启左侧

[寒假笔记] 表白神器——音乐盒

[复制链接]
发表于 2019-2-8 10:59:22 | 显示全部楼层 |阅读模式
学习笔记
学习科目: 无源蜂鸣器发声
使用定时器控制蜂鸣器发出一定频率的声音
学习安排: 能用蜂鸣器发出高中低音的哆来咪发so la xi
能看着简谱编写节拍和音符数组
开始时间: 2019-02-06
结束时间: 2019-02-08
金沙滩里的程序只能演奏两只老虎这一首歌,太不好玩了,就想着把它升级一下,结合数码管和按键做个音乐盒出来,在金沙滩lesson9_6程序的基础上改了改,就可以用数码管显示当前歌曲序号,用按键可以暂停,还可以换一首歌听,自己又编了小星星和生日快乐歌,程序写好之后差不多了,就想找个高级点的音乐,于是就把纸短情长加了上来,但是我音乐知识匮乏,编写的可能有误,希望有懂乐理的同学能交流一下,这个程序发挥一下自己的脑洞,再结合个点阵,流水灯什么的就可以拿去送人了
尝试写了一下流程图,第一次写有点乱 ,
下面分享一下源程序   最后附带演示视频  
[mw_shl_code=cpp,true]/****************************
表白神器  音乐盒
****************************
晶振值33.1776MHz
功能简介:K16控制暂停与继续
          K4 上一曲
                                        K12下一曲
    数码管显示当前歌曲序号
****************************
歌曲分别是:
纸短情长
两只老虎
祝你生日快乐歌
小星星
****************************/
#include <stc12c5a60s2.h>
#define Xtal 33177600  //赋晶振值
#define All 4          //歌曲总数


sbit BUZZ = P1^3;  //蜂鸣器控制引脚
sbit KEY = P2^4;   //按键定义
sbit KEY4 = P2^7;
sbit KEY12 = P2^5;
sbit ADDR0 = P2^0; //数码管显示控制
sbit ADDR1 = P2^1;
sbit ADDR2 = P2^2;
sbit ENLED = P1^1; //总线收发器

bit KeySta = 1;  //当前按键状态
bit KeySta4 = 1;  
bit KeySta12 = 1;
bit backup = 1;  //默认弹起时时一
bit backup4 = 1;
bit backup12 = 1;
bit cntkey = 0;  //判断暂停还是播放
bit breakflage = 0;//用按键改变歌曲标志

unsigned char keybuf = 0xFF;
unsigned char keybuf4 = 0xFF;
unsigned char keybuf12 = 0xFF;
unsigned char Num = 1; //第几首歌
unsigned char sizeofNote;//节拍的多少
               
extern void Delay_s(unsigned int s);//延时函数

void PlayControl(unsigned char Num);//歌曲选择函数
void Play();//演奏函数

unsigned char code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};//数码管显示字符转换表
unsigned int code NoteFrequ[] = {  //中音1-7低音1-7和高音1-7对应频率列表
    523,  587,  659,  698,  784,  880,  988,  //中音1-7
    1047, 1175, 1319, 1397, 1568, 1760, 1976,  //高音1-7
          262, 294, 330, 349, 392, 440, 494,300//低音1-7  最后一个纯属凑数
};
unsigned int code NoteReload[] = { //中音1-7和高音1-7对应的定时器重载值
    65536 - (Xtal/12) / (523*2),  //中音1
    65536 - (Xtal/12) / (587*2),  //2
    65536 - (Xtal/12) / (659*2),  //3
    65536 - (Xtal/12) / (698*2),  //4
    65536 - (Xtal/12) / (784*2),  //5
    65536 - (Xtal/12) / (880*2),  //6
    65536 - (Xtal/12) / (988*2),  //7
    65536 - (Xtal/12) / (1047*2), //高音1
    65536 - (Xtal/12) / (1175*2), //2
    65536 - (Xtal/12) / (1319*2), //3
    65536 - (Xtal/12) / (1397*2), //4
    65536 - (Xtal/12) / (1568*2), //5
    65536 - (Xtal/12) / (1760*2), //6
    65536 - (Xtal/12) / (1976*2), //7
          65536 - (Xtal/12) / (262*2), //低音1
    65536 - (Xtal/12) / (294*2), //2
    65536 - (Xtal/12) / (330*2), //3
    65536 - (Xtal/12) / (349*2), //4
    65536 - (Xtal/12) / (392*2), //5
    65536 - (Xtal/12) / (440*2), //6
    65536 - (Xtal/12) / (494*2), //7
                65536 - (Xtal/12) / (300*2), //纯属凑数
};

unsigned char *SongNote, *SongBeat;//指针
unsigned char code XiaoXingXingNote[] = {   //小星星
        1, 1, 5, 5,    6, 6, 5,    4, 4, 3, 3,    2, 2, 1,  
        5, 5, 4, 4,    3, 3, 2,    5, 5, 4, 4,    3, 3, 2 ,
        1, 1, 5, 5,    6, 6, 5,    4, 4, 3, 3,    2, 2, 1, 0xff    };

unsigned char code XiaoXingXingBeat[] = {
        4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,
        4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,
        4, 4, 4, 4,    4, 4, 8,    4, 4, 4, 4,    4, 4, 8,};
unsigned char code TwoTigerNote[] = {    //两只老虎音符表
        1,   2,   3, 1,    1,   2,   3, 1,   3, 4, 5,   3, 4, 5,
        5,6, 5,4, 3, 1,    5,6, 5,4, 3, 1,   1, 5, 1,   1, 5, 1,0xff};

unsigned char code TwoTigerBeat[] = {    //两只老虎节拍表,4表示一拍,1就是1/4拍,8就是2拍
        4,   4,   4, 4,    4,   4,   4, 4,   4, 4, 8,   4, 4, 8,
        3,1, 3,1, 4, 4,    3,1, 3,1, 4, 4,   4, 4, 8,   4, 4, 8,
    };
unsigned char code HappybirthdayNote[] = {  //生日快乐歌
        5, 5, 6, 5, 8, 7, 22, 5, 5, 6, 5, 9, 8, 5, 5, 12, 10, 8, 7, 6, 11, 11,  10, 8, 9, 8,0xff };
unsigned char code HappybirthdayBeat[] = {
        2, 2, 4, 4, 4, 4, 4, 2, 2,  4, 4, 4, 8, 2, 2, 4, 4, 4, 4, 8, 2, 2, 4, 4, 4, 8};       
unsigned char code         ZhiduanqingchangNote[] = { //纸短情长
              //8, 9, 10, 9, 8, 6, 22, 11, 10, 9, 22, 7, 8, 9, 7, 7, 5, 22, 7, 9, 8, 8, 3, 4, 2, 3, 1, 1, 5, 2,  //前奏
        19, 3, 3, 3, 2, 3, 1, 2, 2, 2, 1, 2, 5, 1, 1, 1, 20, 1, 20, 19,     2, 2, 2, 3, 20, 19, 4, 4, 4, 3, 4, 1, 2, 2, 2, 1, 2, 5,
               1, 1, 1, 20, 1, 20, 3, 2, 2, 1, 1, 22, 22, 22, 1, 1, 2,             3, 3, 3, 22, 3, 2, 1, 21, 6, 5, 22, 5, 6, 7, 8, 3, 3, 22, 8, 7, 8,
              7, 3, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,5, 3, 2, 1, 20, 1,               3, 2, 2, 1, 2, 5, 1, 2, 5, 22, 22, 22, 1, 1, 2,
        3, 3, 2, 3, 22, 3, 2, 1, 5, 5, 3, 5, 22, 5, 6, 7,                   8, 8, 8, 8, 8, 7, 6, 7, 6, 3, 5, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,
        5, 3, 2, 1, 6, 1, 3, 2, 2, 1, 2, 1, 20, 1, 22,                      22, 20, 1, 3, 2, 2, 22, 2, 1, 2, 1, 1, 22, 20, 1, 1, 0xff};       
unsigned char code ZhiduanqingchangBeat[] = {
              //2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 8, 12, 4, 5, 4, 8, 8, 4,   //前奏
        8, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 4, 2,       2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 4, 5, 2, 2, 2, 2, 2, 5,
        2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 8, 4, 4, 2, 2, 2, 2,                4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2,
        4, 2, 5, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 5, 1, 1,             2, 2, 2, 2, 2, 4, 2, 16, 16,4, 4, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,                   2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 4, 2, 2, 2, 4, 2, 2, 2, 2, 2,
        2, 4, 2, 5, 1, 1, 2, 2, 2, 2, 2, 4, 1, 12, 4,                     2, 1, 1, 2, 2, 8, 2, 1, 1, 2, 2, 8, 2, 2, 12, 16};
bit enable = 1;   //蜂鸣器发声使能标志
bit tmrflag = 0;  //定时器中断完成标志
unsigned char T0RH = 0xFF;  //T0重载值的高字节
unsigned char T0RL = 0x00;  //T0重载值的低字节


void main()
{
   
    EA = 1;       //使能全局中断
    TMOD = 0x11;  //配置T0,T1工作在模式1
    TH0 = T0RH;   
    TL0 = T0RL;
    ET0 = 1;      //使能T0中断
          ET1 = 1;      //使能T1中断
          TR1 = 1;      //启动T1
          ENLED = 0x00; //使能总线收发器
          ADDR2=1; ADDR1=0; ADDR0=1;//选择最右边的数码管
          P23 = 0;      //启用KEY
   
    while (1)
    {
                        P0 = LedChar[Num]; //数码管显示当前歌曲序号
      PlayControl(Num);  
                        if(breakflage == 1)
                                breakflage = 0;
                        Delay_s(100);
    }
}

void PlayControl(unsigned char Num)
{
        switch(Num)
        {
                case 4:SongNote = XiaoXingXingNote;    SongBeat =  XiaoXingXingBeat;  sizeofNote = sizeof(XiaoXingXingNote);   Play();break;
    case 2:SongNote = TwoTigerNote;        SongBeat =TwoTigerBeat;        sizeofNote = sizeof(TwoTigerNote);       Play();break;
                case 3:SongNote = HappybirthdayNote;   SongBeat =HappybirthdayBeat;   sizeofNote = sizeof(HappybirthdayNote);  Play();break;
                case 1:SongNote = ZhiduanqingchangNote;SongBeat =ZhiduanqingchangBeat;sizeofNote = sizeof(ZhiduanqingchangNote)lay();break;               
                default:break;
        }
}
/* 演奏函数 */
void Play()//演奏函数
{
    unsigned char beat;   //当前节拍索引
    unsigned char note;   //当前节拍对应的音符
    unsigned int time = 0;      //当前节拍计时
    unsigned int beatTime = 0;  //当前节拍总时间
    unsigned int soundTime = 0; //当前节拍需发声时间

    for (beat=0; beat<sizeofNote; )  //用节拍索引作为循环变量
    {
                                if(breakflage == 1)
                                        break;
        while (!tmrflag);  //每次定时器中断完成后,检测并处理节拍
        tmrflag = 0;
        if (time == 0)  //当前节拍播完则启动一个新节拍
        {
                                        if(breakflage == 1)
                                                break;
            note = SongNote[beat] - 1;
                                          if(note == 0xfe)
                                                {
                                                        if(Num == All)
                                                                Num = 1;
                                                        else
                                                        Num++;
                                                        break;
                                                }
            T0RH = NoteReload[note] >> 8;
            T0RL = NoteReload[note];
            beatTime = (SongBeat[beat] * NoteFrequ[note]) >> 2;//计算节拍总时间,右移2位相当于除4,移位代替除法可以加快执行速度
            soundTime = beatTime - (beatTime >> 2);//计算发声时间,
            enable = 1;  //指示蜂鸣器开始发声
                                                if (note == 21)//简谱的0 等待
                                                        enable =0;
            time++;
        }
        else  //当前节拍未播完则处理当前节拍
        {
            if (time >= beatTime)  //当前持续时间到达节拍总时间时归零,
            {                      //并递增节拍索引,以准备启动新节拍
                time = 0;
                beat++;
            }
            else  //当前持续时间未达到总时间时,
            {
                time++;   //累加时间计数
                if (time == soundTime)  //到达发声时间后,指示关闭蜂鸣器,
                {                       //插入0.25*总时间的静音间隔,
                    enable = 0;         //用以区分连续的两个节拍
                }
            }
        }
    }
}
/*控制蜂鸣器发声 */
void InterruptTimer0() interrupt 1
{
    TH0 = T0RH;   //重新加载重载值
    TL0 = T0RL;
    tmrflag = 1;
    if (enable)   //使能时反转蜂鸣器控制电平
        BUZZ = ~BUZZ;
    else          //未使能时关闭蜂鸣器
        BUZZ = 1;
}
/*按键扫描*/
void InterruptTimer1() interrupt 3 //按键部分参看之前的帖子 带有计次功能的秒表
{
        TH1 = 0xC9;
        TL1 = 0xEA;
       
          keybuf = (keybuf<<1) | KEY;
    if (keybuf == 0x00)
        KeySta = 0;
    else if (keybuf == 0xFF)
        KeySta = 1;
    else{}
                       
          keybuf4 = (keybuf4<<1) | KEY4;
    if (keybuf4 == 0x00)
        KeySta4 = 0;
    else if (keybuf4 == 0xFF)
        KeySta4 = 1;
    else{}
                       
         keybuf12= (keybuf12<<1) | KEY12;
    if (keybuf12 == 0x00)
        KeySta12 = 0;
    else if (keybuf12 == 0xFF)
        KeySta12 = 1;
    else{}
                       
                              if (KeySta != backup)  
        {
            if (backup == 0)        
                                                {
                                                        cntkey=~cntkey;
                                                        TR0 = cntkey;
                                                }
            backup = KeySta;               
        }
                if (KeySta4 != backup4)
                {
                        if(backup4 ==0)
                        {
                                breakflage = 1;
                                if(Num <All)
                                  Num++;
                          else if(Num == All)
                                        Num =1;
                                P0 = LedChar[Num];
                        }
                        backup4 = KeySta4;
                }               
               
                if (KeySta12 != backup12)
                {
                        if(backup12 ==0)
                        {
                                breakflage = 1;
                                if(Num >1)
                                  Num--;
                          else if(Num == 1)
                                        Num =All;
                                P0 = LedChar[Num];
                        }
                        backup12 = KeySta12;
                }                       
               
}
[/mw_shl_code]

[url=][/url]



程序框图.jpg

音乐盒演示视频.zip

9.17 MB, 下载次数: 517

好懒~~不想说~~~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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