单片机使用普通IO口,实现多路带虚拟DMA的虚拟串口,C语言代码

概述

做单片机项目的时候,有时候为了减少成本,又需要用到多个串口,在整个程序影响不大的情况下,可以使用虚拟串口来实现串口数据的收发。

本文的虚拟串口程序是因公司的项目需要而写的,使用的是STM32单片机,亲测可用,记录在这里,方便有需要的人用。

很些串口配置功能还没有实现,因为公司项目急,暂时只做了需要的功能进去。有兴趣的朋友可以自己进行修改。

这个虚拟串口有几个优点:
1、自带虚拟DMA功能,收发数据无需等待,对其他程序的干扰小
2、支持多路虚拟串口配置
3、不定长接收数据,接收完一帧自动装填到接收缓冲区等待读取
4、可以同时接收与发送数据

当然也有一些缺点:
1、使用定时器中断来实现数据收发,程序频繁中断可能对程序有不好的影响
2、高于9600的波特率,需要更频繁的定时器中断来支持,所以如果需要高波特率的串口,这个程序就不太适合了

简单描述一下使用方法

配置虚拟串口参数,具体可配置参数在h文件有定义,有些参数的配置还未实现

uint8_t VrUart_Config_Init(uint8_t Chx,VrUart_Config_typedef Uart);

初始化虚拟串口,会配置引脚,新增虚拟串口回路的话,需要手动修改这个函数

void VrUartInit(void);

自动发送与读取数据,需要把这个函数放在定时中断内,波特率9600的情况下,20us执行一次,波特率4800的情况下,40us执行一次,以此类推

void VrUart_It_Task(void);

读取虚拟串口接收的数据,接收到的数据会自动放在缓冲区内,接收完一帧就可以读取了,VrChx是虚拟串口通道号

uint16_t VrUART_Recv_Data_Stream(uint8_t VrChx, uint8_t* pData);

虚拟串口装填发送队列

uint8_t VrUART_Send_Data_Stream(uint8_t VrChx, uint8_t* pData, uint16_t Size);

H文件,需要根据实际情况配置引脚

#ifndef _VrUART_H
#define _VrUART_H
#include "stm32f0xx.h"
#include "stm32f0xx_gpio.h"
typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口引脚
-----------------------------------------------------------------------------------------------------------*/

#define VrRxPIN_0        GPIO_Pin_3
#define VrRxGPIO_0       GPIOA
#define VrRXRCC_0        RCC_AHBPeriph_GPIOA
#define VrTxPIN_0        GPIO_Pin_2
#define VrTxGPIO_0       GPIOA
#define VrTXRCC_0        RCC_AHBPeriph_GPIOA
/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口数量以及名称
-----------------------------------------------------------------------------------------------------------*/
#define VrUartNum 1
#define UART_TEST 0

/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口IO口操作方式
-----------------------------------------------------------------------------------------------------------*/
static uint8_t Get_VrIORX(uint8_t ret)
{
switch(ret)
{
case 0:
 return GPIO_ReadInputDataBit(VrRxGPIO_0, VrRxPIN_0)	;
default :
 return  0	;
}
}


#define VrIOTX_1(ret) do{\
switch(ret)\
{\
case 0:\
 GPIO_SetBits(VrTxGPIO_0, VrTxPIN_0)	;break;\
default :\
break;\
}\
}while(0)
#define VrIOTX_0(ret) do{\
switch(ret)\
{\
case 0:\
 GPIO_ResetBits(VrTxGPIO_0, VrTxPIN_0)	;break;\
default :\
break;\
}\
}while(0)


typedef struct
{	
	uint8_t RunMode;    //1为半双工 0为全双工   //暂时无法配置
  uint8_t StopBit;    //1为1位,2为2位        //暂时无法配置
  uint8_t ParityBit;	//0为不校验,1为奇校验,2为偶校验
	uint8_t BaudRate;   //0为4800,1为9600       //暂时无法配置  当前为9600
	uint8_t Enable;	                            //暂时无法配置
	uint16_t RxTTL;		
}VrUart_Config_typedef;		


