STC硬件主板--电子乐谱展示的设计
摘要:本设计基于我们所学习的单片机课程讲座、实例以及自学,掌握单片机技术是一门不可或缺的技术,对我们将来的工作以及生活都有密切的联系。单片机是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域的广泛应用。从上世纪80年代,由当时的4位、8位单片机,发展到现在的32位300M的高速单片机。
关键词:IAP15F2K61S2芯片 STC89C51单片机 蜂鸣器 数码管 led灯 中断 定时器 嵌入式系统
前言:近年来,随着电子技术和微机计算机的迅速发展,单片机的档次不断提高,其运用领域也在不断的扩大,已在工业控制,尖端科学,智能仪器仪表,日用家电,汽车电子系统,办公自动化,个人信息终端及通信产品中得到了广泛的运用,成为现代电子系统中最重要的智能化的核心部件,在嵌入式系统中充当着人体头脑的最重要的作用。
一、课程设计目的,任务要求
设计概述:该设计的目的是设计出一个可以显示电子音乐的乐谱的工程。我们经常可以在酷狗,虾米音乐等等音乐app里使用听歌识曲的功能,哼着一个没有歌词的调子就可以搜出这个歌曲,对于喜欢音乐的人来说极其便捷。显然,通过一个曲调就识别然后匹配出相应的歌曲,这里必可以通过识别这首歌的一段乐谱,然后在乐曲库里寻找和这段曲调相似的歌曲。另一方面,有些人的确在音乐方面比较懵懂,播放一个乐曲直接学,很多调都听不准所以学不准,如果只有音乐没有乐谱,对于学习音乐就比较困难。所有如果能识别出音乐的乐谱并输出给人们看,会大大减小学习音乐的困难,是音乐感不强的人学音乐的福利。
任务要求:播放一段音乐,通过不同按键控制不同显示乐谱的方式,在数码管上显示简谱,或者用led灯显示乐谱。
二、程序的设计流程图
播放音乐子程序设计流程图如下:
整个程序设计流程图如下:
三、程序相关电路及工作原理说明
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个按键引脚
2.共阴极数码管电路图如下: P0口输入高电压led灯亮
3.按键电路示意图:按下是低电平, 高电平时常态
4.无源蜂鸣器电路示意图如下:输入方波,发出声音
四、程序设计框架
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)=1us。16位计数器总的就是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代表低八度的S0,0x21代表中八度的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数组中的位置
入口参数:tem:music数组中定义的简谱音符
出口参数:返回的是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]; //把音符相应的计时器重装载值赋予timeh和timel
timel=quzi[tem+1];
i++;
TH0=timeh; //把timeh和timel赋予计时器
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;
}