STM32F103C8T6驱动ESP8266转串口模块(一)——模块AP模式+TCP客户端的HAL库驱动代码详解(CubeMX工程)

2023-05-16

1.STM32驱动ESP8266模块

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

1.1 CubeMX配置STM32F103C8T6芯片引脚

1.1.1 选择芯片型号

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述

1.1.3 生成keil工程文件

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

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

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

在这里插入图片描述

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

1.2 接线

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

在这里插入图片描述

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

在这里插入图片描述

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

1.3 驱动逻辑和代码

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

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

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

在这里插入图片描述

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

  • 向模块发送第一个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传输,这里不再赘述。生成代码后,界面如下:

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

出现如下界面:

在这里插入图片描述

将“Target” → \rightarrow “Use MicroLIB”勾选上。这里若不勾选,则串口无法使用。勾选后,需要重新进行编译。

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文件。

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

在这里插入图片描述

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

在这里插入图片描述

打开“Inc”文件夹:

在这里插入图片描述

可以看到,这个位置就是项目中的头文件所在的位置了。在这个文件夹下,新建一个文本文档,命名为“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文件中添加我们的业务代码了:

在这里插入图片描述

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之间的无线通信。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F103C8T6驱动ESP8266转串口模块(一)——模块AP模式+TCP客户端的HAL库驱动代码详解(CubeMX工程) 的相关文章

  • CodeBlocks 20.03下载及安装指南 使用自带MinGW进行环境配置

    原本用的好好的CodeBlocks 17 12 xff0c 今天手欠无聊去搜了一下官网发现两年都没更新的cb竟然在这两天更新了 对于一直喜欢用最新版的我 xff0c 一定要更新 xff0c 然后 我还把之前的各项配置都删掉了 好吧 xff0
  • 程序是从main主函数开始运行吗?

    很多人开始学计算机语言 xff0c 编写代码时都会有一个疑问 xff1a 程序真的是从main主函数开始的吗 xff1f 之前什么都不做吗 xff1f main结束后就不能执行其他函数了吗 xff1f 下面本篇文章就为此问题做一个简单的解释
  • C++内存越界(转)

    glibc detected free invalid pointer glibc detected malloc memory corruption glibc detected double free or corruption out
  • 总结几种结构体初始化的方法(转)

    总结几种结构体初始化的方法 转自 xff1a http www cnblogs com vongang archive 2011 07 30 2122076 html 结构体能自由组装数据 xff0c 是一种很常见的数据打包方法 当我们定义
  • C++各大有名库的介绍(转)

    C 43 43 各大有名库的介绍 C 43 43 各大有名库的介绍之C 43 43 标准库 标准库中提供了C 43 43 程序的基本设施 虽然C 43 43 标准库随着C 43 43 标准折腾了许多年 xff0c 直到标准的出台才正式定型
  • 内存分配——静态存储区 栈 堆 与static变量 (转)

    一 内存基本构成 可编程内存在基本上分为这样的几大部分 xff1a 静态存储区 堆区和栈区 他们的功能不同 xff0c 对他们使用方式也就不同 静态存储区 xff1a 内存在程序编译的时候就已经分配好 xff0c 这块内存在程序的整个运行期
  • Java的集合框架最全详解(图)

    Java的集合框架最全详解 xff08 图 xff09 前言 xff1a 数据结构对程序设计有着深远的影响 xff0c 在面向过程的C 语言中 xff0c 数据库结构用struct来描述 xff0c 而在面向对象的编程中 xff0c 数据结
  • 如何查看sybase存储过程的内容?

    如何查看sybase存储过程的内容 xff1f 1 在isql xff08 或SQL Advantage xff09 中执行 xff1a sp helptext 存储过程名 可以查看存储过程的内容 2 在 SQL CENTRAL里连接你的服
  • Oracle的表分析是做什么的?

    Oracle的表分析是做什么的 xff1f analyze table tablename compute statistics 分析的结果被Oracle用于基于成本的优化生成更好的查询计划 那么 xff0c 问题在于 xff1a Orac
  • React Refs

    React 支持一种非常特殊的属性 Ref xff0c 可以用来绑定到 render 输出的任何组件上 这个特殊的属性允许引用 render 返回的相应的支撑实例 xff08 backing instance xff09 这样就可以确保在任
  • μC/OS-II学习之:任务,信号量、邮箱、队列及其区别

    一 xff1a UCOS是一种抢占式的多任务操作系统 xff0c 如果最高优先级的任务不主动放弃CPU的使用的话 xff0c 其他任务是无法运行的 xff0c 通常情况下 xff0c 高优先级的任务在使用完CPU或其他资源后都要主动放弃 x
  • 51 单片机简单的多任务调度例子(转)

    51 单片机简单的多任务调度例子 看大家都在学操作系统 xff0c 我也想学学 所以想用51写一个来玩玩 xff0c 发现比较郁闷 弄了几下 xff0c 不想再弄了 xff0c 51弄这个没啥意思 我用的89S52 xff0c 除了速度慢
  • java代码编写随笔总结

    一 项目模块定义 说明 xff1a 一个产品分为各个独立的原子服务 xff0c 通过这些独立的原子服务进行组合来满足各种业务的需求 1 各原子服务关系与原则 xff1a 依赖关系 xff1a 只能上级依赖下级 xff0c 不可下级依赖上级
  • 缓冲区溢出原理学习

    什么是缓冲区溢出 xff1f 缓冲区 简单说来是一块连续的计算机内存区域 可以保存相同数据类型的多个实例 动态变量在程序运行时定位于堆栈之中 我们这里只关心动态缓冲区的溢出问题 即基于堆栈的缓冲区溢出 进程的内存组织形式 一个进程在内存中被
  • Java学习笔记12:求s=1+11+111+1111+......+1111......1的值

    64 author 朱凌风 64 weather 阵雨 64 date 06 19 2011 64 function 计算1 43 11 43 111 43 43 111 1的值 package com jerome import java
  • 配置Ubuntu软件源

    引子 Ubuntu系统的软件源就是指Ubuntu系统的软件更新管理器下载更新软件的来源 xff0c 是一个软件仓库 Ubuntu系统对这个软件源的配置的信息是放在一个文本文件中的 xff0c 这个文本文件的完整路径一般是 etc apt s
  • NuttX 启动流程

    xff08 嵌入式 实时操作系统 rtos nuttx 7 1 stm32 源代码分析 xff09 NuttX 启动流程 转载请注明出处 xff1a http blog csdn net zhumaill article details 2
  • NuttX 介绍

    xff08 嵌入式 实时操作系统 rtos nuttx 7 1 xff09 NuttX 介绍 转载请注明出处 xff1a http blog csdn net zhumaill article details 24197637 1 Nutt
  • Ubuntu用命令行打开网页的三种方法

    1 第一种方法 links命令 apt install links links websol cn 2 第二种方法 w3m命令 apt install w3m w3m websol cn 3 第三种方法 lynx命令 apt install
  • JS删除数组中指定元素/删除数组中指定对象

    删除数组中指定对象指定元素 let arr 61 name 34 xiaowang 34 id 1 name 34 xiaozhang 34 id 2 createDate 34 xiaoli 34 id 3 删除id为1的对象 xff0c

