STM32 HAL 串口理论和实例


前言

因为最近老板突发奇想,想要做一个轻量级的AI工具,其他保密,我负责整理采集到的数据并上传到服务器。
趁此大好机会,赶紧复习一下串口的知识。一些和实际开发不相关的基础知识我就直接跳过了。

一、基本理论

1、单片机通信基础

  1. 同步和异步通信

在这里插入图片描述

  1. 波特率

在这里插入图片描述

  1. 常见串行通信接口

在这里插入图片描述

2、串口通信基础

串口概念

在这里插入图片描述

RS-232串口电平与CMOS以及TTL的对比

在这里插入图片描述

设备间的RS-232通信示意图

在这里插入图片描述

STM32串口与电脑USB口通信示意图

在这里插入图片描述

RS-232异步通信协议

在这里插入图片描述

3、UART 和 USART 的区别

区别点USARTUART
双工模式半双工全双工
速度
时钟信号要时钟信号和数据信号互相配合不需要
兼容USART可以做到UART的功能做不到USART
数据形式块形式一次一个字节
复杂度复杂简单
波特率不需要知道需要设置波特率
变速恒速传输可变速传输

4、USART

在这里插入图片描述
在这里插入图片描述
引脚数量以及对应的引脚,可以参考MCU手册以及STM32F1硬件手册。

  1. USART框图

在这里插入图片描述
在这里插入图片描述

  1. 设置USART/UART波特率

在这里插入图片描述
在这里插入图片描述
通过波特率计算得到要设置的寄存器参数。
在这里插入图片描述
在这里插入图片描述
将两个×16放在一起合并,简化公式。

  1. USART寄存器介绍

在这里插入图片描述
在这里插入图片描述
通过波特率计算得到要设置的寄存器参数。
在这里插入图片描述
在这里插入图片描述
将两个×16放在一起合并,简化公式。

  1. USART寄存器介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、HAL库的串口机制

HAL库外设初始化MSP回调机制

在这里插入图片描述
在这里插入图片描述

HAL库中断回调机制

在这里插入图片描述
在这里插入图片描述

USART/UART异步通信配置步骤

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

IO引脚复用功能

在这里插入图片描述
在这里插入图片描述

IO引脚复用 F4 F7 H7

每一个引脚都可以选择相应的复用,对F1进行了改进
在这里插入图片描述

同步模式

在同步模式下连接时需要以下引脚:
SCLK:发送器时钟输出。该引脚用于输出发送器数据时钟,以便按照 SPI 主模式进行同步发送(起始位和结束位上无时钟脉冲,可通过软件向最后一个数据位发送时钟脉冲)。RX 上可同步接收并行数据。这一点可用于控制带移位寄存器的外设(如 LCD 驱动器)。时钟相位和极性可通过软件编程。在智能卡模式下,SCLK 可向智能卡提供时钟。在硬件流控制模式下需要以下引脚:
nCTS:“清除以发送”用于在当前传输结束时阻止数据发送(高电平时)。
nRTS:“请求以发送”用于指示 USART 已准备好接收数据(低电平时)。

中断控制

出现以下情况时,可使UART 产生中断:
在这里插入图片描述
由于所有中断事件在发送到中断控制器之前会一起进行“或运算”操作,所以任意时刻 UART 只能向中断产生一个中断请求。通过查询中断状态函数,软件可以在同一个中断服务函数里处理多个中断事件(多个并列的if 语句)。

FIFO 操作

Stellaris 系列ARM 的UART 模块包含有2 个16 字节的FIFO:一个用于发送,另一个用于接收。可以将两个FIFO 分别配置为以不同深度触发中断。可供选择的配置包括:1/8、 1/4、1/2、3/4 和7/8 深度。例如,如果接收FIFO 选择1/4,则在UART 接收到4 个数据时产生接收中断。

  1. 发送FIFO的基本工作过程

