1.STM32驱动ESP8266模块

笔者所使用的ESP8266模块为正点原子开发的模块,该模块将通信接口变成了串口。接下来关于ESP8266模块的介绍均以此模块为基础。

1.1 CubeMX配置STM32F103C8T6芯片引脚

1.1.1 选择芯片型号

我们需要给芯片配置引脚,使它能够完成我们所需要的功能。

1.双击打开CubeMX软件,弹出界面如下图所示:

STM32读取ESP8266数据 stm32配置esp8266_#define

2.点击上图中的“ACCESS TO MCU SELECTOR”按钮,进入如下界面:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_02

3.在“Part Number”中输入“STM32F103C8”字符,然后双击“Reference”中的“STM32F103C8Tx”,进入如下界面:

STM32读取ESP8266数据 stm32配置esp8266_#define_03

这样,我们就选择好了芯片类型。在上图所示的界面中,我们就可以配置芯片引脚,以满足我们的开发需要。

1.1.2 配置串口引脚和时钟频率

1.点击“System Core”,再点击“RCC”,将时钟源设置为外部时钟源:

STM32读取ESP8266数据 stm32配置esp8266_单片机_04

2.点击“SYS”,将“Debug”设置为“Serial Wire”,“Timebase Source”设置为“SysTick”:

STM32读取ESP8266数据 stm32配置esp8266_单片机_05

3.点击“Connectivity”,然后点击“USART3”,将通信方式设置为“Asynchronous”(异步通信):

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_06

4.在“UART3”界面,点击“DMA Settings”,将串口3的通信引脚模式设置为DMA(直接寄存器访问)模式。这是因为在笔者的工程中,WiFi需要不断地接收陀螺仪发送过来的数据并不断地将其发送出去,所以采用DMA模式,减轻CPU的负担:

STM32读取ESP8266数据 stm32配置esp8266_stm32_07

读者在新建工程时,需要点击“Add”添加DMA通道。添加好后,将“Mode”设置为“Normal”,“Data Width”设置为“Byte”,如上图所示。

6.点击“Clock Configuration”,将“HCLK(MHz)”设置为72:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_08

1.1.3 生成keil工程文件

1.所有的部分都配置好后,点击“Project Manager”,在“Toolchain/IDE”中选择编辑器为“MDK-ARM”,然后在“Min Version”中选择“V5”(此处笔者电脑安装的是V5版本,读者可以根据自己安装的Keil版本选择对应版本):

STM32读取ESP8266数据 stm32配置esp8266_stm32_09

2.在“Project Manager”界面,点击“Code Generator”,在“Generated files”中将第一项也勾选上:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_10

3.点击界面右上角的“GENERATE CODE”:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_11

STM32读取ESP8266数据 stm32配置esp8266_stm32_12

出现上述界面,就说明代码已经生成好了。

4.选择上图中的“Open Project”,代码界面如下图所示:

STM32读取ESP8266数据 stm32配置esp8266_tcp/ip_13

至此,我们就配置好了芯片的引脚并生成了代码。

1.2 接线

正点原子ESP8266模块保留了六个针脚,分别是VCC、GND、RXD、TXD、RST和IO0。平常使用过程中,我们仅仅需要使用前四个引脚即可。在上面的配置过程中,我们选择使用串口3与ESP8266模块进行通信,因此需要看芯片的引脚定义,看哪两个引脚是串口3的引脚:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_14

可以看到,PB10为TX(发送),PB11为RX(接收)。则PB10接模块的RX脚,PB11接模块的TX脚。接线方式如下:

STM32读取ESP8266数据 stm32配置esp8266_#define_15

芯片板可以通过micro-USB线供电,也可以通过外接电源稳压模块供电。注意,在使用稳压模块时,最好使用5V电压给芯片供电,否则烧录器无法识别到芯片板的存在。ESP8266也可以使用稳压模块向其供电,5V和3.3V均可。

1.3 驱动逻辑和代码

要实现一个完整的工程,我们需要设计好模块的使用逻辑并使用代码将其实现。

1.3.1 AT指令与模块配置过程介绍

首先我们要配置模块的各项参数。参考正点原子的模块使用资料,结合本身项目需求,笔者选择将模块设置为AP模式,使用AP模式下的透传功能实现大量数据的透明传输。配置过程如下:

STM32读取ESP8266数据 stm32配置esp8266_#define_16

