[HAL]STM32与ESP8266的交互

使用Clion+CubeMX开发,开发板为野火指南者,芯片为STM32F103ZET6.

简单实现使用串口经过STM32与ESP8266通信。

Cube MX设置

串口设置

开发板使用USART1与PC通信,因此设置USART1,使用PA9和PA10端口通信,参数为115200-8-N-1。同时添加RX和TX的DMA设置,NVIC开启串口的global中断。

板载的ESP8266与STM芯片使用USART3通信,使用PB10和PB11端口,参数为115200-8-N-1.同理也需要添加RX和TX的DMA设置,NVIC开启串口global中断.

控制引脚设置

ESP8266需要添加片选引脚和复位引脚.板载ESP8266使用PB8作为片选引脚(高电平为使能),PB9作为复位引脚(低电平复位).

其他设置

SYS中设置调试器模式.

RCC中设置HSE使用外部晶振,在Clock Configuration中设置时钟.

生成代码时选择根据外设生成对应的.C/.H文件.

Clion代码

使用DMA接收数据,使用空闲中断处理.将PC发送的数据转发给ESP8266并将ESP8266的回复信息转发到PC.需要注意添加的代码位于指定的user code区间防止被覆盖.

usart设置

在usart.c中添加必要的变量和缓冲区.

// BUFFER_SIZE定义在main.h中,不大于200
/* USER CODE BEGIN 0 */
uint8_t usart1_rx_buffer[BUFFER_SIZE];
volatile uint8_t usart1_rx_len = 0;
volatile uint8_t usart1_recv_end_flag = 0;
uint8_t usart3_rx_buffer[BUFFER_SIZE];
volatile uint8_t usart3_rx_len = 0;
volatile uint8_t usart3_recv_end_flag = 0;
/* USER CODE END 0 */

在usart.h中添加变量声明

/* USER CODE BEGIN Private defines */
extern uint8_t usart1_rx_buffer[BUFFER_SIZE];
extern volatile uint8_t usart1_rx_len;
extern volatile uint8_t usart1_recv_end_flag;
extern uint8_t usart3_rx_buffer[BUFFER_SIZE];
extern volatile uint8_t usart3_rx_len;
extern volatile uint8_t usart3_recv_end_flag;
/* USER CODE END Private defines */

GPIO设置

在main.c的while(1)循环之前设置ESP8266使能和复位

/* USER CODE BEGIN 2 */
// 设置ESP8266选择
HAL_GPIO_WritePin(CH_GPIO_Port, CH_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
/* USER CODE END 2 */

中断响应

在main.c的while(1)循环之前开启接收DMA和空闲中断

/* USER CODE BEGIN 2 */
// 开启USART的接收DMA传输中断
HAL_UART_Receive_DMA(&huart1, usart1_rx_buffer, BUFFER_SIZE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart3, usart3_rx_buffer, BUFFER_SIZE);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
/* USER CODE END 2 */

在stm32f1xx_it.c中修改中断响应,分别修改USART1_IRQHandler和USART3_IRQHandler.当接收DMA接收完成时,依次触发DMA接收中断和USART全局中断.

/* USER CODE BEGIN USART1_IRQn 0 */
// 局部变量
uint32_t tmp_flag = 0;
uint32_t temp;
// 空闲状态
tmp_flag = __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE);
if((tmp_flag != RESET))
{
    // 关闭空闲状态
    __HAL_UART_CLEAR_IDLEFLAG(&huart1);
    // 清空寄存器
    temp = huart1.Instance->SR;
    temp = huart1.Instance->DR;
    HAL_UART_DMAStop(&huart1);
    // 获取未传输比特数 用于计算已传输比特数
    temp = hdma_usart1_rx.Instance->CNDTR;
    usart1_rx_len = BUFFER_SIZE - temp;
    // 开始标志位
    usart1_recv_end_flag = 1;
}
/* USER CODE END USART1_IRQn 0 */

在main.c的循环部分添加中断处理部分

// PC传输中断
if(usart1_recv_end_flag == 1)
{
    // 发送到ESP8266
    HAL_UART_Transmit(&huart3, usart1_rx_buffer, usart1_rx_len, 0xFF);
    // 清空缓冲和标志位
    memset(usart3_rx_buffer, 0, BUFFER_SIZE);
    usart1_rx_len = 0;
    usart1_recv_end_flag = 0;
    // 开启接收中断
    HAL_UART_Receive_DMA(&huart1, usart1_rx_buffer, BUFFER_SIZE);
}
// ESP8266传输中断
if(usart3_recv_end_flag == 1)
{
    // 发送到PC显示
    HAL_UART_Transmit(&huart1, usart3_rx_buffer, usart3_rx_len, 0xFF);
    // 清空缓存和标志位
    memset(usart3_rx_buffer, 0, BUFFER_SIZE);
    usart3_rx_len = 0;
    usart3_recv_end_flag = 0;
    // 开启接收中断
    HAL_UART_Receive_DMA(&huart3, usart3_rx_buffer, BUFFER_SIZE);
}
/* USER CODE END WHILE */

从而实现STM32转发PC和ESP8266的串口通信,接下来就可以通过ESP8266的AT指令进行设置和操作.同时在PC的串口调试工具上检测到回复的信息.

我遇到的坑

  1. ESP8266的使能引脚和复位引脚配置错误,导致ESP8266的通信出现一些问题.
  2. 代码中的发送指令使用HAL_UART_Transmit_DMA,导致发送/接收到的指令不全,因此改用HAL_UART_Transmit函数.
  3. 网上找到的代码和野火提供的代码大多都是基于STM32标准库和Keil开发环境,使用HAL库和CubeMX开发的代码不多,而且最开始学习STM32也是基于STM32标准库,因此会有一些不适应.
Lei Yang
Lei Yang
PhD candidate

My research interests include visual speech recognition and semantics segmentation.