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

news2024/11/14 18:34:31

模拟串口通信 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1436947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用HTML5 + JavaScript实现下雪效果

用HTML5 JavaScript实现下雪效果 <canvas>是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。 <canvas> 标签/元素只是图形容器&#xff0c;必须使用脚本来绘制图形。 HTML5 canvas 图形标签基础https://blog.csdn.net/cnds123/article/details/…

架构(十三)动态本地锁

一、引言 加锁大家都知道&#xff0c;但是目前提供动态锁的基本都是分布式锁&#xff0c;根据订单或者某个收费款项进行加锁。比如这个1订单要收刷卡费用&#xff0c;那就OREDER_1做为key丢到redis进行分布式加锁。这也是当下分布式锁最流行的方式。 但是对于平台项目或者一些并…

飞天使-k8s知识点14-kubernetes散装知识点3-Service与Ingress服务发现控制器

文章目录 Service与Ingress服务发现控制器存储、配置与角色 Service与Ingress服务发现控制器 在 Kubernetes 中&#xff0c;Service 和 Ingress 是两种不同的资源类型&#xff0c;它们都用于处理网络流量&#xff0c;但用途和工作方式有所不同。Service 是 Kubernetes 中的一个…

redis:七、集群方案(主从复制、哨兵模式、分片集群)和面试模板

redis集群方案 在Redis中提供的集群方案总共有三种&#xff08;一般一个redis节点不超过10G内存&#xff09; 主从复制哨兵模式分片集群 主从复制&#xff08;主从数据同步&#xff09; replid和offset Replication Id&#xff1a;简称replid&#xff0c;是数据集的标记&a…

回归预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现POA-BP鹈鹕算法优化BP神经网络多变量回归预测&#xff08;完整源码…

Java LinkedList 实现栈和队列

Java LinkedList 实现栈和队列 package com.zhong.collection;import java.util.LinkedList;public class LinkedListDemo {public static void main(String[] args) {// LinkedList 创建一个队列LinkedList<String> queue new LinkedList<>();// 进队System.out…

大华 DSS 数字监控系统 attachment_getAttList.action SQL 注入漏洞复现

0x01 产品简介 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。 0x02 漏洞概述 大华 DSS存在SQL注入漏洞,攻击者 /portal/attachment_getAttList.action 路由发送特殊构造的数据包,利用报错注入获…

git合入的parents和child

最近在管理代码&#xff0c;有2的权限&#xff0c;看到一些以前1看不到的东西。 有时候会遇到多个人基于同一节点提交代码&#xff0c;那就要选择先合入和后合入&#xff0c;如果这多人修改到同一个文件同一个地方&#xff0c;就可能产生冲突&#xff0c;一般要避免这种情况出…

NLP_神经概率语言模型(NPLM)

文章目录 NPLM的起源NPLM的实现1.构建实验语料库2.生成NPLM训练数据3.定义NPLM4.实例化NPLM5.训练NPLM6.用NPLM预测新词 NPLM小结 NPLM的起源 在NPLM之前&#xff0c;传统的语言模型主要依赖于最基本的N-Gram技术&#xff0c;通过统计词汇的共现频率来计算词汇组合的概率。然而…

Stata学习(1)

一、五大窗口 Command窗口&#xff1a;实现人机交互 来导入一个自带数据&#xff1a; sysuse是导入系统自带的数据&#xff0c;auto导入该数据的名称&#xff0c;后面的clear是清除之前的数据 结果窗口&#xff1a;展示计算结果、查找功能 在Edit的find可以实现查找功能&#…

如何使用C#调用LabVIEW算法

新建一个工程 这是必须的&#xff1b; 创建项目 项目 点击完成&#xff1b; 将项目另存为&#xff1b;方便后续的使用&#xff1b; 创建 一个测试VI 功能很简单&#xff0c;用的一个加法&#xff1b;将加数A&#xff0c;B设置为输入&#xff0c;和C设置为输出&#xff0c;…

Spring Boot项目中解决跨域问题(四种方式)

目录 一&#xff0c;跨域产生的原因二&#xff0c;什么情况下算跨域三&#xff0c;实际演示四&#xff0c;解决跨域的方法1&#xff0c;CrossOrigin注解2&#xff0c;添加全局过滤器3&#xff0c;实现WebMvcConfigurer4&#xff0c;Nginx解决跨域5&#xff0c;注意 开发项目的时…

黑马Java——集合进阶(List、Set、泛型、树)

一、集合的体系结构 1、单列集合&#xff08;Collection&#xff09; 二、Collection集合 1、Collection常见方法 1.1代码实现&#xff1a; import java.util.ArrayList; import java.util.Collection;public class A01_CollectionDemo1 {public static void main(String[] a…

Android实现底部导航栏方法(Navigation篇)

Navigation实现底部导航栏 前言导入和基本使用导入基础使用创建nav文件编辑Nav文件添加页面&#xff08;代码版&#xff09;添加页面&#xff08;图解版&#xff09; 创建导航动作 action创建action&#xff08;代码版&#xff09;创建action&#xff08;图解版&#xff09; 编…

Linux的进程信号

注意&#xff1a;首先需要提醒一个事情&#xff0c;本节提及的进程信号和下节的信号量没有任何关系&#xff0c;请您区分对待。 1.信号概念 1.1.生活中的信号 我们在生活中通过体验现实&#xff0c;记忆了一些信号和对应的处理动作&#xff0c;这意味着信号有以下相关的特点&…

上位机图像处理和嵌入式模块部署(统计函数执行时间)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 和pc上位机相比较&#xff0c;嵌入式设备的计算资源很多时候都是不足的。但是&#xff0c;嵌入式设备胜在稳定性和成本上面&#xff0c;这方面又是…

【XR806开发板试用】xr806使用tcp socket与手机通信

本文为极术社区XR806开发板活动试用文章。 参考&#xff1a;基于星辰处理器的全志XR806开源鸿蒙开发板上手体验 搭建环境。并成功编译。 项目源码 &#xff1a; https://gitee.com/kingwho/smart-home 在同一个局域网中&#xff0c;手机与xr806连接后&#xff0c;手机 APP 每隔…

lnmp一键安装包+wordpress

理论知识 1. LNMP组成介绍​ LNMP代表的是Linux系统下NginxMySQLPHP组成的动态网站系统解决方案。如图所示&#xff0c;Linux是目前最流行的免费操作系统&#xff1b;Nginx性能稳定、功能丰富、处理静态文件速度快且消耗系统的资源极少&#xff1b;MySQL是一个性能卓越、服务稳…

手拉手Vue3+vite引入echarts

技术栈springboot3hutool-alloshi-coreVue3viteechartsTailwindCSS软件版本IDEAIntelliJ IDEA 2022.2.1JDK17Spring Boot3.1hutool-all5.8.18oshi-core6.4.1Vue35.0.10vite5.0.10axios1.6.7echarts5.4.3 ECharts是一个使用 JavaScript 实现的开源可视化库&#xff0c;可以流畅…

awd总结

总结&#xff1a; 由于是第一次参加AWD比赛&#xff0c;各方面经验都不足&#xff0c;在参赛的前几天也是疯狂搜集各种脚本、框架、工具等&#xff0c;同时也参考b站的视频进行学习&#xff0c;我发现就是还是实操才能更快的学习 我觉得就是我前期的准备工作不足&#xff0c;…