我们需要按上述流程完成模块模式的配置。那么,我们需要完成如下几件事请:

  • 向模块发送第一个AT指令;
  • 等待模块的返回内容并判断是否配置成功;
  • 如果成功,发送下一个AT指令;如果不成功,则再发送一次当前AT指令;
  • 直至最后开始发送数据的AT指令发送并配置成功

发送指令比较简单,使用HAL库的串口发送函数即可:

HAL_UART_Transmit(&huart3, (uint8_t *)AT_RESTORE, sizeof(AT_RESTORE), 0xFFFF)

其中,AT_RESTORE表示AT指令中的“重启”指令。该指令发送完成后,向串口发送检查指令“AT\r\n”。经笔者测试,模块会对检查指令返回字符串“AT\r\n\r\nOK\r\n”(在CubeMX工程中,“char”类型被定义为“uint8_t”类型)。同样,其他指令返回内容的数据类型也为字符串。所以,我们需要读取到串口3的返回值并识别是否是重启成功的返回值。声明uint8_t类型:

uint8_t AT[] = "AT\r\n";//检查指令
uint8_t ATRE[] = "AT\r\n\r\nOK\r\n";//检查指令的正确返回值

其中“AT”和“ATRE”均为字符串类型,AT是MCU向串口3(也就是模块)发送的检查指令,而ATRE为该指令的正确返回值。如果返回值不是ATRE的内容,则需要重新发送AT指令。

检查返回值的代码如下:

int checkResponse(uint8_t* response){
	for(int i = 0;i < ATResRecCount;i ++){
		if(ATCmdRxBuffer[i] != response[i]){
			return 0;
		}
	}
	return 1;
}

在上面的函数中,输入参数是正确的返回值。ATCmdRxBuffer中是串口接收到的实际的返回值。在这个函数中,按照顺序依次将实际的返回值和正确的返回值比较,如果实际返回值与正确返回值不一致,则函数返回整型0,否则返回1。我们可以根据返回值来判断模块是否返回了正确的返回值,进而决定是否进行下一步。

1.3.2 DMA空闲中断接收不定长数据与模块驱动代码

根据正点原子的模块资料:《ATK-ESP8266 WIFI用户手册_V1.5》(下载链接:WIFI模块ATK-ESP8266(ESP 01) — 正点原子资料下载中心 1.0.0 文档 (openedv.com)),不同的AT指令的返回值长度是不同的。也就是说,在串口向模块发送指令后,串口需要接收不定长的数据。在这里,我们可以使用DMA空闲中断来接收不定长数据,并判断返回值是否是配置成功的返回值。

1.我们已经在CubeMX中配置了串口3的DMA传输,这里不再赘述。生成代码后,界面如下:

STM32读取ESP8266数据 stm32配置esp8266_stm32_17

点击左上角红色方框框柱的图标,对软件生成的工程代码进行编译,检查是否存在问题。编译完成后,控制台显示如下信息:

STM32读取ESP8266数据 stm32配置esp8266_STM32读取ESP8266数据_18

出现字样“0 Error(s),0 Warning(s)”,这说明生成的代码中不存在问题。然后,点击“魔术棒”:

STM32读取ESP8266数据 stm32配置esp8266_stm32_19

出现如下界面:

STM32读取ESP8266数据 stm32配置esp8266_stm32_20

将“Target”STM32读取ESP8266数据 stm32配置esp8266_#define_21

2.在工程文件的文件树中找到“stm32f1xx_it.c”文件,找到如下代码块并在代码块中的* USER CODE BEGIN USART3_IRQn 1 /和/ USER CODE END USART3_IRQn 1 */中间添加代码,最终代码结果如下所示:

/**
  * @brief This function handles USART3 global interrupt.
  */
void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */

  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */
	if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET){
		__HAL_UART_CLEAR_IDLEFLAG(&huart3);
		HAL_UART_IdleCpltCallback(&huart3);
	}
  /* USER CODE END USART3_IRQn 1 */
}

3.由于在项目中涉及到接收机和遥控器之间的信息交换,有关WiFi驱动的代码较为复杂,笔者将驱动WiFi的相关代码统一放在一个头文件当中。在这里,笔者先介绍一下如何在Cube工程中添加.h文件。

打开项目文件夹,如下图所示:

STM32读取ESP8266数据 stm32配置esp8266_stm32_22

打开上图中红框框出的“Core”文件夹:

STM32读取ESP8266数据 stm32配置esp8266_tcp/ip_23

打开“Inc”文件夹:

STM32读取ESP8266数据 stm32配置esp8266_单片机_24

可以看到,这个位置就是项目中的头文件所在的位置了。在这个文件夹下,新建一个文本文档,命名为“esp8266”,并将文件后缀修改为“.h”。创建好后,打开keil工程,找到如下代码块并添加代码:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "esp8266.h" 
#include "stdio.h"
/* USER CODE END Includes */

然后编译。编译完成后,展开“main.c”文件的文件树,可以看到“esp8266.h”文件已经添加至文件树,我们就可以在esp8266.h文件中添加我们的业务代码了:

STM32读取ESP8266数据 stm32配置esp8266_stm32_25

4.添加好头文件后,我们就可以开始着手编写ESP8266模块的驱动代码了。首先,头文件的首尾部分需要添加如下信息:

#ifndef __ESP8266_H__
#define __ESP8266_H__	 

/**
   业务代码 
    */

#endif

注意,在“#endif”这一行,要按回车键再添加一行空白行,否则编译时会报错。

5.在“stm32f1xx_it.c”文件中,找到如下代码:

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_usart3_tx;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;

将第十行复制并粘贴到“esp8266.h”文件中。

6.完成上一步骤后,在“esp8266.h”文件中添加如下代码。每行代码的意义以及作用笔者将在代码注释中加以说明。

/**
  * @brief  项目中需要的AT指令
  *	@note
  * @param  
  * @retval
  */
extern DMA_HandleTypeDef hdma_usart3_rx;

#define ATRELENGTH 256 //接收缓冲区的大小。这个大小需要大于一组数据的长度

uint8_t ATCmdRxBuffer[ATRELENGTH]; //接收缓冲区
uint8_t ATResRecCount = 0; //每次接收到的字节数
uint8_t ATCmdRxFlag = 0; //接收状态标志

7.在“main.c”文件中找到如下对应的位置并添加代码:

/* USER CODE BEGIN 2 */
	__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);//使能串口3 IDLE中断
	HAL_Delay(100);
/* USER CODE END 2 */

/**
......
	*/

/* USER CODE BEGIN 4 */
/* 中断接收回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	if(huart -> Instance == USART3){
        //重新启动DMA接收
		HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
	}
}
/* IDLE空闲中断回调函数 */
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart){
	ATCmdRxFlag = 1;//设置接收完成标志
}
/* USER CODE END 4 *

8.在“mian.h”文件中找到如下位置并添加代码:

/* USER CODE BEGIN EFP */
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);
/* USER CODE END EFP */

9.以上步骤都完成后,在“esp8266.h”文件中添加代码。其中,LED灯的引脚配置为将PA4脚设置为GPIO_Output模式,已经在CubeMX中配置。完整的代码如下:

#ifndef __ESP8266_H__
#define __ESP8266_H__	 
/**
  * @brief  esp8266模块需要的各类定义和函数文件
  *	@note		
  * @param  
  * @retval	
	*
	*
	*
  */
	
#include "stm32f1xx_hal.h"
#include "usart.h"
#include "string.h"
#include "main.h"

/**
  * @brief  项目中需要的AT指令
  *	@note
  * @param  
  * @retval
  */
extern DMA_HandleTypeDef hdma_usart3_rx;

#define ATRELENGTH 256

uint8_t ATCmdRxBuffer[ATRELENGTH];
uint8_t ATResRecCount = 0;
uint8_t ATCmdRxFlag = 0;

uint8_t AT_RESTORE[] = "AT+RESTORE\r\n"; //恢复出厂设置

uint8_t AT[] = "AT\r\n";//检查指令
uint8_t ATRE[] = "AT\r\n\r\nOK\r\n";//检查指令的正确返回值

uint8_t AT_MODEAP[] = "AT+CWMODE=2\r\n";//设置为AP模式
uint8_t ATMODEAPRE[] = "AT+CWMODE=2\r\n\r\nOK\r\n";//设置为AP模式指令的正确返回值 

uint8_t AT_RST[] = "AT+RST\r\n";//重启生效
uint8_t ATRSTRE[] = "AT+RST\r\n\r\nOK\r\n";//重启生效指令的正确返回值