/*---------------------------------------------------------------------------------------------------------
                                     虚拟串口对外接口
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUart_Config_Init(uint8_t Chx,VrUart_Config_typedef Uart);
void VrUartInit(void);
void VrUart_It_Task(void);
uint16_t VrUART_Recv_Data_Stream(uint8_t VrChx, uint8_t* pData);
uint8_t VrUART_Send_Data_Stream(uint8_t VrChx, uint8_t* pData, uint16_t Size);

#endif

C文件

#include "stm32f0xx.h"
#include "stm32f0xx_gpio.h"
#include <string.h>
#include "VrUart.h"

VrUart_Config_typedef VrUart[VrUartNum];

void VrUartInit(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;

	RCC_AHBPeriphClockCmd(VrRXRCC_0, ENABLE);
	RCC_AHBPeriphClockCmd(VrTXRCC_0, ENABLE);
	
	//TX
  GPIO_InitStruct.GPIO_Pin = VrTxPIN_0;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_Speed =GPIO_Speed_Level_3;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; 
  GPIO_Init(VrTxGPIO_0, &GPIO_InitStruct);
	GPIO_SetBits(VrTxGPIO_0, VrTxPIN_0);
	
	//RX
	GPIO_InitStruct.GPIO_Pin = VrRxPIN_0;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Speed =GPIO_Speed_Level_3;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉输入
  GPIO_Init(VrRxGPIO_0, &GPIO_InitStruct);		
		
	VrUart[UART_TEST].BaudRate = 1;
	VrUart[UART_TEST].ParityBit = 0;
	VrUart[UART_TEST].StopBit = 1;
	VrUart[UART_TEST].Enable = 1;
	VrUart[UART_TEST].RxTTL = 100;	
	VrUart[UART_TEST].RunMode = 1;	
}





uint8_t VrUart_Config_Init(uint8_t Chx,VrUart_Config_typedef Uart)
{
	if(Chx >= VrUartNum)
		return 0;
	VrUart[Chx].BaudRate = Uart.BaudRate;
	VrUart[Chx].ParityBit = Uart.ParityBit;
	VrUart[Chx].StopBit = Uart.StopBit;
	VrUart[Chx].Enable = Uart.Enable;
	VrUart[Chx].RxTTL = Uart.RxTTL;	
	VrUart[Chx].RunMode = Uart.RunMode;	
		return 1;
}




#define VRUART_TX_BUFF_SIZE        60       //8bit
#define VRUART_RX_BUFF_SIZE        60       //8bit


static uint8_t  VrUART_Tx_Buf[VrUartNum][VRUART_TX_BUFF_SIZE];					//串口发送缓冲区
static uint8_t  VrUART_Rx_Buf[VrUartNum][VRUART_RX_BUFF_SIZE];					//串口接收缓冲区
static uint16_t VrUART_Tx_Buf_Size[VrUartNum];												//串口要发送的数据数量
static uint16_t VrUART_Rx_Buf_Size[VrUartNum];												//串口接收到的数据数量

/*---------------------------------------------------------------------------------------------------------
                                         校验1byte的数据!
Chx:通道号
* Ptr 数据缓存
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUart_PickUp_Byte_From_RxStream(uint8_t Chx,uint8_t* Ptr)
{
	uint8_t i, tmp, CheckVal=0, Value=0;
	for(i=0; i<8; i++)
	{
		tmp = *Ptr ++;
		Value += tmp << i;
		CheckVal += tmp;
	}
	
	if(VrUart[Chx].ParityBit == 0)
	   return Value;
	
	CheckVal += *Ptr;  //加上校验位
	CheckVal = CheckVal & 0x01;
	
	if(VrUart[Chx].ParityBit == 1)//奇校验
	{
			if(CheckVal == 1)
		    return Value;
    	else
		    return 0xA;			//如果校验失败,为提升V92xx校验的准确性,统一返回0xA;
	}
		
	if(VrUart[Chx].ParityBit == 2)//偶校验
	{
			if(CheckVal == 0)
		    return Value;
    	else
		    return 0xA;			//如果校验失败,为提升V92xx校验的准确性,统一返回0xA;
	}	
	
    return Value;
}

/*---------------------------------------------------------------------------------------------------------
                                         数据接收
-----------------------------------------------------------------------------------------------------------*/
void VrUart_Rx_It_Task(uint8_t Chx)
{
	 uint8_t  i;
	static uint8_t RxIOBuf[VrUartNum][9]; //临时储存1byte的数据(包括校验位)
	static uint8_t RxIOBufPoint[VrUartNum];
	static uint8_t	RxIO_Busy[VrUartNum]={0};	
	static uint8_t  VrUART_Rx_Tmp[VrUartNum][VRUART_TX_BUFF_SIZE];
	static uint8_t  VrUART_Rx_Tmp_Size[VrUartNum]={0};
	static uint8_t RxStep[VrUartNum]={0};
	
	if(	VrUart[Chx].Enable == 0)
		return;	
	
	if(Chx >= VrUartNum)
		return;
	
	
	#ifdef HALF_DUPLEX
	 if(VrUART_Tx_Buf_Size > 0)
		 return ;
	#endif
	 
	if(RxIO_Busy[Chx] > 0)
		RxIO_Busy[Chx] --;
	
	else if(VrUART_Rx_Tmp_Size[Chx] > 0)	//总线已空闲,且缓冲区有数据,表示当前数据帧接收完成
	{
			if(VrUART_Rx_Buf_Size[Chx] == 0)		/*串口接收缓冲区无数据,可将临时缓冲区数据移至接收缓冲区。
																				如果缓冲区还有数据,则临时缓冲区等待,如果有新的数据进来且临时缓冲区不溢出的情况下则继续接收。*/
			{
				for(i=0; i<VrUART_Rx_Tmp_Size[Chx]; i++)
					VrUART_Rx_Buf[Chx][i] = VrUART_Rx_Tmp[Chx][i];
				VrUART_Rx_Buf_Size[Chx] = VrUART_Rx_Tmp_Size[Chx];
				VrUART_Rx_Tmp_Size[Chx] = 0;
			}
	}
		
	switch(RxStep[Chx])
	{
		//在接收周期开始时,检测到4个低电平,表示起始位开始了。
		case 0:
		case 1:	
		case 2:				
			if(Get_VrIORX(Chx) == 0)
				RxStep[Chx] ++;
			else
				RxStep[Chx] = 0;
			break;
			
		case 3:				
			if(Get_VrIORX(Chx) == 0)
			{
				RxIOBufPoint[Chx] = 0;
				RxStep[Chx] ++;
			}
			else
				RxStep[Chx] = 0;
			break;
			
		case 7:					//5次采样值的中间次直接提取计算。
		case 12:
		case 17:
		case 22:
		case 27:
		case 32:
		case 37:				
		case 42:
		case 47:
		  RxIOBuf[Chx][RxIOBufPoint[Chx]++]  = Get_VrIORX(Chx);  
		  if(RxStep[Chx] == 42 && VrUart[Chx].ParityBit == 0)
		  {
			  RxStep[Chx] +=5; //跳过校验位的判断
		  }
			RxStep[Chx] ++;
			break;
		
		case 51:		//为避免时钟有偏移,此处不是立即从50开始提取停止位的高电平。
		case 52:
			if(Get_VrIORX(Chx) == 1)
				RxStep[Chx] ++;
			else
				RxStep[Chx] = 0;
			break;
		case 53:		//接收到三个停止位后,完成数据位接收。
			if(Get_VrIORX(Chx) == 1)
			{
				if(VrUART_Rx_Tmp_Size[Chx] < VRUART_RX_BUFF_SIZE)  //防止串口异常,连续接收数据,导致溢出。
				{
					VrUART_Rx_Tmp[Chx][VrUART_Rx_Tmp_Size[Chx]] = VrUart_PickUp_Byte_From_RxStream(Chx,&RxIOBuf[Chx][0]);	//提取完整byte数据至临时缓冲区
					VrUART_Rx_Tmp_Size[Chx] ++;																												  //临时缓冲区移位								
				}
				RxIO_Busy[Chx] = VrUart[Chx].RxTTL ;	
			  RxStep[Chx] = 0;
			}
			else
				RxStep[Chx] = 0;
			break;
	  		
			default:
				RxStep[Chx] ++;
				break;	
	}
}