只要有数据填充到发送FIFO 里,就会立即启动发送过程。由于发送本身是个相对缓慢的过程,因此在发送的同时其它需要发送的数据还可以继续填充到发送 FIFO 里。当发送 FIFO 被填满时就不能再继续填充了,否则会造成数据丢失,此时只能等待。这个等待并不会很久,以9600 的波特率为例,等待出现一个空位的时间在1ms 上下。发送 FIFO 会按照填入数据的先后顺序把数据一个个发送出去,直到发送 FIFO 全空时为止。已发送完毕的数据会被自动清除,在发送FIFO 里同时会多出一个空位。

  1. 接收FIFO的基本工作过程

当硬件逻辑接收到数据时,就会往接收FIFO 里填充接收到的数据。程序应当及时取走这些数据,数据被取走也是在接收FIFO 里被自动删除的过程,因此在接收 FIFO 里同时会多出一个空位。如果在接收 FIFO 里的数据未被及时取走而造成接收FIFO 已满,则以后再接收到数据时因无空位可以填充而造成数据丢失。

  1. FIFO解决的问题

收发FIFO 主要是为了解决UART 收发中断过于频繁而导致CPU 效率不高的问题而引入的。在进行 UART 通信时,中断方式比轮询方式要简便且效率高。但是,如果没有收发 FIFO,则每收发一个数据都要中断处理一次,效率仍然不够高。如果有了收发FIFO,则可以在连续收发若干个数据(可多至14 个)后才产生一次中断然后一并处理,这就大大提高了收发效率。
完全不必要担心FIFO 机制可能带来的数据丢失或得不到及时处理的问题,因为它已经帮你想到了收发过程中存在的任何问题,只要在初始化配置UART 后,就可以放心收发了, FIFO 和中断例程会自动搞定一切。

串口频繁进入中断导致的问题

void USART1_IRQHandler(void)         
{
    if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
    {
        USART_ReceiveData(USART1);
        USART_ClearFlag(USART1, USART_FLAG_ORE);
    }

    if( USART_GetITStatus(USART1,USART_IT_RXNE) != RESET )    
    {
        USART_ClearITPendingBit(USART1,USART_IT_RXNE);
    
        USART_ClearFlag(USART1,USART_IT_RXNE);
        Uart1_Val.Real_Data = USART_ReceiveData( USART1 );
        Uart1_IT_Reci_Fuc();

    }
}

二、CubeMX 配置串口

选择处理器

在这里插入图片描述
我这里项目用的是 H750ZBT6 的芯片

选择连接设备的串口

在这里插入图片描述
设置为异步通信、不使用硬件流控制。
可以发现另一侧的芯片引脚图上的引脚自动显示为 USART 的 TX、RX 引脚了:
在这里插入图片描述

在GPIO Settings中设置引脚

在这里插入图片描述
修改为快速输出和上拉电阻之后,modify 标志位会自动更新。

设置Parameter Settings

设置波特率115200,8位字长,无奇偶校验位,1个停止位,接受和发送均开启,过采样8位;
在这里插入图片描述
FIFO开了也没问题,开了效果更好。

时钟配置

当你选择了波特率之后,时钟会自动调整为符合波特率的分频系数:
在这里插入图片描述

NVIC Settings

在NVIC Settings中USART2 global interrupt勾选Enable:
在这里插入图片描述

在A->Z中找到NVIC,并按如下图配置

继续用到了中断,那么就一定要设置中断控制器 NVIC 。
在这里插入图片描述

Project Manage设置

在这里插入图片描述

创建代码

在创建的工程中找到main.c就会有串口初始化代码:
在这里插入图片描述

生成代码一览

初始化函数

static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_8;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
  {
    Error_Handler();
  }

在stm32h7xx_hal_msp.c中可以看到USART2的回调函数

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(huart->Instance==USART2)
  {
  /* USER CODE BEGIN USART2_MspInit 0 */
  /* USER CODE END USART2_MspInit 0 */
  /* Initializes the peripherals clock
 */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART2;
    PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

    /* Peripheral clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
    /* USER CODE BEGIN USART2_MspInit 1 */
    /* USER CODE END USART2_MspInit 1 */
  }
}

生成的代码真的不行,还是得继续优化优化。

HAL 串口 API

  1. 串口初始化(波特率/停止位/使能串口)
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

参数:串口句柄

