找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 12723|回复: 47
收起左侧

PC机通过RS232控制单片机(Proteus仿真+代码)

  [复制链接]
回帖奖励 5 黑币 回复本帖可获得 5 黑币奖励! 每人限 1 次
ID:404722 发表于 2019-2-3 00:05 | 显示全部楼层 |阅读模式
单片机与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仿真工程文件可到本帖附件中下载)
0.png

单片机源程序如下:
  1. #include<reg52.h>
  2. #include<string.h>
  3. #include<intrins.h>
  4. #define uchar unsigned char
  5. #define        uint unsigned int
  6. uchar str[17];
  7. uchar ch;
  8. uchar m,n;
  9. sbit K1=P3^7;
  10. bit play=0;
  11. sbit LEN=P3^4;
  12. sbit LCDEN=P3^6;
  13. sbit RS=P3^3;
  14. sbit RW=P3^5;
  15. sbit BF=P2^7;
  16. sbit LED=P0;
  17. sbit BEEP=P3^2;
  18. uint I=0,J,K;
  19. uchar code keyval[]="123456789*0#"; //按键对应的符号
  20. char s[ ]="MCS-51 Microcomputer";
  21. uchar OpenLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
  22. uchar CloseLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
  23. uchar code T[49][2]={{0,0},
  24. {0xF8,0x8B},{0xF8,0xF2},{0xF9,0x5B},{0xF9,0xB7},{0xFA,0x14},{0xFA,0x66},{0xFA,0xB9},{0xFB,0x03},{0xFB,0x4A},{0xFB,0x8F},{0xFB,0xCF},{0xFC,0x0B},
  25. {0xFC,0x43},{0xFC,0x78},{0xFC,0xAB},{0xFC,0xDB},{0xFD,0x08},{0xFD,0x33},{0xFD,0x5B},{0xFD,0x81},{0xFD,0xA5},{0xFD,0xC7},{0xFD,0xE7},{0xFE,0x05},
  26. {0xFE,0x21},{0xFE,0x3C},{0xFE,0x55},{0xFE,0x6D},{0xFE,0x84},{0xFE,0x99},{0xFE,0xAD},{0xFE,0xC0},{0xFE,0x02},{0xFE,0xE3},{0xFE,0xF3},{0xFF,0x02},
  27. {0xFF,0x10},{0xFF,0x1D},{0xFF,0x2A},{0xFF,0x36},{0xFF,0x42},{0xFF,0x4C},{0xFF,0x56},{0xFF,0x60},{0xFF,0x69},{0xFF,0x71},{0xFF,0x79},{0xFF,0x81}
  28. };
  29. uchar code music[][2]={
  30. {0,4},{20,8},{25,4},{22,4},{20,8},
  31. {20,8},{17,4},{20,4},{22,4},{25,4},
  32. {20,8},{20,8},{22,8},{20,4},{17,4},
  33. {15,8},{15,8},{17,8},{20,4},{17,4},
  34. {15,4},{17,4},{13,8},{22,8},{20,8},
  35. {22,8},{20,8},{17,8},{22,8},{20,16},
  36. {20,4},{20,4},{17,4},{15,4},{13,16},
  37. {13,4},{13,4},{15,4},{17,4},{13,16},
  38. {0xFF,0xFF}};
  39. //生日快乐歌的音符频率表,不同频率由不同的延时来决定
  40. uchar code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,
  41.                                                 212,212,106,126,159,169,190,119,119,126,159,142,159,0};
  42. //生日快乐歌节拍表,节拍决定每个音符的演奏长短
  43. uchar code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24,
  44. 9,3,12,12,12,12,12,9,3,12,12,12,24,0};
  45. void delay(uchar p)
  46. {
  47.     uchar i,j;
  48.     for(;p>0;p--)
  49.     for(i=181;i>0;i--)
  50.     for(j=181;j>0;j--);
  51. }
  52. void DelayMS(uint x)
  53. {
  54.         uchar t;
  55.         while(x--) for(t=0;t<120;t++);
  56. }
  57. //2¥·?oˉêy
  58. void PlayMusic2()
  59. {
  60. //        uint i=0,j,k;
  61.         if(SONG_LONG[I]!=0||SONG_TONE[I]!=0)
  62.         {        
  63.                 for(J=0;J<SONG_LONG[I]*20;J++)
  64.                 {
  65.                         BEEP=~BEEP;
  66.                         
  67.                         for(K=0;K<SONG_TONE[I]/3;K++);
  68.                 }
  69.                 DelayMS(10);
  70.                 I++;               
  71.         }
  72.         else
  73.                 I=0;
  74. }
  75. void delay_us(uchar t) //微秒延时
  76. {uchar i;
  77. for(i=0;i<=t;i++);
  78. }
  79. void Alarm(uchar t) //报警
  80. {
  81. uchar i,j,k;         
  82. for(j=0;j<t;j++)
  83. { for(i=0;i<200;i++)
  84.       {BEEP=0;delay_us(50);BEEP=1;delay_us(50);}
  85.    for(k=0;k<100;k++)
  86.       {BEEP=0;delay_us(110);BEEP=1;delay_us(110);}
  87. }
  88. }
  89. void pause()
  90. {
  91.     uchar i,j;
  92.     for(i=150;i>0;i--)
  93.     for(j=150;j>0;j--);
  94. }
  95. void delay_ms(unsigned int xms)
  96. {
  97.         int i,j;
  98.         for(i=xms;i>0;i--)
  99.                 for(j=110;j>0;j--);
  100. }
  101. void PlayMusic()
  102. {
  103.         uchar i=0;
  104.         
  105.         while(1)
  106.         {         if(i==40){ ET0=0;
  107.                         break;         }
  108.                 m=music[i][0];
  109.                 n=music[i][1];
  110.                    if(m==0x00)
  111.             {
  112.                         TR0=0;
  113.                         delay(n);
  114.                         i++;
  115.                 }
  116.             else if(m==0xFF)
  117.            {
  118.                    TR0=0;
  119.                    delay(30);
  120.                    i=0;
  121.            }
  122.            else if(m==music[i+1][0])
  123.            {
  124.                    TR0=1;
  125.                    delay(n);
  126.                    TR0=0;
  127.                    pause();
  128.                    i++;
  129.            }
  130.            else
  131.            {
  132.                    TR0=1;
  133.                    delay(n);
  134.                    i++;
  135.            }
  136.         }
  137. }
  138. uchar keypad4_3()//按键扫描函数:要去抖,若有按键按下,返回对应的按键值(0-11),没有按键按下返回12
  139. {
  140.         uchar i,row,temp;
  141.         uchar key=12;//按键号,初值设置为12,目的是:没有按键按下时返回12;
  142.                   //若不设初值(默认值为0),没有按键按下时,将返回0,会误认为0被按下  
  143.         row=0xef; //从第一行开始      
  144.         for(i=0;i<4;i++)
  145.         {
  146.                 P1=0xff;  
  147.                 P1=row;        //第i行信号,对应行为低,其他全为高
  148.                 row=_crol_(row,1);           //生成下一行信号
  149.                 temp=P1; //读入扫描信号
  150.                 temp=temp&0x07; //屏蔽高5位信号,只保留低3位列信号
  151.                 if(temp!=0x07)//有按键被按下,因为第i行某列有按键按下,则低3位中有一位为低  
  152.                  {  
  153.                         delay_ms(20);  //延时去抖
  154.                         temp=P1;  
  155.                         temp=temp&0x07;  
  156.                         if(temp!=0x07)   //再次确认有按键被按下
  157.                           {  
  158.                         switch(temp)  //根据低3位列信号,判断哪个按键被按下
  159.                     {  
  160.                             case 0x06:key=0+3*i;break; //第i行第1列按键被按下
  161.                         case 0x05:key=1+3*i;break; //第i行第2列按键被按下  
  162.                         case 0x03:key=2+3*i;break; //第i行第3列按键被按下                                    
  163.                     }
  164.                                 
  165.                                 do
  166.                                 {
  167.                                         temp=P1;              //再次扫描按键
  168.                                           temp=temp&0x07;  
  169.                                   }while(temp!=0x07); //等待按键释放   
  170.                           }  
  171.              }
  172.         }  
  173.         return(key);//扫面结束,返回按键值
  174. }        
  175. uchar RdACAdr()//读当前光标地址
  176. {   
  177.         uchar result;
  178.         P2 = 0xff;        //读地址前先置高电平,防止误判
  179.         RS = 0;
  180.         delay_ms(5);
  181.     RW = 1;
  182.         LCDEN = 1;
  183.         delay_ms(5);
  184.         result=P2&0x7f; //去掉最高位忙闲标记,只保留低7位地址值
  185.         LCDEN = 0;
  186.         return result;                     
  187. }

  188. uchar DectectBusyBit(void)//状态判断函数(忙/闲?)
  189. {   
  190.         bit result;
  191.         P2 = 0xff;        //读状态前先置高电平,防止误判
  192.         RS = 0;
  193.         delay_ms(5);
  194.     RW = 1;
  195.         LCDEN = 1;
  196.         delay_ms(5);
  197.         result=BF; //若LCM忙,则反复测试,在此处原地踏步;当LCM闲时,才往下继续
  198.         LCDEN = 0;
  199.         return result;                     
  200. }

  201. void WrComLCD(unsigned char ComVal)//写命令函数
  202. {
  203.         while(DectectBusyBit()==1);         //先检测LCM是否空闲
  204.         RS = 0;
  205.         delay_ms(1);
  206.     RW = 0;
  207.         LCDEN = 1;
  208.         P2 = ComVal;
  209.         delay_ms(1);
  210.         LCDEN = 0;        
  211. }

  212. void WrDatLCD(uchar DatVal)//写数据函数
  213. {
  214.         while(DectectBusyBit()==1);
  215.         RS = 1;
  216.         delay_ms(1);
  217.     RW = 0;
  218.         LCDEN = 1;
  219.         P2 = DatVal;
  220.         delay_ms(1);
  221.         LCDEN = 0;        
  222. }
  223. void WrStrDat(uchar *p)//显示英文字符串(长度不超过32)
  224. {        
  225.         uchar i=0,t;
  226.    
  227.         while(p[i]!='\0')
  228.         {  
  229.                   WrDatLCD(p[i]);
  230.                 i++;
  231.                 delay_ms(5);
  232.         
  233.                 t=RdACAdr();
  234.         if(t==0x10) WrComLCD(0xc0);//读当前坐标,如果第1行写完换行到第2行        
  235.                 if(t==0x50) WrComLCD(0x80);//读当前坐标,如果第2行写完换行到第1行        
  236.         }        
  237. }

  238. void LCD_Init(void)//1602初始化函数
  239. {
  240.         //delay(15);
  241.         WrComLCD(0x38);
  242.         //delay(5);     // 功能设定:16*2行、5*7点阵、8位数据接口
  243.         WrComLCD(0x38);
  244.         //delay(5);
  245.         WrComLCD(0x38);   
  246. //多次重复设定功能指令,因为LCD启动后并不知道使用的是4位数据接口还是8位的,所以开始时总是默认为4位,这样刚开始写入功能设定指令时,低4位被忽略,为了可靠,最好多写几遍该指令
  247.         WrComLCD(0x01);    // 清屏
  248.         WrComLCD(0x06);    // 光标自增、屏幕不动  
  249.         delay_ms(1);              // 延时,等待上面的指令生效,下面再显示,防止出现乱码
  250.         WrComLCD(0x0C);    // 开显示、关光标
  251. }

  252. void uart_init(unsigned int bps)
  253. { unsigned char t;
  254.   SCON=0x50;    //工作方式一:8位异步收发,波特率可变,允许接收数据
  255.   PCON=0x00;    //SMOD=0
  256.   TI=0;    //软件清零,表示未发送完成
  257.   EA=1;   //开总中断
  258.   ET1=1;
  259.   ET0=1;   //开T0
  260.   ES=1;   //开串口中断
  261.   TMOD=0x21;   //设置T1定时器8位自动装载模式
  262.   switch(bps)
  263.   { case 1200:t=0xe8;break;
  264.     case 2400:t=0xf4;break;
  265.     case 4800:t=0xfa;break;
  266.     case 9600:t=0xfd;break;
  267.   }
  268.   TH1=t;
  269.   TL1=t;
  270.   TR1=1;   //开启T1
  271. }
  272. void uart_send(unsigned char ch)
  273. {
  274.   ES=0;   //关串口中断
  275.   SBUF=ch;
  276.   while(TI==0);  //等待发送完成
  277.   TI=0;  //清除中断标记
  278.   ES=1;   //开中断
  279. }
  280. unsigned char receive(void)
  281. {        unsigned char dat;
  282.         while(RI==0);   //等待接收完毕
  283.         RI=0;          //将接收中断标志RI清0,为接收下一帧数据做准备
  284.      dat=SBUF;         //将接收缓冲器中的数据存入dat
  285.      return dat;        //将接收到的数据返回
  286. }

  287. void main (void)
  288. {         uchar temp=0xff;
  289.         uchar tt=0xff;
  290.         uchar a,b=0;
  291.         uchar  keypadVal=12;
  292.         //TMOD=0x21;  EA=1; ET0=1;
  293.          //ET1=0;  
  294.         //LEN=1;
  295.         PT0=1;
  296.         //PS=1;
  297.         P0=0xff;
  298.         //LEN=0;
  299.           uart_init(9600);//串口初始化
  300.           LCD_Init();//1602液晶屏初始化
  301.           delay_ms(5);   //延时,等待初始化完成
  302.         WrComLCD(0x80);           //设置显示地址第一行第一位:0X00(0x80+0x00)   
  303.         
  304.           while(1)
  305.     {
  306.                  
  307.                  keypadVal=keypad4_3();
  308.                 if(keypadVal<12)
  309.                 {
  310.                         uart_send(keyval[keypadVal]);
  311.                 }
  312.                 /*if(K1==0)//如按键按下,就发送"MCS-51 Microcomputer"
  313.         {
  314.                         b=0;//计数器清0
  315.                   a=strlen(s);//取数组长度
  316.                   for(;b<a;b++)//循环取数据发送,从0到a
  317.               { SBUF=s[b];//发送"MCS-51 Microcomputer"
  318.                 while(!TI);//没有发送完,就等待.
  319.                 TI=0;//清发送结束标志
  320.                }
  321.            while(!K1);//如果按键没有松开,等待
  322.          }*/
  323.       if(RI||a=='m')//如果接收到数据,就把接收到的数回发给PC
  324.          { uchar pos;
  325.                  
  326.                          RI=0;//清接收标志
  327.                
  328.                         //if(a!='m')
  329.            //{
  330.                                    a=SBUF ;//读串行口数据
  331.                            SBUF=a;//把从PC机发送过来的数据返回给PC机
  332.                         //        if(a=='m')
  333.                         //        play=~play;
  334.                   // }
  335.                            
  336.                         
  337.            while(!TI);//没有发送完等待
  338.            TI=0;//清发送完中断标志
  339.                    if(a>'0'&&a<='8')
  340.                    {
  341.                            if(temp==a)
  342.                                    tt=tt^(_crol_(0x01,a-'0'-1));
  343.                         //P0=0x00;
  344.                         else
  345.                                 tt=tt&(~OpenLight[a-'0'-1]);
  346.                         temp=a;
  347.                         P0=tt;
  348.                    }
  349.                         else
  350.                         {
  351.                                 if(a=='n')
  352.                                 {
  353.                                 //换行
  354.                                          pos=RdACAdr();
  355.                                 if(pos>=0x00&&pos<0x10){ WrComLCD(0xc0);WrStrDat("                ");WrComLCD(0xc0);}//读当前坐标,如果第1行写完换行到第2行        
  356.                                         else if(pos>=0x40&&pos<0x50) { WrComLCD(0x80);WrStrDat("                ");WrComLCD(0x80);}//读当前坐标,如果第2行写完换行到第1行        
  357.                                 }
  358.                                 else if(a=='c')
  359.                                 {
  360.                                 WrComLCD(0x01);
  361.                                 WrComLCD(0x80);
  362.                                 }
  363.                                 else if(a=='d')
  364.                                 {
  365.                                         //删除
  366.                                         WrComLCD(0x10);
  367.                                         WrDatLCD(' ');
  368.                                         WrComLCD(0x10);
  369.                                 }
  370.                                 else if(a=='m')
  371.                                 {        //ET0=1;
  372.                                         PlayMusic2();
  373.                                        
  374.                                         //if(play)
  375.                                         //Alarm(5);
  376.                                 }                  
  377.                                 else
  378.                                 {
  379.                                     WrDatLCD(a);
  380.                                            //WrComLCD(0x80);
  381.                                            pos=RdACAdr();
  382.                                 if(pos==0x10) WrComLCD(0xc0);//读当前坐标,如果第1行写完换行到第2行        
  383.                                         if(pos==0x50) WrComLCD(0x80);//读当前坐标,如果第2行写完换行到第1行        
  384.                                 }
  385.                                 
  386.                         }
  387.        }
  388.          
  389.   }
  390. }
  391. void T0_int() interrupt 1
  392. {
  393. BEEP=!BEEP;
  394. TH0=T[m][0]; TL0=T[m][1];
  395. }  