/*---------------------------------------------------------------------------------------------------------
  Function:  VrUART_Recv_Data_Stream                                                                    
                                                                                                        
  Parameter:  VrChx:改参数为预留,方便后期扩展多个虚拟串口时使用,首个虚拟串口时,直接填0 
  Returns:                                                                                               
  Description: 返回接收到的虚拟串口数据的长度。调用后,系统将自动删除缓冲区数据。
-----------------------------------------------------------------------------------------------------------*/
uint16_t VrUART_Recv_Data_Stream(uint8_t VrChx, uint8_t* pData)
{
	uint16_t i, Size=0;
	
	if(VrChx >= VrUartNum)
		return 0;
	
	Size = VrUART_Rx_Buf_Size[VrChx];
	
	if(Size > 0)
	{
		for(i=0; i<Size; i++)
			*(pData++) = VrUART_Rx_Buf[VrChx][i];
		VrUART_Rx_Buf_Size[VrChx] = 0;
	}
	return Size;
}




/*---------------------------------------------------------------------------------------------------------
  Function:    USART_Send_Data_Stream                                                                     
                                                                                                        
  Parameter:   VrChx:改参数为预留,方便后期扩展多个虚拟串口时使用,首个虚拟串口时,直接填0      																					   																					
  Returns:     0:发送正常,1发送失败                                                                                           
  Description: 接收到的数据存入缓冲区USART_Rx_Buf,数据长度USART_Rx_Data_Size,数据处理后应将USART_Rx_Data_Size置0,
							 同时调用USART_Uart_DMA_Rx_Enable()开始接收数据
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUART_Send_Data_Stream(uint8_t VrChx, uint8_t* pData, uint16_t Size)
{
	uint16_t i;
	
	if(VrChx >= VrUartNum)
		return 1;
		
	if(VrUART_Tx_Buf_Size[VrChx] > 0)		//当前缓冲区还有数据,不能填充发送
		return 1;
	
	if(Size > VRUART_TX_BUFF_SIZE)
		return 1;
	
	for(i=0; i < Size; i++)
	{
			VrUART_Tx_Buf[VrChx][i] = *pData; 
			pData++;
	}
  VrUART_Tx_Buf_Size[VrChx] = Size;
	
	return 0;
}



/*---------------------------------------------------------------------------------------------------------
  Function:  VrUart_Tx_It_Task                                                                    
                                                                                                        
  Parameter:  
  Returns:                                                                                               
  Description: 置于中断任务内,用于发送虚拟串口的电平,每100uS运行一次。
-----------------------------------------------------------------------------------------------------------*/
void VrUart_Tx_It_Task(uint8_t VrChx)
{
	static uint8_t Step[VrUartNum] = {0};
	static uint8_t VrUART_Tx_Byte_Offset[VrUartNum] ={0};
	static uint8_t CheckVal[VrUartNum] ;
	
	if(	VrUart[VrChx].Enable == 0)
		return;	
	
	if(VrChx >= VrUartNum)
		return;
	
	switch(Step[VrChx])
	{
		case 0:
			if(VrUART_Tx_Buf_Size[VrChx] > 0)		//发现数据,开始发送
			{
				VrIOTX_0(VrChx);			//发送起始位
				CheckVal[VrChx] = 0;
				Step[VrChx] ++;
			}
			break;
			
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:			
			if(VrUART_Tx_Buf[VrChx][VrUART_Tx_Byte_Offset[VrChx]]	& (0x01 << (Step[VrChx]-1)))		//发送第一位
			{
				VrIOTX_1(VrChx);
				CheckVal[VrChx] ++;
			}
			else
			{
				VrIOTX_0(VrChx);
			}
			Step[VrChx] ++;
			if(VrUart[VrChx].ParityBit == 0 && Step[VrChx] == 9)  //跳过校验位
			{
				Step[VrChx] ++;
			}
			break;
			
		case 9:
			if(VrUart[VrChx].ParityBit == 1)
			{
				if(CheckVal[VrChx]%2 == 1)
				  VrIOTX_0(VrChx);		
			  else
				  VrIOTX_1(VrChx);
			}
			else if(VrUart[VrChx].ParityBit == 2)
			{
				if(CheckVal[VrChx]%2 == 0)
				  VrIOTX_0(VrChx);		
			  else
				  VrIOTX_1(VrChx);
			}			
			Step[VrChx] ++;
			break;
		case 10:
			VrIOTX_1(VrChx);		//发送停止位
			if((VrUART_Tx_Byte_Offset[VrChx]+1) >= VrUART_Tx_Buf_Size[VrChx])		// 数据帧发送完成了
			{
				VrUART_Tx_Buf_Size[VrChx] = 0;
				VrUART_Tx_Byte_Offset[VrChx] = 0;
			}
			else
				VrUART_Tx_Byte_Offset[VrChx] ++;
			Step[VrChx] = 0;
		 break;
		default:
			Step[VrChx] = 0;
		  break;
	}
}




