关于模拟串口的波特率的分析

时间:2022-01-09 23:32:24

在用单片机开发各种嵌入式应用系统中,由于异步串行通讯连接简单,因而成为经常用到的一种通信模式,很多应用中还要求实现多路异步串行通信。为了提高系统的性能价格比,就要求设计工程师用软件增加实现一路或多路异步串行通信。本文即是对模拟串口的波特率做出的一点分析。
   首先简单的介绍一下串行异步通讯的数据格式定义,发送或接收一个完整的字节信息,必须有“起始位”、“若干数据位”、“奇偶校验位”和“停止位”;定义每 位信息的时间宽度——每秒发送的信息位个数,即为“波特率”。本文附带的模拟串口源程序采用数据帧的格式为 1位起始位(低电平),8位数据位(先低位在高位),1位停止位(高电平),且在线路空闲状态时总是保持为高电平。当在11M晶振时钟频率下,采用波特率 为9600或19200时。该模拟串口都可以无误差的进行传输,具体分析后面进行。
   参照源程序,我们知道,在这个模拟串口设计中,模拟的是单片机串行异步通讯方式1,由P1^0做接收端,P1^1做发送端,通过定时器1定时溢出中断来确 定每位数据的时间,由StartBitOn()函数不断查询接收端的状态,当出现低电平(即起始位)的时候,调用接收程序,接收发送的数据。
针对以下附带的源程序分析知,PGetChar(),PSendChar()都是通过移位方式来接收数据,每接收一位数据,需要定时器溢出产生中断一次, 故要得到一帧的数据,就必须经过10个数据位的时间才能完成,同样的,在模拟串口的发送端,要完整的发送一帧数据也要经过10个数据位的时间。因而在如下 连接时,引出了以下的问题。
当RS232单次发送一个字符时,可以正常接收和发送回RS232。
当RS232连续发送一串字符时,通过模拟串口返回给RS232的字符只有原来的一半。如发送1234567890这样一个字符串时,接收的字符为13579。
那为什么在单独发送一个字符是不会丢失,而连续发送时就只有原来的一半了呢!
____________          ______________________________
|        |        |                      |
|        |        |                      |
| PC      |---------->| PGetChar()              |
|      RS232|        |    |      MCU          |
|        |        |    V                  |
|        |<----------| PSendChar()              |
-------------          -------------------------------
源程序:
/**********************************************
IO 口模拟232串行异步通讯程序

**********************************************/
#i nclude <reg51.h>
sbit BT_SND =P1^1;
sbit BT_REC =P1^0;

#define F_TM F0 //自定义标志位,作为中断标志位
#define TIMER0_ENABLE TL0=TH0; TR0=1;//TR0 = 1,启动T
#define TIMER0_DISABLE TR0=0;

// Acc 累加器做发送的移位寄存器
sbit ACC0 = ACC^0;
sbit ACC1 = ACC^1;
sbit ACC2 = ACC^2;
sbit ACC3 = ACC^3;
sbit ACC4 = ACC^4;
sbit ACC5 = ACC^5;
sbit ACC6 = ACC^6;
sbit ACC7 = ACC^7;

//定时器计数器0的中断
void IntTimer0() interrupt 1
{
     F_TM=1;
}
//发送一个字符
//数据格式一个启动位(0),8数据位,一个停止位(1)
void PSendChar(unsigned char Getch)
{
     ACC=Getch;
     F_TM=0;
     BT_SND=0; //启动位
     TIMER0_ENABLE; //记数器0启动
   while(!F_TM) ;
     BT_SND=ACC0; //先送出低位
     F_TM=0;
   while(!F_TM) ;
     BT_SND=ACC1;
     F_TM=0;
   while(!F_TM) ;
     BT_SND=ACC2;
     F_TM=0;
   while(!F_TM);
     BT_SND=ACC3;
     F_TM=0;
   while(!F_TM);
     BT_SND=ACC4;
     F_TM=0;
   while(!F_TM);
     BT_SND=ACC5;
     F_TM=0;
   while(!F_TM);
     BT_SND=ACC6;
     F_TM=0;
   while(!F_TM);
     BT_SND=ACC7;
     F_TM=0;
   while(!F_TM);
     BT_SND=1;
     F_TM=0;
   while(!F_TM);
   TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
   rch>>=1;
   if(BT_REC)
   {
   rch|=0x80;
   }
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
   if(BT_REC)
   {
     break;
   }
}
TIMER0_DISABLE; //停止timer
return rch;

}
//检查是不是有起始位
bit StartBitOn()
{
   return (BT_REC==0);

}
void main()
{
unsigned char Getch;

TMOD=0x22; /*定时器1为工作模式2(8位自动重装),0为模式2(8位
自动重装) */
PCON=00;
TR0=0; //在发送或接收才开始使用
TF0=0;
TH0=(256-96); //9600bps 就是 1000000/9600=104.167微秒 执行的
//时间是104.167*11.0592/12= 96
TL0=TH0;
ET0=1;//定时器/记数器T0的溢出中断允许位,ET,允 许中断
EA=1;
   while(1)
   {
      
   if(StartBitOn())
   {
     Getch=PGetChar();
     PSendChar(Getch);
   }
}
}
实验环境:
   串口调试助手软件
   AT89S51单片机及相应的硬件设备
   win2000操作系统
原因分析如下:
PC的RS232像单片机发送1234567890字符串时,是连续一次发送的。当StartBitOn()检测到低电平起始位时,运行 Getch=PGetChar()函数,显然上面说过要得到一帧的数据,就必须经过10个数据位的时间,同样运行PSendChar(Getch)函数 时,也必须经过10个数据位的时间,且都是在忽略单片机本身执行指令的时间得到的。因而在当单片机接收了一个数据在接收下一位数据期间,必须至少消耗20 个数据位的时间,等它在开始检测起始位时候,已经传输到第三个字符了,因而才会出现第上面的情况,当发送1234567890,而接收的字符是13579 的原因。也就才会出现了很多工程师朋友们会对用软件实现的UART在可靠性和效率方面持怀疑态度的现象
if(StartBitOn())
     {
         Getch=PGetChar();    //采用移位方式要消耗10个数据位的时间
         PSendChar(Getch);    //采用移位方式要消耗10个数据位的时间
       }

BT_REC 接收到的电平:
第1帧数据(10个数据位)第2帧(10个数据位)第3帧(10个起始位)

起始位1                  起始位2              起始位3
  
   参照上面的数据格式示意图知,当单片机StartBitOn()检测到起始位1并完成相应的接收转发Getch=PGetChar(); PSendChar(Getch),在进行下一次检测时,检测到的下一个起始位就是起始位3了。

   综合上面分析可得出以下结论,在用软件模拟串行通讯时候,是以时间来模拟硬件设备的,用软件实现的UART的效率肯定没有办法和硬件UART比,在上叙连 接时候,实际接收的数据确实只能有原来的一半,因为它是连续进行接收和发送两项工作的,但在实际应用中,可以通过一定的手段,比如先一次接收好PC所发送 过来的所有数据,保存在预先设置的缓冲区里,稍后再去进行发送的工作,这样在接收的时候消耗的时间就只有10个数据