模拟串口LV2,解决硬件串口资源不足问题!!!!

模拟串口通信 2.0 版本!!

我在前面的文章里面有写了 虚拟串口通信,虽然说能用,但是用过的小伙伴都说 “好!”

优缺点:

先说一点,2.0版本并不适用于同硬件串口的所有场合,仅仅针对自己开发的电子垃圾的主从结构,比如自己的两块板子通信,或者硬件串口没得了又想追加串口不得已的情况。

优点:

1.可以设置任意波特率, 并且从机不可以识别任意波特率数据(只要主时钟频率高就行了)。
2.可以实现不定长度数据收发,并且用户能完成收发标志 1.0版本可不行。
3.减少资源消耗,同比1.0版本,在芯片内部外设资源上面有所减少。
4.同比1.0版本 大大提高收发数据稳定性。反正我实验没有丢过数据。

好下面说缺点:

缺点:

1. 2.0版本相对于1.0版本,硬件接线上面多出了1根时钟线(相当于 一个串口收发需要三根线,三根线,三根线,如果有多路虚拟串口,则每一路仅需要追加 2 根线,笑哭!!! )。
2. 因为是任意波特率所以无法和其他硬件串口做对接 如果非要接 那你就吧波特率规定到和硬件一样。
3. 串口通信点对点,懂得都懂。只不过用的IO模拟 ,有些引脚上面的驱动能力要强那么丢丢,一对多可试试。
好!!到这里有人就会问了:三根线,你走SPI啥!再不济你走I2C,I2C还少根线。你三根线还是模拟的数据玩个鸡毛!!
嗯?问的好!我竟无言以对,但是这是全双工通信,而且没有比较严格的主从关系,而且通信只能主机发起,不能从机发起。

好!!那又有人会问:那和SPI 有啥子区别啥,都是三线全双工通信。

好那就看下面:
在这里插入图片描述
这是一个数据收发的时序图,上面可以看到从主模块出来的TX是在时钟的下降沿发出通信起始位,然后在每个下降沿到达时改变信号状态,在时钟上升沿进行数据采集,而RX则是反过来,在上升沿发起通信并且修改电平状态,下降沿采集状态。所以在两数据通信的时候上升和下降沿都有意义。



首先是主控板 :

下面来看配置情况:

这里我定义的三个通用IO口
IO_CLK :时钟输出脚(GPIO)
IO_RX :接受输入脚(GPIO)
IO_TX :数据输出脚(GPIO)
开一个定时器 使能 更新溢出中断(定时时间自己设置一个就行)

在这里插入图片描述
在这里插入图片描述

以上就是基本的软件配置信息,这一块比较简单大家大概配置一下就行了,剩下的就是代码部分了

/* *****************************************************************
*Copyright (C), 2019-2023, TTT
*Filename:          M_File_Oled.c
*Author  :          ZY
*Created Date :     15/02/2023
*Abstract   :       处理全局参数
*History :
*V1.0.0   : Initial (by ZY,15/02/2023)
***************************************************************** */
#include "M_File_IOUart.h"

//定义结构体
struct IOUart  IOUart_h_Struct={
    .IO_UART_RecvStat = COM_STOP_BIT};
/*****************************************
* 函数名: IoUartSendByte
* 功能说明: 模拟串口发送1个字节数据
* 形参:Byte 要发送的字节
* 返回值: 无
******************************************/
void IoUartSendByte(uint8_t Byte)
{
   
	   uint8_t tmp = 0;
		static uint8_t count = 0;
		static uint8_t Old_IO_UART_StartTXFlag  = 0;
		// 开始位
		if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT != Old_IO_UART_StartTXFlag) 
		{
   
			if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT == 1)
			{
   
				IO_UART_TXD(0); //将TXD的引脚的电平置低
				count = 0;
			}
			Old_IO_UART_StartTXFlag = IOUart_h_Struct.IO_UART_StartTXFlag1BIT;
		}
		else 
		{
   
			if(count < 8)	
			{
   				
				tmp = (Byte >> count) & 0x01;
				if(tmp==0)
				{
   
					IO_UART_TXD(0);
				}
				else
				{
   
					IO_UART_TXD(1);
				}
				count++;
			}
			else
			{
   
				IO_UART_TXD(1);//将TXD的引脚的电平拉高
				IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 0;   //结束 发送数据
				Old_IO_UART_StartTXFlag = 0;
				count =0;
			}
		}   
}



