独闷闷网

 找回密码
 立即注册
搜索
查看: 5217|回复: 1
打印 上一主题 下一主题
收起左侧

[原创] 蜂鸣器音乐+点阵+键盘Proteus仿真

[复制链接]
跳转到指定楼层
楼主
发表于 2015-6-28 15:19:57 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 重庆-风雪 于 2015-6-28 15:35 编辑

蜂鸣器音乐+点阵+键盘Proteus仿真

这几天写了一个蜂鸣器音乐加了点阵和键盘,用Proteus仿真,分享给大家,菜鸟发贴,还请大家批评指正!
图画得渣,多多包含!
程序都写了注释,就不多作讲解:直接贴代码
/********************************************************
**  程序名称:main.c
**  程序功能:主程序文件
**  程序版本:  V 1.0
**        程序作者:郭  健
**        创建时间:2015-6-17
**        修改时间:
**  程序说明:蜂鸣器演奏歌曲,按键控制播放/暂停,选择歌曲,
**                         停止播放,点阵显示正在播放的歌曲编号
********************************************************/
/* 包含的头文件 */
#include <reg51.h>
#include "Mydef.h"
#include "Music.h"
#include "Key.h"
#include "Matrix8x8.h"

/*********************************************************
**        函数名称:        ConfigTimer
**        函数功能:        配置定时器
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
void ConfigTimer()
{
        TMOD = 0x11;
        PT0  = 1;
        TH1  = 0xA5;
        TL1  = 0xFF;
        TR1  = 1;
        ET0  = 1;
        ET1  = 1;
        EA   = 1;        
}


/*********************************************************
**        函数名称:        Init
**        函数功能:        初始化
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
void Init()
{
        BeepDr    = 0;
        spMus     = lzlh;
        DisChoose = 0;
        bMusStart = 0;        
        /* 打开定时器 */
        bSoftTR0  = 1;
        TR0       = 1;        
}



/*********************************************************
**        函数名称:        main
**        函数功能:        主函数
**        入口参数:        NULL
**        出口参数:        0
**        函数说明:
*********************************************************/
int main ()
{
    ConfigTimer();
    Init();
    while(1)
        {
                KeyService();
                Player();
                /* 音乐播放时显示 */
                if (bMusStart)
                {
                        Matrix8x8Scan(dianzhen[DisChoose]);
                }
                else
                {
                        Matrix8x8_VCC = 0x00;
                        Matrix8x8_GND = 0xFF;
                }
        }

    return 0;
}

/*********************************************************
**        函数名称:        Timer0
**        函数功能:        定时器0的中断服务函数
**        入口参数:        interrupt 1
**        出口参数:        NULL
**        函数说明:        根据播放器送来的初值产生频率,并驱动蜂鸣器
*********************************************************/
void Timer0() interrupt 1
{
    TH0 = ucT0_H;
    TL0 = ucT0_L;
        if (bMusStart)
        {
                BeepDr = !BeepDr;
        }
}

/*********************************************************
**        函数名称:        Timer1
**        函数功能:        定时器1的中断服务函数
**        入口参数:        interrupt 3
**        出口参数:        NULL
**        函数说明:        产生节拍,为播放器提供每个音符的播放时长
*********************************************************/
void Timer1() interrupt 3
{
    TH1 = 0xA5;
    TL1 = 0xFF;
        if (bMusStart)
        {
                ucTimerCnt++;
            if ((BEATS <= ucTimerCnt)&&(1 == bSoftTR0))
            {        
                        ucTimerCnt = 0;
                uiSoftT0Cnt++;
            }
        }
}
/*********************************************************
**                     程序结束
*********************************************************/

/********************************************************
**  程序名称:Mydef.c
**  程序功能:申明常用定义
**  程序版本: V 0.1
**        程序作者:郭  健
**        创建时间:2015-6-13
**        修改时间:
**  程序说明:命名风格r结尾表示驱动(Drive),In结尾表示感应(Induction)
                        
*********************************************************/
#ifndef   __MYDEF_H__
#define   __MYDEF_H__

/* 宏定义 */
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long

