单片机IO口模拟串口程序发送+接收.docx
- 文档编号:26226919
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:15
- 大小:43.36KB
单片机IO口模拟串口程序发送+接收.docx
《单片机IO口模拟串口程序发送+接收.docx》由会员分享,可在线阅读,更多相关《单片机IO口模拟串口程序发送+接收.docx(15页珍藏版)》请在冰豆网上搜索。
单片机IO口模拟串口程序发送+接收
前一阵一直在做单片机的程序,由于串口不够,需要用10口来模拟出一个串口。
经过若干曲
折并参考了一些现有的资料,基本上完成了。
现在将完整的测试程序,以及其中一些需要总结的部分贴出来。
程序硬件平台:
11.0592M晶振,STC单片机(兼容51)
/***************************************************************
*在单片机上模拟了一个串口,使用P2.1作为发送端
*把单片机中存放的数据通过P2.1作为串口TXD发送出去
***************************************************************/
#inelude
#include
#include
typedefunsignedcharuchar;
inti;
ucharcodeinfo[]=
{
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x5
5
};
//SCON:
serailmode1,8-bitUART
//T0工作在方式1,十六位定时
//SMOD=1;
0xFE;II定时器0初始值,延时417us,目的是令模拟串口的波特率为
2400bpsfosc=11.0592MHz
定时器0初始值,延时417us,目的是令模拟串口的波特率为
TL0=0x7F;II
2400bpsfosc=11.0592MHz
IITH0=0xFD;II定时器0初始值,延时417us,目的是令模拟串口的波特率
为2400bpsfosc=18.432MHz
IITL0=0x7F;II定时器0初始值,延时417us,目的是令模拟串口的波特率为
2400bpsfosc=18.432MHz
}
voidWaitTF0(void)
{
while(!
TFO);
TFO=O;
THO=OxFE;//定时器重装初值fosc=11.0592MHz
TL0=0x7F;//定时器重装初值fosc=11.0592MHz
//先传低位
//外层循环,遍历数组
voidWByte(ucharinput)
{
//发送启始位
ucharj=8;
TR0=1;
newTXD=(bit)O;
WaitTF0();
//发送8位数据位
while(j--)
{
newTXD=(bit)(input&0x01);
WaitTF0();
input=input>>1;
}
//发送校验位(无)
//发送结束位
newTXD=(bit)1;
WaitTF0();
TR0=0;
}
voidSendata()
{
for(i=0;i { WByte(info[i]); } } voidmain() { UartInit(); while (1) { Sendata(); } } IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa /*************************************************************** *模拟接收程序,这个程序的作用从模拟串口接收数据,然后将这些数据发送到实际串口 *在单片机上模拟了一个串口,使用P3.2作为发送和接收端 *以P3.2模拟串口接收端,从模拟串口接收数据发至串口 ***************************************************************/ #inelude #include #include typedefunsignedcharuchar; //这里用来切换晶振频率,支持11.0592MHz和18.432MHz //#defineF18_432 #defineF11_0592 uchartmpbuf2[64]={0}; //用来作为模拟串口接收数据的缓存 struct { ucharrecv: 6;//tmpbuf2数组下标,用来将模拟串口接收到的数据存放到tmpbuf2中 ucharsend: 6;//tmpbuf2数组下标,用来将tmpbuf2中的数据发送到串口 }tmpbuf2_point={0,0}; sbitnewRXD=P3A2;//模拟串口的接收端设为P3.2 voidUartlnit() { SCON=0x50;//SCON: serailmode1,8-bitUART TMOD|=0x21;//TMOD: timer1,mode2,8-bitreload,自动装载预置数(自动将TH1 送到TL1);T0工作在方式1,十六位定时 PCON|=0x80;//SMOD=1; #ifdefF11_0592 TH1=0xE8;//Baud: 2400fosc=11.0592MHz2400bps为从串口接收数据的速率 TL1=0xE8;//计数器初始值,fosc=11.0592MHz因为TH1一直往TL1送,所以这个初值的意义不大 THO=OxFF;//定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0;//定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz #endif #ifdefF18_432 TH仁0xD8;//Baud: 2400fosc=18.432MHz2400bps为从串口接收数据的速率 TL1=0xD8;//计数器初始值,fosc=18.432MHz因为TH1一直往TL1送,所以这 个初值的意义不大 TH0=0xFF;//定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHz TL0=0x60;//定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHz #endif IE|=0x81;//中断允许总控制位EA=1;使能外部中断0 TF0=0; IT0=1;//设置外部中断0为边沿触发方式 TR1=1;//启动TIMER1,用于产生波特率 } voidWaitTF0(void) { while(! TF0); TF0=0; #ifdefF11_0592 TH0=0xFF;//定时器重装初值模拟串口的波特率为9600bpsfosc=11.0592MHzTL0=0xA0;//定时器重装初值模拟串口的波特率为9600bpsfosc=11.0592MHz#endif #ifdefF18_432 TH0=0xFF; //定时器重装初值fosc=18.432MHz TL0=0x60; //定时器重装初值fosc=18.432MHz #endif } //接收一个字符 ucharRByte() { ucharOutput=0; 模拟串口的波特率为9600bpsfosc=11.0592MHz模拟串口的波特率为9600bpsfosc=11.0592MHz #ifdefF11_0592 THO=OxFF;//定时器重装初值TLO=OxAO;//定时器重装初值#endif #ifdefF18_432 TH0=0xFF;//定时器重装初值fosc=18.432MHz TL0=0x60;//定时器重装初值fosc=18.432MHz#endif TF0=0; WaitTF0();//等过起始位 //接收8位数据位 while(i--) { Output>>=1; if(newRXD)0utput|=0x80;//先收低位 WaitTF0();//位间延时 } TR0=0;//停止Timer0 returnOutput; } //向COM1发送一个字符 voidSendChar(ucharbyteToSend) { SBUF=byteToSend; while(! TI); TI=0; } voidmain() { UartInit(); while (1) { if(tmpbuf2_point.recv! =tmpbuf2_point.send)//差值表示模拟串口接收数据缓存中还 有多少个字节的数据未被处理(发送至串口) { SendChar(tmpbuf2[tmpbuf2_point.send++]); } } } //夕卜部中断0,说明模拟串口的起始位到来了 voidSimulated_Serial_Start()interrupt0 { EX0=0;//屏蔽外部中断0 tmpbuf2[tmpbuf2_point.recv++]=RByte();//从模拟串口读取数据,存放到tmpbuf2数 组中 IE0=0;//防止外部中断响应2次,防止外部中断函数执行2次 EX0=1;//打开外部中断0 } 以上是两个独立的测试程序,分别是模拟串口发送的测试程序和接收的测试程序 上面两个程序在编写过程中参考了这篇文章《51单片机模拟串口的三种方法》(在后文中 简称《51》),但在它的基础上做了一些补充,下面是若干总结的内容: 1、《51》在接收数据的程序中,采用的是循环等待的方法来检测起始位(见《51》的附: 51 10口模拟串口通讯C源程序(定时器计数法)”部分),这种方法在较大程序中,可能会错 过起始位(比如起始位到来的时候程序正好在干别的,而没有处于判断起始位到来的状态), 或者一直在检测起始位,而没有办法完成其他工作。 为了避免这个问题,在本接收程序中采用了外部中断的方法,将外部中断0引脚作为模拟串口的接收端,设IT0=1(将外部中断0设 为边缘触发)。 这样当起始位(低电平)到来时,就会引发外部中断,然后在外部中断处理函数中接收余下的数据。 这种方法可以保证没数据的时候程序该干什么干什么,一旦模拟串口接收端有数据,就可以立即接收到。 2、加入了模拟串口接收缓冲区。 在较大程序中,单片机要完成的工作很多,在模拟串口接收 到了数据之后立即处理的话,有可能处理不过来造成丢失数据,或者影响程序其他部分执行。 本程序中加入了64个字节的缓冲区,从模拟串口接收到的数据先存放在缓冲区中。 这样就算程序一时没工夫处理这些数据,腾出手来之后也能在缓冲区中找到它们。 3、《51》文中的WByte函数和RByte函数中都先打开计数器后关闭计数器。 如果使用本文的 外部中断法来接收数据,并且外部中断处理函数里外都调用了WByte或RByte的话,需要将 这两个函数中的TR0=1,TR0=0操作的语句除去,并在Uartlnit()中加入一句TR0=1;即 让TR0始终开着就可以。 由于之前没有意识到这个问题,因此在具体应用时出现了奇怪的问题: 表现为中断处理函数执行完毕之后,似乎回不到主程序,程序停在了一个不知道的地方。 后来经过排查后找到了问题所在,那个程序的中断处理函数中用了RByte,中断处理函数外用到了WByte,而这两 个函数的最后都有TR0=0。 这样当中断处理函数执行完毕后,TR0实际上是为0的,返回主程 序后(中断前的主程序可能正好处于其他的WByte或RByte执行中),原先以来定时器0溢 出改变TF0才能执行下去的WByte函数就无法进行下去,从而导致整个程序停下来不动。 (在本文的接收测试程序中不存在这个问题,因为中断处理程序中虽调用了RByte,但中断处理 程序外却没有调用RByte或WByte) 下面是修改后的RByte、WByte和WaitTFO函数,仅供参考: ******************************************** 定时器0溢出后重装初值 ********************************************voidWaitTF0(void){ TF0=0; #ifdefF11_0592 TH0=0xFF;//定时器重装初值fosc=11.0592MHz TL0=0xA0;//定时器重装初值fosc=11.0592MHz#endif #ifdefF18_432 fosc=18.432MHz fosc=18.432MHz TH0=0xFF;II定时器重装初值 TL0=0x60;II定时器重装初值 #endif while(! TF0); TF0=0; } I********************************************** *从串口B接收一个字符 **********************************************I ucharRByte() { ucharOutput=0; uchari=8; IITR0=1;II启动Timer0 I* #ifdefF11_0592 TH0=0xFF;II定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0;II定时器重装初值模拟串口的波特率为9600bpsfosc=11.0592MHz #ifdefF18_432 TH0 =0xFF; TL0= 0x60;II #endif II定时器重装初值fosc=18.432MHz 定时器重装初值fosc=18.432MHz #endif */ WaitTFO();//等过起始位 //接收8位数据位 while(i--) { Output>>=1; //先收低位 //此句和下一句不能加,如果加上了将导致耗 //等过结束位 //停止TimerO if(newRXD)Output|=0x80; WaitTFO();〃位间延时 } //while(! TFO)if(newRXD)break; 时过长,影响下一个字节的接收 //WaitTFO(); //TR0=0; returnOutput;} /********************************************** *发送一个字节到串口B **********************************************/ voidWByte(ucharinput) { //发送启始位 ucharj=8; //TR0=1; newTXD=(bit)0; WaitTF0(); //发送8位数据位 while(j--) { newTXD=(bit)(input&0x01);//先传低位 WaitTF0(); input=input>>1; } //发送校验位(无) //发送结束位 newTXD=(bit)1; WaitTF0(); //TR0=0; } 4、在上面的新修改后的RByte()函数中,有被注释掉的如下两句: //while(! TFO)if(newRXD)break;//此句和下一句不能加,如果加上了将导致耗时过 长,影响下一个字节的接收 //WaitTFO();//等过结束位 这两句在《51》文中的程序是存在的,但是使用中断接收法后,加上这两句后出现了问题。 表现为接收到的下一个字节的数据不完整或直接接收不到,似乎这两句占用了过多的时间。 看这两句的目的似乎是要延时以跳过结束位,但是我感觉这个结束位可以不用管它,反正结 束位是个高电平,不会妨碍下一个字节是否到来的判断(下一个字节的起始位是低电平)。 那 就由它去吧,没有必要为了它而占用CPU的时间。 在本文的程序中,去掉这两句后程序执行正确,如果其他朋友在使用时真的出现问题,可以试着再把它们加上试一下。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单片机 IO 模拟 串口 程序 发送 接收