typedef struct
{
 USART_TypeDef               *Instance;     //执行寄存器基地址,HAL库已定义,若为串口1,取值为 USART1 
 UART_InitTypeDef            Init;          //设置串口各个参数,下面有结构体
 UART_AdvFeatureInitTypeDef  AdvancedInit; 
 uint8_t                     *pTxBuffPtr;   //发送数据缓存指针
 uint16_t                    TxXferSize;    //发送数据量
 __IO uint16_t               TxXferCount;   //剩余发送数据量
 uint8_t                     *pRxBuffPtr;   //接收数据缓存指针
 uint16_t                    RxXferSize;    //单次最大接收量
 __IO uint16_t               RxXferCount;   //剩余要接收的数据量
 uint16_t                    Mask;          //
 DMA_HandleTypeDef           *hdmatx;       //DMA发送句柄
 DMA_HandleTypeDef           *hdmarx;       //DMA接收句柄
 HAL_LockTypeDef             Lock; 
 __IO HAL_UART_StateTypeDef  gState; 
 __IO HAL_UART_StateTypeDef  RxState; 
 __IO uint32_t               ErrorCode; 
}UART_HandleTypeDef;

typedef struct
{
 uint32_t BaudRate;        //波特率
 uint32_t WordLength;      //字长,一般是8位:8 位字长数据格式 UART_WORDLENGTH_8B
 uint32_t StopBits;        //停止位,一般是一位:UART_STOPBITS_1
 uint32_t Parity;          //奇偶校验
 uint32_t Mode;            //收/发模式设置,全双工:UART_MODE_TX_RX
 uint32_t HwFlowCtl;       //硬件流设置
 uint32_t OverSampling;    //过采样设置,一般过采样为 16 倍或 8 倍
 uint32_t OneBitSampling;  //一位采样
 uint32_t Prescaler;       //分频
 uint32_t FIFOMode;        //FIFO 模式
 uint32_t TXFIFOThreshold; //发送 FIFO 阈值
 uint32_t RXFIFOThreshold; //接受 FIFO 阈值
}UART_InitTypeDef

调用函数 HAL_UART_Init
对串口进行初始化的时候,需要先设置 Instance 和 Init 两个成员变量的值。

函数 HAL_UART_Init 使用的一般格式为:

//UART 初始化设置
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler);

HAL_UART_Init 内部会调用串口使能函数使能相应串口,
所以调用了该函数之后我们就不需要重复使能串口了。当然,HAL 库也提供了具体的串口使能和关闭方法,具体使用方法如下:

__HAL_UART_ENABLE(handler); //使能句柄 handler 指定的串口
__HAL_UART_DISABLE(handler); //关闭句柄 handler 指定的串口

这里还需要提醒大家,串口作为一个重要外设,在调用的初始化函数 HAL_UART_Init 内部,会先调用 MSP 初始化回调函数进行 MCU 相关的初始化,函数为:

void HAL_UART_MspInit(UART_HandleTypeDef *huart);

我们在程序中,只需要重写该函数即可。一般情况下,该函数内部用来编写 IO 口初始化,时钟使能以及 NVIC 配置。

  1. 使能串口和GPIO时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();  //使能 GPIOA 时钟
  1. GPIO 口初始化设置(速度,上下拉等)以及复用映射配置

HAL 库中 IO 口初始化参数设置和复用映射配置是在函数
HAL_GPIO_Init 中一次性完成。
这里要复用 PA9 和 PA10 为串口发送接收相关引脚,我们需要配置 IO 口为复用,同时复用映射到串口 1。

GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_9|GPIO_PIN_10;  //PA9/PA10
GPIO_Initure.Mode=GPIO_MODE_AF_PP;        //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP;            //上拉
GPIO_Initure.Speed= GPIO_SPEED_FREQ_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1;   //复用为 USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);       //初始化 PA9/PA10
  1. 开启串口相关中断,配置串口中断优先级

HAL 库中定义了一个使能串口中断的标识符 __HAL_UART_ENABLE_IT,可以把它当一个函数来使用,例如我要使能接收完成中断:

__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); //开启接收完成中断