#endif  
/*********************************************************
**                     程序结束
*********************************************************/
/********************************************************
**  程序名称:Music.h
**  程序功能:蜂鸣器驱动的头文件
**  程序版本: V 1.0
**        程序作者:郭  健
**        创建时间:2015-6-13
**        修改时间:
**  程序说明:单片机演奏一个音符,是通过引脚,周期性的输出一个特定频率的方波。
        这就需要单片机,在半个周期内输出低电平、另外半个周期输出高电平,周而复始。
        众所周知,周期为频率的倒数,可以通过音符的频率计算出周期;演奏时,要根据音符的不同,把对应的
        、半个周期的定时时间初始值,送入定时器,再由定时器按时输出高低电平。
        下面是个网上广泛流传的单片机音乐演奏程序,很多人都关心如何修改乐曲的内容,但是不知如何入手。
        做而论道对这个软件,做了一些说明,希望对大家有所帮助,以后大家自己就能够编写进去新的乐曲。
        在这个程序中,包括了两个数据表,其中存放了事先算好的、各种音符频率所对应的、半周期的定时时
        间初始值。有了这些数据,单片机就可以演奏从低音、中音、高音和超高音,四个八度共28个音符。
        演奏乐曲时,就根据音符的不同数值,从表中找到定时时间初始值,送入定时器即可控制音调。
        乐曲的数据,也要写个数据表:表中每三个数字,说明了一个音符,
        它们分别代表:第一个数字是音符的数值1234567之一,代表多来咪发...;
        第二个数字是0123之一,代表低音、中音、高音、超高音;
        第三个数字是时间长度,以四分之一拍为单位
*********************************************************/
#ifndef  __MUSIC_H__
#define  __MUSIC_H__

/* 包含的头文件 */
#include <reg51.h>
#include "Mydef.h"

/*
** 宏定义
*/
/*
**        音乐速度,每分钟120拍,每秒2拍,乐谱是以四分之一拍为单位
**        1个单位为125ms(八分之一秒),定时器1 25ms中断一次,
**        1个单位需中断5次
*/
#define  BEATS  5

/* 申明音乐乐谱结构体 */
typedef struct
{
        /* 音符的数值 */
        uchar yf;
        /* 音调:0123代表低音、中音、高音、超高音 */
        uchar yd;
        /* 时间长度,以四分之一拍为单位 */
        uchar jp;
}sOpern;

/*
* 音乐播放器定义
*/
/* 申明选择播放哪首歌曲的指针 */
extern sOpern *spMus;
/* 申明音乐长度和第几个音符 */
extern uint uiMusicLen,uiMusStep;
/* 音乐是否播放标志位 */
extern bit bMusStart;

/* 定时器重载值缓冲变量 */
extern uchar ucT0_H,ucT0_L;

/*
*        定义一个软件定时器,以定时器1为时基,用作控制音乐的节拍长度
*/
/* 申明软件定时器的计数器 */
extern uint uiSoftT0Cnt;
/* 申明软件定时器在硬件定时器里的时基 */
extern uchar ucTimerCnt;
/* 申明软件定时器的开关 */
extern bit bSoftTR0;

/* 蜂鸣器驱动引脚 */
sbit BeepDr = P3^7;


/* 四个八度的28个频率数据申明 */
extern code uchar FreqH[4][7];
extern code uchar FreqL[4][7];

/*
*         乐谱申明
*/
/* 世上只有妈妈好 */
extern code sOpern sszymmh[];
/* 两只老虎 */
extern code sOpern lzlh[];

/*********************************************************
**        函数名称:        Player
**        函数功能:        音乐播放
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
extern void Player ();

#endif
/*********************************************************
**                     程序结束
*********************************************************/

/********************************************************
**  程序名称:Music.c
**  程序功能:蜂鸣器驱动文件
**  程序版本: V 1.0
**        程序作者:郭  健
**        创建时间:2015-6-13
**        修改时间:
**  程序说明:单片机演奏一个音符,是通过引脚,周期性的输出一个特定频率的方波。
        这就需要单片机,在半个周期内输出低电平、另外半个周期输出高电平,周而复始。
        众所周知,周期为频率的倒数,可以通过音符的频率计算出周期;演奏时,要根据音符的不同,把对应的
        、半个周期的定时时间初始值,送入定时器,再由定时器按时输出高低电平。
        下面是个网上广泛流传的单片机音乐演奏程序,很多人都关心如何修改乐曲的内容,但是不知如何入手。
        做而论道对这个软件,做了一些说明,希望对大家有所帮助,以后大家自己就能够编写进去新的乐曲。
        在这个程序中,包括了两个数据表,其中存放了事先算好的、各种音符频率所对应的、半周期的定时时
        间初始值。有了这些数据,单片机就可以演奏从低音、中音、高音和超高音,四个八度共28个音符。
        演奏乐曲时,就根据音符的不同数值,从表中找到定时时间初始值,送入定时器即可控制音调。
        乐曲的数据,也要写个数据表:表中每三个数字,说明了一个音符,
        它们分别代表:第一个数字是音符的数值1234567之一,代表多来咪发...;
        第二个数字是0123之一,代表低音、中音、高音、超高音;
        第三个数字是时间长度,以四分之一拍为单位
*********************************************************/

