[DemoCode下载] 模拟串口

[复制链接]
1364|9
 楼主| dongnanxibei 发表于 2017-2-22 14:44 | 显示全部楼层 |阅读模式
/***************************************************
*作    者:温子祺

*说    明:模拟串口实验   
***************************************************/

传统的8051系列单片机一般都配备一个串口,而STC89C52RC增强型单片机也不例外,只有一个串口可供使用,这样就出问题了,假如当前单片机系统要求二个串口或多个串口进行同时通信,8051系列单片机只有一个串口可供通信就显得十分尴尬,但是在实际的应用中,有两种方法可以选择。
方法1:使用能够支持多串口通信的单片机,不过通过更换其他单片机来代替8051系列单片机,这样就会直接导致成本的增加,优点就是编程简单,而且通信稳定可靠。
方法2:在IO资源比较充足的情况下,可以通过IO来模拟串口的通信,虽然这样会增加编程的难度,模拟串口的波特率会比真正的串口通信低一个层次,但是唯一优点就是成本上得到控制,而且通过不同的IO组合可以实现更加之多的模拟串口,在实际应用中往往会采用模拟串口的方法来实现多串口通信。
普遍使用串口通信的数据流都是1位起始位、8位数据位、1位停止位的格式的,如表1
1
起始位
8位数据位
停止位
0
Bit0
Bit1
Bit2
Bit3
Bit4
Bit5
Bit6
Bit7
1
要注意的是,起始位作为识别是否有数据到来,停止位标志数据已经发送完毕。起始位固定值为0,停止位固定值为1,那么为什么起始位要是0,停止位要是1呢?这个很好理解,假设停止位固定值为1,为了更加易识别数据的到来,电平的跳变最为简单也最容易识别,那么当有数据来的时候,只要在规定的时间内检测到发送过来的第一位的电平是否0值,就可以确定是否有数据到来;另外停止位为1的作用就是当没有收发数据之后引脚置为高电平起到抗干扰的作用。
在平时使用红外无线收发数据时,一般都采用模拟串口来实现的,但是有个问题要注意,波特率越高,传输距离越近;波特率越低,传输距离越远。对于这些通过模拟串口进行数据传输,波特率适宜为1200b/s来进行数据传输。
   例子:在使用单片机的串口接收数据实验当中,使用串口调试助手发送16字节数据,单片机采用模拟串口的方法将接收到的数据返发到PC机。

 楼主| dongnanxibei 发表于 2017-2-22 14:45 | 显示全部楼层
模拟串口实验代码:
  1. #include "stc.h"

  2. #define RXD P3_0              //宏定义:接收数据的引脚
  3. #define TXD P3_1              //宏定义:发送数据的引脚
  4. #define RECEIVE_MAX_BYTES 16//宏定义:最大接收字节数

  5. #define TIMER_ENABLE()  {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
  6. #define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
  7. #define TIMER_WAIT()    {while(!fTimeouts);fTimeouts=0;}//等待T/C超时


  8. unsigned char fTimeouts=0;//T/C超时溢出标志位
  9. unsigned char RecvBuf[16];//接收数据缓冲区
  10. unsigned char RecvCount=0;//接收数据计数器


  11. /****************************************
  12. *函数名称:SendByte
  13. *输    入:byte 要发送的字节
  14. *输    出:无
  15. *功    能:串口发送单个字节
  16. ******************************************/
  17. void SendByte(unsigned char b)
  18. {
  19.      unsigned char i=8;
  20.      
  21.      TXD=0;

  22.      TIMER_ENABLE();
  23.      TIMER_WAIT();
  24.      

  25.      while(i--)
  26.      {
  27.         if(b&1)TXD=1;
  28.         else    TXD=0;

  29.         TIMER_WAIT();
  30.         
  31.         b>>=1;

  32.      }

  33.      
  34.      TXD=1;

  35.      TIMER_WAIT();
  36.      TIMER_DISABLE();
  37. }
  38. /****************************************
  39. *函数名称:RecvByte
  40. *输    入:无
  41. *输    出:单个字节
  42. *功    能:串口 接收单个字节
  43. ******************************************/
  44. unsigned char RecvByte(void)
  45. {
  46.      unsigned char i;
  47.      unsigned char b=0;
  48.      
  49.      TIMER_ENABLE();
  50.      TIMER_WAIT();

  51.      for(i=0;i<8;i++)
  52.      {
  53.          if(RXD)b|=(1<<i);

  54.          TIMER_WAIT();
  55.      }

  56.      TIMER_WAIT();  //等待结束位
  57.       TIMER_DISABLE();
  58.   
  59.      return b;

  60. }
  61. /****************************************
  62. *函数名称:PrintfStr
  63. *输    入:pstr 字符串
  64. *输    出:无
  65. *功    能:串口 打印字符串
  66. ******************************************/
  67. void PrintfStr(char * pstr)
  68. {
  69.       while(pstr && *pstr)
  70.      {
  71.            SendByte(*pstr++);
  72.      }
  73. }
  74. /****************************************
  75. *函数名称:TimerInit
  76. *输    入:无
  77. *输    出:无
  78. *功    能:T/C初始化
  79. ******************************************/
  80. void TimerInit(void)
  81. {
  82.      TMOD=0x02;
  83.      TR0=0;
  84.      TF0=0;
  85.      TH0=(256-99);
  86.      TL0=TH0;
  87.      ET0=1;
  88.      EA=1;
  89. }
  90. /****************************************
  91. *函数名称:StartBitCome
  92. *输    入:无
  93. *输    出:0/1
  94. *功    能:是否有起始位到达
  95. ******************************************/
  96. unsigned char StartBitCome(void)
  97. {
  98.          return (RXD==0);
  99. }
  100. /****************************************
  101. *函数名称:main
  102. *输    入:无
  103. *输    出:无
  104. *功    能:函数主体
  105. ******************************************/
  106. void main(void)
  107. {
  108.      unsigned char i;

  109.      TimerInit();

  110.      PrintfStr("Hello 8051\r\n");

  111.      while(1)
  112.      {
  113.         if(StartBitCome())
  114.         {                     
  115.            RecvBuf[RecvCount++]=RecvByte();
  116.            
  117.            if(RecvCount>=RECEIVE_MAX_BYTES)
  118.            {
  119.               RecvCount=0;

  120.               for(i=0;i<RECEIVE_MAX_BYTES;i++)
  121.               {
  122.                   SendByte(RecvBuf[i]);
  123.               }
  124.            }      
  125.         }

  126.      }
  127. }
  128. /****************************************
  129. *函数名称:TimerIRQ
  130. *输    入:无
  131. *输    出:无
  132. *功    能:T/C0中断服务函数
  133. ******************************************/
  134. void Timer0IRQ(void) interrupt 1 using 0
  135. {
  136.      fTimeouts=1;
  137. }
 楼主| dongnanxibei 发表于 2017-2-22 14:46 | 显示全部楼层