复制代码

所有资料51hei提供下载:
PC机控制单片机.zip (259.53 KB, 下载次数: 341)

评分

参与人数 2黑币 +65 收起 理由
cheng95 + 15
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:34643 发表于 2019-2-3 10:23 | 显示全部楼层
下载下来试试。
回复

使用道具 举报

ID:44037 发表于 2019-2-3 16:33 | 显示全部楼层
谢谢分享   学习一下
回复

使用道具 举报

ID:164988 发表于 2019-2-3 16:45 | 显示全部楼层
功能挺强的,下载试试,
回复

使用道具 举报

ID:85865 发表于 2019-2-3 17:53 | 显示全部楼层
感觉还不错
回复

使用道具 举报

ID:71465 发表于 2019-2-4 23:45 | 显示全部楼层
下载看看
回复

使用道具 举报

ID:98767 发表于 2019-2-18 23:55 | 显示全部楼层
下个参考学习下
回复

使用道具 举报

ID:23844 发表于 2019-2-19 08:55 | 显示全部楼层
功能挺强的,下载参考学习
回复

使用道具 举报

ID:427282 发表于 2019-4-13 19:47 | 显示全部楼层
很好的   
回复

使用道具 举报

ID:477360 发表于 2019-4-25 10:51 | 显示全部楼层
必须好评,认真学习一下
回复

