软件模拟串行通信——UART协议实现

起因
      双单片机通信,MCU资源受限,无法使用片上UART通道,只可用普通I/O
成果
      使用2个普通I/O端口,通过软件实现UART协议的数据全双工收发,8bits数据位、1600波特率,可移植其它单片机
资源占用
      ·1个定时器
      ·2个普通I/O端口
IDE
      keil V4
硬件
      51最小系统板

一、UART协议

二、思路
      1>数据的发送其实就是控制发送引脚Ptxd的电平随着固定时序变化,那么固定的、周期性的时序需要一个定时器来产生。为了保证定时器周期高精度稳定可控,选用自动重装定时器模式 。为了尽可能的减少资源占用,发送和接收共用一个定时器资源。
      2>程序的设计采用状态机设计模式,这样可以避免独占CPU,并且利于移植到各种状态机系统。状态迁移程序在ISR中实现。  
      3>定时器的中断频率为波特率的3倍[之所以这样做后面介绍接收时详述]。
      4>开启发送之后,ISR中发送相关程序得到执行,每3次中断处理1bit.根据时序,先产生bit0[起始位],然后根据8bit型数据依次在时序线上控制Ptxd的电平高低。数据位发送完毕后,若设置了校验位,则对8bits数据中bit1的数量进行判断,根据奇偶校验对bit1的数量通过控制Prxd的电平进行奇偶补全[发送数据位时对bit1进行计数]。然后根据设置发送对应长度的bit1[停止位]。最后,判断发送数据是否达到发送长度,若发送长度到达,则结束发送程序执行,否则继续循环发送。
      5>开启接收之后,ISR中接收相关程序得到执行,并与发送程序互不干涉。首先每次中断都判断Prxd的电平是否置低[检测起始位],当检测到bit0之后,四个中断后进行数据位最低位的接收。这里之所以选择四个中断的延时,是因为当检测到bit0[起始位]的时刻,不管此时处于实际起始位的哪个时间点位置,四个中断后即下一次判断的位置总会是最接近一位数据中心那个点[一个bit中有三次中断点,有一个最接近中心,越接近中心越能免受发送与接收波特率不同步的影响,具体细节画图可知]。之后的每一位改为每三个中断进行一次判断处理[与波特率同步]。处理到校验位时,如果设置了奇偶校验,则对校验结果输出至设置好的校验标志位中,外围程序可根据此标准位判断是否校验出错。然后判断接收数据的长度是否达到设置长度,若没有,则等待至Prxd变为高电平后继续循环接收。