/* 包含的头文件 */
#include "Mydef.h"
#include "Music.h"

/*
* 音乐播放器定义
*/
/* 选择播放哪首歌曲的指针 */
sOpern *spMus= 0;
/* 音乐长度和第几个音符 */
uint uiMusicLen,uiMusStep = 0;
/* 音乐是否播放标志位 */
bit bMusStart = 0;

/*
*        定义一个软件定时器,以定时器1为时基,用作控制音乐的节拍长度
*/
/* 软件定时器的计数器 */
uint uiSoftT0Cnt = 0;
/* 软件定时器在硬件定时器里的时基 */
uchar ucTimerCnt = 0;
/* 软件定时器的开关 */
bit bSoftTR0 = 0;

/* 定时器重载值缓冲变量 */
uchar ucT0_H,ucT0_L;

//单片机晶振采用11.0592MHz
/* 频率-半周期数据表 高八位 共保存了四个八度的28个频率数据 */
code uchar FreqH[4][7] = {
        /* 低音1234567 */
        0xF2, 0xF3, 0xF5, 0xF5, 0xF6, 0xF7, 0xF8,
        /* 中音1234567 */
        0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC,
        /* 高音1234567 */
        0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE,
        /* 超高音1234567 */
        0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF
};

/* 频率-半周期数据表 低八位 */
code uchar FreqL[4][7] = {
        /* 低音1234567 */
        0x42, 0xC1, 0x17, 0xB6, 0xD0, 0xD1, 0xB6,
        /* 中音1234567 */
        0x21, 0xE1, 0x8C, 0xD8, 0x68, 0xE9, 0x5B,
        /* 高音1234567 */
        0x8F, 0xEE, 0x44, 0x6B, 0xB4, 0xF4, 0x2D,
        /* 超高音1234567 */
        0x47, 0x77, 0xA2, 0xB6, 0xDA, 0xFA, 0x16
};

/*
*         乐谱
*/
/* 世上只有妈妈好 */
code sOpern sszymmh[] ={
        {6,2,6},{5,2,2},{3,2,4},{5,2,4},{1,3,4},{6,2,2},{5,2,2},
        {6,2,8},{3,2,4},{5,2,2},{6,2,2},{5,2,4},{3,2,4},{1,2,2},
        {6,1,2},{5,2,2},{3,2,2},{2,2,8},{2,2,6},{3,2,2},{5,2,4},
        {5,2,2},{6,2,2},{3,2,4},{2,2,4},{1,2,8},{5,2,6},{3,2,2},
        {2,2,2},{1,2,2},{6,1,2},{1,2,2},{5,1,12},{0,0,0}
};
/* 两只老虎 */
code sOpern lzlh[] = {
        {1,3,4},{2,3,4},{3,3,4},{1,3,4},{1,3,4},{2,3,4},
        {3,3,4},{1,3,4},{3,3,4},{4,3,4},{5,3,8},{3,3,4},
        {4,3,4},{5,3,8},{5,3,3},{6,3,1},{5,3,3},{4,3,1},
        {3,3,4},{1,3,4},{5,3,3},{6,3,1},{5,3,3},{4,3,1},
        {3,3,4},{1,3,4},{2,3,4},{5,2,4},{1,3,8},{2,3,4},
        {5,2,4},{1,3,8},{0,0,0}
};

/*********************************************************
**        函数名称:        Player
**        函数功能:        音乐播放
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
void Player ()
{
    if (spMus && bMusStart)
    {
            /* 根据乐谱 计算定时器重载值 */
        ucT0_H = FreqH[spMus[uiMusStep].yd][spMus[uiMusStep].yf - 1];
        ucT0_L = FreqL[spMus[uiMusStep].yd][spMus[uiMusStep].yf - 1];

        /* 到达本音符的节拍时间 */
        if (uiSoftT0Cnt >= spMus[uiMusStep].jp)         
        {
                        /* 检测播放是否结束 */
                        if (0 != spMus[uiMusStep].yf)
                        {
                                /* 播放未结束,继续播放下一个 */
                            uiSoftT0Cnt = 0;
                                uiMusStep++;
                        }
            else
            {
                                /* 播放结束,暂停4个节拍 */
                                TR0 = 0;
                                /* 上个节拍计时没清0 所以加上 spMus[uiMusStep - 1].jp */
                if (uiSoftT0Cnt >= 16 + spMus[uiMusStep - 1].jp)
                                {
                                         /* 继续播放下一个音符 */
                                        uiMusStep = 0;
                                        uiSoftT0Cnt = 0;
                                        TR0 = 1;
                                }
            }                                 
        }
    }
}


