目录
引言
在物联网(IoT)和嵌入式系统开发中,设备之间的通信是实现功能的关键环节之一。其中,串口通信作为一种简单而高效的通信方式,常被用于设备与上位机(如电脑)之间的数据交互。本文不再详细介绍串口通信协议,而是详细介绍如何使用 ESP-IDF框架下,通过ESP32的串口与上位机进行通信。通过本文的介绍,即使是初学者也能快速掌握ESP32串口通信的基本方法,并将其应用到自己的项目中。
硬件准备
1.ESP32-DevKitC开发板,如下图:
2.USB转TTL模块,就是常用的CH340模块,如下图:
3.一条数据线和杜邦线若干。
软件环境
1.esp-idf+vscode开发环境,具体安装方法请移步至官方文档或相关教程,这里不作深入探讨。
2.串口上位机,任选即可。
ESP32串口资源及常用API介绍
板上串口资源
ESP32 DevKit-C 开发板基于 ESP32 芯片,该芯片总共支持 3个 UART 接口,分别是 UART0、UART1 和 UART2。
UART0
-
用途:通常用于调试日志输出和程序下载。
-
引脚:默认情况下,UART0 的 TX 和 RX 引脚分别连接到开发板的 GPIO1 和 GPIO3。
UART1
-
用途:用户自定义通信,通常用于与其他设备的串口通信。
-
引脚:可通过软件配置任意 GPIO 引脚作为 UART1 的 TX 和 RX。
UART2
-
用途:用户自定义通信,也可以用于与其他设备的串口通信。
-
引脚:同样可以通过软件配置任意 GPIO 引脚作为 UART2 的 TX 和 RX。
常用API
1.uart_driver_install
该函数用于安装 UART 驱动程序,并为 UART 驱动分配所需的内部资源。
函数原型
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int event_queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags);
参数
-
uart_num:UART 端口号(如 UART_NUM_0、UART_NUM_1、UART_NUM_2)。
-
rx_buffer_size:接收缓冲区大小。
-
tx_buffer_size:发送缓冲区大小。
-
event_queue_size:事件队列大小。
-
uart_queue:指向事件队列句柄的指针,如果不需要事件队列,可以传入
NULL
。 -
intr_alloc_flags:中断分配标志。
2.uart_param_config
该函数用于配置 UART 的通信参数。
函数原型
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);
参数
-
uart_num:UART 端口号。
-
uart_config:指向 uart_config_t结构体的指针,该结构体包含波特率、数据位、停止位、奇偶校验位等参数。
3.uart_set_pin
该函数用于将 UART 的信号分配到指定的 GPIO 引脚。
函数原型
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num);
参数
-
uart_num:UART 端口号。
-
tx_io_num:TX 信号的 GPIO 引脚编号。
-
rx_io_num:RX 信号的 GPIO 引脚编号。
-
rts_io_num:RTS 信号的 GPIO 引脚编号,如果不使用,可以传入
UART_PIN_NO_CHANGE
。 -
cts_io_num:CTS 信号的 GPIO 引脚编号,如果不使用,可以传入
UART_PIN_NO_CHANGE
。
4.uart_write_bytes
该函数用于通过 UART 发送数据。
函数原型
int uart_write_bytes(uart_port_t uart_num, const void *src, size_t size);
参数
-
uart_num:UART 端口号。
-
src:指向要发送的数据的指针。
-
size:要发送的数据的大小。
5.uart_read_bytes
该函数用于从 UART 的接收缓冲区读取数据。
函数原型
int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, TickType_t ticks_to_wait);
参数
-
uart_num:UART 端口号。
-
buf:指向接收缓冲区的指针。
-
length:要读取的最大字节数。
-
ticks_to_wait:等待时间,单位为 FreeRTOS 的 tick。如果设置为
portMAX_DELAY
,则会阻塞直到数据可用。
注意事项
-
引脚复用:ESP32 的 GPIO 引脚具有多种功能,因此在使用 UART1 和 UART2 时,需要通过软件配置来指定具体的引脚,下图来自乐鑫官方文档,可以看到还是有很多引脚可以映射为串口通信引脚的。本文配置为使用UART_NUM_1,TX和RX分别是引脚22和23。
-
硬件限制:UART0 的引脚通常用于调试和程序下载,不建议更改其默认配置。
-
通信速率:ESP32 的 UART 接口支持高达 5 Mbps 的通信速率。
代码部分
1.uart.h
主要是引脚分配和接收缓冲区定义以及相关函数声明。
/**
* @file uart.h
* @brief
* @author XJJane_^o^ (13778247139@163.com)
* @version 1.0
* @date 2025-05-25
*
* @copyright Copyright (c) 2025
*
* @par 修改日志:
*/
#ifndef _UART_H
#define _UART_H
#include <stdint.h>
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_err.h"
#include <string.h>
#define UART_NUM UART_NUM_1
#define UART_TX_PIN 22
#define UART_RX_PIN 23
#define UART_BUF_SIZE 1024
#define UART_TAG "UART"
static uint8_t s_uart2_rx_buf[UART_BUF_SIZE];
void uart_init(uint32_t baud_rate);
void uart_transmit(const char* data);
void uart_receive(void);
#endif
2.uart.c
具体函数实现,包括串口初始化,串口发送函数,串口接收函数。
/**
* @file uart.c
* @brief
* @author XJJane_^o^ (13778247139@163.com)
* @version 1.0
* @date 2025-05-25
*
* @copyright Copyright (c) 2025
*
* @par 修改日志:
*/
#include "uart.h"
/**
* @brief 串口初始化
* @param baud_rate 波特率
*/
void uart_init(uint32_t baud_rate){
const uart_config_t uart_config = {
.baud_rate = baud_rate,
.data_bits = UART_DATA_8_BITS, // 8位数据位
.parity = UART_PARITY_DISABLE, // 无校验位
.stop_bits = UART_STOP_BITS_1, // 1位停止位
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, // 禁用流控
.source_clk = UART_SCLK_DEFAULT, // 使用默认时钟源
};
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, UART_BUF_SIZE, 0, 0, NULL, 0)); // 串口驱动安装
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config)); // 串口参数配置
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); // 串口引脚配置
}
/**
* @brief 串口发送
* @param data 要发送的字符串
*/
void uart_transmit(const char* data){
uart_write_bytes(UART_NUM, data, strlen(data));
ESP_LOGI(UART_TAG,"TX -> %s",data);
}
/**
* @brief 串口接收
*/
void uart_receive(void){
int len = uart_read_bytes(UART_NUM,s_uart2_rx_buf,sizeof(s_uart2_rx_buf)-1,pdMS_TO_TICKS(10));
if(len > 0){
s_uart2_rx_buf[len] = '\0';
ESP_LOGI(UART_TAG,"RX -> %s",s_uart2_rx_buf);
}
}
3.main.c
创建2个任务,一个用于发送(以1秒间隔不停发送字符串给上位机),一个用于接收(不停接收来自上位机的消息)。
/**
* @file main.c
* @brief
* @author XJJane_^o^ (13778247139@163.com)
* @version 1.0
* @date 2025-05-25
*
* @copyright Copyright (c) 2025
*
* @par 修改日志:
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "uart.h"
/**
* @brief 串口发送任务
*/
void uart_transmit_task(void){
while(1){
uart_transmit("Hello XJJane_^o^");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/**
* @brief 串口接收任务
*/
void uart_receive_task(void){
while(1){
uart_receive();
}
}
void app_main(void)
{
uart_init(115200);
xTaskCreatePinnedToCore(uart_transmit_task, "uart_transmit_task", 2048, NULL, 5, NULL, 1);
xTaskCreatePinnedToCore(uart_receive_task, "uart_receive_task", 2048, NULL, 6, NULL, 1);
}
接线示意
最终效果
上位机设置1秒定时发送(左),打开esp-idf的串口监视器(右),最终效果如下:
最终效果
PS:完整代码工程请读者移步至我的gitee仓库:ESP32_UartDemo: 实现ESP32与上位机的串口通信