代码分析

在模拟串口实验代码中,宏的使用占用了相当的一部分。

#define RXD P3_0          //宏定义:接收数据的引脚

#define TXD P3_1          //宏定义:发送数据的引脚

#define TIMER_ENABLE()  {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C

#define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C

#define TIMER_WAIT()    {while(!fTimeouts);fTimeouts=0;}//等待T/C超时

   

模拟串口接收引脚为P3.0,发送引脚为P3.1。为了达到精确的定时,减少模拟串口时收发数据的累积误差,有必要通过对T/C进行频繁的使能和禁止等操作。例如宏TIMER_ENABLE为使能T/C,宏TIMER_DISABLE禁止T/C,宏TIMER_WAIT等待T/C超时。

模拟串口的工作波特率为9600b/s,在串口收发的数据流当中,每一位的时间为1/9600≈104us,

若单片机工作在12MHz频率下,使用T/C0工作在方式2,那么为了达到104us的定时时间,TH0、TL0的初值为256-104=152,在实际的模拟串口中,往往出现收发数据不正确的现象。原因就在于TH0、TL0的初值,或许很多人会疑惑,按道理来说,计算T/C0的初值是没有错的。对,是没有错,但是在SendByte和Recv的函数当中,执行每一行代码都要消耗一定的时间,这就是所谓的“累积误差”导致收发数据出现问题,因此我们必须通过实际测试得到TH0、TL0的初值,最佳值256-99=157。那么在T/C初始化TimerInit函数中,TH0、TL0的初值不能够按照常规来计算得到,实际初值在正常初值附近,可以通过实际测试得到。

模拟串口主要复杂在模拟串口发送与接收,具体实现函数在SendByte和RecvByte函数,这两个函数必须要遵循“1位起始位、8位数据位、1位停止位”的数据流。

SendByte函数用于模拟串口发送数据,以起始位“0”作为移位传输的起始标志,然后将要发送的自己从低字节到高字节移位传输,最后以停止位“1”作为移位传输的结束标志。

RecvByte函数用于模拟串口接收数据,一旦检测到起始位“0”,就立刻将接收到的每一位移位存储,最后以判断停止位“1”结束当前数据的接收。
 楼主| dongnanxibei 发表于 2017-2-22 14:47 | 显示全部楼层
main函数完成T/C的初始化,在while(1)死循环以检测起始位“0”为目的,当接收到的数据达到宏RECEIVE_MAX_BYTES的个数时,将接收到的数据返发到外设。
zhuomuniao110 发表于 2017-2-22 18:10 | 显示全部楼层
在51的世界里,IO口模拟各种通信协议是必须会的。
xixi2017 发表于 2017-4-20 17:16 | 显示全部楼层
起始位作为识别是否有数据到来,停止位标志数据已经发送完毕
Tiger535 发表于 2017-4-22 19:13 | 显示全部楼层
不错,真需要。这方面的资料。
heisexingqisi 发表于 2017-4-23 15:13 | 显示全部楼层
波特率越低,传输距离越远。波特率低的话,抗干扰能力越强。
643757107 发表于 2017-4-24 11:26 | 显示全部楼层
使用能够支持多串口通信的单片机,不过通过更换其他单片机来代替8051系列单片机,这样就会直接导致成本的增加,优点就是编程简单,而且通信稳定可靠。
捉虫天师 发表于 2017-4-24 22:45 | 显示全部楼层
通信接口的IO模拟关键是时序。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

225

主题

3870

帖子

18

粉丝
快速回复 在线客服 返回列表 返回顶部