/*---------------------------------------------------------------------------------------------------------
  Function:  VrUart_It_Task                                                                    
                                                                                                        
  Parameter:  
  Returns:                                                                                               
  Description: 置于中断任务内,用于发送和接收虚拟串口的电平,每20uS运行一次。
-----------------------------------------------------------------------------------------------------------*/
void VrUart_It_Task(void)
{
	static uint8_t TxCnt;
	uint8_t Chx;
	//每5个任务周期执行一次发送bit任务
	if(TxCnt >= 4)
	{
		TxCnt = 0;
		for( Chx = 0 ;Chx <VrUartNum;Chx++)
		{
			VrUart_Tx_It_Task(Chx);
		}
	}
	else
		TxCnt ++;
	for( Chx = 0 ;Chx <VrUartNum;Chx++)
	{
		VrUart_Rx_It_Task(Chx);
	} 
}

再放一份修改后使用HAL库的源码

亲测可用,增加虚拟串口使能控制功能,不用时可关闭,减少中断函数的运行时间

//配置串口使能
void VrUart_SetEn(uint8_t chx,uint8_t en)
以下是H文件
#ifndef _VrUART_H
#define _VrUART_H
#include "main.h"
#include "stm32f0xx_hal_gpio.h"

typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口引脚
-----------------------------------------------------------------------------------------------------------*/
//#define V9260_TX_Pin GPIO_PIN_4
//#define V9260_TX_GPIO_Port GPIOA
//#define V9260_RX_Pin GPIO_PIN_5
//#define V9260_RX_GPIO_Port GPIOA
#define VrRxPIN_0        V9260_TX_Pin
#define VrRxGPIO_0       V9260_TX_GPIO_Port
#define VrRXRCC_0        RCC_AHBPeriph_GPIOA
#define VrTxPIN_0        V9260_RX_Pin 
#define VrTxGPIO_0       V9260_RX_GPIO_Port
#define VrTXRCC_0        RCC_AHBPeriph_GPIOA
/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口数量以及名称
-----------------------------------------------------------------------------------------------------------*/
#define VrUartNum 1
#define UART_92XX 0

