• 模拟串口


    /***************************************************
     *作    者:温子祺
     *联系方式:wenziqi@hotmail.com
     *说    明:模拟串口实验   
     ***************************************************/

    传统的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机。

    模拟串口实验代码:

     

     

    1 #include "stc.h"
    2
    3  #define RXD P3_0 //宏定义:接收数据的引脚
    4  #define TXD P3_1 //宏定义:发送数据的引脚
    5  #define RECEIVE_MAX_BYTES 16//宏定义:最大接收字节数
    6
    7  #define TIMER_ENABLE() {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
    8  #define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
    9  #define TIMER_WAIT() {while(!fTimeouts);fTimeouts=0;}//等待T/C超时
    10
    11
    12 unsigned char fTimeouts=0;//T/C超时溢出标志位
    13  unsigned char RecvBuf[16];//接收数据缓冲区
    14  unsigned char RecvCount=0;//接收数据计数器
    15  
    16
    17  /****************************************
    18 *函数名称:SendByte
    19 *输 入:byte 要发送的字节
    20 *输 出:无
    21 *功 能:串口发送单个字节
    22 ******************************************/
    23  void SendByte(unsigned char b)
    24 {
    25 unsigned char i=8;
    26
    27 TXD=0;
    28
    29 TIMER_ENABLE();
    30 TIMER_WAIT();
    31
    32
    33 while(i--)
    34 {
    35 if(b&1)TXD=1;
    36 else TXD=0;
    37
    38 TIMER_WAIT();
    39
    40 b>>=1;
    41
    42 }
    43
    44
    45 TXD=1;
    46
    47 TIMER_WAIT();
    48 TIMER_DISABLE();
    49 }
    50  /****************************************
    51 *函数名称:RecvByte
    52 *输 入:无
    53 *输 出:单个字节
    54 *功 能:串口 接收单个字节
    55 ******************************************/
    56 unsigned char RecvByte(void)
    57 {
    58 unsigned char i;
    59 unsigned char b=0;
    60
    61 TIMER_ENABLE();
    62 TIMER_WAIT();
    63
    64 for(i=0;i<8;i++)
    65 {
    66 if(RXD)b|=(1<<i);
    67
    68 TIMER_WAIT();
    69 }
    70
    71 TIMER_WAIT(); //等待结束位
    72   TIMER_DISABLE();
    73
    74 return b;
    75
    76 }
    77  /****************************************
    78 *函数名称:PrintfStr
    79 *输 入:pstr 字符串
    80 *输 出:无
    81 *功 能:串口 打印字符串
    82 ******************************************/
    83  void PrintfStr(char * pstr)
    84 {
    85 while(pstr && *pstr)
    86 {
    87 SendByte(*pstr++);
    88 }
    89 }
    90  /****************************************
    91 *函数名称:TimerInit
    92 *输 入:无
    93 *输 出:无
    94 *功 能:T/C初始化
    95 ******************************************/
    96  void TimerInit(void)
    97 {
    98 TMOD=0x02;
    99 TR0=0;
    100 TF0=0;
    101 TH0=(256-99);
    102 TL0=TH0;
    103 ET0=1;
    104 EA=1;
    105 }
    106  /****************************************
    107 *函数名称:StartBitCome
    108 *输 入:无
    109 *输 出:0/1
    110 *功 能:是否有起始位到达
    111 ******************************************/
    112 unsigned char StartBitCome(void)
    113 {
    114 return (RXD==0);
    115 }
    116  /****************************************
    117 *函数名称:main
    118 *输 入:无
    119 *输 出:无
    120 *功 能:函数主体
    121 ******************************************/
    122  void main(void)
    123 {
    124 unsigned char i;
    125
    126 TimerInit();
    127
    128 PrintfStr("Hello 8051\r\n");
    129
    130 while(1)
    131 {
    132 if(StartBitCome())
    133 {
    134 RecvBuf[RecvCount++]=RecvByte();
    135
    136 if(RecvCount>=RECEIVE_MAX_BYTES)
    137 {
    138 RecvCount=0;
    139
    140 for(i=0;i<RECEIVE_MAX_BYTES;i++)
    141 {
    142 SendByte(RecvBuf[i]);
    143 }
    144 }
    145 }
    146
    147 }
    148 }
    149  /****************************************
    150 *函数名称:TimerIRQ
    151 *输 入:无
    152 *输 出:无
    153 *功 能:T/C0中断服务函数
    154 ******************************************/
    155  void Timer0IRQ(void) interrupt 1 using 0
    156 {
    157 fTimeouts=1;
    158 }
    159  

     代码分析

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

    #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/9600104us

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

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

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

    RecvByte函数用于模拟串口接收数据,一旦检测到起始位“0,就立刻将接收到的每一位移位存储,最后以判断停止位“1结束当前数据的接收。

    main函数完成T/C的初始化,在while(1)死循环以检测起始位“0为目的,当接收到的数据达到宏RECEIVE_MAX_BYTES的个数时,将接收到的数据返发到外设。

  • 相关阅读:
    pom.xml将jar包导入
    获取当前系统日期的前一天日期
    判断socket连接是否失效
    java读取数据,2,2,1方式读取
    笔记
    回调机制
    吧字符串按逗号分割为数组
    时间格式的转变
    java.net.SocketException四大异常解决方案
    log4j
  • 原文地址:https://www.cnblogs.com/wenziqi/p/1769252.html
Copyright © 2020-2023  润新知