概述
做单片机项目的时候,有时候为了减少成本,又需要用到多个串口,在整个程序影响不大的情况下,可以使用虚拟串口来实现串口数据的收发。
本文的虚拟串口程序是因公司的项目需要而写的,使用的是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;
}