摘要:
本项目以AT89C52为基础,通过矩阵键盘实现7个基本音节的低、中、高音,并在数码管上加以显示,同时通过按键切换模式,不同模式可以播放不同的歌曲
一.主体思路
单片机控制蜂鸣器需要一定频率的方波信号,在一个频率周期内让单片机的某个IO口一半时间高电平,一半时间低电平,一直重复便会生成这个频率的方波信号,就可以驱动蜂鸣器发出想要的声音
如下图为音符延时表
播放音乐要设置每个音符的频率,时长,即在播放音乐过程中,先选择该音符对应的频率和时长,然后进入定时器中断程序使蜂鸣器发声,同时计数变量自增,选择下一音符,以此类推,播放整首音乐
二.主要内容
1.主要功能
(一)矩阵键盘按键控制发出7个基本音节的低、中、高音
(二)蜂鸣器播放指定音乐
(三)单一按键发声
2.硬件电路设计
2.1 元件选取
如上图所示,选取AT89C52开发板,7SEG-MPX1-CA晶体数码管,NPN型三极管、BUZZER(ACTIVE)有源蜂鸣器、按钮BUTTON以及电阻RES和排阻RESPACK-8以及复位所需元器件
2.2 单片机IO口选取
如上图所示,使用了P0、P1的全部IO口,使用了P3的0~3IO口和第7IO以及复位所需IO口
2.3 硬件部分作用
(一)蜂鸣器模块
该模块的作用是发出声音,此处选择有源蜂鸣器,将电压参数改为5V,如下图所示
IO口为高电平时蜂鸣器鸣叫,但由于蜂鸣器的驱动电流较大,IO口无法直接进行驱动,所以需要经过三极管放大电流驱动,其中R1位三极管基极的限流电阻,R2是基极下拉电阻,保证基极浮空或者处于高组态时,三极管有效关断,防止误触发。
(二)矩阵键盘模块
该模块的作用是选择7个基本音节,传递键值给数码管模块。
先不断使行变为低电平,即假设某行按键被按下,再扫描该行的哪一列的按键被按下,消除抖动后传递该按键的值
(三)7SEG-MPX1-CA晶体数码管模块
该模块的作用是显示输出的数据,显示矩阵键盘的值
(四)按键模块
该模块的作用是切换模式,使晶体数码管显示不同的内容
(五)复位模块
该模块的作用是使系统复位,从而切换其他模式
3.软件设计
先进行模块化编程
如上图所示,将整体程序分为主函数main,延时函数delay,按键模块key,蜂鸣器模块Buzzer,音乐模块music,定时器模块timer
(一)music模块
使用define定义相关引脚(低、中、高音频率,下标,音节时长),如上图所示
再使用sbit定义蜂鸣器的P3^7引脚
创建数码管上显示数字的数组dis,创建频率数组FreqTable,代表每个音节的频率,创建音节字符数组tab,和音乐乐谱,音乐乐谱创建过程如下图
定义display显示函数,使P0端口为dis数组中的元素值,从而驱动数码管显示数字
定义fun为音符函数,首先扫描矩阵键盘按键值,定义cntt变量和flag变量,使按键每次按下蜂鸣器只发出一次声音,而不是一直发声
定义fun2、fun3、fun4为音乐函数,先定义计数变量MusicSelect,如果在乐谱数组中没有到结束位置,就先选择音符对应的频率,然后通过延时函数和中断函数使该段时长发出该音符的声音,然后关闭并再次打开定时器,如果到了结束位置,则关闭计时器并一直循环
定义定时器中断函数timer_routine
进入中断函数后,给TH0和TL0赋值频率数组除以和取模256后的值,并使蜂鸣器IO口反转发声
(二)BUZZER模块
使用sbit定义蜂鸣器P3^7引脚
编写蜂鸣器专用延时函数,一次延时500us,再编写蜂鸣器发声函数,在短暂的时间段内使蜂鸣器发声和延时
(三)timer模块
定义timer_init函数,对定时器进行初始化。
初始化步骤如下:
1.首先设置TMOD寄存器,定时器T0工作在方式1,使TMOD寄存器的M1,M0=01,设置C/T=0,为定时器工作模式,对T0的运行仅由TR0来控制,应使相应的GATE位为0,定时器T1不使用,各相关位均设为0,所以TMOD寄存器应初始化为0x01。
2.计算定时器T0的计数初值,设定时时间为50ms,通过公式计算可得出相应的高八位和低八位,分别装入TH0和TL0。
3.设置IE寄存器,将寄存器中的EA,ET0位置1
4.启动和停止定时器T0,将定时器控制寄存器TCON中的TR0=1,则启动定时器T0;TR0=0,则启动定时器T0计时
(四)key模块
通过控制按钮改变点阵中所显示的内容
首先使用sbit定义相关引脚,如上图所示
模式切换函数key( ):
不断扫描是否有按键按下,若有按键按下,则该引脚变为低电平,对其进行消除抖动,并判断是否松开,若松开,进行二次消除抖动,并传回相关按键的值
矩阵键盘函数key_scan( ):
每次都先初始化P1端口,重置为高电平,然后使某一行为低电平,扫描该行是否有某列的按键按下,若有,则传递该行该列的键值
(五)delay模块
编写能够延时1ms和延时12us的函数
(六)main模块
首先对定时器进行初始化
在循环中不断扫描key的按键值,不同的按键值代表点阵显示不同的模式,每个模式都有相对应的功能
4.源程序代码
(一)main模块
#include <reg52.h>
#include <intrins.h>
#include "delay.h"
#include "timer.h"
#include "Buzzer.h"
#include "Key.h"
#include "music.h"
#define uchar unsigned char
#define uint unsigned int
uchar ys;
void main(){timerInit();while(1){ys=key();if(ys==1){while(1)fun(); }else if(ys==2){while(1)fun2();}else if(ys==3){while(1)fun3();}else if(ys==4){while(1)fun4();}}
}
(二)delay模块
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
void delay(uint x){uchar i, j;while(x--){i = 2;j = 239;do{while (--j);}while (--i);}
}
void delay12us(uint x){uchar i;for(i=0;i<x;i++){;}
}
(三)key模块
#include <reg52.h>
#include "delay.h"
#define uchar unsigned char
#define uint unsigned int
sbit L0=P1^0;
sbit L1=P1^1;
sbit L2=P1^2;
sbit L3=P1^3;
sbit L4=P1^4;
sbit L5=P1^5;
sbit L6=P1^6;
sbit L7=P1^7;
sbit K0=P3^0;
sbit K1=P3^1;
sbit K2=P3^2;
sbit K3=P3^3;
uchar Key(){uint number=0;if(K0==0){delay(20);while(K0==0);delay(20);number=1;}if(K1==0){delay(20);while(K1==0);delay(20);number=2;}if(K2==0){delay(20);while(K2==0);delay(20);number=3;}if(K3==0){delay(20);while(K3==0);delay(20);number=4;}return number;
}
uchar key_scan(){uchar KeyNumber;P1=0xFF;L0=0;if(L4==0){delay(20);while(L4==0);delay(20);KeyNumber=0;}if(L5==0){delay(20);while(L5==0);delay(20);KeyNumber=1;}if(L6==0){delay(20);while(L6==0);delay(20);KeyNumber=2;}if(L7==0){delay(20);while(L7==0);delay(20);KeyNumber=3;}P1=0xFF;L1=0;if(L4==0){delay(20);while(L4==0);delay(20);KeyNumber=4;}if(L5==0){delay(20);while(L5==0);delay(20);KeyNumber=5;}if(L6==0){delay(20);while(L6==0);delay(20);KeyNumber=6;}if(L7==0){delay(20);while(L7==0);delay(20);KeyNumber=7;}P1=0xFF;L2=0;if(L4==0){delay(20);while(L4==0);delay(20);KeyNumber=8;}if(L5==0){delay(20);while(L5==0);delay(20);KeyNumber=9;}if(L6==0){delay(20);while(L6==0);delay(20);KeyNumber=10;}if(L7==0){delay(20);while(L7==0);delay(20);KeyNumber=11;}P1=0xFF;L3=0;if(L4==0){delay(20);while(L4==0);delay(20);KeyNumber=12;}if(L5==0){delay(20);while(L5==0);delay(20);KeyNumber=13;}if(L6==0){delay(20);while(L6==0);delay(20);KeyNumber=14;}if(L7==0){delay(20);while(L7==0);delay(20);KeyNumber=15;}return KeyNumber;
}
(四)timer模块
#include <reg52.h>
sbit P37=P3^7;
void timerInit(){TMOD&=0xF0;TMOD|=0x01;TL0=0x18;TH0=0xFC;TF0=0;TR0=1;ET0=1;EA=1;PT0=0;P37=0;
}
(五)Buzzer模块
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit P37=P3^7;
void Buzzer_Delay500us(){uchar i;_nop_();i=247;while(--i);
}
void Buzzer_Time(uint ms){uint i;for(i=0;i<ms*2;i++){P37=!P37;Buzzer_Delay500us();}
}
(六)music模块
#include <reg52.h>
#include <intrins.h>
#include "delay.h"
#include "timer.h"
#include "Buzzer.h"
#include "Key.h"
#define uchar unsigned char
#define uint unsigned int
#define SPEED 500
#define di1 159
#define di2 142
#define di3 126
#define di4 119
#define di5 106
#define di6 95
#define di7 84#define zh1 80
#define zh2 71
#define zh3 63
#define zh4 60
#define zh5 53
#define zh6 47
#define zh7 42#define gao1 40
#define gao2 35
#define gao3 32
#define gao4 30
#define gao5 27
#define gao6 24
#define gao7 21
#define P 0
#define S1 1
#define S1_ 2
#define S2 3
#define S2_ 4
#define S3 5
#define S4 6
#define S4_ 7
#define S5 8
#define S5_ 9
#define S6 10
#define S6_ 11
#define S7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
sbit P37=P3^7;
uchar i,ans;
uint k,KeyNum;
uchar flag;
uchar cntt;
uchar FreqSelect,MusicSelect;
uchar dis[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
};uint FreqTable[]={0,63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};uchar tab[]={di1,di2,di3,di4,di5,di6,di7,zh1,zh2,zh3,zh4,zh5,zh6,zh7,gao1,gao2};uchar code MusicA[]={//1P, 4,P, 4,P, 4,M6, 2,M7, 2,H1, 4+2,M7, 2,H1, 4,H3, 4,M7, 4+4+4,M3, 2,M3, 2,//2M6, 4+2,M5, 2,M6, 4,H1, 4,M5, 4+4+4,M3, 4,M4, 4+2,M3, 2,M4, 4,H1, 4,//3M3, 4+4,P, 2,H1, 2,H1, 2,H1, 2,M7, 4+2,M4_,2,M4_,4,M7, 4,M7, 8,P, 4,M6, 2,M7, 2,0xFF
};
uchar code MusicB[]={//4H1, 4+2,M7, 2,H1, 4,H3, 4,M7, 4+4+4,M3, 2,M3, 2,M6, 4+2,M5, 2,M6, 4,H1, 4,//5M5, 4+4+4,M2, 2,M3, 2,M4, 4,H1, 2,M7, 2+2,H1, 2+4,H2, 2,H2, 2,H3, 2,H1, 2+4+4,//6H1, 2,M7, 2,M6, 2,M6, 2,M7, 4,M5_,4,M6, 4+4+4,H1, 2,H2, 2,H3, 4+2,H2, 2,H3, 4,H5, 4,0xFF
};
uchar code MusicC[]={//7H2, 4+4+4,M5, 2,M5, 2,H1, 4+2,M7, 2,H1, 4,H3, 4,H3, 4+4+4+4,//8M6, 2,M7, 2,H1, 4,M7, 4,H2, 2,H2, 2,H1, 4+2,M5, 2+4+4,H4, 4,H3, 4,H3, 4,H1, 4,//9H3, 4+4+4,H3, 4,H6, 4+4,H5, 4,H5, 4,H3, 2,H2, 2,H1, 4+4,P, 2,H1, 2,//10H2, 4,H1, 2,H2, 2,H2, 4,H5, 4,H3, 4+4+4,H3, 4,H6, 4+4,H5, 4+4,//11H3, 2,H2, 2,H1, 4+4,P, 2,H1, 2,H2, 4,H1, 2,H2, 2+4,M7, 4,M6, 4+4+4,P, 4,0xFF
};
void display(uchar k){P0=dis[k];
}
//音符函数
void fun(){KeyNum=key_scan();display(KeyNum);if(cntt!=KeyNum){cntt=KeyNum;flag=1;}if(flag){for(k=0;k<249;k++){P37=~P37;delay12us(tab[KeyNum]);}flag=0;P37=0;}delay(70);
}
void fun2(){MusicSelect=0;Buzzer_Time(100);while(1){if(MusicA[MusicSelect]!=0xFF) //如果不是停止标志位{FreqSelect=MusicA[MusicSelect]; //选择音符对应的频率MusicSelect++;delay(SPEED/4*MusicA[MusicSelect]); //选择音符对应的时值MusicSelect++;TR0=0;delay(5); //音符间短暂停顿TR0=1;}else //如果是停止标志位{TR0=0;while(1);}}
}
void fun3(){MusicSelect=0;Buzzer_Time(100);while(1){if(MusicB[MusicSelect]!=0xFF) //如果不是停止标志位{FreqSelect=MusicB[MusicSelect]; //选择音符对应的频率MusicSelect++;delay(SPEED/4*MusicB[MusicSelect]); //选择音符对应的时值MusicSelect++;TR0=0;delay(5); //音符间短暂停顿TR0=1;}else //如果是停止标志位{TR0=0;while(1);}}
}
void fun4(){MusicSelect=0;Buzzer_Time(100);while(1){if(MusicC[MusicSelect]!=0xFF) //如果不是停止标志位{FreqSelect=MusicC[MusicSelect]; //选择音符对应的频率MusicSelect++;delay(SPEED/4*MusicC[MusicSelect]); //选择音符对应的时值MusicSelect++;TR0=0;delay(5); //音符间短暂停顿TR0=1;}else //如果是停止标志位{TR0=0;while(1);}}
}
void timer_routine() interrupt 1{if(FreqTable[FreqSelect]){ //如果不是休止符TH0 = FreqTable[FreqSelect]/256; //设置定时初值TL0 = FreqTable[FreqSelect]%256; //设置定时初值P37=!P37; //翻转蜂鸣器IO口}
}
至此,我们已经基本完成了整个项目,.h文件自然不必多说,相信各位读者的能力。
相信大家对蜂鸣器和矩阵键盘的使用更加熟练,也更多的了解到了AT89C52单片机,欢迎各位一起讨论和交流。