Bootstrap

(总结)STM32中USART原理及应用(PC、BLE、ESP8266通信实现)

!!!鸣谢轩哥上课笔记!!!

一,USART基本原理

(1)基本概念

  1. UART 和 USART ?_uart同步模式 在什么场景下使用-CSDN博客
  2. 由于异步通信不需要时钟来进行数据同步,但是通信双方必须提前约定好字符格式通信速率

  3. 字符格式:需要通信双方在协议层规定好传输的数据包(字符帧)的格式,字符帧由起始位、数据位、校验位、停止位组成。这样通信双方就可以利用起始位和停止位实现同步。

  4. 通信速率:如果设置好了通信的字符格式,还需要通信双方约定好通信速率,也就是单位时间内传输的有效二进制数的个数,所以也被称为波特率(bps baud pre second)。一般串口通信常用的波特率为9600bps、38400bps、57600bps、115200bps

问题一:数据包中一定要有起始位吗

答:是,在异步通信中,没有单独的时钟信号来同步发送器和接收器。起始位提供了一个信号,告诉接收器数据传输的开始,这样接收器就可以开始同步接收后续的数据位。

问题二:数据包中一定要有停止位吗

答:否,在USART通信时,数据包一般是需要停止位的,但是停止位并不是必须的。但是在大多数情况下,为了确保通信的稳定性和可靠性,推荐使用停止位。

问题三,数据包中一定要有校验位吗

答:否,是否使用校验位需要看具体要求;例如当噪声较大需要提高通信可靠性时通常就需要校验位,当考虑到额外开销或性能提升时,就可以不需要校验位

问题四,波特率9600是什么意思

答:即1s内传输9600个bit位

(2)USART的使用流程

  • 打开KEIL5工程,然后把ST公司提供的标准外设库的关于USART的源文件添加到工程中
  • 参考并分析ST公司提供的代码例程以及提供的关于USART源文件的开头注释,进行理解

二,MCU  和  计算机如何进行通信

(1)硬件接线原理

  • 需要CH340芯片将USB通信协议转成TTL电平协议
  • 当数据通过USB先到达CH340芯片进行转换,从TXD输出;当数据从MCU发送时通过RXD先会经过CH340进行转换,从USB输出出去
  • 通过1-3, 2-4短接,使得MCU的发送端与计算机的接收端相接,MCU的接收端和计算机的发送端相接

(2)代码实现(一次中断只能接收和转发一个字节)

/*
  *******************************************************************************
  * @file    main.c 
  * @author  
  * @version V1.0
  * @date    2024/09/27
  * @brief   主要学习MCU内部的UART串口外设的应用,串口属于异步通信,采用全双工的
					   通信模式,具有TX发送端和RX接收端
						 
						 需要配置UART串口外设的波特率和字符帧格式(1bit停止位、8bit数据位)
						 
						 如果MCU打算使用UART1和PC端通信  则需要把USART1的接口1-3短接 2-4短接
  ******************************************************************************
*/

#include "stm32f4xx.h" //必须包含


/* Private typedef   用于记录用户自定义的一些数据类型的别名-------------------*/

/* Private define    用于记录用户自定义的类型,比如结构体、共用体、枚举-------*/

/* Private macro     用于记录用户自定义的宏定义-------------------------------*/

/* Private variables 用于记录用户自定义的全局变量-----------------------------*/

/* Private function prototypes 用于记录用户自定义的函数声明-------------------*/

/* Private functions 用于记录用户自定义的函数原型-----------------------------*/



/**
  * @brief  延时微秒
  * @param  
						@nus :待延时的微秒  注意:不能超过798915us
  * @retval None
  */
void delay_us(uint32_t nus)
{
	SysTick->CTRL = 0; 		// 关闭定时器
	SysTick->LOAD = nus * 21 - 1; 		// 设置重载值  nus * 21 - 1 
	SysTick->VAL 	= 0; 		// 清除当前值
	SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
	while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
	SysTick->CTRL = 0; 		// 关闭定时器
}

/**
  * @brief  延时毫秒
  * @param  
						@nms :待延时的毫秒  
  * @retval None
  */
