通过计算大家知道,串口的每位需延时0.104秒,中间可执行96个指令周期。
#define uchar unsigned char
sbit P1_0 = 0x90
sbit P1_1 = 0x91
sbit P1_2 = 0x92
#define RXD P1_0
#define TXD P1_1
#define WRDYN 44 //写延时
#define RDDYN 43 //读延时
//往串口写一个字节
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 Delay2cp(unsigned char i)
{
while(--i)//刚好两个
指令周期。
}
此种方法在接收上存在一定的难度,主要是采样定位存在需较准确,另外还必须知道
每条语句的指令周期数。
方法二:计数法
51的计数器在每指令周期加1,直到溢出,同时硬件置溢出标志位。这样我们就可以
通过预置初值的方法让机器每96个指令周期产生一次溢出,程序不断的查询溢出标志来决定是否
发送或接收下一位。
//计数器初始化
void S2INI(void)
{
TMOD =0x02//计数器0,方式2
TH0=0xA0//预值为256-96=140,十六进制A0
TL0=TH0
TR0=1//开始计数
TF0=0
}
void WByte(uchar input)
{
//发送启始位
uchar i=8
TR0=1
TXD=(bit)0
WaitTF0()
//发送8位数据位
while(i--)
{
TXD=(bit)(input&0x01)//先传低位
WaitTF0()
input=input>>1
}
//发送校验位(无)
//发送结束位
TXD=(bit)1
WaitTF0()
TR0=0
}
//查询计数器溢出标志位
void WaitTF0( void )
{
while(!TF0)
TF0=0
}
这种方法接收和发送都很准确,另外不需要计算每条语句的指令周期数。
方法三:中断法
中断的方法和计数器的方法差不多,只是当计算器溢出时便产生一次中断,用户可以
在中断程序中置标志,程序不断的查询该标志来决定是否发送或接收下一位,当然程序中需对中
断进行初始化,同时编写中断程序。本程序使用Timer0中断。
#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()//位间延时
}
return Output
}
//中断1处理程序
void IntTimer0() interrupt 1
{
TM0_FLAG=1//设置标志位。
}
//查询传输标志位
void WaitTF0( void )
{
while(!TM0_FLAG)
TM0_FLAG=0//清标志位
}
用普通 I/O 口也可以模拟标准 UART 串行口,进行串行通信。帧
UART 通信规范是以 8 位二进制数为一帧,低位在前,逐位的传输。
为了区分各个帧,在每一帧之前,要有一个 0 作为起始标记,之后,有一个 1,作为结束符。
在结束符之前,还可选发一个“校验位”,但是,目前多数的应用都不选择这个位。
那么,每次的串行通信,就是传送一个字节,加上前后的标记,共 10 位二进制数。
空闲时,发送的都是 1;一旦出现了 0,就说明开始传输数据了。
波特率
串行通信的一个重要指标就是传输速度,就是每秒传送了多少位二进制数。
这个速度称为波特率,单位是 bps,中文就是“位/秒”。
时间设定
当以 9600bps 来传送数据时,每一位数的持续时间是 (1/9600)s,这也就是间隔时间。
如果选用晶振频率是 11059200Hz,一个机器周期T的时间就是 (12/11059200)s
那么,一位数的持续时间 (1/9600)s,是多少个机器周期T呢 ?
这是很容易算的,就是下面的这个算式:
X = (1/9600) / (12/11059200) = 11059200 / 12 / 9600 = 96T
为了精确定时,可以利用定时器来定时,每当 96T 时间到了,就发送出去一位二进制数,这就行了。
实验程序
用 IO 口模拟串口输出的程序如下:
#include<reg52.h>
sbit TXD1 = P2^0//用IO口模拟串口发送端
sbit RXD1 = P2^1//用IO口模拟串口接收端
bit T96 //位变量
//----------------------------------------
void Wait96(void) //延时,控制波特率
{
while(T96) //等待出现0
T96 = 1 //清标志
}
//----------------------------------------
void WByte(char x)//发送一帧数据
{
char i
TL0 = 160 //初值=256-96=160
TXD1 = 0//发送起始位0
TR0 = 1 //启动定时器
Wait96()//等待96T
for (i = 0i <8i++) { //8位数
TXD1 = x &1 //先传低位
x >>= 1
Wait96() //等待96T
}
TXD1 = 1//发送结束位1
Wait96()//等待96T
TR0 = 0 //关闭定时器
}
//----------------------------------------
void main()
{
char i
TMOD = 0x02 //T0定时方式2
TH0 = 160 //初值=256-96=160
IE = 0x82
T96 = 1 //清标志
while(1) {
for (i = 0x41i <0x5bi++) //A~Z
WByte(i)
WByte(0x0D)
WByte(0x0A)
}
}
//----------------------------------------
void inttime0() interrupt 1 //T0中断
{
T96 = 0 //设置标志
}
//----------------------------------------
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)