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、实验效果
由于不能上传视频,所以我将效果视频上传至优酷,请点击观看------->效果视频展示
来源:https://blog.csdn.net/qq_42449351/article/details/98657588