单片机交流
直播中

赵辉

12年用户 850经验值
私信 关注
[问答]

如何利用51单片机的IO口来实现TTL串口模拟?

如何利用51单片机的IO口来实现TTL串口模拟?

回帖(1)

张玉华

2021-10-8 14:53:05
问题说明:

在我们使用51单片机时会发现他就只有一个RX跟TX,但是要实现一个完整的数据采集、上传与管理,一个串口是完全不够的(采集装置一般需要一对串口,而通讯装置也需要一对串口)那么问题就产生了,51单片机只提供一个串口,我们应该怎么办?


  • 本文的模拟TTL串口是利用51单片机的IO口来实现串口模拟。我们知道单片机的所有IO口我们都能控制它的高低电平(1/0),而我们要通过它来模拟TTL串口,我们首先得知道TTL串口的工作原理




    这如上图所示
    起始位:由1个逻辑 0 的数据位表示
    结束位:由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示
    有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8、9位
    校验位:可选,为的是数据的抗干扰性(没用过)
  • 串口通信的波特率,说到底只是每位电平持续的时间,波特率越高,持续的时间越短。如波特率为9600bps,即每一位传送时间为1000ms/9600=0.104ms,即位与位之间的延时为0.104毫秒。
  • 单片机常用11.0592M的的晶振,传感器一般也使用11.0592M的波特率来通讯。用此频率则每个指令周期(取出指令并执行的时间)的时间为(12/11.0592)us,波特率为9600的指令周期s=(1000000/9600)/(12/11.0592)=96,刚好为一整数。如此便于我们用程序指令实现较为准确的延时。
  • 现在进入主题!模拟串口一般有三种方法,我们以11.0592M晶振 9600波特率为例。以下方法皆通过本人测试可用。
    方法一: 延时法
    该方法使用指令周期延时的方法来模拟波特率实现串口通讯,较为简单,使用较为广泛也适用于模拟多串口通讯,但是对采样的精确度要求较高,需要重复调试其指令周期的延时,本人在51实验测试通过,有时传输的数据会出错,不稳定,所以不推荐。
  • 通过上述计算大家知道,串口的每位需延时0.104秒,中间可执行96个指令周期

#include "reg52.h"
#define uchar unsigned char
#define RXD P1_0 //接收脚
#define TXD P1_1 //发送脚
#define WRDYN 44 //写延时
#define RDDYN 43 //读延时
//延时程序*
void Delay2cp(unsigned char i)
{
while(--i); //i=1,两个指令周期。
}
//往串口发送一个字节
void WByte(uchar input)
{
uchar i=8;
TXD=(bit)0; //发送启始位
Delay2cp(39);
//发送8位数据位
while(i--)
{
TXD=(bit)(input&0x01); //先传低位
Delay2cp(36);
input=input>>1;
}
//发送校验位(无)
TXD=(bit)1; //发送结束位
Delay2cp(46);
}
//从串口接收一个字节
uchar RByte(void)
{
uchar Output=0;
uchar i=8;
uchar temp=RDDYN;
//接收8位数据位
Delay2cp(RDDYN*1.5); //此处注意,等过起始位
while(i--)
{
Output >>=1;
if(RXD) Output |=0x80; //先收低位
Delay2cp(35); //(96-26)/2,循环共占用26个指令周期
}
while(--temp) //在指定的时间内搜寻结束位。
{
Delay2cp(1);
if(RXD)break; //收到结束位便退出
}
return Output;
}


方法二: 计数法
该方法通过单片机的时钟计数来产生准确的波特率来实现串口模拟。该方法较为准确,只要波特率正确就可以实现通讯。(只有发送部分)




void S2INI(void)
{
TMOD |=0x02; //计数器0,方式2
TH0=0xA0; //预值为256-96=140,十六进制A0
TL0=TH0;
TR0=1; //开始计数
TF0=0;
}//计数器初始化




void WaitTF0( void )
{
while(!TF0);
TF0=0;
}//查询计数器溢出标志位


//向串口发送一个字节数据,过程同上
void WByte(uchar input)
{
uchar i=8;
TR0=1;
TXD=(bit)0;
WaitTF0();
while(i--)
{
TXD=(bit)(input&0x01);
WaitTF0();
input=input>>1;
}
TXD=(bit)1;
WaitTF0();
TR0=0;
}


方法三:中断法
与计数法大同小异,就不解释了。(只有接收)


#define TM0_FLAG P1_2 //设传输标志位
//计数器及中断初始化
void S2INI(void)
{
TMOD |=0x02; //计数器0,方式2
TH0=0xA0; //预值为256-96=140,十六进制A0
TL0=TH0;
TR0=0; //在发送或接收才开始使用
TF0=0;
ET0=1; //允许定时器0中断
EA=1; //中断允许总开关
}
//接收一个字符
uchar RByte()
{
uchar Output=0;
uchar i=8;
TR0=1; //启动Timer0
TL0=TH0;
WaitTF0(); //等过起始位
//接收8位数据位
while(i--)
{
Output >>=1;
if(RXD) Output |=0x80; //先收低位
WaitTF0(); //位间延时
}
while(!TM0_FLAG) if(RXD) break;
TR0=0; //停止Timer0
return Output;
}
//中断1处理程序
void IntTimer0() interrupt 1
{
TM0_FLAG=1; //设置标志位。
}
//查询传输标志位
void WaitTF0( void )
{
while(!TM0_FLAG) ;
TM0_FLAG=0; //清标志位
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分