单片机与PC机通过RS232相连,编写一个异步串行口通信程序,实现单片机与PC机上的串口助手之间的通信。 功能实现: 1、当PC机向单片机发送字符‘1’~‘8’,打开对应的8个灯; 2、当PC机再次向单片机发送字符‘1’~‘8’时,关闭对应的灯; 3、当PC机向单片机发送字符‘d’时,会在液晶屏上删除一个字符; 4、当PC机向单片机发送字符‘n’时,会在液晶屏上换行显示; 5、当PC机向单片机发送字符‘c’时,会清屏显示; 6、当PC机向单片机发送字符‘m’时,会打开音乐播放,结束后可继续操作;
7、当PC机向单片机发送其它字符时,会显示在1602液晶屏上; 8、当矩阵按键有按键按下时,单片机会把键值发送到PC机上。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- #include<reg52.h>
- #include<string.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- uchar str[17];
- uchar ch;
- uchar m,n;
- sbit K1=P3^7;
- bit play=0;
- sbit LEN=P3^4;
- sbit LCDEN=P3^6;
- sbit RS=P3^3;
- sbit RW=P3^5;
- sbit BF=P2^7;
- sbit LED=P0;
- sbit BEEP=P3^2;
- uint I=0,J,K;
- uchar code keyval[]="123456789*0#"; //按键对应的符号
- char s[ ]="MCS-51 Microcomputer";
- uchar OpenLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
- uchar CloseLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
- uchar code T[49][2]={{0,0},
- {0xF8,0x8B},{0xF8,0xF2},{0xF9,0x5B},{0xF9,0xB7},{0xFA,0x14},{0xFA,0x66},{0xFA,0xB9},{0xFB,0x03},{0xFB,0x4A},{0xFB,0x8F},{0xFB,0xCF},{0xFC,0x0B},
- {0xFC,0x43},{0xFC,0x78},{0xFC,0xAB},{0xFC,0xDB},{0xFD,0x08},{0xFD,0x33},{0xFD,0x5B},{0xFD,0x81},{0xFD,0xA5},{0xFD,0xC7},{0xFD,0xE7},{0xFE,0x05},
- {0xFE,0x21},{0xFE,0x3C},{0xFE,0x55},{0xFE,0x6D},{0xFE,0x84},{0xFE,0x99},{0xFE,0xAD},{0xFE,0xC0},{0xFE,0x02},{0xFE,0xE3},{0xFE,0xF3},{0xFF,0x02},
- {0xFF,0x10},{0xFF,0x1D},{0xFF,0x2A},{0xFF,0x36},{0xFF,0x42},{0xFF,0x4C},{0xFF,0x56},{0xFF,0x60},{0xFF,0x69},{0xFF,0x71},{0xFF,0x79},{0xFF,0x81}
- };
- uchar code music[][2]={
- {0,4},{20,8},{25,4},{22,4},{20,8},
- {20,8},{17,4},{20,4},{22,4},{25,4},
- {20,8},{20,8},{22,8},{20,4},{17,4},
- {15,8},{15,8},{17,8},{20,4},{17,4},
- {15,4},{17,4},{13,8},{22,8},{20,8},
- {22,8},{20,8},{17,8},{22,8},{20,16},
- {20,4},{20,4},{17,4},{15,4},{13,16},
- {13,4},{13,4},{15,4},{17,4},{13,16},
- {0xFF,0xFF}};
- //生日快乐歌的音符频率表,不同频率由不同的延时来决定
- uchar 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};
- //生日快乐歌节拍表,节拍决定每个音符的演奏长短
- uchar 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};
- void delay(uchar p)
- {
- uchar i,j;
- for(;p>0;p--)
- for(i=181;i>0;i--)
- for(j=181;j>0;j--);
- }
- void DelayMS(uint x)
- {
- uchar t;
- while(x--) for(t=0;t<120;t++);
- }
- //2¥·?oˉêy
- void PlayMusic2()
- {
- // uint i=0,j,k;
- if(SONG_LONG[I]!=0||SONG_TONE[I]!=0)
- {
- for(J=0;J<SONG_LONG[I]*20;J++)
- {
- BEEP=~BEEP;
-
- for(K=0;K<SONG_TONE[I]/3;K++);
- }
- DelayMS(10);
- I++;
- }
- else
- I=0;
- }
- void delay_us(uchar t) //微秒延时
- {uchar i;
- for(i=0;i<=t;i++);
- }
- void Alarm(uchar t) //报警
- {
- uchar i,j,k;
- for(j=0;j<t;j++)
- { for(i=0;i<200;i++)
- {BEEP=0;delay_us(50);BEEP=1;delay_us(50);}
- for(k=0;k<100;k++)
- {BEEP=0;delay_us(110);BEEP=1;delay_us(110);}
- }
- }
- void pause()
- {
- uchar i,j;
- for(i=150;i>0;i--)
- for(j=150;j>0;j--);
- }
- void delay_ms(unsigned int xms)
- {
- int i,j;
- for(i=xms;i>0;i--)
- for(j=110;j>0;j--);
- }
- void PlayMusic()
- {
- uchar i=0;
-
- while(1)
- { if(i==40){ ET0=0;
- break; }
- m=music[i][0];
- n=music[i][1];
- if(m==0x00)
- {
- TR0=0;
- delay(n);
- i++;
- }
- else if(m==0xFF)
- {
- TR0=0;
- delay(30);
- i=0;
- }
- else if(m==music[i+1][0])
- {
- TR0=1;
- delay(n);
- TR0=0;
- pause();
- i++;
- }
- else
- {
- TR0=1;
- delay(n);
- i++;
- }
- }
- }
- uchar keypad4_3()//按键扫描函数:要去抖,若有按键按下,返回对应的按键值(0-11),没有按键按下返回12
- {
- uchar i,row,temp;
- uchar key=12;//按键号,初值设置为12,目的是:没有按键按下时返回12;
- //若不设初值(默认值为0),没有按键按下时,将返回0,会误认为0被按下
- row=0xef; //从第一行开始
- for(i=0;i<4;i++)
- {
- P1=0xff;
- P1=row; //第i行信号,对应行为低,其他全为高
- row=_crol_(row,1); //生成下一行信号
- temp=P1; //读入扫描信号
- temp=temp&0x07; //屏蔽高5位信号,只保留低3位列信号
- if(temp!=0x07)//有按键被按下,因为第i行某列有按键按下,则低3位中有一位为低
- {
- delay_ms(20); //延时去抖
- temp=P1;
- temp=temp&0x07;
- if(temp!=0x07) //再次确认有按键被按下
- {
- switch(temp) //根据低3位列信号,判断哪个按键被按下
- {
- case 0x06:key=0+3*i;break; //第i行第1列按键被按下
- case 0x05:key=1+3*i;break; //第i行第2列按键被按下
- case 0x03:key=2+3*i;break; //第i行第3列按键被按下
- }
-
- do
- {
- temp=P1; //再次扫描按键
- temp=temp&0x07;
- }while(temp!=0x07); //等待按键释放
- }
- }
- }
- return(key);//扫面结束,返回按键值
- }
- uchar RdACAdr()//读当前光标地址
- {
- uchar result;
- P2 = 0xff; //读地址前先置高电平,防止误判
- RS = 0;
- delay_ms(5);
- RW = 1;
- LCDEN = 1;
- delay_ms(5);
- result=P2&0x7f; //去掉最高位忙闲标记,只保留低7位地址值
- LCDEN = 0;
- return result;
- }
- uchar DectectBusyBit(void)//状态判断函数(忙/闲?)
- {
- bit result;
- P2 = 0xff; //读状态前先置高电平,防止误判
- RS = 0;
- delay_ms(5);
- RW = 1;
- LCDEN = 1;
- delay_ms(5);
- result=BF; //若LCM忙,则反复测试,在此处原地踏步;当LCM闲时,才往下继续
- LCDEN = 0;
- return result;
- }
- void WrComLCD(unsigned char ComVal)//写命令函数
- {
- while(DectectBusyBit()==1); //先检测LCM是否空闲
- RS = 0;
- delay_ms(1);
- RW = 0;
- LCDEN = 1;
- P2 = ComVal;
- delay_ms(1);
- LCDEN = 0;
- }
- void WrDatLCD(uchar DatVal)//写数据函数
- {
- while(DectectBusyBit()==1);
- RS = 1;
- delay_ms(1);
- RW = 0;
- LCDEN = 1;
- P2 = DatVal;
- delay_ms(1);
- LCDEN = 0;
- }
- void WrStrDat(uchar *p)//显示英文字符串(长度不超过32)
- {
- uchar i=0,t;
-
- while(p[i]!='\0')
- {
- WrDatLCD(p[i]);
- i++;
- delay_ms(5);
-
- t=RdACAdr();
- if(t==0x10) WrComLCD(0xc0);//读当前坐标,如果第1行写完换行到第2行
- if(t==0x50) WrComLCD(0x80);//读当前坐标,如果第2行写完换行到第1行
- }
- }
- void LCD_Init(void)//1602初始化函数
- {
- //delay(15);
- WrComLCD(0x38);
- //delay(5); // 功能设定:16*2行、5*7点阵、8位数据接口
- WrComLCD(0x38);
- //delay(5);
- WrComLCD(0x38);
- //多次重复设定功能指令,因为LCD启动后并不知道使用的是4位数据接口还是8位的,所以开始时总是默认为4位,这样刚开始写入功能设定指令时,低4位被忽略,为了可靠,最好多写几遍该指令
- WrComLCD(0x01); // 清屏
- WrComLCD(0x06); // 光标自增、屏幕不动
- delay_ms(1); // 延时,等待上面的指令生效,下面再显示,防止出现乱码
- WrComLCD(0x0C); // 开显示、关光标
- }
- void uart_init(unsigned int bps)
- { unsigned char t;
- SCON=0x50; //工作方式一:8位异步收发,波特率可变,允许接收数据
- PCON=0x00; //SMOD=0
- TI=0; //软件清零,表示未发送完成
- EA=1; //开总中断
- ET1=1;
- ET0=1; //开T0
- ES=1; //开串口中断
- TMOD=0x21; //设置T1定时器8位自动装载模式
- switch(bps)
- { case 1200:t=0xe8;break;
- case 2400:t=0xf4;break;
- case 4800:t=0xfa;break;
- case 9600:t=0xfd;break;
- }
- TH1=t;
- TL1=t;
- TR1=1; //开启T1
- }
- void uart_send(unsigned char ch)
- {
- ES=0; //关串口中断
- SBUF=ch;
- while(TI==0); //等待发送完成
- TI=0; //清除中断标记
- ES=1; //开中断
- }
- unsigned char receive(void)
- { unsigned char dat;
- while(RI==0); //等待接收完毕
- RI=0; //将接收中断标志RI清0,为接收下一帧数据做准备
- dat=SBUF; //将接收缓冲器中的数据存入dat
- return dat; //将接收到的数据返回
- }
- void main (void)
- { uchar temp=0xff;
- uchar tt=0xff;
- uchar a,b=0;
- uchar keypadVal=12;
- //TMOD=0x21; EA=1; ET0=1;
- //ET1=0;
- //LEN=1;
- PT0=1;
- //PS=1;
- P0=0xff;
- //LEN=0;
- uart_init(9600);//串口初始化
- LCD_Init();//1602液晶屏初始化
- delay_ms(5); //延时,等待初始化完成
- WrComLCD(0x80); //设置显示地址第一行第一位:0X00(0x80+0x00)
-
- while(1)
- {
-
- keypadVal=keypad4_3();
- if(keypadVal<12)
- {
- uart_send(keyval[keypadVal]);
- }
- /*if(K1==0)//如按键按下,就发送"MCS-51 Microcomputer"
- {
- b=0;//计数器清0
- a=strlen(s);//取数组长度
- for(;b<a;b++)//循环取数据发送,从0到a
- { SBUF=s[b];//发送"MCS-51 Microcomputer"
- while(!TI);//没有发送完,就等待.
- TI=0;//清发送结束标志
- }
- while(!K1);//如果按键没有松开,等待
- }*/
- if(RI||a=='m')//如果接收到数据,就把接收到的数回发给PC
- { uchar pos;
-
- RI=0;//清接收标志
-
- //if(a!='m')
- //{
- a=SBUF ;//读串行口数据
- SBUF=a;//把从PC机发送过来的数据返回给PC机
- // if(a=='m')
- // play=~play;
- // }
-
-
- while(!TI);//没有发送完等待
- TI=0;//清发送完中断标志
- if(a>'0'&&a<='8')
- {
- if(temp==a)
- tt=tt^(_crol_(0x01,a-'0'-1));
- //P0=0x00;
- else
- tt=tt&(~OpenLight[a-'0'-1]);
- temp=a;
- P0=tt;
- }
- else
- {
- if(a=='n')
- {
- //换行
- pos=RdACAdr();
- if(pos>=0x00&&pos<0x10){ WrComLCD(0xc0);WrStrDat(" ");WrComLCD(0xc0);}//读当前坐标,如果第1行写完换行到第2行
- else if(pos>=0x40&&pos<0x50) { WrComLCD(0x80);WrStrDat(" ");WrComLCD(0x80);}//读当前坐标,如果第2行写完换行到第1行
- }
- else if(a=='c')
- {
- WrComLCD(0x01);
- WrComLCD(0x80);
- }
- else if(a=='d')
- {
- //删除
- WrComLCD(0x10);
- WrDatLCD(' ');
- WrComLCD(0x10);
- }
- else if(a=='m')
- { //ET0=1;
- PlayMusic2();
-
- //if(play)
- //Alarm(5);
- }
- else
- {
- WrDatLCD(a);
- //WrComLCD(0x80);
- pos=RdACAdr();
- if(pos==0x10) WrComLCD(0xc0);//读当前坐标,如果第1行写完换行到第2行
- if(pos==0x50) WrComLCD(0x80);//读当前坐标,如果第2行写完换行到第1行
- }
-
- }
- }
-
- }
- }
- void T0_int() interrupt 1
- {
- BEEP=!BEEP;
- TH0=T[m][0]; TL0=T[m][1];
- }
复制代码
所有资料51hei提供下载:
PC机控制单片机.zip
(259.53 KB, 下载次数: 345)
|