三、代码[关键部分]
static void Xuart_isr(void) interrupt 3
{	Pts = ~Pts;
	if(++CNTisr == 3)
	{
		CNTisr = 0;
		/***************************SEND***************************/
		if(Fsend == SET)
		{
			switch(SEQsend)
			{
				// 起始位0:1bit
				case 0:
					Ptxd = LOW;
					SEQsend = 1;
					break;
				// 数据位SEND	  
				case 1:
					if((*ptr_send) & (1 << CNTsend_i))
					{
						CNTbit1++;
						Ptxd = HIGH;	
					}
					else
					{
						Ptxd = LOW;
					}
	
					if(CNTsend_i >= 7)
					{
						CNTsend_i = 0;
	
						switch(Xuart_config.bit_parity)
						{
							case NONE:	// 无校验
								SEQsend = 4;
								break;
							case ODD:	// 奇校验
								SEQsend = 2;
								break;
							case EVEN:	// 偶校验
								SEQsend = 3;
								break;
							default:
								SEQsend = 4;
								break;
						}
						
					}
					else{
						CNTsend_i++;
					}
	
					break;
				// 奇校验
				case 2:
					if((CNTbit1 % 2) == 0)	// 偶数个1
					{
						Ptxd = HIGH;	
					}
					else
					{
						Ptxd = LOW;
					}
					CNTbit1 = 0;
					SEQsend = 4;
					break;
				// 偶校验
				case 3:
					if((CNTbit1 % 2) == 0)	// 偶数个1
					{
						Ptxd = LOW;	
					}
					else
					{
						Ptxd = HIGH;
					}
					SEQsend = 4;
					break;
				// 停止位
				case 4:
					CNTbit1 = 0;
					switch(Xuart_config.bit_stop)
					{
						case S_1BIT:
							SEQsend = 5;	
							break;
						case S_2BIT:
							if(++CNTstbit == 2)
							{
								CNTstbit = 0;
								SEQsend = 5;
							}
							break;
						default:
							//SEQsend = 5;
							break;
					}
					Ptxd = HIGH;
					break;
				// 下一帧or结束
				case 5:
					if(CNTsend_j >= len_send - 1)  // 发送完毕处理
					{
						CNTsend_j = 0;
						Fsend_end = SET;
						Fsend = CLR;
						//TR1 = 0;
					}
					else
					{
						CNTsend_j++;
						ptr_send++;
					}
					SEQsend = 0;
					break;
				default:
					SEQsend = 0;
					CNTsend_i = 0;
					CNTsend_j = 0;
					CNTbit1 = 0;
					Fsend_end = SET;
					Fsend = CLR;
					TR1 = 0;
	
	
			}	
		}	
	}
	
	
	/**************************RECEIVE*************************/
	if(Freceive == SET)
	{
		switch(SEQreceive)
		{
			// 起始位bit0
			case 0:
				if(Prxd == LOW)	// 收到起始位bit0
				{
					SEQreceive = 1;
					CNTrec = 0;
					break;
				}
				break;
			// 8bits_bit1数据 
			case 1:
				if(++CNTrec == 4) // 第一次进来时为4个周期,提升容错率
				{				  // 其它时机进来为3个周期,保持同步性
					CNTrec = 1;
					if(Prxd == HIGH)
					{
						CNTbit1_++;
						*ptr_receive |= (U8)(1 << CNTreceive_i);
					}	
					else
					{
						*ptr_receive &= (U8)(~(1 << CNTreceive_i));	
					}

					if(CNTreceive_i >= 7)	// 1帧接收完毕
					{
						CNTreceive_i = 0;
						CNTrec = 0;
						switch(Xuart_config.bit_parity) 
						{
							case NONE:	 // 无校验
								SEQreceive = 4;		   
								break;
							case ODD:	 // 奇校验
								SEQreceive = 2;
								break;
							case EVEN:	 // 偶校验
								SEQreceive = 3;
								break;
							default:
								SEQreceive = 4;		   
								break;	
						}
					}
					else
					{
						CNTreceive_i++;
					}	
				}
				break; 
			// 奇校验
			case 2:
				if(++CNTrec == 3)	// 3个周期进入
				{
					CNTrec = 0;
					if((CNTbit1_ % 2) & (!Prxd))	 // 校验正确
					{
						Freceive_parity = RIGHT;		
					}
					else							 // 校验错误
					{
						Freceive_parity = ERROR;	
					}
					CNTbit1_ = 0;
					SEQreceive = 4;
					break;	
				}
				break;
			// 偶校验
			case 3:
				if(++CNTrec == 3)	// 3个周期进入
				{
					CNTrec = 0;
					if((CNTbit1_ % 2) & (Prxd))	 	// 校验正确
					{
						Freceive_parity = RIGHT;		
					}
					else							 // 校验错误
					{
						Freceive_parity = ERROR;	
					}
					SEQreceive = 4;
					break;	
				}
				break;
			// 帧结束
			case 4:
				if(Prxd == HIGH)
				{
					if(CNTreceive_j >= (len_receive - 1))
					{
						CNTreceive_j = 0;
						Freceive = CLR;
						Freceive_end = SET;
					}
					else
					{
						CNTreceive_j++;
						ptr_receive++;
					}
					SEQreceive = 0;	
					break;
				}	
				break;
			default:
				break;	
		}	
	}
}

四、回环测试
1.示波器


2.串口助手回环测试




  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值