重庆-风雪 发表于 2015-6-28 15:19:57

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

本帖最后由 重庆-风雪 于 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);                }                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
**      修改时间:
**程序说明:命名风格:Dr结尾表示驱动(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次
*/
#defineBEATS5

/* 申明音乐乐谱结构体 */
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;
extern code uchar FreqL;
/*
*         乐谱申明
*/
/* 世上只有妈妈好 */
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 = {
      /* 低音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 = {
      /* 低音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.yd].yf - 1];
      ucT0_L = FreqL.yd].yf - 1];

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


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

/********************************************************
**程序名称:Key.h
**程序功能:键盘扫描头文件
**程序版本: V 0.1
**      程序作者:郭健
**      创建时间:2015-6-25
**      修改时间:
**程序说明:
*********************************************************/
#ifndef__KEY_H__
#define__KEY_H__
#defineKEYNUM100
/*********************************************************
**      函数名称:      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号键 */
                case0x38:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                              KeyCnt   = 0;
                              ucKeyNum = 1;
                        }
                        break;
                }
                /* 触发2号键 */
                case0x34:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                              KeyCnt   = 0;
                              ucKeyNum = 2;
                        }
                        break;      
                }
                /* 触发3号键 */
                case0x2C:
                {
                        KeyCnt++;
                        if (KeyCnt >= KEYNUM)
                        {
                              KeyCnt   = 0;
                              ucKeyNum = 3;
                        }
                        break;
                }
                /* 触发4号键 */
                case0x1C:
                {
                        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__

#defineMatrix8x8_VCCP0
#defineMatrix8x8_GNDP2

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

/*********************************************************
**      函数名称:      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 = {
      
{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++;
      x = _crol_(x,1);
      if (y==8)
      {
                y = 0;
    }
}
/*********************************************************
**                     程序结束
*********************************************************/

jianhong_wu 发表于 2015-6-30 23:50:22

感谢分享。果断加精。
页: [1]
查看完整版本: 蜂鸣器音乐+点阵+键盘Proteus仿真