void delay_ms(uint32_t nms)
{
	while(nms--)
	{
		SysTick->CTRL = 0; 		// 关闭定时器
		SysTick->LOAD = 21*1000 - 1; 		// 设置重载值  nus * 21 - 1 
		SysTick->VAL 	= 0; 		// 清除当前值
		SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
		while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
		SysTick->CTRL = 0; 		// 关闭定时器
	}
}


/**
  * @brief  Configures the USART1 Peripheral.
  * @param  None
  * @retval None
  * @note   USART1串口的IO口是PA9和PA10
  */
void USART1_Config(void)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART1的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = 9600;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART1, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART1的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
  //打开USART1串口
  USART_Cmd(USART1, ENABLE);
}



/**
  * @brief  程序的入口
  * @param  None
  * @retval None
  */
int main(void)
{

	//硬件的初始化
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组 2bit抢占(0~3) 2bit响应(0~3)
	USART1_Config();
	
	
  while(1)
  {	
		
  }
}


/**
* @brief  This function handles USRAT1 interrupt request.
* @param  None
* @retval None
*/
void USART1_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART1); 
		  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}


/********************** (C) COPYRIGHT Your Name [email protected]***END OF FILE****/

步骤总结:

  1. 打开GPIO时钟
  2. 打开串口时钟
  3. 配置IO口参数,设置IO口为复用模式
  4. 选择IO口的复用功能
  5. 配置串口参数(选择波特率,配置数据位、校验位、停止位的位数,关闭流控,打开全双工)
  6. 配置串口中断(选择中断通道,设置抢占优先级,设置响应优先级,打开中断通道)
  7. 选择串口的中断源(接收到数据则触发中断请求)
  8. 打开串口
  9. 编写串口中断服务函数

三,MCU 和  BLE 如何进行通信

(1)硬件接线原理

  • 当MCU与BLE进行通信时,需要3-5,4-6短接,这样蓝牙和MCU就能相互传输数据
  • 传感器的TX和RX需要与MCU的UART1的RX和TX进行交叉连接,就是说传感器TX需要连接在MCU的RX,传感器RX需要连接在MCU的TX

(2)BLE的基本概念

  1. 所用的BLE工作频率为2.4GHz
  2. 所用的BLE模式是从模式,即被动等待配对连接

(3)修改参数(具体看BLE数据手册)

想要使用手机连接蓝牙模块,则需要配置蓝牙模块的参数,对于蓝牙模块的参数,需要使用蓝牙模块的厂商提供的AT指令集实现。

(4.1)代码实现1(配置蓝牙信息)

/*
  *******************************************************************************
  * @file    main.c 
  * @author  
  * @version V1.0
  * @date    2024/09/29
  * @brief   使用UART2来修改蓝牙模块的参数 蓝牙模块默认采用9600bps进行通信,UART2
						 采用的引脚是PA2和PA3
						 
						 如果MCU打算使用UART1和PC端通信  则需要把USART1的接口1-3短接 2-4短接
						 如果MCU打算使用UART2和BLE端通信 则需要把USART2的接口1-3短接 2-4短接
  ******************************************************************************
*/

#include "stm32f4xx.h" //必须包含


/* Private typedef   用于记录用户自定义的一些数据类型的别名-------------------*/

/* Private define    用于记录用户自定义的类型,比如结构体、共用体、枚举-------*/

/* Private macro     用于记录用户自定义的宏定义-------------------------------*/

/* Private variables 用于记录用户自定义的全局变量-----------------------------*/

/* Private function prototypes 用于记录用户自定义的函数声明-------------------*/

/* Private functions 用于记录用户自定义的函数原型-----------------------------*/



/**
  * @brief  延时微秒
  * @param  
						@nus :待延时的微秒  注意:不能超过798915us
  * @retval None
  */
void delay_us(uint32_t nus)
{
	SysTick->CTRL = 0; 		// 关闭定时器
	SysTick->LOAD = nus * 21 - 1; 		// 设置重载值  nus * 21 - 1 
	SysTick->VAL 	= 0; 		// 清除当前值
	SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
	while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
	SysTick->CTRL = 0; 		// 关闭定时器
}

/**
  * @brief  延时毫秒
  * @param  
						@nms :待延时的毫秒  
  * @retval None
  */