//#define VrIORX			   HAL_GPIO_ReadPin(GPIOA, V9260_RX_Pin)	
//#define VrIOTX_1		   HAL_GPIO_WritePin(GPIOA,V9260_TX_Pin,GPIO_PIN_SET)
//#define VrIOTX_0   	   HAL_GPIO_WritePin(GPIOA,V9260_TX_Pin,GPIO_PIN_RESET)

/*---------------------------------------------------------------------------------------------------------
                                          配置虚拟串口IO口操作方式
-----------------------------------------------------------------------------------------------------------*/
static uint8_t Get_VrIORX(uint8_t ret)
{
switch(ret)
{
case 0:
 return HAL_GPIO_ReadPin(VrRxGPIO_0, VrRxPIN_0)	;
default :
 return  0	;
}
}


#define VrIOTX_1(ret) do{\
switch(ret)\
{\
case 0:\
 HAL_GPIO_WritePin(VrTxGPIO_0, VrTxPIN_0,GPIO_PIN_SET)	;break;\
default :\
break;\
}\
}while(0)

#define VrIOTX_0(ret) do{\
switch(ret)\
{\
case 0:\
 HAL_GPIO_WritePin(VrTxGPIO_0, VrTxPIN_0,GPIO_PIN_RESET);break;\
default :\
break;\
}\
}while(0)


typedef struct
{	
	uint8_t RunMode;    //1为半双工 0为全双工   //暂时无法配置
  uint8_t StopBit;    //1为1位,2为2位        //暂时无法配置
  uint8_t ParityBit;	//0为不校验,1为奇校验,2为偶校验
	uint8_t BaudRate;   //0为4800,1为9600       //暂时无法配置  当前为9600
	uint8_t Enable;	                            //暂时无法配置
	uint16_t RxTTL;		
}VrUart_Config_typedef;		


