51单片机模拟串口的三种方法Word文档格式.docx
《51单片机模拟串口的三种方法Word文档格式.docx》由会员分享,可在线阅读,更多相关《51单片机模拟串口的三种方法Word文档格式.docx(13页珍藏版)》请在冰豆网上搜索。
通过上述计算大家知道,串口的每位需延时秒,中间可执行96个指令周期。
#defineucharunsignedchar
sbitP1_0=0x90;
sbitP1_1=0x91;
sbitP1_2=0x92;
#defineRXDP1_0
#defineTXDP1_1
#defineWRDYN44//写延时
#defineRDDYN43//读延时
//往串口写一个字节
voidWByte(ucharinput)
{
uchari=8;
TXD=(bit)0;
//发送启始
位
Delay2cp(39);
//发送8位数据位
while(i--)
{
TXD=(bit)(input&
0x01);
//先传低位
Delay2cp(36);
input=input>
>
1;
}
//发送校验位(无)
TXD=(bit)1;
//发送结束
Delay2cp(46);
}
//从串口读一个字节
ucharRByte(void)
ucharOutput=0;
uchartemp=RDDYN;
Delay2cp(RDDYN*;
//此处注意,等过起始位
Output>
=1;
if(RXD)Output|=0x80;
//先收低位
Delay2cp(35);
//(96-26)/2,循环共
占用26个指令周期
while(--temp)//在指定的
时间内搜寻结束位。
Delay2cp
(1);
if(RXD)break;
//收到结束位便退出
returnOutput;
//延时程序*
voidDelay2cp(unsignedchari)
while(--i);
//刚好两个
指令周期。
此种方法在接收上存在一定的难度,主要是采样定位存在需较准确,另外还必须知道
每条语句的指令周期数。
此法可能模拟若干个串口,实际中采用它的人也很多,但如果你用Keil
C,本人不建议使用此种方法,上述程序在P89C52、AT89C52、W78E52三种单片机上实验通过。
方法二:
计数法
51的计数器在每指令周期加1,直到溢出,同时硬件置溢出标志位。
这样我们就可以
通过预置初值的方法让机器每96个指令周期产生一次溢出,程序不断的查询溢出标志来决定是否
发送或接收下一位。
//计数器初始化
voidS2INI(void)
TMOD|=0x02;
//计数器0,方式2
TH0=0xA0;
//预值为256-96=140,十六进制A0
TL0=TH0;
TR0=1;
//开始计数
TF0=0;
//发送启始位
WaitTF0();
//发送结束位
TR0=0;
}
//查询计数器溢出标志位
voidWaitTF0(void)
while(!
TF0);
接收的程序,可以参考下一种方法,不再写出。
这种办法个人感觉不错,接收和发送
都很准确,另外不需要计算每条语句的指令周期数。
方法三:
中断法
中断的方法和计数器的方法差不多,只是当计算器溢出时便产生一次中断,用户可以
在中断程序中置标志,程序不断的查询该标志来决定是否发送或接收下一位,当然程序中需对中
断进行初始化,同时编写中断程序。
本程序使用Timer0中断。
#defineTM0_FLAGP1_2//设传输标志位
//计数器及中断初始化
//在发送或
接收才开始使用
ET0=1;
//允许定时
器0中断
EA=1;
//中断允许
总开关
//接收一个字符
ucharRByte()
TR0=1;
//启动Timer0
TL0=TH0;
//等过起始
WaitTF0();
//位间延时
TM0_FLAG)if(RXD)break;
//停止
Timer0
//中断1处理程序
voidIntTimer0()interrupt1
TM0_FLAG=1;
//设置标志位。
//查询传输标志位
while(!
TM0_FLAG);
TM0_FLAG=0;
//清标志位
中断法也是我推荐的方法,和计数法大同小异。
发送程序参考计数法,相信是件很容
易的事。
另外还需注明的是本文所说的串口就是通常的三线制异步通信串口(UART),只用RXD、TXD、
GND。
附:
51IO口模拟串口通讯C源程序(定时器计数法)
#include
sbitBT_SND=P1^0;
sbitBT_REC=P1^1;
/**********************************************
IO口模拟232通讯程序
使用两种方式的C程序占用定时器0
**********************************************/
#defineMODE_QUICK
#defineF_TMF0
#defineTIMER0_ENABLETL0=TH0;
#defineTIMER0_DISABLETR0=0;
sbitACC0=ACC^0;
sbitACC1=ACC^1;
sbitACC2=ACC^2;
sbitACC3=ACC^3;
sbitACC4=ACC^4;
sbitACC5=ACC^5;
sbitACC6=ACC^6;
sbitACC7=ACC^7;
F_TM=1;
//发送一个字符
voidPSendChar(unsignedcharinch)
#ifdefMODE_QUICK
ACC=inch;
F_TM=0;
BT_SND=0;
//startbit
TIMER0_ENABLE;
//启动
F_TM);
BT_SND=ACC0;
//先送出低位
BT_SND=ACC1;
BT_SND=ACC2;
BT_SND=ACC3;
BT_SND=ACC4;
BT_SND=ACC5;
BT_SND=ACC6;
BT_SND=ACC7;
BT_SND=1;
TIMER0_DISABLE;
//停止timer
#else
unsignedcharii;
ii=0;
while(ii<
8)
if(inch&
1)
else
ii++;
inch>
#endif
unsignedcharPGetChar()
//等过起始位
ACC0=BT_REC;
ACC1=BT_REC;
ACC2=BT_REC;
ACC3=BT_REC;
ACC4=BT_REC;
ACC5=BT_REC;
ACC6=BT_REC;
ACC7=BT_REC;
F_TM)
if(BT_REC)
break;
returnACC;
unsignedcharrch,ii;
rch=0;
rch>
rch|=0x80;
returnrch;
//检查是不是有起始位
bitStartBitOn()
return(BT_REC==0);
voidmain()
unsignedchargch;
TMOD=0x22;
/*定时器1为工作模式2(8位自动重装),0为模式2(8位
自动重装)*/
PCON=00;
TR0=0;
//在发送或接收才开始使用
TF0=0;
TH0=(256-96);
//9600bps就是1000000/9600=微秒执行的
timer是
//
*12=96
ET0=1;
EA=1;
PSendChar(0x55);
PSendChar(0xaa);
PSendChar(0x00);
PSendChar(0xff);
while
(1)
if(StartBitOn())
gch=PGetChar();
PSendChar(gch);