void delay_ms(uint32_t nms)
{
	while(nms--)
	{
		SysTick->CTRL = 0; 		// 关闭定时器
		SysTick->LOAD = 21*1000 - 1; 		// 设置重载值  nus * 21 - 1 
		SysTick->VAL 	= 0; 		// 清除当前值
		SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
		while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
		SysTick->CTRL = 0; 		// 关闭定时器
	}
}


/**
  * @brief  Configures the USART1 Peripheral.
  * @param  None
  * @retval None
  * @note   USART1串口的IO口是PA9和PA10
  */
void USART1_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART1的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART1, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART1的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
  //打开USART1串口
  USART_Cmd(USART1, ENABLE);
}

/**
  * @brief  Configures the USART2 Peripheral.
  * @param  None
  * @retval None
  * @note   USART2串口的IO口是PA2和PA3
  */
void USART2_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART2的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2|GPIO_Pin_3;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART2的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART2, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART2的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
  //打开USART2串口
  USART_Cmd(USART2, ENABLE);
}


/**
* @brief  UART2发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART2_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART2,*str++);   //把字符串发给BLE
		  while( USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}


/**
  * @brief  程序的入口
  * @param  None
  * @retval None
  */
int main(void)
{

	//硬件的初始化
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组 2bit抢占(0~3) 2bit响应(0~3)
	USART1_Config(9600);   // 和PC端通信
	USART2_Config(9600);   // 和BLE 通信
	
	//给蓝牙模块发送测试指令 "AT\r\n"
	UART2_SendString("AT\r\n");
	
	//给蓝牙模块发送修改名称 "AT+NAMEGZ2404\r\n"
	UART2_SendString("AT+NAMEGZ2404\r\n");
	
	//给蓝牙模块发送重启指令
	UART2_SendString("AT+RESET\r\n");
	
  while(1)
  {	
		
  }
}


/**
* @brief  This function handles USRAT1 interrupt request.
* @param  None
* @retval None
*/
void USART1_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART1); 
		  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}

/**
* @brief  This function handles USRAT2 interrupt request.
* @param  None
* @retval None
*/
void USART2_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART2); 
		  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}


/********************** (C) COPYRIGHT Your Name [email protected]***END OF FILE****/

(4.2)代码实现2(实现手机发送字符串,通过蓝牙的透传模式,显示在显示屏)

???透传模式 ???

        当手机连接了蓝牙后,蓝牙会进入透传模式(该模式下无法修改蓝牙参数),透传模式只能进行数据的转发 

/*
  *******************************************************************************
  * @file    main.c 
  * @author  
  * @version V1.0
  * @date    2024/09/29
  * @brief   使用UART2来修改蓝牙模块的参数 蓝牙模块默认采用9600bps进行通信,UART2
						 采用的引脚是PA2和PA3
						 
						 如果MCU打算使用UART1和PC端通信  则需要把USART1的接口1-3短接 2-4短接
						 如果MCU打算使用UART2和BLE端通信 则需要把USART2的接口1-3短接 2-4短接
  ******************************************************************************
*/

#include "stm32f4xx.h" //必须包含
#include "string.h"

/* Private typedef   用于记录用户自定义的一些数据类型的别名-------------------*/

/* Private define    用于记录用户自定义的类型,比如结构体、共用体、枚举-------*/

/* Private macro     用于记录用户自定义的宏定义-------------------------------*/

/* Private variables 用于记录用户自定义的全局变量-----------------------------*/

uint8_t  u2_rxbuf[512] = {0}; //作为UART2的接收缓冲区
uint32_t u2_rxcnt = 0;        //作为UART2的接收计数器

/* Private function prototypes 用于记录用户自定义的函数声明-------------------*/

/* Private functions 用于记录用户自定义的函数原型-----------------------------*/



/**
  * @brief  延时微秒
  * @param  
						@nus :待延时的微秒  注意:不能超过798915us
  * @retval None
  */
void delay_us(uint32_t nus)
{
	SysTick->CTRL = 0; 		// 关闭定时器
	SysTick->LOAD = nus * 21 - 1; 		// 设置重载值  nus * 21 - 1 
	SysTick->VAL 	= 0; 		// 清除当前值
	SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
	while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
	SysTick->CTRL = 0; 		// 关闭定时器
}

/**
  * @brief  延时毫秒
  * @param  
						@nms :待延时的毫秒  
  * @retval None
  */
