描述:用软件模拟串口实现自收自发(单片机接收到什么数据,也会发回电脑)
本质是用定时器、中断来产生相应的波特率
只要解决以下三个问题
**
1.UART原理
2.理解波特率,用定时器配置波特率
3.没有SBUF寄存器,怎么解决,数据缓冲区**
1.UART原理
2.理解波特率
波特率
波特率=发送二进制数据位的速率
1/波特率=发送一位二进制数据持续的时间
定时器计数时间
计时器计数频率可以选择时钟源/12,且时钟源为12 MHz。那么,定时器0的计数频率为:12MHz / 12 = 1 kHz。
定时器的计数频率确定了每秒计数的次数,因此计数一次的时间可以通过倒数计算得出。
配置定时器与波特率关系
定时器从重装载值到溢出的时间,其实就是1/波特率(发送一位二进制数据持续的时间)
3.解决没有SBUFF寄存器的问题
简而言之就是通过一些特殊的按位与或操作,详见后续代码注释(比如PIN_TXD = TxdBuf & 0x01; 部分)
#include<reg52.h>
sbit PIN_RXD = P1^2;
sbit PIN_TXD = P1^3;
bit RxdEnd = 0; //接收完成标志
bit RxdOrTxd = 0; //RXD = 0,TXD = 1 判断是发送还是接收
bit TxdEnd = 0; //发送完成标志
unsigned char RxdBuf = 0; //数据接收缓冲区,相当于SBUF
unsigned char TxdBuf = 0; //数据发送缓冲区,相当于SBUF
void ConfigUART(unsigned int baud); //配置串口
void StartRXD(); //开始接收
void StartTXD(unsigned char dat); //开始发送
void main()
{
EA = 1; //开总中断
ConfigUART(4800); //波特率4800
while(1)
{
while(PIN_RXD==1); //检测到起始位0进入下一步
StartRXD(); //开始接收
while(!RxdEnd); //RxdEnd==1退出循环 表示接收完毕
StartTXD(RxdBuf);//把单片机接收到的数据 再发送回电脑
while(!TxdEnd);//TxdEnd==1退出循环 表示发送完毕
}
}
void ConfigUART(unsigned int baud)
{
TMOD &= 0xF0; //清除定时器低四位
TMOD |= 0x02; //定时器0, 模式2, 8位自动重装载模式
TH0 = 256 - (12000000/12)/baud; //高位预装载值
}
//从计时到溢出的次数为=(12000000/12/)/baud,一次的时间是12/12000000,1/baud 是发送一位二进制数持续的时间x
//定时器0, 模式2, 8位自动重装载模式
//计数值存在TL0中,自动重装载值存在TH0中
void StartRXD()
{
TL0 = 256 - ((256 - TH0)>>1); //从计数值TL0到溢出的时间是(256 - TH0)>>1
//256 - TH0对应了一个波特周期,右移一位相当于/2,就是半个波特周期
ET0 = 1;//使能T0中断
TR0 = 1;//启动TO
RxdEnd = 0;//清零接收结束标志
RxdOrTxd = 0;//接收模式
}
void StartTXD(unsigned char dat)
{
TxdBuf = dat;
TL0 = TH0;//T0计数初值
ET0 = 1;//使能T0中断
TR0 = 1;//启动TO
PIN_TXD = 0;//发送起始位
TxdEnd = 0;//清零发送结束标志
RxdOrTxd = 1;//发送模式
}
void InterruptTimer0() interrupt 1
{
static unsigned char cnt = 0; // 接收/发送 到第几位计数
if(RxdOrTxd==1) //发送部分
{
cnt++;
if(cnt <= 8)
{
PIN_TXD = TxdBuf & 0x01; //每次发一位,从低位发起
//TxdBuf和0x01(也就是00000001)与的效果是除了最低位其他都清0
//所以就将最低位发送
TxdBuf >>= 1; //右移一位,次低位为低位
}
else if(cnt == 9)
{
PIN_TXD = 1; //发送停止位
}
else
{
cnt = 0; //清除计数
TR0 = 0; //关闭定时器
TxdEnd = 1; //置发送完成标
}
}
else //接收部分
{
if(cnt == 0)
{
if(!PIN_RXD) //检测到接收起始位0
{
RxdBuf = 0;//先把数据缓冲区清0
cnt++;
}
else//如果接收起始位不为0,表示未开始接收
{
TR0 = 0;//关闭定时器
}
}
else if(cnt <= 8)
{
RxdBuf >>= 1; //数据右移一位
if(PIN_RXD) //如果接收到的数据为1,就或上0x80让高位为1
{
RxdBuf |= 0x80; //或0x80只会改变最高位,其他位都不变
}
cnt++;
}
else
{
cnt = 0;
TR0 = 0;
if(PIN_RXD) //检测到接收停止位
{
RxdEnd = 1; //置接收完成标志位
}
}
}
}