前一阵一直在做单片机的程序,由于串口不够,需要用IO口来模拟出一个串口。经过若干曲折并参考了一些现有的资料,基本上完成了。现在将完整的测试程序,以及其中一些需要总结的部分贴出来。

程序硬件平台:11.0592M晶振,STC单片机(兼容51)

/***************************************************************

*    在单片机上模拟了一个串口,使用P2.1作为发送端

*    把单片机中存放的数据通过P2.1作为串口TXD发送出去

***************************************************************/

#i nclude

#i nclude

#i nclude

typedef unsigned char uchar;

int i;

uchar code info[] =

{

0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55

};

sbit newTXD = P2^1;//模拟串口的发送端设为P2.1

void UartInit()

{SCON  = 0x50;   // SCON: serail mode 1, 8-bit UARTTMOD |= 0x21;   // T0工作在方式1,十六位定时PCON |= 0x80;   // SMOD=1;TH0      = 0xFE;    // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz    TL0   = 0x7F;    // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz

//    TH0      = 0xFD;    // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz//    TL0   = 0x7F;    // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz}

void WaitTF0(void)

{while(!TF0);TF0=0;TH0=0xFE;    // 定时器重装初值 fosc=11.0592MHzTL0=0x7F;    // 定时器重装初值 fosc=11.0592MHz//    TH0      = 0xFD;    // 定时器重装初值 fosc=18.432MHz//    TL0   = 0x7F;    // 定时器重装初值 fosc=18.432MHz}

void WByte(uchar input)

{//发送启始位     uchar j=8;TR0=1;newTXD=(bit)0;WaitTF0();//发送8位数据位     while(j--){newTXD=(bit)(input&0x01);      //先传低位         WaitTF0();input=input>>1;}//发送校验位(无)     //发送结束位     newTXD=(bit)1;WaitTF0();TR0=0;

}

void Sendata()

{for(i=0;i

}

void main()

{UartInit();while(1){Sendata();}

}

##############################################################################

/***************************************************************

*       模拟接收程序,这个程序的作用从模拟串口接收数据,然后将这些数据发送到实际串口

*    在单片机上模拟了一个串口,使用P3.2作为发送和接收端

*    以P3.2模拟串口接收端,从模拟串口接收数据发至串口

***************************************************************/

#i nclude

#i nclude

#i nclude

typedef unsigned char uchar ;

//这里用来切换晶振频率,支持11.0592MHz和18.432MHz//#define F18_432#define F11_0592

uchar tmpbuf2[64]={0};

//用来作为模拟串口接收数据的缓存

struct

{

uchar recv :6 ;//tmpbuf2数组下标,用来将模拟串口接收到的数据存放到tmpbuf2中   uchar send :6 ;//tmpbuf2数组下标,用来将tmpbuf2中的数据发送到串口}tmpbuf2_point={0,0};

sbit newRXD=P3^2 ;//模拟串口的接收端设为P3.2

void UartInit()

{

SCON=0x50 ;// SCON: serail mode 1, 8-bit UARTTMOD|=0x21 ;// TMOD: timer 1, mode 2, 8-bit reload,自动装载预置数(自动将TH1送到TL1);T0工作在方式1,十六位定时PCON|=0x80 ;// SMOD=1;

#ifdef F11_0592

TH1=0xE8 ;// Baud:2400  fosc=11.0592MHz 2400bps为从串口接收数据的速率TL1=0xE8 ;// 计数器初始值,fosc=11.0592MHz 因为TH1一直往TL1送,所以这个初值的意义不大TH0=0xFF ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz    TL0=0xA0 ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz        #endif

#ifdef F18_432

TH1=0xD8 ;// Baud:2400  fosc=18.432MHz 2400bps为从串口接收数据的速率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;使能外部中断0TF0=0 ;

IT0=1 ;// 设置外部中断0为边沿触发方式TR1=1 ;// 启动TIMER1,用于产生波特率}

void WaitTF0(void)

{

while(!TF0);

TF0=0 ;

#ifdef F11_0592

TH0=0xFF ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHzTL0=0xA0 ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz#endif

#ifdef F18_432

TH0=0xFF ;

// 定时器重装初值 fosc=18.432MHzTL0=0x60 ;

// 定时器重装初值 fosc=18.432MHz#endif

}

//接收一个字符uchar RByte()

{

uchar Output=0 ;

uchar i=8 ;

TR0=1 ;//启动Timer0

#ifdef F11_0592

TH0=0xFF ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHzTL0=0xA0 ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz#endif

#ifdef F18_432

TH0=0xFF ;// 定时器重装初值 fosc=18.432MHzTL0=0x60 ;// 定时器重装初值 fosc=18.432MHz#endif

TF0=0 ;

WaitTF0();//等过起始位//接收8位数据位while(i--)

{

Output>>=1 ;

if(newRXD)Output|=0x80 ;//先收低位       WaitTF0();//位间延时}

TR0=0 ;//停止Timer0return Output ;

}

//向COM1发送一个字符void SendChar(uchar byteToSend)

{

SBUF=byteToSend ;

while(!TI);

TI=0 ;

}

void main()

{

UartInit();

while(1)

{if(tmpbuf2_point.recv!=tmpbuf2_point.send)//差值表示模拟串口接收数据缓存中还有多少个字节的数据未被处理(发送至串口)

{

SendChar(tmpbuf2[tmpbuf2_point.send++]);

}

}

}

//外部中断0,说明模拟串口的起始位到来了void Simulated_Serial_Start()interrupt 0

{

EX0=0 ;//屏蔽外部中断0tmpbuf2[tmpbuf2_point.recv++]=RByte();//从模拟串口读取数据,存放到tmpbuf2数组中IE0=0 ;//防止外部中断响应2次,防止外部中断函数执行2次EX0=1 ;//打开外部中断0}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

以上是两个独立的测试程序,分别是模拟串口发送的测试程序和接收的测试程序

上面两个程序在编写过程中参考了这篇文章《“附:51 IO口模拟串口通讯C源程序(定时器计数法)”部分),这种方法在较大程序中,可能会错过起始位(比如起始位到来的时候程序正好在干别的,而没有处于判断起始位到来的状态),或者一直在检测起始位,而没有办法完成其他工作。为了避免这个问题,在本接收程序中采用了外部中断的方法,将外部中断0引脚作为模拟串口的接收端,设IT0=1(将外部中断0设为边缘触发)。这样当起始位(低电平)到来时,就会引发外部中断,然后在外部中断处理函数中接收余下的数据。这种方法可以保证没数据的时候程序该干什么干什么,一旦模拟串口接收端有数据,就可以立即接收到。

2、加入了模拟串口接收缓冲区。在较大程序中,单片机要完成的工作很多,在模拟串口接收到了数据之后立即处理的话,有可能处理不过来造成丢失数据,或者影响程序其他部分执行。本程序中加入了64个字节的缓冲区,从模拟串口接收到的数据先存放在缓冲区中。这样就算程序一时没工夫处理这些数据,腾出手来之后也能在缓冲区中找到它们。

3、《51》文中的WByte函数和RByte函数中都先打开计数器后关闭计数器。如果使用本文的外部中断法来接收数据,并且外部中断处理函数里外都调用了WByte或RByte的话,需要将这两个函数中的TR0=1,TR0=0操作的语句除去,并在UartInit()中加入一句TR0=1;即让TR0始终开着就可以。

由于之前没有意识到这个问题,因此在具体应用时出现了奇怪的问题:表现为中断处理函数执行完毕之后,似乎回不到主程序,程序停在了一个不知道的地方。后来经过排查后找到了问题所在,那个程序的中断处理函数中用了RByte,中断处理函数外用到了WByte,而这两个函数的最后都有TR0=0。这样当中断处理函数执行完毕后,TR0实际上是为0的,返回主程序后(中断前的主程序可能正好处于其他的WByte或RByte执行中),原先以来定时器0溢出改变TF0才能执行下去的WByte函数就无法进行下去,从而导致整个程序停下来不动。(在本文的接收测试程序中不存在这个问题,因为中断处理程序中虽调用了RByte,但中断处理程序外却没有调用RByte或WByte)

下面是修改后的RByte、WByte和WaitTF0函数,仅供参考:/**********************************************

*            定时器0溢出后重装初值

**********************************************/

void WaitTF0(void)

{

TF0=0 ;

#ifdef F11_0592

TH0=0xFF ;// 定时器重装初值 fosc=11.0592MHzTL0=0xA0 ;// 定时器重装初值 fosc=11.0592MHz#endif

#ifdef F18_432

TH0=0xFF ;// 定时器重装初值 fosc=18.432MHzTL0=0x60 ;// 定时器重装初值 fosc=18.432MHz#endif

while(!TF0);

TF0=0 ;

}

/**********************************************

*            从串口B接收一个字符

**********************************************/

uchar RByte()

{

uchar Output=0 ;

uchar i=8 ;

//    TR0=1;                             //启动Timer0/*#ifdef F11_0592TH0      = 0xFF;    // 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHzTL0   = 0xA0;    // 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz#endif#ifdef F18_432TH0      = 0xFF;    // 定时器重装初值 fosc=18.432MHzTL0   = 0x60;    // 定时器重装初值 fosc=18.432MHz#endif

*/

WaitTF0();//等过起始位

//接收8位数据位while(i--)

{

Output>>=1 ;

if(newRXD)Output|=0x80 ;//先收低位        WaitTF0();//位间延时}

//  while(!TF0) if(newRXD) break;    //此句和下一句不能加,如果加上了将导致耗时过长,影响下一个字节的接收//    WaitTF0();                        //等过结束位//    TR0=0;                             //停止Timer0

return Output ;

}

/**********************************************

*            发送一个字节到串口B

**********************************************/

void WByte(uchar input)

{

//发送启始位uchar j=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(!TF0) if(newRXD) break;    //此句和下一句不能加,如果加上了将导致耗时过长,影响下一个字节的接收//    WaitTF0();                        //等过结束位

这两句在《51》文中的程序是存在的,但是使用中断接收法后,加上这两句后出现了问题。表现为接收到的下一个字节的数据不完整或直接接收不到,似乎这两句占用了过多的时间。

看这两句的目的似乎是要延时以跳过结束位,但是我感觉这个结束位可以不用管它,反正结束位是个高电平,不会妨碍下一个字节是否到来的判断(下一个字节的起始位是低电平)。那就由它去吧,没有必要为了它而占用CPU的时间。

在本文的程序中,去掉这两句后程序执行正确,如果其他朋友在使用时真的出现问题,可以试着再把它们加上试一下

串口发送程序linux,单片机IO口模拟串口程序(发送+接收相关推荐

  1. 单片机IO口模拟串口程序(发送+接收

    单片机IO口模拟串口程序(发送+接收)[转] qcmc 发表于 - 2011-6-23 0:42:00 前一阵一直在做单片机的程序,由于串口不够,需要用IO口来模拟出一个串口.经过若干曲折并参考了一些 ...

  2. 20120726-分析解决“STM8L101单片机IO口模拟串口通讯发生的奇怪现象”

    近日因工作关系,需要用STM8L101F3P6这款单片机用IO口模拟串行通讯,波特率2400bps,前辈同事已经写完了程序,我需要拿来研究一下该款MCU的低功耗的情况,而在调试的过程中,发现1个奇怪的 ...

  3. 芯圣SQ013单片机IO口模拟串口 延时法

    因为懒,许久未来更新,不过学习还是没有停止.今天我们介绍用芯圣SQ013模拟串口. 串口通讯概述 我们常用的串口通讯协议 为1个起始位+8个数据位+1位结束位.起始位为低电平,结束位高电平.一般我们常 ...

  4. STM8学习笔记----普通IO口模拟串口功能

    串口在产品应用中很常见,但是单片机的默认带的串口往往比较少,有时候就会出现串口不够用,所以就想着能不能用普通IO口模拟串口来实现串口的功能. 要模拟串口首先要清楚串口数据传输过程中的原理. 常用的串口 ...

  5. 国产单片机IO口模拟IrDA1.0协议

    单片机IO口模拟IrDA1.0协议 IrDA1.0协议是一种利用红外通信的无线传输协议,可以很好的解决一些便携式设备与主机之间通信的问题,具有携带方便,低功耗,成本低,传输可靠等特点,缺点是传输距离较 ...

  6. STM32 IO口模拟串口通讯

    转自:http://ziye334.blog.163.com/blog/static/224306191201452833850647 前阵子,调项目时需要用到低波特率串口通讯(300的波特率),才发 ...

  7. linux下IO口模拟I2C的一些总结

    2019独角兽企业重金招聘Python工程师标准>>> 以前一直在用I2C接口,因为总是有线程的例子就一直没有去深入的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序. ...

  8. 基于s32k146的IO口模拟串口

    zhe最近在搞一个IO口模拟LIN的代码,其中包括IO口模拟UART的部分,就记录一下,希望对像我们这样的初学者能有所帮助. 1. 串口协议 串口的特点:全双工,串行,异步 串口协议(我这里选用最常见 ...

  9. io口模拟串口 1602显示数据位内容[转]

    一个项目的一部分内容,让初学者模拟着做一下,还是很有用处的. 这样的程序可以实现一个51单片机有两个串口 #include<regx52.h> sbit BT_SND =P1^1; sbi ...

最新文章

  1. Http Chunked Transfer Coding
  2. 七夕节,程序员们都怎么哄女朋友开心?
  3. 十八、Matplotlib数据可视化
  4. (11) nginx + keepalived 双机热备
  5. java范例_Java范例集锦(一)
  6. Jdbc创建表 利用循环添加数据 ,更新数据
  7. mysql访问类型最好的_【干货满满】最全的MySQL性能指南(一):选择最佳的数据类型...
  8. JSON中的JSON.parseArray()、JSON.parseObject()、JSON.tojsonString()
  9. Git是目前世界上最先进的分布式版本控制系统(没有之一)。
  10. VJC案例-查找最大值
  11. Revit工作时处理CAD图层的5种方法,快get起来
  12. 图像算术编码 matlab,实验二:算术编码及MATLAB实现.doc
  13. 2022-10-03笔记(内网横向)
  14. 性价比高的国产蓝牙耳机有哪些?盘点几款口碑比较好的国产蓝牙耳机
  15. “存算”协同,让存储发挥极致性能
  16. Problem E. L04-05 计算银行存款余额和利息(单利计算简单问题)
  17. flume高并发优化——(9)配置文件交由zookeeper管理
  18. SpringMVC常用方法大全
  19. 2021-03-10
  20. 高级篇Docker复杂安装详说

热门文章

  1. stm32f405rgt6与as5048a的SPI通信问题
  2. .so文件移动游戏破解范例
  3. Mac 终端——常用命令语
  4. 关于p操作和v操作的理解
  5. Vue 中Lodop 实现批量打印
  6. 建筑智能化资质办理要求
  7. Aptana Studio3汉化方式
  8. python tushare backtrader股票回测双均线策略
  9. 2018-8-10-控件
  10. 汽车插座的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告