void delay_ms(uint32_t nms)
{
	while(nms--)
	{
		SysTick->CTRL = 0; 		// 关闭定时器
		SysTick->LOAD = 21*1000 - 1; 		// 设置重载值  nus * 21 - 1 
		SysTick->VAL 	= 0; 		// 清除当前值
		SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
		while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
		SysTick->CTRL = 0; 		// 关闭定时器
	}
}


/**
  * @brief  Configures the USART1 Peripheral.
  * @param  None
  * @retval None
  * @note   USART1串口的IO口是PA9和PA10
  */
void USART1_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART1的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART1, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART1的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
  //打开USART1串口
  USART_Cmd(USART1, ENABLE);
}

/**
  * @brief  Configures the USART2 Peripheral.
  * @param  None
  * @retval None
  * @note   USART2串口的IO口是PA2和PA3
  */
void USART2_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART2的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2|GPIO_Pin_3;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART2的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART2, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART2的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
  //打开USART2串口
  USART_Cmd(USART2, ENABLE);
}


/**
* @brief  UART2发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART2_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART2,*str++);   //把字符串发给BLE
		  while( USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}

/**
  * @brief  LED的初始化
  * @param  None
  * @retval None
  */
void LED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	//打开外设的时钟   LED --- PF9
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
	
	//配置引脚的参数
  GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL;
	
	//初始化GPIO端口
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOF,GPIO_Pin_9); //输出高电平	
}

void SG90_PWMConfig(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	//打开外设的时钟    PC6
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
	
	//打开定时器的时钟  TIM3_CH1
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	//配置引脚的参数
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_6;			
  GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP ;
  GPIO_Init(GPIOC, &GPIO_InitStructure); 
	
	//需要选择GPIO引脚要复用的功能
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);
	
	//配置定时器的时基  舵机就要20ms的时基
  TIM_TimeBaseStructure.TIM_Prescaler = 840-1;   // 84MHZ / 840 = 100000HZ --> 10us计数1次
	TIM_TimeBaseStructure.TIM_Period = 2000-1;		 // 20ms * 1000 / 10us = 2000次
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;   // 不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM3选择递增计数
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
	
	//配置定时器的通道
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;							//PWM模式1  CNT < CCR 通道有效
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //开启输出比较
  TIM_OCInitStructure.TIM_Pulse = 50;													  //比较值的初值 CCR寄存器的初值
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;			//输出极性为高电平  高电平有效
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);											//对TIM14的CH1进行初始化
	
	//使能预装载寄存器
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
	
	//使能自动重装载
	TIM_ARRPreloadConfig(TIM3, ENABLE);
	
	//打开定时器
	TIM_Cmd(TIM3, ENABLE);
}



/**
  * @brief  程序的入口
  * @param  None
  * @retval None
  */
int main(void)
{

	//硬件的初始化
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组 2bit抢占(0~3) 2bit响应(0~3)
	USART1_Config(9600);   // 和PC端通信
	USART2_Config(9600);   // 和BLE 通信
	LED_Init();
	SG90_PWMConfig();
	
	
	//给蓝牙模块发送测试指令 "AT\r\n"
	//UART2_SendString("AT\r\n");
	
	//给蓝牙模块发送修改名称 "AT+NAMEGZ2404\r\n"
	//UART2_SendString("AT+NAMEGZ2404\r\n");
	
	//给蓝牙模块发送重启指令
	//UART2_SendString("AT+RESET\r\n");
	
  while(1)
  {	
		//判断UART2是否接收到数据  假设接收到 "led_on",则让LED点亮
		if( u2_rxcnt > 0 && strstr((char *)u2_rxbuf,"led_on"))
		{
			
			GPIO_ResetBits(GPIOF,GPIO_Pin_9); //输出低电平	
			u2_rxcnt = 0; //计数器复位
			memset((char *)u2_rxbuf,0,512); //清空数组
		}
		
		//判断UART2是否接收到数据  假设接收到 "led_off",则让LED熄灭
		if( u2_rxcnt > 0 && strstr((char *)u2_rxbuf,"led_off"))
		{
			
			GPIO_SetBits(GPIOF,GPIO_Pin_9); //输出高电平	
			u2_rxcnt = 0; //计数器复位
			memset((char *)u2_rxbuf,0,512); //清空数组
		}
		
		if( u2_rxcnt > 0 && strstr((char *)u2_rxbuf,"begin"))
		{
			
			//指定动作:0 - 180
			for(int i = 50; i < 250 ; i++)
			{
				TIM_SetCompare1(TIM3,i);
				delay_ms(1);
			}
			
			u2_rxcnt = 0; //计数器复位
			memset((char *)u2_rxbuf,0,512); //清空数组
		}
		
		if( u2_rxcnt > 0 && strstr((char *)u2_rxbuf,"stop"))
		{
			//指定动作: 180 - 0
			for(int i = 250; i >= 50; i--)
			{
				TIM_SetCompare1(TIM3,i);
				delay_ms(1);
			}
				
			u2_rxcnt = 0; //计数器复位
			memset((char *)u2_rxbuf,0,512); //清空数组
		}
  }
}


