Proteus仿真51单片机生日快乐音乐播放器

实验名称:基于51单片机音乐播放器

实验目的:

声音的音调不同是因为声音的频率不同造成的。那么,就可以通过单片机发送不同频率的脉冲信号给蜂鸣器,来达到让蜂鸣器发出不同音调声音的目的。不同频率的脉冲信号就意外着每个脉冲之间必须有着不一样的时差,这可以通过延时或者定时计数器定时的方式来实现。

我们便是通过这个目的,去通过对于单片机的操作,蜂鸣器变调,使蜂鸣器完成对于歌曲乐谱的播放,再通过LCD1602把我们想显示的汉字显示在液晶屏幕上。制作成一个建议的基于单片机的音乐播放器。

实验环境:

Keil4编写代码,Proteus仿真程序

LCD1602液晶显示汉字:

这个程序代码关联性不强,所以我分成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显示特殊图形已经为大家介绍完毕。

PWM控制扬声器/蜂鸣器演奏音乐:

首先我们来看硬件连接,很简单
在这里插入图片描述
我们在硬件上只是简单的通过一个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矩阵

我们在程序里面加入了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音乐播放器

发布了12 篇原创文章 · 获赞 11 · 访问量 3824

猜你喜欢

转载自blog.csdn.net/su_fei_ma_su/article/details/104271472