【C51单片机】PC机控制单片机(仿真)

倾然丶 夕夏残阳落幕 提交于 2019-11-25 23:19:32

1、功能描述

1.单片机与PC机通过RS232相连,编写一个异步串行口通信程序,实现单片机与PC机上的串口助手之间的通信。具体功能如下:

①PC机向单片机发送命令打开或关闭指定LED灯

②PC机向单片机发送命令启动或停止蜂鸣器播放音乐

③PC机向单片机发送字符并显示在1602液晶显示器上,也可发命令删除字符、换行、清除显示

④在单片机一端,点击矩阵按键,并将按键序号发送给PC机

2、PROTEUS中设计的电路图

3、源代码

 

#include<reg52.h>
#include<intrins.h>
#include <stdio.h>
sbit LCDEN=P3^4;
sbit RS=P3^5;
sbit RW=P3^6;
sbit BF=P1^7;
sbit FM=P3^2;

unsigned char code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,
						212,212,106,126,159,169,190,119,119,126,159,142,159,0};
//生日快乐歌节拍表,节拍决定每个音符的演奏长短
unsigned char code SONG_LONG[]=
{9,3,12,12,12,24,9,3,12,12,12,24,9,3,12,12,12,12,12,9,3,12,12,12,24,0};

unsigned char code keyval[]="0123456789ABCDEF"; //按键对应的符号
unsigned char code keycode[]={
0xfe,0xfd,0xfb,0xf7,
0xef,0xdf,0xbf,0x7f};	 //按键键值编码

void delay(unsigned int t)	  //延时t毫秒
{int i,j;
 for(i=0;i<=t;i++)
	 for(j=0;j<=110;j++);
}


void  send(unsigned char s)	 //发送函数,将8个灯的状态(0/1),以字符串的形式发送给上位机,先发最高位
{
      unsigned char i,t;
	  for(i=0;i<8;i++)
	  {	
	    s<<=1;
		t=CY;
		SBUF='0'+ t;	//发送灯的状态给上位机
    	while(!TI);//等待发送完
    	TI=0;//清除发送结束标志	
	  }			
}


unsigned char DectectBusyBit(void)//状态判断函数(忙/闲?)
{   
	bit result;
	P1 = 0xff;	//读状态前先置高电平,防止误判
	RS = 0;
	delay(5);
    RW = 1;
	LCDEN = 1;
	delay(5);
	result=BF; //若LCM忙,则反复测试,在此处原地踏步;当LCM闲时,才往下继续
	LCDEN = 0;
	return result;		      
}

void WrComLCD(unsigned char ComVal)//写命令函数
{
	while(DectectBusyBit()==1);         //先检测LCM是否空闲
	RS = 0;
	delay(1);
    RW = 0;
	LCDEN = 1;
	P1 = ComVal;
	delay(1);
	LCDEN = 0;	
}

void WrDatLCD(unsigned char DatVal)//写数据函数
{
while(DectectBusyBit()==1); 
	RS = 1;
	delay(1);
    RW = 0;
	LCDEN = 1;
	P1 = DatVal;
	delay(1);
	LCDEN = 0;	
}

void LCD_Init(void)//1602初始化函数
{ 
	WrComLCD(0x38);     // 功能设定:16*2行、5*7点阵、8位数据接口
	WrComLCD(0x38);
	WrComLCD(0x38);    
//多次重复设定功能指令,因为LCD启动后并不知道使用的是4位数据接口还是8位的,所以开始时总是默认为4位
	WrComLCD(0x01);    // 清屏 
	WrComLCD(0x06);    // 光标自增、屏幕不动  
	delay(1);	      // 延时,等待上面的指令生效,下面再显示,防止出现乱码
	WrComLCD(0x0e);    // 开显示、开光标,闪烁
}