/*---------------------------------------------------------------------------------------------------------
                                     虚拟串口对外接口
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUart_Config_Init(uint8_t Chx,VrUart_Config_typedef Uart);
void VrUartInit(void);
void VrUart_It_Task(void);
uint16_t VrUART_Recv_Data_Stream(uint8_t VrChx, uint8_t* pData);
uint8_t VrUART_Send_Data_Stream(uint8_t VrChx, uint8_t* pData, uint16_t Size);
void VrUart_SetEn(uint8_t chx,uint8_t en);
#endif



以下是C文件
#include "main.h"
#include <string.h>
#include "VrUart.h"

VrUart_Config_typedef VrUart[VrUartNum];

void VrUartInit(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//TX
  GPIO_InitStruct.Pin = VrTxPIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed =GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(VrTxGPIO_0, &GPIO_InitStruct);
  HAL_GPIO_WritePin(VrTxGPIO_0,VrTxPIN_0,GPIO_PIN_SET);
	
	//RX
  GPIO_InitStruct.Pin = VrRxPIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed =GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(VrRxGPIO_0, &GPIO_InitStruct);
	
	VrUart[UART_92XX].BaudRate = 1;
	VrUart[UART_92XX].ParityBit = 1;
	VrUart[UART_92XX].StopBit = 1;
	VrUart[UART_92XX].Enable = 1;
	VrUart[UART_92XX].RxTTL = 100;	
	VrUart[UART_92XX].RunMode = 1;	
	
}





uint8_t VrUart_Config_Init(uint8_t Chx,VrUart_Config_typedef Uart)
{
	if(Chx >= VrUartNum)
		return 0;
	VrUart[Chx].BaudRate = Uart.BaudRate;
	VrUart[Chx].ParityBit = Uart.ParityBit;
	VrUart[Chx].StopBit = Uart.StopBit;
	VrUart[Chx].Enable = Uart.Enable;
	VrUart[Chx].RxTTL = Uart.RxTTL;	
	VrUart[Chx].RunMode = Uart.RunMode;	
		return 1;
}




#define VRUART_TX_BUFF_SIZE        60       //8bit
#define VRUART_RX_BUFF_SIZE        60       //8bit


static uint8_t  VrUART_Tx_Buf[VrUartNum][VRUART_TX_BUFF_SIZE];					//串口发送缓冲区
static uint8_t  VrUART_Rx_Buf[VrUartNum][VRUART_RX_BUFF_SIZE];					//串口接收缓冲区
static uint16_t VrUART_Tx_Buf_Size[VrUartNum];												//串口要发送的数据数量
static uint16_t VrUART_Rx_Buf_Size[VrUartNum];												//串口接收到的数据数量

/*---------------------------------------------------------------------------------------------------------
                                         校验1byte的数据!
Chx:通道号
* Ptr 数据缓存
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUart_PickUp_Byte_From_RxStream(uint8_t Chx,uint8_t* Ptr)
{
	uint8_t i, tmp, CheckVal=0, Value=0;
	for(i=0; i<8; i++)
	{
		tmp = *Ptr ++;
		Value += tmp << i;
		CheckVal += tmp;
	}
	
	if(VrUart[Chx].ParityBit == 0)
	   return Value;
	
	CheckVal += *Ptr;  //加上校验位
	CheckVal = CheckVal & 0x01;
	
	if(VrUart[Chx].ParityBit == 1)//奇校验
	{
			if(CheckVal == 1)
		    return Value;
    	else
		    return 0xA;			//如果校验失败,为提升V92xx校验的准确性,统一返回0xA;
	}
		
	if(VrUart[Chx].ParityBit == 2)//偶校验
	{
			if(CheckVal == 0)
		    return Value;
    	else
		    return 0xA;			//如果校验失败,为提升V92xx校验的准确性,统一返回0xA;
	}	
	
    return Value;
}

/*---------------------------------------------------------------------------------------------------------
                                         数据接收
-----------------------------------------------------------------------------------------------------------*/
void VrUart_Rx_It_Task(uint8_t Chx)
{
	 uint8_t  i;
	static uint8_t RxIOBuf[VrUartNum][9]; //临时储存1byte的数据(包括校验位)
	static uint8_t RxIOBufPoint[VrUartNum];
	static uint8_t	RxIO_Busy[VrUartNum]={0};	
	static uint8_t  VrUART_Rx_Tmp[VrUartNum][VRUART_TX_BUFF_SIZE];
	static uint8_t  VrUART_Rx_Tmp_Size[VrUartNum]={0};
	static uint8_t RxStep[VrUartNum]={0};
	
	if(	VrUart[Chx].Enable == 0)
		return;	
	
	if(Chx >= VrUartNum)
		return;
	
	
	#ifdef HALF_DUPLEX
	 if(VrUART_Tx_Buf_Size > 0)
		 return ;
	#endif
	 
	if(RxIO_Busy[Chx] > 0)
		RxIO_Busy[Chx] --;
	
	else if(VrUART_Rx_Tmp_Size[Chx] > 0)	//总线已空闲,且缓冲区有数据,表示当前数据帧接收完成
	{
			if(VrUART_Rx_Buf_Size[Chx] == 0)		/*串口接收缓冲区无数据,可将临时缓冲区数据移至接收缓冲区。
																				如果缓冲区还有数据,则临时缓冲区等待,如果有新的数据进来且临时缓冲区不溢出的情况下则继续接收。*/
			{
				for(i=0; i<VrUART_Rx_Tmp_Size[Chx]; i++)
					VrUART_Rx_Buf[Chx][i] = VrUART_Rx_Tmp[Chx][i];
				VrUART_Rx_Buf_Size[Chx] = VrUART_Rx_Tmp_Size[Chx];
				VrUART_Rx_Tmp_Size[Chx] = 0;
			}
	}
		
	switch(RxStep[Chx])
	{
		//在接收周期开始时,检测到4个低电平,表示起始位开始了。
		case 0:
		case 1:	
		case 2:				
			if(Get_VrIORX(Chx) == 0)
				RxStep[Chx] ++;
			else
				RxStep[Chx] = 0;
			break;
			
		case 3:				
			if(Get_VrIORX(Chx) == 0)
			{
				RxIOBufPoint[Chx] = 0;
				RxStep[Chx] ++;
			}
			else
				RxStep[Chx] = 0;
			break;
			
		case 7:					//5次采样值的中间次直接提取计算。
		case 12:
		case 17:
		case 22:
		case 27:
		case 32:
		case 37:				
		case 42:
		case 47:
		  RxIOBuf[Chx][RxIOBufPoint[Chx]++]  = Get_VrIORX(Chx);  
		  if(RxStep[Chx] == 42 && VrUart[Chx].ParityBit == 0)
		  {
			  RxStep[Chx] +=5; //跳过校验位的判断
		  }
			RxStep[Chx] ++;
			break;
		
		case 51:		//为避免时钟有偏移,此处不是立即从50开始提取停止位的高电平。
		case 52:
			if(Get_VrIORX(Chx) == 1)
				RxStep[Chx] ++;
			else
				RxStep[Chx] = 0;
			break;
		case 53:		//接收到三个停止位后,完成数据位接收。
			if(Get_VrIORX(Chx) == 1)
			{
				if(VrUART_Rx_Tmp_Size[Chx] < VRUART_RX_BUFF_SIZE)  //防止串口异常,连续接收数据,导致溢出。
				{
					VrUART_Rx_Tmp[Chx][VrUART_Rx_Tmp_Size[Chx]] = VrUart_PickUp_Byte_From_RxStream(Chx,&RxIOBuf[Chx][0]);	//提取完整byte数据至临时缓冲区
					VrUART_Rx_Tmp_Size[Chx] ++;																												  //临时缓冲区移位								
				}
				RxIO_Busy[Chx] = VrUart[Chx].RxTTL ;	
			  RxStep[Chx] = 0;
			}
			else
				RxStep[Chx] = 0;
			break;
	  		
			default:
				RxStep[Chx] ++;
				break;	
	}
}