/*********************************************************
**                     程序结束
*********************************************************/

/********************************************************
**  程序名称:Key.h
**  程序功能:键盘扫描头文件
**  程序版本: V 0.1
**        程序作者:郭  健
**        创建时间:2015-6-25
**        修改时间:
**  程序说明:
*********************************************************/
#ifndef  __KEY_H__
#define  __KEY_H__
#define  KEYNUM  100
/*********************************************************
**        函数名称:        KeyScan
**        函数功能:        键盘扫描
**        入口参数:        NULL
**        出口参数:        ucKeyNum:触发的按键
**        函数说明:
*********************************************************/
extern uchar KeyScan();

/*********************************************************
**        函数名称:        KeyService
**        函数功能:        按键处理
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
extern void KeyService();

#endif
/*********************************************************
**                     程序结束
*********************************************************/

/********************************************************
**  程序名称:Key.c
**  程序功能:键盘扫描
**  程序版本: V 0.1
**        程序作者:郭  健
**        创建时间:2015-6-25
**        修改时间:
**  程序说明:按键定义:
**                         按键1(对应P3.2):播放/暂停
**                         按键2(对应P3.3):选择第一首歌曲
**                         按键3(对应P3.4):选择第二首歌曲
**                         按键4(对应P3.5):停止
*********************************************************/
/* 包含的头文件 */
#include <reg51.h>
#include "Mydef.h"        
#include "Music.h"
#include "Key.h"
#include "Matrix8x8.h"

/*********************************************************
**        函数名称:        KeyScan
**        函数功能:        键盘扫描
**        入口参数:        NULL
**        出口参数:        ucKeyNum:触发的按键
**        函数说明:
*********************************************************/
uchar KeyScan()
{
        /* 按键消抖计数 */
        static unsigned int KeyCnt = 0;
        /* 触发的按键,初始化值为一个无效值,抗干扰,所谓无效值,就是在按键处理时不需要处理的值 */
        uchar ucKeyNum = 100;

        /* 拉高IO,准备扫描 */
        P3 = P3 | 0x3C;
        /* 扫描 */
        switch(P3 & 0x3C)
        {
                /* 触发1号键 */
                case  0x38:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                                KeyCnt   = 0;
                                ucKeyNum = 1;
                        }
                        break;
                }
                /* 触发2号键 */
                case  0x34:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                                KeyCnt   = 0;
                                ucKeyNum = 2;
                        }
                        break;        
                }
                /* 触发3号键 */
                case  0x2C:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                                KeyCnt   = 0;
                                ucKeyNum = 3;
                        }
                        break;
                }
                /* 触发4号键 */
                case  0x1C:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                                KeyCnt   = 0;
                                ucKeyNum = 4;
                        }
                        break;
                }
                /* 无 */
                case 0x3C:
                {
                         KeyCnt   = 0;
                         ucKeyNum = 0;
                         break;
                }
                /* 组合按键,置为无效值,所谓无效值,就是在按键处理时不需要处理的值 */
                default:
                {
                         KeyCnt   = 0;
                         ucKeyNum = 100;
                }
        }
        return ucKeyNum;
}

