声音的音调不同是因为声音的频率不同造成的。那么,就可以通过单片机发送不同频率的脉冲信号给蜂鸣器,来达到让蜂鸣器发出不同音调声音的目的。不同频率的脉冲信号就意外着每个脉冲之间必须有着不一样的时差,这可以通过延时或者定时计数器定时的方式来实现。
我们便是通过这个目的,去通过对于单片机的操作,蜂鸣器变调,使蜂鸣器完成对于歌曲乐谱的播放,再通过LCD1602把我们想显示的汉字显示在液晶屏幕上。制作成一个建议的基于单片机的音乐播放器。
Keil4编写代码,Proteus仿真程序
这个程序代码关联性不强,所以我分成3个模块为大家讲解程序,首先为大家讲解比较重要的1602。1602是一种工业字符型液晶,全称LCD1602,能够同时显示16x02即32个字符。LCD1602液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。
void write_command(uchar com)
{
check_busy();
E=0;
RS=0;
RW=0;
out=com;
E=1;
delay(2);
E=0;
delay(2);
}
void write_date(uchar dat)
{
check_busy();
E=0;
RS=1;
RW=0;
out=dat;
E=1;
delay(2);
E=0;
delay(2);
}
Out即为整个双向数据端,根据我们每个人的接线去操作即可。完成了写函数以后,我们就可以根据指令集对1602进行初始化,这里我们不在对指令集和初始化进行介绍。
对于1602写汉字我们需要下一番功夫,因为1602内置的CGROM里存储了常见的192个字符,但是没有中文,我们想显示中文就必须字节创建一个字库。查看1602数据手册,我们可以看到1602有一个CGRAM区,作为用户自定义区,大小为64字节,每8个字节为一组显示一个字符,总共可以显示8种自定义字符。每种字符显示都有自己的显示编码从第一种字符到第八个字符,依次是:{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}。
现在的问题就是建字库,我查看数据手册发现1602其内部数据矩阵并非是8 * 8的,而是5 * 8的只用右边区域进行显示,知道这点我们就直接上字模提取软件,且只用右边位置即可。
总得来说,就是我们先建一个字库,然后设定好一个显示地址,然后在每个地址写入每个字符的显示编码,即可在对应的区域显示我们的字符了。
我们这里放个字库截图吧:
table2就是我们建的字库,table3是每个字库显示编码,table4即直接用了CGROM的字符。想知道我们建的库长啥样,见文末。。
显示函数我们列一下:
void lcd1602xianshi(void)
{
uchar i;
init_1602();
write_command(0x40);//向用户自定义RAM区写入我们定义好的字库
for(i=0;i<64;i++)
{
write_date(table2[i]);
delay(1);
}
write_command(0x80+0x03);//把填充好的字库显示在第一行第三个字符位置后面
for(i=0;i<8;i++)
{
write_date(table3[i]);
delay(100);
}
write_command(0xc0+0x02);//把happy birthday显示到第二行第二个字符后面
for(i=0;i<14;i++)
{
write_date(table4[i]);
delay(100);
}
a=2;
}
程序的意思是,首先我们要把做好的字模数组存储到1602的0x40(CGRAM中),然后选择显示区域,0x80后,从这个显示地点开始,依次写入显示编码,0x00 --> 0x07,依次对应CGRAM中的第一个图形,到第8个图形。然后再选择1602的第二行,显示英文字符,不需要做字模,因为1602内部是有ASCII码字模集的。至此,1602显示特殊图形已经为大家介绍完毕。
首先我们来看硬件连接,很简单
我们在硬件上只是简单的通过一个NPN三极管去做成了一个放大电路从而驱动有源蜂鸣器,在软件上因为89C51不像STM32有自己PWM硬件外设,所以我们只能去模拟PWM波形的产生,从而控制蜂鸣器的声调。
模拟PWM其实很简单,就是通过改变定时器定时时间,从而改变输出到蜂鸣器的电压,然后改变蜂鸣器的声调。每个声调持续的时间也可以通过简单的延时来完成(但是延时前后一定要关闭定时器中断)。
一首曲子肯定要有7个音调,哆来咪发嗦啦西,只有一个声部肯定不够,要分高声部和低声部,这样我们就要设置14个音调,每个音调持续的时间也不一定,有的音持续时间长,有的音持续时间短。所以我们还需要一个可以标志音持续时间的数字。
综上所述,我们还是直接上截图:
Sszymmh[]这个数组里的数据每3个为一组,前两个数确定PWM占空比,后一个确定占空比持续时间。
我们这里上一下音乐播放函数:
void t0int() interrupt 1
{
TR0=0;
speaker=!speaker;
TH0=timer0h;
TL0=timer0l;
TR0=1;
}
void song()
{
TH0=timer0h;
TL0=timer0l;
TR0=1;
delayms(time);
}
void music(void)
{
unsigned char k,i;
TMOD=1;
EA=1;
ET0=1;
while(1)
{
i=0;
while(i<75){
k=sszymmh[i]+7*sszymmh[i+1]-1;
timer0h=FREQH[k];
timer0l=FREQL[k];
time=sszymmh[i+2];
i=i+3;
song();
}
}
a=3;
}
函数还是比较容易理解的,我们这里就不啰嗦了。
我们在程序里面加入了8*8矩阵作为一个趣味性的元器件,这个矩阵会形成一个箭头,逐渐往上,等到箭头全部上升完毕,1602显示内容,蜂鸣器播放音乐。
矩阵还是比较简单的,虽然用的引脚好多。。就是8个引脚来扫描,8个引脚搞显示,其实用74hc595会更省事儿,两个引脚就能解决问题,懒得弄了。直接上程序把:
void juzhen()
{
uint n,i;
uint k;
for(i=0;i<8;i++)
{ k=0;
while(k<100)
{for(n=0;n<8;n++)
{
P3=~table1[n];
P1=table[n+i*8];
delay(1);
}
k++;
}
}
a=1;
}
这是基本的电路设计,因为是仿真,没有加入晶振,复位等电路。
接下来看运行以后的程序:
至此,这个简单的项目已经介绍完毕,感兴趣的同学可以给个赞。下面放完整视频:
B站 51音乐播放器