/*******************************************************************************
*Name :            IO_UART_SendData
*Syntax :          void  IO_UART_SendData(const char *Data,uint32_t size)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送字符串
********************************************************************************/
void  IO_UART_SendString(const char *String)
{
   
    for(int i=0; i<strlen(String); i++)
    {
   
        IoUartSendByte(String[i]);
    }
}

/*******************************************************************************
*Name :            IO_UART_SendData
*Syntax :          void  IO_UART_SendData(const char *Data,uint32_t size)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送数据
********************************************************************************/
void IO_UART_SendData(const char *Data,uint32_t size)
{
   
	if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 0)
	{
   
		IOUart_h_Struct.IO_Send_Size = size;
		memcpy(IOUart_h_Struct.IO_UART_TXBuff,Data,size);		
		IOUart_h_Struct.IO_UART_StartTXFLAGBuff = 1;
	}
}


/*******************************************************************************
*Name :            IO_UART_ReciveData
*Syntax :          void  IO_UART_ReciveData(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口接收字符串
********************************************************************************/
void IO_UART_ReciveData(void)
{
   
	if(IOUart_h_Struct.IO_UART_StartRxFlag == 1)
	{
   
			IOUart_h_Struct.IO_UART_RecvStat++;
			if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT)
			{
   									
					//接收到完整的1个字节数据
					if(IOUart_h_Struct.IO_UART_RxNum < _IO_RX_LENGHT)
					{
   
						IOUart_h_Struct.IO_UART_RxBuff[IOUart_h_Struct.IO_UART_RxNum++] = IOUart_h_Struct.IO_UART_RecvData;	//存入缓冲区
					}
					else
					{
   
						IOUart_h_Struct.IO_UART_RxNum = 0;										
					}
						IOUart_h_Struct.IO_UART_StartRxFlag = 0;							
						IOUart_h_Struct.IO_UART_RecvData = 0;
						return;
					
			}
			if(IO_UART_RXD)//读取接收引脚的状态
			{
   
					IOUart_h_Struct.IO_UART_RecvData |= (1 << (IOUart_h_Struct.IO_UART_RecvStat - 1));
			}
			else
			{
   
					IOUart_h_Struct.IO_UART_RecvData &= ~(1 << (IOUart_h_Struct.IO_UART_RecvStat - 1));
			}
	}
}



/*******************************************************************************
*Name :            IO_UART_Send_Buff
*Syntax :          void IO_UART_Send_Buff(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送数据处理
********************************************************************************/
void IO_UART_Send_Buff(void)
{
   
	 static uint8_t OLD_IO_UART_StartTXFLAGBuff = 0;
	 static uint8_t OLD_Send_Count = 0;
	 
	 if(OLD_IO_UART_StartTXFLAGBuff!= IOUart_h_Struct.IO_UART_StartTXFLAGBuff)
	 {
   		 
		 if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 1){
   
			  OLD_Send_Count = 0;			  
			 IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 1; //发送数据
		 }		 
		 OLD_IO_UART_StartTXFLAGBuff = IOUart_h_Struct.IO_UART_StartTXFLAGBuff;		
	 }
	 else
	 {
   
		 if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 1)
		 {
   
			 if(OLD_Send_Count<IOUart_h_Struct.IO_Send_Size)
			 {
   
					IoUartSendByte(IOUart_h_Struct.IO_UART_TXBuff[OLD_Send_Count]);
					if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT == 0)
					{
   						
						OLD_Send_Count++;
						if(OLD_Send_Count<IOUart_h_Struct.IO_Send_Size)
						{
   
							 IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 1; //发送数据
						}
					}else{
   }		 
			 }else{
   
				 IOUart_h_Struct.IO_UART_StartTXFLAGBuff = 0;
				 IOUart_h_Struct.IO_Send_Size =0 ;	
			 }
		 }
	 }
}