huart 是串口句柄,UART_HandleTypeDef 类型。
UART_IT_RXNE 是我们要开启的中断类型值,可选值在头文件 stm32h7xx_hal_uart.h 中有宏定义。
有开启中断就有关闭中断,操作方法为:

__HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断

对于中断优先级配置,方法就非常简单,详细知识情参考 4.5 小节相关知识。参考方法为:

HAL_NVIC_EnableIRQ(USART1_IRQn);       //使能 USART1 中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3
  1. 中断服务函数

串口 1 中断服务函数为:

void USART1_IRQHandler(void) ;

当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写们相应的逻辑代码即可。HAL 库实际上对中断处理过程进行了完整的封装。

  1. 串口数据接收和发送

STM32H7 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包
含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也
是存在该寄存器内。HAL 库操作 USART_DR 寄存器发送数据的函数是:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, 
                                    uint8_t            *pData, 
                                    uint16_t           Size, 
                                    uint32_t           Timeout);

通过该函数向串口寄存器 USART_DR 写入一个数据。
HAL 库操作 USART_DR 寄存器读取串口接收到的数据的函数是:

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, 
                                   uint8_t            *pData, 
                                   uint16_t           Size, 
                                   uint32_t           Timeout);

通过该函数可以读取串口接受到的数据。

汇总设计

//初始化 IO 串口 1 ; bound:波特率
void uart_init(u32 bound)
{
    //UART 初始化设置
    UART1_Handler.Instance=USART1;                    //USART1
    UART1_Handler.Init.BaudRate=bound;                //波特率
    UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位
    UART1_Handler.Init.StopBits=UART_STOPBITS_1;      //一个停止位
    UART1_Handler.Init.Parity=UART_PARITY_NONE;       //无奇偶校验位
    UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
    UART1_Handler.Init.Mode=UART_MODE_TX_RX;          //收发模式
    HAL_UART_Init(&UART1_Handler);                    //HAL_UART_Init()会使能 UART1

    //该函数会开启接收中断:标志位 UART_IT_RXNE,同时设置接收缓冲以及接收缓冲接收最大数据量
    HAL_UART_Receive_IT(&UART1_Handler,(u8 *)aRxBuffer,RXBUFFERSIZE); 
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    //GPIO 端口设置
    GPIO_InitTypeDef GPIO_Initure;
    if(huart->Instance==USART1)//如果是串口 1,进行串口 1 MSP 初始化
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();            //使能 GPIOA 时钟
        __HAL_RCC_USART1_CLK_ENABLE();           //使能 USART1 时钟
        GPIO_Initure.Pin=GPIO_PIN_9;             //PA9
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;       //复用推挽输出
        GPIO_Initure.Pull=GPIO_PULLUP;           //上拉
        GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
        GPIO_Initure.Alternate=GPIO_AF7_USART1;  //复用为 USART1
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);      //初始化 PA9
        GPIO_Initure.Pin=GPIO_PIN_10;            //PA10
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);      //初始化 PA10
#if EN_USART1_RX
        HAL_NVIC_EnableIRQ(USART1_IRQn);         //使能 USART1 中断通道
        HAL_NVIC_SetPriority(USART1_IRQn,3,3);   //抢占优先级 3,子优先级 3
#endif
    }
}
/* 通过判断宏定义标识符 EN_USART1_RX 的值来确定是否开启串口中断通道和设置串口1 中断优先级。
 * 标识符 EN_USART1_RX 在头文件 usart.h 中有定义,默认情况下我们设置为1。
 */

#define EN_USART1_RX 1     //使能(1)/禁止(0)串口 1 接收

//编写中断服务函数
void USART1_IRQHandler(void) 
{ 
    HAL_UART_IRQHandler(&UART1_Handler);   //调用 HAL 库中断处理公用函数
    ......                                 //中断处理完成后的结束工作
}

对于中断服务函数中的 HAL_UART_IRQHandler() 在 HAL 库中已经实现了,如下:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
 uint32_t isrflags = READ_REG(huart->Instance->ISR);
 uint32_t cr1its = READ_REG(huart->Instance->CR1);
 uint32_t cr3its = READ_REG(huart->Instance->CR3);
 uint32_t errorflags;//此处省略部分代码
 if (errorflags == RESET)
 {
     if(((isrflags & USART_ISR_RXNE) != RESET) && 
        ( ((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_RXFTIE) != RESET)) )
     {
         UART_Receive_IT(huart);
         return;
     }
 }//此处省略部分代码
}