/*********************************************************
**        函数名称:        KeyService
**        函数功能:        按键处理
**        入口参数:        NULL
**        出口参数:        NULL
**        函数说明:
*********************************************************/
void KeyService()
{
        /* 获取按键 */
        uchar ucGetKey = KeyScan();
        /*按键自锁标志*/
        static bit bKeyBlock  = 1;        
        /* 按键处理 */
        switch(ucGetKey)
        {
                /* 无按键 */
                case 0:
                {
                        /* 按键解锁,准备响应下一次,给按键加锁是为了,按下一次只响应一次 */
                        bKeyBlock = 1;
                }
                break;
                /* 触发1号键 */
                case 1:
                {
                        if (bKeyBlock)
                        {
                                /* 按键自锁 */
                                bKeyBlock = 0;
                                /* 播放/暂停 */
                                bMusStart = !bMusStart;
                                /* 按键清除,这里不为0的原因是:0是无按键状态,不能混淆 */
                                ucGetKey = 100;
                        }
                        break;
                }


               
                /* 触发2号键 */
                case 2:
                {
                        if (bKeyBlock)
                        {
                                /* 按键自锁 */
                                bKeyBlock = 0;
                                /*播放两只老虎*/
                                spMus     = lzlh;
                                /* 从第一个音符开始播放,所有计时器清0 */
                                uiMusStep = 0;
                                ucTimerCnt  = 0;
                                uiSoftT0Cnt = 0;
                                /* 点阵显示"1" */
                                DisChoose = 0;
                                /* 按键清除,这里不为0的原因是:0是无按键状态,不能混淆 */
                                ucGetKey = 100;
                        }
                        break;
                }
               
               
                /* 触发3号键 */
                case 3:
                {
                        if (bKeyBlock)
                        {
                                /* 按键自锁 */
                                bKeyBlock = 0;
                                /*播放世上只有妈妈好 */
                                spMus     = sszymmh;
                                /* 从第一个音符开始播放,所有计时器清0 */
                                uiMusStep = 0;
                                ucTimerCnt  = 0;
                                uiSoftT0Cnt = 0;
                                /* 点阵显示"2" */
                                DisChoose = 1;
                                /* 按键清除,这里不为0的原因是:0是无按键状态,不能混淆 */
                                ucGetKey = 100;
                        }
                        break;
                }
                /* 触发4号键 */
                case 4:
                {
                        if (bKeyBlock)
                        {
                                /* 按键自锁 */
                                bKeyBlock   = 0;
                                /* 停止播放,所有计时器清0 */
                                bMusStart   = 0;
                                ucTimerCnt  = 0;
                                uiSoftT0Cnt = 0;
                                uiMusStep   = 0;
                                /* 按键清除,这里不为0的原因是:0是无按键状态,不能混淆 */
                                ucGetKey    = 100;
                        }
                        break;
                }
                default :break;
        }
}
/*********************************************************
**                                   程序结束
*********************************************************/

/********************************************************
**  程序名称:Matrix8x8.h
**  程序功能:驱动8x8点阵的头文件
**  程序版本: V 0.1
**        程序作者:郭  健
**        创建时间:2015-6-26
**        修改时间:
**  程序说明:
*********************************************************/
#ifndef  __MATRIX8X8_H__
#define  __MATRIX8X8_H__

#define  Matrix8x8_VCC  P0
#define  Matrix8x8_GND  P2

/* 选择显示的图像 */
extern uchar DisChoose;
/* 点阵显示的图像 */
extern code unsigned char dianzhen[2][8];

/*********************************************************
**        函数名称:        Matrix8x8Scan
**        函数功能:        8x8点阵驱动函数
**        入口参数:        ucpArray:点阵显示的字模所在的数组
**        出口参数:        NULL
**        函数说明:
*********************************************************/
extern void Matrix8x8Scan(uchar *ucpArray);

#endif
/*********************************************************
**                     程序结束
*********************************************************/
/********************************************************
**  程序名称:Matrix8x8.c
**  程序功能:驱动8x8点阵
**  程序版本: V 0.1
**        程序作者:郭  健
**        创建时间:2015-6-26
**        修改时间:
**  程序说明:
*********************************************************/
        
/* 包含的头文件 */
#include <reg51.h>
#include <intrins.h>
#include "Mydef.h"
#include "Matrix8x8.h"

/* 选择显示的图像 */
uchar DisChoose;
/* 点阵显示的图像 */
code unsigned char dianzhen[2][8] = {
        
{0xEF,0xCF,0xEF,0xEF,0xEF,0xEF,0xEF,0xC7},/*"未命名文件",0*/


{0xC3,0xBD,0xFD,0xFB,0xF7,0xEF,0x9F,0x81}/*"未命名文件",0*/
};

/*********************************************************
**        函数名称:        Matrix8x8Scan
**        函数功能:        8x8点阵驱动函数
**        入口参数:        ucpArray:点阵显示的字模所在的数组
**        出口参数:        NULL
**        函数说明:
*********************************************************/
void Matrix8x8Scan(uchar *ucpArray)
{
        static unsigned char x = 0x01,y = 0;
    Matrix8x8_VCC = x;
        Matrix8x8_GND = ucpArray[y];
        y++;
        x = _crol_(x,1);
        if (y==8)
        {
                y = 0;
    }
}
/*********************************************************
**                     程序结束
*********************************************************/
乐于分享,勇于质疑!
沙发
发表于 2015-6-30 23:50:22 | 只看该作者
感谢分享。果断加精。
乐于分享,勇于质疑!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|独闷闷网 ( 粤ICP备12007667号-2 )

GMT+8, 2024-4-28 07:00 , Processed in 0.270223 second(s), 31 queries .

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