unsigned char keypad4_4()//按键扫描函数:要去抖,若有按键按下,返回对应的按键值(0-15),没有按键按下返回16
{
unsigned char i,row,temp;
unsigned char key=16;//按键号,初值设置为16,目的是:没有按键按下时返回16;
          //若不设初值(默认值为0),没有按键按下时,将返回0,会误认为0被按下  
row=0xef; //从第一列开始      
for(i=0;i<4;i++)
{
	P2=0xff;  
	P2=row;	//第i列信号,对应列为低,其他全为高
	row=_crol_(row,1); 	  //生成下一列信号
	temp=P2; //读入扫描信号
	temp=temp&0x0f; //屏蔽高4位列信号,只保留低4位行信号 
	if(temp!=0x0f)//有按键被按下,因为第i列某行有按键按下,则低4位中有一位为低  
 	{  
		delay(10);  //延时去抖
		temp=P2;  
		temp=temp&0x0f;  
		if(temp!=0x0f)   //再次确认有按键被按下
  		{  
        	switch(temp)  //根据低4位行信号,判断哪个按键被按下
            {  
            	case 0x0e:key=0+i;break; //第i列第1行按键被按下 
                case 0x0d:key=4+i;break; //第i列第2行按键被按下  
                case 0x0b:key=8+i;break; //第i列第3行按键被按下
				case 0x07:key=12+i;      //第i列第4行按键被按下 
            }
			
			do
			{
				temp=P2;  	    //再次扫描按键
  				temp=temp&0x0f;  
  			}while(temp!=0x0f); //等待按键释放   
  		}  
     }
}  
return(key);//扫面结束,返回按键值
}

void matrix(){
	unsigned char y,i=0;
	//while(1){
	y=keypad4_4();
	if(y<16){
		SBUF=keyval[y];	//发送灯的状态给上位机
    	while(!TI);//等待发送完
    	TI=0;//清除发送结束标志
	//	i++;		
	}
//	if(i==10) break;
//	} 		
}


//播放函数
void PlayMusic()
{
	unsigned int i=0,j,k;
	while(SONG_LONG[i]!=0||SONG_TONE[i]!=0)
	{	//播放各个音符,SONG_LONG为拍子长度
		for(j=0;j<SONG_LONG[i]*20;j++)
		{
			FM=~FM;
			//SONG_TONE延时表决定了每个音符的频率
			for(k=0;k<SONG_TONE[i]/3;k++);
		}
		delay(50);
		i++;		
	}
}




void main (void)
{ unsigned char t,led=0xff;		//灯初始状态全灭
  unsigned int a=1;
  FM=0;
  SCON=0x50;//串行口方式1
  PCON=0x00;//T1初始化,
  TMOD=0x20;//T1设为方式2
  TH1=0xFD;//选9600波特,fosc=11.0592MHz
  TR1=1;//启动T1

  P0=led;
  send(led); //发送初始状态给上位机 
  LCD_Init();    //初始化1602
  WrComLCD(0x80); //位置为第一行第一个
  while(1)
  {
  	matrix();
  
    if(RI)//接收上位机数据
    {	RI=0;//清接收标志
        t=SBUF;	//接收上位机发来的数据
		if(t!='d' && t!='l' && t!='c')  WrDatLCD(t);

		if(t=='s') 	 //‘s’为上位机同步请求符,
		    send(led);//直接将发送显示状态给上位机
		if(t=='d'){    //'d'为1602显示屏删除一个字符
			WrComLCD(0x10); WrDatLCD(' '); delay(5);  WrComLCD(0x10);
		}
		if(t=='l'){	   //'l'为1602显示屏换行
			if(a==1){
				WrComLCD(0xC0); a=0;}
			else {WrComLCD(0x80);a=1;}
		}
		if(t=='c'){    // 'c'为1602显示屏清屏
			WrComLCD(0x01);	   //清屏
			WrComLCD(0x80);
		}
		if(t=='f'){
			PlayMusic();
		}
		if(t>='0' && t<='7'){		 //接收上位机按键
			t=t-'0';// 获取上位机按键码
			led=led^(1<<t);//按键对应位取反
			P0=led;	//更新单片机显示
			send(led); //发送显示状态给上位机
		}	
    }
  }  
}

4、实验效果

由于不能上传视频,所以我将效果视频上传至优酷,请点击观看------->效果视频展示

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!