[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的串口调试工具上检测到回复的信息.
我遇到的坑
- ESP8266的使能引脚和复位引脚配置错误,导致ESP8266的通信出现一些问题.
- 代码中的发送指令使用HAL_UART_Transmit_DMA,导致发送/接收到的指令不全,因此改用HAL_UART_Transmit函数.
- 网上找到的代码和野火提供的代码大多都是基于STM32标准库和Keil开发环境,使用HAL库和CubeMX开发的代码不多,而且最开始学习STM32也是基于STM32标准库,因此会有一些不适应.