/**
* @brief  This function handles USRAT1 interrupt request.
* @param  None
* @retval None
*/
void USART1_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART1); 
	  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}

/**
* @brief  This function handles USRAT2 interrupt request.
* @param  None
* @retval None
*/
void USART2_IRQHandler(void)
{

  //判断是否接收到数据
  if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      u2_rxbuf[u2_rxcnt++] = USART_ReceiveData(USART2); 
			if( u2_rxcnt >= 512 )
			{
				u2_rxcnt = 0;
			}
		  
			USART_SendData(USART1,u2_rxbuf[u2_rxcnt-1]);   //把接收的数据转发给PC端
  }
}


/********************** (C) COPYRIGHT Your Name [email protected]***END OF FILE****/

核心代码:

定义一个全局数组用于保存串口中断接收的数据,通过strstr函数来比较约定好的字符串,实现手机蓝牙控制外设的功能

//判断UART2是否接收到数据  假设接收到 "led_on",则让LED点亮
if( u2_rxcnt > 0 && strstr((char *)u2_rxbuf,"led_on"))
{
	
	GPIO_ResetBits(GPIOF,GPIO_Pin_9); //输出低电平	
	u2_rxcnt = 0; //计数器复位
	memset((char *)u2_rxbuf,0,512); //清空数组
}



/**
* @brief  串口中断服务函数,用于接收来自蓝牙的数据,保存并转发到显示屏上
* @param  None
* @retval None
*/
void USART2_IRQHandler(void)
{

  //判断是否接收到数据
  if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      u2_rxbuf[u2_rxcnt++] = USART_ReceiveData(USART2); 
			if( u2_rxcnt >= 512 )
			{
				u2_rxcnt = 0;
			}
		  
			USART_SendData(USART1,u2_rxbuf[u2_rxcnt-1]);   //把接收的数据转发给PC端
  }
}

总结:

  • 打开串口、GPIO时钟
  • 配置IO引脚为复用模式
  • 配置串口参数(配置波特率,配置格式,关闭流控,打开全双工)
  • 配置串口中断(配置中断通道,配置抢占优先级,配置响应优先级)
  • 选择串口中断源
  • 编写串口中断服务函数
  • 当蓝牙往MCU发送数据时,会触发串口中断服务函数
  • 当MCU向蓝牙发送数据时,不会触发串口中断服务函数

四,MCU 和  ESP8266 如何进行通信

(1)基本概念

  • STA  :让WIFI连接路由器,实现外网通信
  • AP    :让WIFI作为路由器,实现组网通信
  • STA + AP :WIFI既可以作为路由器实现组网通信;也可以连接路由器,实现外网通信

(2)代码实现(实现联网功能)

/*
  *******************************************************************************
  * @file    main.c 
  * @author  
  * @version V1.0
  * @date    2024/09/29
  * @brief   使用UART3来修改WIFI模块的参数,模块默认采用115200bps进行通信,UART3
						 采用的引脚是PB10和PB11
						 
						 如果MCU打算使用UART1和PC端通信  则需要把USART1的接口1-3短接 2-4短接
						 如果MCU打算使用UART2和BLE端通信 则需要把USART2的接口1-3短接 2-4短接
						 如果MCU打算使用UART3和WIFI通信  则需要把USART3的接口1-3短接 2-4短接
  ******************************************************************************
*/

#include "stm32f4xx.h" //必须包含
#include "string.h"