/*******************************************************************************
*Name :            IO_UART_Time_CountDown_Deal
*Syntax :          void IO_UART_Time_CountDown_Deal(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口倒计时处理
********************************************************************************/
void IO_UART_Time_CountDown_Deal(void)
{
   
	if(IOUart_h_Struct.IO_UART_RxTimeOut>0)
	{
   
		if(IOUart_h_Struct.IO_UART_RxTimeOut == 1)
		{
   
			IOUart_h_Struct.IO_UART_RxTimeOut --;
			IOUart_h_Struct.IO_UART_RxOK = 1;     //一条数据接收完成
			IOUart_h_Struct.IO_UART_RxNum = 0;
			//完整接受到一条数据
			//TsUserM_h_Flag.e_u_UartAppceptFlag[0] = ON;
			return ;
		}
		IOUart_h_Struct.IO_UART_RxTimeOut--;	
	}
	else
	{
   
	}
}
/* *****************************************************************
*Copyright (C), 2019-2023, TTT
*Filename:          M_File_IOUart.h
*Author  :          ZY
*Created Date :     15/02/2023
*Abstract   :       处理全局参数
*History :
*V1.0.0   : Initial (by ZY,15/02/2023)
***************************************************************** */
#ifndef __M_FILE_IOUart_H__
#define __M_FILE_IOUart_H__

//#include "M_File_Flag.h"
/*自己定义导入的.h 文件 ******************************************************/


#define _IO_RX_LENGHT        200       																//接收数据长度
#define _IO_RX_TIMECOUNTDOWN  15																	//接收数据超时倒计时
/*
*@串口接收字节
*/
enum 
{
   
    COM_START_BIT = 0,
    COM_D0_BIT,
    COM_D1_BIT,
    COM_D2_BIT,
    COM_D3_BIT,
    COM_D4_BIT,
    COM_D5_BIT,
    COM_D6_BIT,
    COM_D7_BIT,
    COM_STOP_BIT,
};

struct IOUart{
   
		uint8_t 	IO_UART_RecvData; 							//接收数据
		uint8_t 	IO_UART_RecvStat;  							//接收状态		
		uint16_t    IO_UART_RxNum;								//接收数据长度
		uint8_t 	IO_UART_RxBuff[_IO_RX_LENGHT];				//模拟串口接收数据缓冲区
		uint8_t     IO_UART_TXBuff[_IO_RX_LENGHT];				//模拟串口发送数据缓冲区
	
		uint32_t 	IO_UART_RxTimeOut;            				//模拟串口接收超时计数
		uint8_t 	IO_UART_RxOK;               				//数据接收完成标志
		uint8_t 	IO_UART_StartRxFlag;						//开始接收标志,1开始接收,0不接收			
	
		uint8_t   IO_UART_StartTXFlag1BIT;						//开始发送标志,1开始发送,0不发送
		uint8_t   IO_UART_StartTXFLAGBuff;						//多字节发送
		uint16_t  IO_Send_Size;	
};

/*把下面的引脚重新指定到自己指定的引脚上面 打开宏定义 ******************************************************/
#if 0    
//读取Rx脚
#define IO_UART_RXD  HAL_GPIO_ReadPin(IO_RX_GPIO_Port,IO_RX_Pin)     //模拟串口RX端
//设置Tx脚拉高拉低
#define IO_UART_TXD(n)  if(n) HAL_GPIO_WritePin(IO_TX_GPIO_Port, IO_TX_Pin, GPIO_PIN_SET); \
													else  HAL_GPIO_WritePin(IO_TX_GPIO_Port, IO_TX_Pin, GPIO_PIN_RESET);
//设置时钟脚													
#define IO_UART_CLK(n)  if(n) HAL_GPIO_WritePin(IO_CLK_GPIO_Port, IO_CLK_Pin, GPIO_PIN_SET); \
													else  HAL_GPIO_WritePin(IO_CLK_GPIO_Port, IO_CLK_Pin, GPIO_PIN_RESET);
#endif

extern struct IOUart  IOUart_h_Struct;

extern void IO_UART_Send_Buff(void);
extern void IO_UART_SendData(const char *Data,uint32_t size);
extern void IO_UART_Time_CountDown_Deal(void);
extern void IO_UART_ReciveData(void);

#endif

定时器中断回调里面处理部分:
			HAL_GPIO_TogglePin(IO_CLK_GPIO_Port, IO_CLK_Pin);				//翻转输出时钟

			if(HAL_GPIO_ReadPin(IO_CLK_GPIO_Port, IO_CLK_Pin))      		//判断电平 当前输出为 高电平
			{
   
					IO_UART_Send_Buff();
			}
			else    //引脚拉低
			{
   
				 IO_UART_Time_CountDown_Deal(); 			
				 IO_UART_ReciveData();										 //处理虚拟串口接收数据	
				
				 if((IO_UART_RXD == GPIO_PIN_RESET)&&(IOUart_h_Struct.IO_UART_StartRxFlag == 0))  //判定是否有低电平数据进入
				 {
   
						if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT)			//状态为停止位
						{
   
							IOUart_h_Struct.IO_UART_RecvStat = COM_START_BIT;			//设置状态为起始位							
							IOUart_h_Struct.IO_UART_StartRxFlag = 1;					//开始接收					
							IOUart_h_Struct.IO_UART_RecvData = 0;  						//接收数据							
							IOUart_h_Struct.IO_UART_RxTimeOut = _IO_RX_TIMECOUNTDOWN;//接收到数据把接收超时清零																
						}		 
				 }
			}
			

