/********************************************************
** 程序名称: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;
}
}
/*********************************************************
** 程序结束
*********************************************************/