[KungFu8位 MCU] 教你如何在51单片机上模拟串口通信!!!

[复制链接]
 楼主| Creas_Tall 发表于 2019-2-26 11:21 | 显示全部楼层 |阅读模式
我们可以不使用单片机本身带有的串口,而自己用程序去模拟一个串口并达到和本身的串口具有同样的功能,
首先,我们需要用到CH340串口模块,大家可以上某宝自行购买。
正面:
1545743-20181125202544341-306527922.jpg
反面:
1545743-20181125202614341-1776304069.jpg

然后我们需要了解一下这串口模块上的引脚:
5V  :与VCC短路为5V TLL输出(电源和信号输出都是5V
VCC:可以与3.3V5V用跳帽连接
3.3V:与VCC短路为3.3V TLL输出(电源和信号输出都是3.3V
TXD:发送数据端口(与单片机上的接收引脚用杜邦线连接)
RXD:接收数据端口(与单片机上的发送引脚用杜邦线连接)
GND:地线


 楼主| Creas_Tall 发表于 2019-2-26 11:23 | 显示全部楼层
因为我是有另外一条串口提供了51单片机的电源,所以就没连接5V和VCC,只与单片机连发送、接收和地端口。

现在,让我们一起了解一下串口通信协议是怎样的
我们可以将该协议分成三部分
一、            起始信号
二、            数据位
三、            结束信号
首先,串口主要有发送和接受两个主要功能,
发送
比如说我们需要发送一个0X48的十六进制数,它的二进制为 01001011
则过程为
一、起始信号
  默认电平为高(1),先将发送端的引脚电平拉低(0)持续104us,来作为一个起始信号
二、八个数据位
  数据的发送,是从最低位开始到最高位一位一位发送的,每一个数据位都需要持续104us,
三、结束信号
  需要将发送端的引脚电平拉高(1)持续104us以上。
 楼主| Creas_Tall 发表于 2019-2-26 11:23 | 显示全部楼层
接收
其实接收也很好理解的,怎样发送就怎样接收,只是看接收的技巧而已
比如说刚刚发送的0X48从主机发送到了单片机上,我们需要接收
则过程为
一、起始信号
  我们需要读取到它的起始信号,就是当它把你的接收端引脚持续拉低104us之后,就可以当作是开始接收了。
二、八个数据位
  因为是八个数据位了,所以所接收的数据也会按照发送一样一个一个发送,我们只要去读取这时候的引脚电平就好。
三、结束信号
  最后也是一样,再需要确认一次结束信号,读取电平是否为高电平,如果全部确认成功后,就可以认为是接收一个有效的数据了。
 楼主| Creas_Tall 发表于 2019-2-26 11:24 | 显示全部楼层
下面为个人图解
775915c74b1667e685.png
理论已经说完,接下来就是怎样用代码实现了
那我们一步一步开始吧
 楼主| Creas_Tall 发表于 2019-2-26 11:24 | 显示全部楼层
第一、创建工程
740575c74b17dc8c78.png
 楼主| Creas_Tall 发表于 2019-2-26 11:25 | 显示全部楼层
第二、创建延时函数
528545c74b190b65e9.png
 楼主| Creas_Tall 发表于 2019-2-26 11:25 | 显示全部楼层
代码:
  1. void Delay_us(int x)      //微秒
  2. {
  3.   while( x-- != 0 )
  4.   {
  5.       _nop_();
  6.      _nop_();
  7.    }
  8. }
  9. void Delay_ms(int x)        //毫秒
  10. {
  11.   int i,j;
  12.   while( x-- !=0 )
  13.   {
  14.       for(j=0;j<10;j++)
  15.       for(i=0;i<85;i++);  
  16.   }
  17. }
 楼主| Creas_Tall 发表于 2019-2-26 11:25 | 显示全部楼层
这里的Delay.c里面有两个延时函数,一个为微秒级,一个为毫秒级。

我们还需要再创建一个Delay.h头文件来向外声明这两个函数。

827995c74b1bdd4253.png
 楼主| Creas_Tall 发表于 2019-2-26 11:30 | 显示全部楼层
头文件的话,我们还需要把它的路径添加到工程里,步骤如图
906755c74b2b977c39.png
我不把这两个函数写进main.c的原因是如果以后写的代码过长了,全部都堆在主函数里的话不方便管理,而这样分开的话可以降低他们的耦合关系,下面也同理了。
 楼主| Creas_Tall 发表于 2019-2-26 11:30 | 显示全部楼层
第三、串口函数
这里的函数创建和上面的步骤是一样的了
253575c74b2e3ec4a7.png
 楼主| Creas_Tall 发表于 2019-2-26 11:31 | 显示全部楼层
发送函数:
代码:
  1. void vFn_Uart_Send(unsigned char uContent)
  2. {
  3.   unsigned int i;
  4.   unsigned char uSendContent =0xff;         //定义一个发送数据的变量
  5.   uSendContent = uContent;                //将要发送的值赋给该变量

  6.   //起始信号
  7.   P05 = 0;                            //P05作为发送端的引脚
  8.   Delay_us(61);                         //延迟104us

  9.   //数据发送
  10.    for(i=0;i<8;i++)
  11.    {  
  12.     P05 = uSendContent & 0x01;               //将所要发送的八位数据的最低位和0x01进行于运算,若最低位为1,相于后的结果为1,则P05电平为高,否则为0
  13.     uSendContent = uSendContent >> 1;         //将第二位数据位移到最低位,重复进行七次移动
  14.     Delay_us(61);  

  15.    }  

  16.    //结束信号     
  17.   P05 = 1;                            //将该引脚电平拉高
  18.   Delay_us(61);                         //延时104us
  19. }

这里的104us延迟函数,我已经用示波器测试过时间的了,是可以直接使用的。

因为烧写时候不同的频率也会造成延时函数改变,所以大家有条件的话,也可以自己具体去测量一下的
 楼主| Creas_Tall 发表于 2019-2-26 11:32 | 显示全部楼层
接收函数:
代码:
  1. void vFn_Uart_Receive()
  2. {
  3.   unsigned int i;
  4.   unsigned char uContent = 0x00;        //定义一个用于接收数据变量

  5.         //起始信号
  6.   if(P21 == 0)                    //判断是否接收到起始信号
  7.    {
  8.      Delay_us(29);                  //延时52微秒
  9.        if(P21 == 0)                 //再判断一次接收端电平是否被拉低
  10.       {         
  11.        //数据接收     
  12.          for(i=0;i<8;i++)         
  13.          {            
  14.              uContent = uContent >> 1;   //将值右移一位
  15.              Delay_us(61);           //延时104微秒
  16.              if(P21 == 1)           //判断是否为高电平
  17.              {            
  18.                uContent |=  0x80;     //若为1则与0x80进行或运算(加上0x80也是一样的道理)
  19.              }           
  20.          }            
  21.      }              
  22.         Delay_us(61);                //延时104微秒

  23.       //结束信号
  24.     if(P21 == 1)                  //判断电平是否被拉高
  25.     {                       
  26.       vFn_Uart_Send(uContent);         //将uContent的值用发送函数发送出来
  27.       Delay_ms(1000);         
  28.       }              
  29.   }               
  30. }
 楼主| Creas_Tall 发表于 2019-2-26 11:32 | 显示全部楼层
这里讲解一下接收函数,我们知道起始信号拉低后,有104us的延时,延时完后就是八个数据位了,如果我们每一次都等完104us再去读取引脚的电平的话可能会不太稳定,所以我们可以在每个电平中间去读取,这个时候是最稳定的。下面为图解
774655c74b344a0f63.png
 楼主| Creas_Tall 发表于 2019-2-26 11:32 | 显示全部楼层
第四、主函数编写
因为都把函数放到了其他文件中,这样主函数会看的比较简洁,我们也直接调用相关的函数就行了,主函数使用也没有太多的要求。
这里的话是需要每写完一个函数就去测试的,不过我是已经是全部测试通过的了,然后大家就还是写完一个测试一个,因为一次性写完所有函数然后马上通过的机率不大,还是要修改很多东西
336475c74b35b8092b.png
 楼主| Creas_Tall 发表于 2019-2-26 11:33 | 显示全部楼层
然后我用的是下载软件是这个,烧写的时候我们需要将IRC的频率改成12MHz进行烧写
58865c74b371756e4.png
串口助手的话,我用的是SSCOM,另外因为我这个单片机的主芯片是IAP15W4K48S4,里面的GPIO的波特率也设置为了9600,所以我们也需要将其设置为9600的波特率才能看到数据,不然就是一堆乱码了,再然后就是点击HEX显示和HEX发送了。
 楼主| Creas_Tall 发表于 2019-2-26 11:33 | 显示全部楼层
我们来看是已经发送出来了。
525525c74b38db7127.png
 楼主| Creas_Tall 发表于 2019-2-26 11:34 | 显示全部楼层
再就是测试接收了
961455c74b39f32147.png
这里是把接收直接放到while(1)里面跑,大家也可以使用定时器的,用中断去跑的话就更好了,这里只是一个小小的示范。
 楼主| Creas_Tall 发表于 2019-2-26 11:34 | 显示全部楼层
145505c74b3c37d1d1.png
发送了0x52,也接收了0x52,成功了。
这样一来,我们也就完成模拟串口的收发了。
 楼主| Creas_Tall 发表于 2019-2-26 11:35 | 显示全部楼层
最后再教一下如何用模拟串口发送文字吧,非常简单的一个小函数,
代码
  1. void vFn_Send_Word(unsigned char *s)
  2. {
  3.     while(*s)
  4.     {
  5.         vFn_Uart_Send(*s++);
  6.     }
  7. }

然后再在主函数调用就可以实现了。
vcvfvgvb 发表于 2019-3-29 16:26 | 显示全部楼层
模拟串口的话,要控制好时序和波特率
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

181

帖子

1

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