上面是核心代码,如果接受不定长度代码 可在 IO_UART_Time_CountDown_Deal 函数里面 写接受完成标志。

模拟串口 printf 数据

/*******************************************************************************
Name           	 :FlagM_Uart_Printf
Syntax           :void UartM_485Printf(char *fmt,...)
Parameters(in)   :None              :-
Parameters(out)  :None              :-
Return value     :-                 :-
Description      :模拟串口数据打印
|******************************************************************************/
__align(8) char usart_txBuff[USART_TXBUFF_SIZE];                //字节对齐缓冲区
void FlagM_IOUart_Printf(char *fmt,...)
{
   
    uint32_t length;
    va_list ap;
    va_start(ap,fmt);
    vsprintf(usart_txBuff,fmt,ap);
    va_end(ap);
    length=strlen((const char*)usart_txBuff);   
    IO_UART_SendData(usart_txBuff,length);    
}


从机部分(时钟来来自主机):

IO_CLK :外部中断脚(EXIT)上升下降沿触发
IO_RX :接受输入脚(GPIO)
IO_TX :数据输出脚(GPIO)
在这里插入图片描述
在这里插入图片描述
基本配置比较简单

代码部分:

核心部分一样的,差异就是 主机在定时回调里面处理数据,而从机则是在外部中断中回调里处理数据

	
	if(GPIO_Pin == IO_CLK_Pin)
	{
   
		 if(IO_UART_CLK == GPIO_PIN_RESET)							//RX 时钟引脚为低电平  
		 {
   			
			  IO_UART_Time_CountDown_Deal();  						//处理虚拟串口接收数据			 
			  IO_UART_ReciveData();								//处理接受的数据
				
			  if((IO_UART_RXD == GPIO_PIN_RESET)&&(IOUart_h_Struct.IO_UART_StartRxFlag == 0))
				{
   
					if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT)//状态为停止位
					{
   
						IOUart_h_Struct.IO_UART_RecvStat = COM_START_BIT;	//设置状态为起始位
						
						IOUart_h_Struct.IO_UART_StartRxFlag = 1;					//开始接收					
						IOUart_h_Struct.IO_UART_RecvData = 0;  						//接收数据
						
						IOUart_h_Struct.IO_UART_RxTimeOut = _IO_RX_TIMECOUNTDOWN;//接收到数据把接收超时清零																
					}
				}
		 }
		 else if(IO_UART_CLK == GPIO_PIN_SET)		
		 {
   
			   //这里是数据发送部分 上升沿发送数据
				 IO_UART_Send_Buff(); 
		 }
	}

如有问题可以加群讨论: 764284134

相关推荐

  1. ARM(串口控制硬件) 2023.12.11

    2024-02-07 23:50:01       40 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-07 23:50:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-07 23:50:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-07 23:50:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-07 23:50:01       18 阅读

热门阅读

  1. c语言编程题目:素数判断(for循环和if判断)

    2024-02-07 23:50:01       35 阅读
  2. docker compose部署gitlab 获取初始密码

    2024-02-07 23:50:01       36 阅读
  3. YOLO v8 参数设置,详解cfg/default.yaml

    2024-02-07 23:50:01       29 阅读
  4. jsPDF+html2canvas实现html转pdf下载+打印

    2024-02-07 23:50:01       30 阅读
  5. mysql 数据库基础知识

    2024-02-07 23:50:01       35 阅读
  6. 蓝桥杯基础知识7 vector

    2024-02-07 23:50:01       26 阅读
  7. 2024/2/7 图的基础知识

    2024-02-07 23:50:01       35 阅读
  8. 随身wifi两条命令刷入debian系统

    2024-02-07 23:50:01       29 阅读
  9. eventbus增加个缓存池

    2024-02-07 23:50:01       30 阅读
  10. Linux和Windows文件共享实现方式

    2024-02-07 23:50:01       27 阅读