[ESP32]UART串口使用
ESP32里面有3个串口,uart0默认作为log和console输出,我们可以使用uart1和uart2。
它们默认的管脚如下:
UART | GPIO | UART | GPIO |
---|---|---|---|
U0_RXD | GPIO3 | U0_CTS | GPIO19 |
U0_TXD | GPIO1 | U0_RTS | GPIO22 |
U1_RXD | GPIO9 | U1_CTS | GPIO6 |
U1_TXD | GPIO10 | U1_RTS | GPIO11 |
U2_RXD | GPIO16 | U2_CTS | GPIO8 |
U2_TXD | GPIO17 | U2_RTS | GPIO7 |
如果是使用ESP32的模组,因为接SPI Flash,会占用GPIO6~GPIO11,所以uart1使用默认管脚的时候会有冲突,我们需要把管脚配置到其它的GPIO上,万幸可以这样进行管理配置。
esp32的串口使用可以分为4步:
- 设置串口参数,包括波特率,奇偶校验,数据位与停止位等
- 设置串口使用的GPIO管脚
- 安装驱动,为uart分配资源
- 进行串口通信
1 设置串口参数
调用uart_param_config()方法设置
uart_config_t uart_config = {
.baud_rate = 115200, //波特率
.data_bits = UART_DATA_8_BITS, //数据位
.parity = UART_PARITY_DISABLE, //奇偶校验
.stop_bits = UART_STOP_BITS_1, //停止位
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, //流控
.rx_flow_ctrl_thresh = 122, //硬件RTS阈值
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));
2 配置串口管脚
通过uart_set_pin()设置串口的映射管脚,使用默认的话,可以用UART_PIN_NO_CHANGE,尽量指定一个吧,默认的貌似不太靠谱。uart_set_pin()参数从左到右分别是:TXD,RXD,RTS,CTS。
// Set UART pins(TX: IO16 (UART2 default), RX: IO17 (UART2 default), RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 18, 19));
3 安装uart驱动
使用uart_driver_install()函数安装,函数原型如下:
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags)
消息队列,可以为NULL,长度为0,如果不处理中断事件,则最后一个参数也为0;
若tx_buffer_size为0,即没有发送缓冲区,则uart_write_bytes()的时候,会阻塞等待所以数据发送完才会返回。
rx_buffer_size不能为空,最好为UART_FIFO_LEN(128)整数倍。
// Setup UART buffered IO with event queue
const int uart_buffer_size = (1024 * 2);
QueueHandle_t uart_queue;
// Install UART driver using an event queue here
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, \
uart_buffer_size, 10, &uart_queue, 0));
串口读写数据
发数据
通过uart_write_bytes()发送数据,如果没有设置发送缓冲区,数据发送完都会返回,如果设置了发送缓冲区,则会把发送数据拷贝到缓冲区之后返回,uart的中断服务程序会把发送缓冲区里面的数据移动tx FIFO然后发送出去。
// Write data to UART.
char* test_str = "This is a test string.\n";
uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
uart_write_bytes_with_break()也是发送程序,但是它会在发送完数据之后,设置TX低电平一段时间(RTOS任务节拍为单位)
// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);
uart_tx_chars()也是发送程序,但是它会直接把数据写到硬件的Tx FIFO,返回写入的数据长度。
uart_wait_tx_done() 会检查硬件Tx FIFO的状态,当它为空或超时时返回。
// Wait for packet to be sent
const int uart_num = UART_NUM_2;
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 100)); // wait timeout is 100 RTOS ticks (TickType_t)
收数据
当串口接收到数据时,它会保存到Rx FIFO里面,我们可以通过 uart_read_bytes()读出,这个函数会阻塞待在那里,直到读满需要的字节,或是超时。
当然我们也可以先获取FIFO里面的数据长度[uart_get_buffered_data_len()],然后再读取相应的内容,这样就不会造成不必要的阻塞。
// Read data from UART.
const int uart_num = UART2;
uint8_t data[128];
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 100);
如果不需要FIFO里面的内容可以调用:uart_flush().
使用GPIO16作为TXD,GPIO17作为RXD,每2秒发送一个Hello world,短接这两个管脚即可看到结果。
参考代码:
/* UART asynchronous example, that uses separate RX and TX tasks
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
static const int RX_BUF_SIZE = 1024;
static const int uart_num = UART_NUM_2;
#define TXD_PIN (GPIO_NUM_16)
#define RXD_PIN (GPIO_NUM_17)
void init(void) {
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 122, //
};
// We won't use a buffer for sending data.
uart_driver_install(uart_num, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(uart_num, &uart_config);
uart_set_pin(uart_num, TXD_PIN, RXD_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
int sendData(const char* logName, const char* data)
{
const int len = strlen(data);
const int txBytes = uart_write_bytes(uart_num, data, len);
ESP_LOGI(logName, "Wrote %d bytes", txBytes);
return txBytes;
}
static void tx_task(void *arg)
{
static const char *TX_TASK_TAG = "TX_TASK";
esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
while (1) {
sendData(TX_TASK_TAG, "Hello world");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
static void rx_task(void *arg)
{
static const char *RX_TASK_TAG = "RX_TASK";
int rxBytes;
size_t buf_ok;
uint32_t cnt=0;
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
while (1) {
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, &buf_ok));
if (buf_ok>0) {
ESP_LOGI(RX_TASK_TAG, "read Begin");
rxBytes = uart_read_bytes(uart_num, data, buf_ok, 1000 / portTICK_RATE_MS);
if (rxBytes > 0) {
data[rxBytes] = 0;
ESP_LOGI(RX_TASK_TAG, "Read %d(%d) bytes: '%s'", rxBytes, buf_ok, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
}
ESP_LOGI(RX_TASK_TAG, "Recv Task cnt=%d.", cnt);
}
cnt++;
vTaskDelay(100 / portTICK_PERIOD_MS);
}
free(data);
}
void app_main(void)
{
init();
xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
}
参考:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/uart.html