//设置WiFi模块的名称和密码,读者记得设置自己的名称和密码
uint8_t AT_SAP[] = "AT+CWSAP=\"xxxxxx\",\"xxxxxx\",1,4\r\n";//设置WIFI名称和密码,通道号1,加密方式WPA_WPA2_PSK
uint8_t ATSAPRE[] = "AT+CWSAP=\"xxxxxx\",\"xxxxxx\",1,4\r\n\r\nOK\r\n";//设置名称密码的正确返回值

uint8_t AT_MUX0[] = "AT+CIPMUX=0\r\n";//开启单链接
uint8_t ATMUXRE[] = "AT+CIPMUX=0\r\n\r\nOK\r\n";//开启单链接命令的正确返回值

uint8_t AT_CWLIF[] = "AT+CWLIF\r\n";//查看已接入设备的IP
uint8_t ATCWLIFRENONE[] = "AT+CWLIF\r\n\r\nOK\r\n";//没有设备接入时查看命令的返回值

uint8_t AT_START[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\n";//建立TCP连接
uint8_t ATSTARTRESUCCESS[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\nCONNECT\r\n\r\nOK\r\n";//期待TCP连接成功回应(第一次)
uint8_t ATSTARTREALREADY[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\nALREADY CONNECTED\r\n\r\nERROR\r\n";//已经建立TCP连接的返回值(成功建立TCP连接以后的值)
uint8_t ATSTARTREFAILED[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\n\r\nERROR\r\nCLOSED\r\n";//TCP连接建立失败的返回值

uint8_t AT_CIPMODE1[] = "AT+CIPMODE=1\r\n";//开启透传模式命令
uint8_t ATCIPMODE1RE[] = "AT+CIPMODE=1\r\n\r\nOK\r\n";//开启透传模式的正确返回值

uint8_t AT_SEND[] = "AT+CIPSEND\r\n";//开始传输命令
uint8_t ATSENDRE[] = "AT+CIPSEND\r\n\r\nOK\r\n\r\n>";//开始传输命令的正确返回值

uint8_t AT_QUIT[] = "+++"; //退出透传

uint8_t AT_SAVETRANSLINK[] = "AT+SAVETRANSLINK=1,\"192.168.4.2\",8080,\"TCP\"";//不保存透传连接到flash
uint8_t ATSAVETRANSLINKRE[] = "AT+SAVETRANSLINK=1,\"192.168.4.2\",8080,\"TCP\"\r\n\r\nOK\r\n";//不保存连接到flash的回应


/**
  * @brief  发送AT指令的函数宏定义
  *	@note	
  * @param  
  * @retval 
  */
	
#define ATRESTORE HAL_UART_Transmit(&huart3, (uint8_t *)AT_RESTORE, sizeof(AT_RESTORE), 0xFFFF) //发送恢复出厂设置命令
#define ATCHECK HAL_UART_Transmit(&huart3, (uint8_t *)AT, sizeof(AT), 0xFFFF) //发送AT检查指令	
#define ATMODEAP HAL_UART_Transmit(&huart3, (uint8_t *)AT_MODEAP, sizeof(AT_MODEAP), 0xFFFF) //发送设置为AP模式指令
#define ATRST HAL_UART_Transmit(&huart3, (uint8_t *)AT_RST, sizeof(AT_RST), 0xFFFF) //发送RESET指令
#define ATSAP HAL_UART_Transmit(&huart3, (uint8_t *)AT_SAP, sizeof(AT_SAP), 0xFFFF) //发送设置WIFI名称密码命令
#define ATCWLIF HAL_UART_Transmit(&huart3, (uint8_t *)AT_CWLIF, sizeof(AT_CWLIF), 0xFFFF);//发送查询接入设备命令
#define ATMUX0 HAL_UART_Transmit(&huart3, (uint8_t *)AT_MUX0, sizeof(AT_MUX0), 0xFFFF) //发送开启单链接命令	
#define ATSTART HAL_UART_Transmit(&huart3, (uint8_t *)AT_START, sizeof(AT_START), 0xFFFF) //发送建立TCP连接命令
#define ATCIPMODE1 HAL_UART_Transmit(&huart3, (uint8_t *)AT_CIPMODE1, sizeof(AT_CIPMODE1), 0xFFFF) //发送开启透传模式命令
#define ATSEND HAL_UART_Transmit(&huart3, (uint8_t *)AT_SEND, sizeof(AT_SEND), 0xFFFF) //发送开始传输命令
#define ATQUIT HAL_UART_Transmit(&huart3, (uint8_t *)AT_QUIT, 3, 0xFFFF) //发送退出透传命令
#define ATSAVETRANSLINK HAL_UART_Transmit(&huart3, (uint8_t *)AT_SAVETRANSLINK,sizeof(AT_SAVETRANSLINK), 0xFFFF);//发送保存TCP到Flash

	
/**
  * @brief  各类操作函数
  *	@note	
  * @param  
  * @retval 
  */

/* 检查返回值和期待值是否一致 */
int checkResponse(uint8_t* response){
	for(int i = 0;i < ATResRecCount;i ++){
		if(ATCmdRxBuffer[i] != response[i]){
			return 0;
		}
	}
	return 1;
}

/* 设置小灯泡亮 */
void LED(int Delay)
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	HAL_Delay(Delay);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_Delay(Delay);
}

/* 初始化AP模式下TCP客户端 */
void Init_AP_TCPClient(){
	ATQUIT;//发送退出透传命令
	HAL_Delay(1000);
	HAL_UART_Receive_DMA(&huart3, (uint8_t *)ATCmdRxBuffer,ATRELENGTH);//启动DMA接收
	int compareResult = 0;//字符串比较结果
	int STARTcR = 0;//建立TCP连接时的第二个比较结果
	int STARTcE = 0;//建立TCP连接失败的比较结果
	//发送AT检查指令直至响应成功
	do{
		ATCHECK;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATRE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	LED(500);
	compareResult = 0;
	//发送设置AP模式指令直至响应成功
	do{
		ATMODEAP;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATMODEAPRE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	LED(500);
	compareResult = 0;
	//发送RESET指令
	ATRST;
	HAL_Delay(500);
	if(ATCmdRxFlag == 1){
		ATCmdRxFlag = 0;
		HAL_UART_DMAStop(&huart3);
		ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
		ATResRecCount = 0;
		HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
	}
	LED(500);
	//发送设置SSID PASSWORD命令
	do{
		ATSAP;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATSAPRE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	LED(500);
	compareResult = 0;
	//发送查询接入设备命令直至成功
	do{
		ATCWLIF;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATCWLIFRENONE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult == 1);
	LED(500);
	compareResult = 0;
	//发送开启单链接命令直至成功
	do{
		ATMUX0;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATMUXRE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	LED(500);
	compareResult = 0;
	//发送建立TCP连接命令直至成功
	do{
		ATSTART;
		HAL_Delay(7000);//此处延时需要长一些,该命令反应较慢
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATSTARTRESUCCESS);
			STARTcR = checkResponse(ATSTARTREALREADY);
			STARTcE = checkResponse(ATSTARTREFAILED);
			if(STARTcE == 1){
				do{
					ATMUX0;
					HAL_Delay(500);
					if(ATCmdRxFlag == 1){
						ATCmdRxFlag = 0;
						HAL_UART_DMAStop(&huart3);
						ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
						compareResult = checkResponse(ATMUXRE);
						ATResRecCount = 0;
						HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
					}
				}while(compareResult != 1);
				LED(200);
				compareResult = 0;
			}
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1 && STARTcR != 1);
	LED(500);
	compareResult = 0;
	//发送开启透传命令直至成功
	do{
		ATCIPMODE1;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATCIPMODE1RE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	LED(500);
	compareResult = 0;
	//发送开始传输命令直至成功
	do{
		ATSEND;
		HAL_Delay(500);
		if(ATCmdRxFlag == 1){
			ATCmdRxFlag = 0;
			HAL_UART_DMAStop(&huart3);
			ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
			compareResult = checkResponse(ATSENDRE);
			ATResRecCount = 0;
			HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
		}
	}while(compareResult != 1);
	HAL_UART_DMAStop(&huart3);
	LED(500);
	compareResult = 0;
	HAL_UART_DMAStop(&huart2);
	HAL_Delay(100);
	__HAL_UNLOCK(&huart2);
	HAL_Delay(100);
}

#endif

注意,在建立TCP链接时,电脑需要接入到模块热点后才能成功建立TCP连接。上述代码完成后,就可以实现WiFi模块和电脑之间数据的透明传输了。CPU可以通过串口发送给模块,模块通过WiFi发送给电脑;而电脑也可以通过WiFi发送信息给模块,模块再通过串口将信息发送给CPU。这样就完成了电脑和CPU之间的无线通信。