使用道具 举报

ID:477360 发表于 2019-4-25 10:52 | 显示全部楼层
不错,必须认真学习一下
回复

使用道具 举报

ID:477360 发表于 2019-4-25 10:53 | 显示全部楼层
看起来不错,好好学习学习
回复

使用道具 举报

ID:519500 发表于 2019-4-25 20:02 | 显示全部楼层
不错,值得借鉴
回复

使用道具 举报

ID:545497 发表于 2019-5-23 12:52 | 显示全部楼层

下载看看
回复

使用道具 举报

ID:453316 发表于 2019-8-9 10:44 | 显示全部楼层
感谢!
回复

使用道具 举报

ID:631877 发表于 2019-10-29 10:07 | 显示全部楼层
请问这个是可以发送命令给单片机吗
回复

使用道具 举报

ID:339320 发表于 2019-11-13 15:31 | 显示全部楼层
正好需要rs232的程序,先下载下来看看。谢谢分享。
回复

使用道具 举报

ID:282431 发表于 2019-11-13 16:33 | 显示全部楼层
功能强大的,下载参考学习一下
回复

使用道具 举报

ID:445115 发表于 2019-11-26 14:23 | 显示全部楼层
感谢,值得参考
回复

使用道具 举报

ID:728490 发表于 2020-4-13 19:48 | 显示全部楼层
学习一下
回复