/*---------------------------------------------------------------------------------------------------------
  Function:  VrUART_Recv_Data_Stream                                                                    
                                                                                                        
  Parameter:  VrChx:改参数为预留,方便后期扩展多个虚拟串口时使用,首个虚拟串口时,直接填0 
  Returns:                                                                                               
  Description: 返回接收到的虚拟串口数据的长度。调用后,系统将自动删除缓冲区数据。
-----------------------------------------------------------------------------------------------------------*/
uint16_t VrUART_Recv_Data_Stream(uint8_t VrChx, uint8_t* pData)
{
	uint16_t i, Size=0;
	
	if(VrChx >= VrUartNum)
		return 0;
	
	Size = VrUART_Rx_Buf_Size[VrChx];
	
	if(Size > 0)
	{
		for(i=0; i<Size; i++)
			*(pData++) = VrUART_Rx_Buf[VrChx][i];
		VrUART_Rx_Buf_Size[VrChx] = 0;
	}
	return Size;
}




/*---------------------------------------------------------------------------------------------------------
  Function:    USART_Send_Data_Stream                                                                     
                                                                                                        
  Parameter:   VrChx:改参数为预留,方便后期扩展多个虚拟串口时使用,首个虚拟串口时,直接填0      																					   																					
  Returns:     0:发送正常,1发送失败                                                                                           
  Description: 接收到的数据存入缓冲区USART_Rx_Buf,数据长度USART_Rx_Data_Size,数据处理后应将USART_Rx_Data_Size置0,
							 同时调用USART_Uart_DMA_Rx_Enable()开始接收数据
-----------------------------------------------------------------------------------------------------------*/
uint8_t VrUART_Send_Data_Stream(uint8_t VrChx, uint8_t* pData, uint16_t Size)
{
	uint16_t i;
	
	if(VrChx >= VrUartNum)
		return 1;
		
	if(VrUART_Tx_Buf_Size[VrChx] > 0)		//当前缓冲区还有数据,不能填充发送
		return 1;
	
	if(Size > VRUART_TX_BUFF_SIZE)
		return 1;
	
	for(i=0; i < Size; i++)
	{
			VrUART_Tx_Buf[VrChx][i] = *pData; 
			pData++;
	}
  VrUART_Tx_Buf_Size[VrChx] = Size;
	
	return 0;
}