/* Private typedef   用于记录用户自定义的一些数据类型的别名-------------------*/

/* Private define    用于记录用户自定义的类型,比如结构体、共用体、枚举-------*/

/* Private macro     用于记录用户自定义的宏定义-------------------------------*/

/* Private variables 用于记录用户自定义的全局变量-----------------------------*/

uint8_t  u3_rxbuf[512] = {0}; //作为UART3的接收缓冲区
uint32_t u3_rxcnt = 0;        //作为UART3的接收计数器

/* Private function prototypes 用于记录用户自定义的函数声明-------------------*/

/* Private functions 用于记录用户自定义的函数原型-----------------------------*/



/**
  * @brief  延时微秒
  * @param  
						@nus :待延时的微秒  注意:不能超过798915us
  * @retval None
  */
void delay_us(uint32_t nus)
{
	SysTick->CTRL = 0; 		// 关闭定时器
	SysTick->LOAD = nus * 21 - 1; 		// 设置重载值  nus * 21 - 1 
	SysTick->VAL 	= 0; 		// 清除当前值
	SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
	while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
	SysTick->CTRL = 0; 		// 关闭定时器
}

/**
  * @brief  延时毫秒
  * @param  
						@nms :待延时的毫秒  
  * @retval None
  */
void delay_ms(uint32_t nms)
{
	while(nms--)
	{
		SysTick->CTRL = 0; 		// 关闭定时器
		SysTick->LOAD = 21*1000 - 1; 		// 设置重载值  nus * 21 - 1 
		SysTick->VAL 	= 0; 		// 清除当前值
		SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
		while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
		SysTick->CTRL = 0; 		// 关闭定时器
	}
}


/**
  * @brief  Configures the USART1 Peripheral.
  * @param  None
  * @retval None
  * @note   USART1串口的IO口是PA9和PA10
  */
void USART1_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART1的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART1, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART1的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
  //打开USART1串口
  USART_Cmd(USART1, ENABLE);
}

/**
  * @brief  Configures the USART2 Peripheral.
  * @param  None
  * @retval None
  * @note   USART2串口的IO口是PA2和PA3
  */
void USART2_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOA的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //打开USART2的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2|GPIO_Pin_3;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //配置UART2的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART2, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART2的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
  //打开USART2串口
  USART_Cmd(USART2, ENABLE);
}

/**
  * @brief  Configures the USART3 Peripheral.
  * @param  None
  * @retval None
  * @note   USART3串口的IO口是PB10和PB11
  */
void USART3_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef  NVIC_InitStructure;
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  //打开GPIOB的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  
  //打开USART3的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
  
  //选择GPIO引脚的复用功能
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
  
  //配置GPIO的引脚
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;				//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10|GPIO_Pin_11;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  
  //配置UART3的参数    最常用的格式: 1bit停止位  8bit数据位  No校验位  9600bps
  USART_InitStructure.USART_BaudRate = baud;													//波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;					//数据位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;							//停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;									//校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     //全双工
  USART_Init(USART3, &USART_InitStructure);
   
  //配置USART的中断
  NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
	//选择UART3的中断源   接收到数据则触发中断请求
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
	
  //打开USART3串口
  USART_Cmd(USART3, ENABLE);
}


/**
* @brief  UART1发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART1_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART1,*str++);   //把字符串发给BLE
		  while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}
/**
* @brief  UART2发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART2_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART2,*str++);   //把字符串发给BLE
		  while( USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}



/**
* @brief  UART3发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART3_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART3,*str++);   //把字符串发给WIFI
		  while( USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}

/**
  * @brief  程序的入口
  * @param  None
  * @retval None
  */