使用道具 举报

ID:695749 发表于 2020-4-14 21:42 | 显示全部楼层
能否介绍详细一些,让大家愉快学习,学有所获。
回复

使用道具 举报

ID:611724 发表于 2020-4-15 11:33 | 显示全部楼层
谢谢!学习一下
回复

使用道具 举报

ID:501219 发表于 2020-5-8 09:52 | 显示全部楼层
谢谢分享
回复

使用道具 举报

ID:355469 发表于 2020-5-8 10:47 | 显示全部楼层
学习学习。
回复

使用道具 举报

ID:282431 发表于 2020-5-8 10:49 | 显示全部楼层
功能挺强的,谢谢!学习一下
回复

使用道具 举报

ID:470269 发表于 2020-5-24 18:46 | 显示全部楼层
谢谢分享♥
回复

使用道具 举报

ID:759496 发表于 2020-5-24 23:29 | 显示全部楼层

谢谢分享   学习一下
回复

使用道具 举报

ID:758013 发表于 2020-5-28 18:11 | 显示全部楼层
不错 挺好
回复

使用道具 举报

ID:763926 发表于 2020-5-29 03:33 | 显示全部楼层
可以下载
试一下
回复

使用道具 举报

ID:760741 发表于 2020-5-29 17:13 | 显示全部楼层
这真的是非常优秀的资料
回复