HAL_UART_IRQHandler 内部通过判断中断类型是否为接收完成中断,确定是否调用 HAL 另外一个函数 UART_Receive_IT()。函数 UART_Receive_IT()的作用是把每次中断接收到的字符保存在串口句柄的缓存指针 pRxBuffPtr 中,同时每次接收一个字符,其计数器 RxXferCount 减 1,直到接收完成 RxXferSize 个字符之后 RxXferCount 设置为 0,同时调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理。
串口接收中断的一般流程:
在这里插入图片描述

三、案例:ESP8266

static ESP_T gEsp = {0};
static UART_HandleTypeDef   gUART2  = {0};

// 功能描述: ESP模块初始化
int32_t ESP_Init(void)
{
    GPIO_InitTypeDef tGPIO;

    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    //TX
    tGPIO.Pin        = GPIO_PIN_2;
    tGPIO.Mode       = GPIO_MODE_AF_PP;
    tGPIO.Pull       = GPIO_PULLUP;
    tGPIO.Speed      = GPIO_SPEED_FREQ_HIGH;
    tGPIO.Alternate  = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOA, &tGPIO);
    //RX
    tGPIO.Pin = GPIO_PIN_3;
    tGPIO.Alternate  = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOA, &tGPIO);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
    HAL_NVIC_SetPriority(USART2_IRQn, 3, 2);

    gUART2.Instance                      = USART2;
    gUART2.Init.BaudRate                 = 115200;
    gUART2.Init.WordLength               = UART_WORDLENGTH_8B;
    gUART2.Init.StopBits                 = UART_STOPBITS_1;
    gUART2.Init.Parity                   = UART_PARITY_NONE;
    gUART2.Init.Mode                     = UART_MODE_TX_RX;
    gUART2.Init.HwFlowCtl                = UART_HWCONTROL_NONE;
    gUART2.Init.OverSampling             = UART_OVERSAMPLING_16;
    gUART2.Init.OneBitSampling           = UART_ONE_BIT_SAMPLE_DISABLE;
    gUART2.Init.ClockPrescaler           = UART_PRESCALER_DIV1;
    gUART2.AdvancedInit.AdvFeatureInit   = UART_ADVFEATURE_NO_INIT;
    if(HAL_UART_Init(&gUART2) != HAL_OK) {}
    if(HAL_UARTEx_SetTxFifoThreshold(&gUART2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) {}
    if(HAL_UARTEx_SetRxFifoThreshold(&gUART2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) {}
    if(HAL_UARTEx_DisableFifoMode(&gUART2) != HAL_OK) {}
    HAL_UART_Receive_IT(&gUART2, &gEsp.bRxData, 1);

    ESP_ExitTransmit();
    while(ESP_ConnectAP(CFG_SSID, CFG_PASSWORD) < 0);
    while(ESP_ConnectServer(CFG_IP, CFG_PORT) < 0);
    return 0;
}

/****************************************************************************************
* 功能描述: 中断接收处理
* 输入参数: void
* 输出参数: void
* 返 回 值: void
****************************************************************************************/
void USART2_IRQHandler(void)
{
    uint32_t iCnt = 0;

    HAL_UART_IRQHandler(&gUART2);
    while(HAL_UART_GetState(&gUART2) != HAL_UART_STATE_READY)
    {
        iCnt++;
        if(iCnt > 0x1FFFF) break;
    }
    iCnt=0;
    while(HAL_UART_Receive_IT(&gUART2, &gEsp.bRxData, 1) != HAL_OK)
    {
        iCnt++;
        if(iCnt > 0x1FFFF) break;
    }
    if(gEsp.iCnt >= sizeof(gEsp.aBuf))
    {
        gEsp.iCnt = 0;
    }
    gEsp.aBuf[gEsp.iCnt++] = gEsp.bRxData;
    //Shell_Putc(gEsp.bRxData);
    if(gEsp.iCnt > 6)
    {
        gEsp.iState  = ESP_GetAckSta(gEsp.aBuf);
        if((gEsp.iState != AT_ACK_UNKNOW) && (gEsp.iState != AT_ACK_BUSY))
        {
            gEsp.pCmd = ESP_GetAckAt(gEsp.aBuf);
            ESP_Handle();
            gEsp.iCnt = 0;
            memset(gEsp.aBuf, 0, sizeof(gEsp.aBuf));
        }
    }
}

//中断接收处理
void USART2_IRQHandler(void)
{
    uint32_t iCnt = 0;

    HAL_UART_IRQHandler(&gUART2);
    while(HAL_UART_GetState(&gUART2) != HAL_UART_STATE_READY)
    {
        iCnt++;
        if(iCnt > 0x1FFFF) break;
    }
    iCnt=0;
    while(HAL_UART_Receive_IT(&gUART2, &gEsp.bRxData, 1) != HAL_OK)
    {
        iCnt++;
        if(iCnt > 0x1FFFF) break;
    }
    if(gEsp.iCnt >= sizeof(gEsp.aBuf))
    {
        gEsp.iCnt = 0;
    }
    gEsp.aBuf[gEsp.iCnt++] = gEsp.bRxData;
}

//发送 AT 指令
static int32_t ESP_SendCmd(const char *pCmd)
{
    HAL_UART_Transmit(&gUART2, (const uint8_t *)pCmd, strlen(pCmd), 1000);
    while(__HAL_UART_GET_FLAG(&gUART2, UART_FLAG_TC)!=SET);    //等待串口回应
    return 0;
}

#define AT_ACK_ERROR    -1
#define AT_ACK_OK       0
#define AT_ACK_BUSY     1
#define AT_ACK_UNKNOW   2

//ESP8266对象结构体
typedef struct
{
    uint8_t     aIP[4];     //IP
    uint8_t     aMAC[6];    //MAC
    uint8_t     bRxData;    //接收单字节数据
    uint8_t     *pCmd;      //应答命令
    int32_t     iState;     //应答状态
    uint32_t    iCnt;       //接收数据计数
    uint8_t     aBuf[512];  //接收数据缓冲
    int32_t     iAck;
}ESP_T;

/****************************************************************************************
* 功能描述: 获取应答状态
* 输入参数: pBuf: 接收数据缓存
* 返 回 值: 应答命令状态
****************************************************************************************/
static int32_t ESP_GetAckSta(uint8_t *pBuf)
{
    if(strstr((const char *)pBuf, "ERROR") != NULL)
    {
        return AT_ACK_ERROR;
    }
    else if( (strstr((const char *)pBuf, "OK") != NULL) ||
             (strstr((const char *)pBuf, ">") != NULL))
    {
        return AT_ACK_OK;
    }
    else if((strstr((const char *)pBuf, "WIFI ") != NULL) ||
            strstr((const char *)pBuf, "busy") != NULL)
    {
        return AT_ACK_BUSY;
    }
    else
    {
        return AT_ACK_UNKNOW;
    }
}

/****************************************************************************************
* 功能描述: 等待应答
* 输入参数: pAt: AT指令指针
            timeout: 超时时间, 单位秒
* 返 回 值: 0.成功 -1.失败
****************************************************************************************/
static int32_t ESP_WaitAck(uint8_t *pCmd, uint32_t timeout)
{
    int8_t i=0;

    gEsp.pCmd = AT_UNKNOW;
    gEsp.iState  = AT_ACK_UNKNOW;
    do
    {
        for(i=0; i<100; i++)
        {
            HAL_Delay(10);
            if(gEsp.iState == AT_ACK_OK || gEsp.iState == AT_ACK_ERROR)
            {
                return 0;
            }
        }
        if(gEsp.iState == AT_ACK_BUSY)
        {
            continue;
        }
    }while(timeout--);
    return -1;
}

//举例:获取GMR信息
int32_t ESP_GetGMR(uint8_t *pBuf)
{
    ESP_SendCmd(AT_GMR);
    if(ESP_WaitAck(AT_GMR, 3) < 0)
    {
        return -1;
    }
    return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值