(总结)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 xxxx@126.com***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 xxxx@126.com***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 xxxx@126.com***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 xxxx@126.com***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); 
      }
    }

    作者:100 Miles485

    物联沃分享整理
    物联沃-IOTWORD物联网 » (总结)STM32中USART原理及应用(PC、BLE、ESP8266通信实现)

    发表回复