使用道具 举报

ID:787162 发表于 2020-6-22 22:31 | 显示全部楼层
正好需要这种资料,谢谢分享
回复

使用道具 举报

ID:788653 发表于 2020-6-24 14:03 | 显示全部楼层
下选择了学习一下
回复

使用道具 举报

ID:787812 发表于 2020-6-25 09:45 | 显示全部楼层
学习一下
回复

使用道具 举报

ID:787812 发表于 2020-6-25 11:16 | 显示全部楼层
下载试试
回复

使用道具 举报

ID:789431 发表于 2020-6-25 12:57 | 显示全部楼层
正好需要这种资料,谢谢分享
回复

使用道具 举报

ID:789431 发表于 2020-6-25 13:03 | 显示全部楼层

正好需要rs232的程序,先下载下来看看。谢谢分享
回复

使用道具 举报

ID:789443 发表于 2020-6-25 13:24 | 显示全部楼层
不错,值得借鉴
回复

使用道具 举报

ID:600952 发表于 2020-6-26 14:17 | 显示全部楼层
请问这个是可以发送命令给单片机吗
正好需要rs232的程序,先下载下来看看。谢谢分享
回复

使用道具 举报

ID:793342 发表于 2020-6-30 07:46 | 显示全部楼层
谢谢分享,想下载学习学习
回复

使用道具 举报

ID:28992 发表于 2021-3-2 08:23 | 显示全部楼层
Great job, thanks for your sharing.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表