随机推荐

  • React G2Plot 水波图

    官方文档 xff1a https antv g2plot v1 gitee io zh docs manual introduction 安装依赖 span class token function npm span span class
  • 数据链路层

    本篇目录 数据链路层的三个基本问题 使用点对点信道的数据链路层 使用广播信道的数据链路层 以太网MAC层的硬件地址 一 数据链路层的三个基本问题 封装成帧 xff1a 帧是数据链路层的传送单位 一个帧的帧长等于帧的数据部分加上帧的首部和尾部
  • 输入三个数求出最大值(5种方法)

    这是一个很简单的C语言程序 xff0c 重要的是考验思考问题的角度 xff1a 方法1 xff1a include lt stdio h gt void main int a b c scanf 34 d d d 34 amp a amp
  • 把二维数组数据读入txt文本(C语言)

    我们经常需要把计算后的数据存入txt文本 xff0c 下例提供了一种简单思路 xff1a include lt stdio h gt include lt stdlib h gt int main int a 2 3 61 5 2 8 4
  • 查询txt文本信息行数(C和C++分别实现)

    在一些程序设计中 xff0c 我们经常要先查询txt文本的行数 xff0c 据此 xff0c 才能对数组进行动态内存分配 C语言实现 include lt stdio h gt include lt stdlib h gt define A
  • 从txt中读取数据存入二维数组

    在实际应用中 xff0c 经常需要把txt中的数据读入到一个数组中 xff0c 然后再参与运算 在C语言中可以利用fscanf 函数从文件中读取数据 xff0c 示例如下 xff1a void main xff08 xff09 double
  • 仿射变换

    AffineTransform类描述了一种二维仿射变换的功能 xff0c 它是一种二维坐标到二维坐标之间的线性变换 xff0c 保持二维图形的 平直性 xff08 译注 xff1a straightness xff0c 即变换后直线还是直线
  • OpenCV下的直线拟合

    出处 xff1a http blog csdn net Tangyongkang OpenCV中 CvSeq 对象由以下语句生成 创建 CvSeq的容器对象 CvMemStorage storage 61 cvCreateMemStorag
  • 利用meshgrid函数绘制二维高斯函数曲面

    meshgrid函数用于根据给定的横纵坐标点生成坐标网格 xff0c 以便计算二元函数的取值 设二维高斯函数表达式为 xff1a 程序如下 xff1a u 61 10 0 1 10 v 61 10 0 1 10 U V 61 meshgri
  • 要想成功必备的9大好习惯 以及必须克服的9个坏习惯

    要想成功 必备 9 大好习惯 以及 必须克服的 9 个坏习惯 你想成功吗 xff1f 那就及早培养有利于成功的好习惯 习惯的力量是惊人的 xff0c 35岁以前养成的习惯决定着你是否成功 有这样一个寓言故事 一位没有继承人的富豪死后将自己的
  • 数据结构算法学习之路

    1 二分法竞猜商品价格 include lt stdio h gt include lt stdlib h gt int main int oldprice price 61 0 i 61 0 printf 34 请设置商品的真实价格 xf
  • React markdown 编辑器

    react markdown 是一款 github 上开源的适用于 react 的 markdown 组件 xff0c 可以基本实现 markdown 的功能 xff0c 且可以根据自己实际应用定制的 remark 组件 安装 安装 mar
  • ROS下IMU串口通讯接口(通用版)

    1 源码 include lt string gt include lt ros ros h gt 包含ROS的头文件 include lt sensor msgs JointState h gt include lt tf transfo
  • openrave安装 win7(10)

    1 软件安装 1 xff09 其中 xff0c boost 1 44需独立编译 xff0c 放到指定文件夹下 xff0c 例如 D boost 1 44 0 xff1b 2 xff09 ps 最大的坑在这里 xff0c 务必把msvc bo
  • 嵌入式常见的数据结构

    0 引言1 线性表1 1 顺序表1 1 1 定义类型1 1 2 相关操作1 1 3 相关操作的实现 1 2 链表1 2 1 定义类型1 2 2 相关操作1 2 3 相关操作的实现 2 栈2 1 顺序栈2 1 1 定义类型2 1 2 相关操作
  • vslam

    目录 隐藏 1 SLAM 介绍 1 1 什么是SLAM 1 2 SLAM与视觉里程计 xff08 Visual Odometry xff09 1 3 SLAM和SfM 2 主流开源SLAM方案 2 1 视觉传感器 2 2 激光传感器 2 3
  • 华为mate手机从解锁到root成功全步骤

    警告 请保持电量充足 xff0c 不然小心变砖 解锁手机会恢复出厂设置 xff0c 原因未知 xff08 伤心 xff0c 不想查了 xff09 xff0c 请需要解锁的diy爱好者 xff0c 自行备份数据 一 安装adb驱动 下载安装a
  • <Zhuuu_ZZ>HIVE(十一)函数

    Hive内置函数 一 Hive函数分类二 字符函数二 类型转换函数和数学函数三 日期函数四 集合函数五 条件函数六 聚合函数和表生成函数6 1 聚合函数6 2 表生成函数 xff1a 输出可以作为表使用 一 Hive函数分类 从输入输出角度
  • 嵌入式软件工程师的自我修养: Cortex-M3 ARM代码编译,链接与启动过程深度分析

    本篇文章以武汉杰开科技的汽车级MCU芯片AC7811为硬件平台 xff0c 使用GNU GCC作为开发工具 详细分析Compile Link Loader的过程以及Image 二进制程序 启动的详细分析 整个过程分析涉及到RW可读写DATA
  • STM32F103C8T6驱动ESP8266转串口模块(一)——模块AP模式+TCP客户端的HAL库驱动代码详解(CubeMX工程)

    1 STM32驱动ESP8266模块 笔者所使用的ESP8266模块为正点原子开发的模块 xff0c 该模块将通信接口变成了串口 接下来关于ESP8266模块的介绍均以此模块为基础 1 1 CubeMX配置STM32F103C8T6芯片引脚