int main(void)
{

	//硬件的初始化
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组 2bit抢占(0~3) 2bit响应(0~3)
	USART1_Config(9600);   // 和PC端通信
	USART2_Config(9600);   // 和BLE 通信
	USART3_Config(115200); // 和WIFI通信
	
	//向WIFI模块发送测试指令 "AT\r\n"
	UART3_SendString("AT\r\n");
	delay_ms(500);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送设置模式指令 "AT+CWMODE_DEF=3\r\n"
	UART3_SendString("AT+CWMODE_DEF=3\r\n");
	delay_ms(500);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送连接热点指令 "AT+CWJAP_DEF=\"    \",\"    \"\r\n"
	UART3_SendString("AT+CWJAP_DEF=\"gz2404\",\"12345678\"\r\n");
	delay_ms(7000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送建立TCP连接指令 "AT+CIPSTART=\"TCP\",\"www.baidu.com\",80\r\n"   
	UART3_SendString("AT+CIPSTART=\"TCP\",\"www.baidu.com\",80\r\n");
	delay_ms(5000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送设置透传指令 "AT+CIPMODE=1\r\n"
	UART3_SendString("AT+CIPMODE=1\r\n");
	delay_ms(1000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;		
	
	//向WIFI模块发送设置透传指令 "AT+CIPSEND\r\n"
	UART3_SendString("AT+CIPSEND\r\n");
	delay_ms(5000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;		
	
  while(1)
  {	
		
  }
}


/**
* @brief  This function handles USRAT1 interrupt request.
* @param  None
* @retval None
*/
void USART1_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART1); 
		  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}

/**
* @brief  This function handles USRAT2 interrupt request.
* @param  None
* @retval None
*/
void USART2_IRQHandler(void)
{
	uint8_t data = 0;
	
  //判断是否接收到数据
  if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到变量data中
      data = USART_ReceiveData(USART2); 
		  USART_SendData(USART1,data);   //把接收的数据转发给PC端
  }
}


/**
* @brief  This function handles USRAT3 interrupt request.
* @param  None
* @retval None
*/

void USART3_IRQHandler(void)
{

	
  //判断是否接收到数据
  if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到缓冲区中
      u3_rxbuf[u3_rxcnt++] = USART_ReceiveData(USART3); 
  }
}


/********************** (C) COPYRIGHT Your Name [email protected]***END OF FILE****/

核心代码:

        发送AT指令联网时需要进行延时等待,提高代码健壮性

/**
* @brief  UART3发送字符串
  * @param  	
					  str :指的是待发送的字符串 
  * @retval None
  */
void UART3_SendString(char *str)
{
	while(*str != '\0')
	{
		  USART_SendData(USART3,*str++);   //把字符串发给WIFI
		  while( USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET); //等待上一次字节发送完成
	}
}

/**
  * @brief  程序的入口
  * @param  None
  * @retval None
  */
int main(void)
{

	//硬件的初始化
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组 2bit抢占(0~3) 2bit响应(0~3)
	USART1_Config(9600);   // 和PC端通信
	USART2_Config(9600);   // 和BLE 通信
	USART3_Config(115200); // 和WIFI通信
	
	//向WIFI模块发送测试指令 "AT\r\n"
	UART3_SendString("AT\r\n");
	delay_ms(500);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送设置模式指令 "AT+CWMODE_DEF=3\r\n"
	UART3_SendString("AT+CWMODE_DEF=3\r\n");
	delay_ms(500);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送连接热点指令 "AT+CWJAP_DEF=\"    \",\"    \"\r\n"
	UART3_SendString("AT+CWJAP_DEF=\"gz2404\",\"12345678\"\r\n");
	delay_ms(7000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送建立TCP连接指令 "AT+CIPSTART=\"TCP\",\"www.baidu.com\",80\r\n"   
	UART3_SendString("AT+CIPSTART=\"TCP\",\"www.baidu.com\",80\r\n");
	delay_ms(5000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;												//下标归零
	
	//向WIFI模块发送设置透传指令 "AT+CIPMODE=1\r\n"
	UART3_SendString("AT+CIPMODE=1\r\n");
	delay_ms(1000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;		
	
	//向WIFI模块发送设置透传指令 "AT+CIPSEND\r\n"
	UART3_SendString("AT+CIPSEND\r\n");
	delay_ms(5000);
	
	UART1_SendString((char *)u3_rxbuf);	//输出内容
	memset((char *)u3_rxbuf,0,512);			//清空数组
	u3_rxcnt = 0;		
	
  while(1)
  {	
		
  }
}


/**
* @brief  This function handles USRAT3 interrupt request.
* @param  None
* @retval None
*/

void USART3_IRQHandler(void)
{	
  //判断是否接收到数据
  if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
  {   
      //把串口收到的字节存储到缓冲区中
      u3_rxbuf[u3_rxcnt++] = USART_ReceiveData(USART3); 
  }
}
;