/*---------------------------------------------------------------------------------------------------------
  Function:  VrUart_Tx_It_Task                                                                    
                                                                                                        
  Parameter:  
  Returns:                                                                                               
  Description: 置于中断任务内,用于发送虚拟串口的电平,每100uS运行一次。
-----------------------------------------------------------------------------------------------------------*/
void VrUart_Tx_It_Task(uint8_t VrChx)
{
	static uint8_t Step[VrUartNum] = {0};
	static uint8_t VrUART_Tx_Byte_Offset[VrUartNum] ={0};
	static uint8_t CheckVal[VrUartNum] ;
	
	if(	VrUart[VrChx].Enable == 0)
		return;	
	
	if(VrChx >= VrUartNum)
		return;
	
	switch(Step[VrChx])
	{
		case 0:
			if(VrUART_Tx_Buf_Size[VrChx] > 0)		//发现数据,开始发送
			{
				VrIOTX_0(VrChx);			//发送起始位
				CheckVal[VrChx] = 0;
				Step[VrChx] ++;
			}
			break;
			
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:			
			if(VrUART_Tx_Buf[VrChx][VrUART_Tx_Byte_Offset[VrChx]]	& (0x01 << (Step[VrChx]-1)))		//发送第一位
			{
				VrIOTX_1(VrChx);
				CheckVal[VrChx] ++;
			}
			else
			{
				VrIOTX_0(VrChx);
			}
			Step[VrChx] ++;
			if(VrUart[VrChx].ParityBit == 0 && Step[VrChx] == 9)  //跳过校验位
			{
				Step[VrChx] ++;
			}
			break;
			
		case 9:
			if(VrUart[VrChx].ParityBit == 1)
			{
				if(CheckVal[VrChx]%2 == 1)
				  VrIOTX_0(VrChx);		
			  else
				  VrIOTX_1(VrChx);
			}
			else if(VrUart[VrChx].ParityBit == 2)
			{
				if(CheckVal[VrChx]%2 == 0)
				  VrIOTX_0(VrChx);		
			  else
				  VrIOTX_1(VrChx);
			}			
			Step[VrChx] ++;
			break;
		case 10:
			VrIOTX_1(VrChx);		//发送停止位
			if((VrUART_Tx_Byte_Offset[VrChx]+1) >= VrUART_Tx_Buf_Size[VrChx])		// 数据帧发送完成了
			{
				VrUART_Tx_Buf_Size[VrChx] = 0;
				VrUART_Tx_Byte_Offset[VrChx] = 0;
			}
			else
				VrUART_Tx_Byte_Offset[VrChx] ++;
			Step[VrChx] = 0;
		 break;
		default:
			Step[VrChx] = 0;
		  break;
	}
}




/*---------------------------------------------------------------------------------------------------------
  Function:  VrUart_It_Task                                                                    
                                                                                                        
  Parameter:  
  Returns:                                                                                               
  Description: 置于中断任务内,用于发送和接收虚拟串口的电平,每20uS运行一次。
-----------------------------------------------------------------------------------------------------------*/
void VrUart_It_Task(void)
{
	volatile static uint8_t TxCnt;
	uint8_t Chx;
	//每5个任务周期执行一次发送bit任务
	if(TxCnt >= 4)
	{
		TxCnt = 0;
		for( Chx = 0 ;Chx <VrUartNum;Chx++)
		{
			if(VrUart[Chx].Enable == 0)
				continue;
			
			VrUart_Tx_It_Task(Chx);
		}
	}
	else
		TxCnt ++;
	for( Chx = 0 ;Chx <VrUartNum;Chx++)
	{
		if(VrUart[Chx].Enable == 0)
				continue;
		
		VrUart_Rx_It_Task(Chx);
	} 
}


void VrUart_SetEn(uint8_t chx,uint8_t en)
{
	if(chx >= VrUartNum)
		return;
	
	if(en > 0)
		VrUart[chx].Enable = 1;
	else
		VrUart[chx].Enable = 0;
}




猜你喜欢

转载自blog.csdn.net/tiantangmoke/article/details/103176144