系列文章目录
ESP32学习(1):ESP-IDF基于Visual Studio Code环境
ESP32学习(2):ESP32-S3上运行hello world
ESP32学习(3):ESP32上运行ILI9341驱动LCD
ESP32学习(4):ESP32-S3上实现ILI9488驱动LCD的显示
前言
提示:串口收发是常用的功能,这里先整理记录一下。主要包含初始化,发送数据,接收数据,任务建立与消息队列。
一、串口初始化
串口1初始化,主要初始化管脚,接收缓冲区大小,波特率等常用参数。
#include "driver/uart.h" #define TXD_PIN (GPIO_NUM_14) //串口1发送管脚 #define RXD_PIN (GPIO_NUM_13) //串口1接收管脚 static const int RX_BUF_SIZE = 1024; //串口接收缓冲区大小 void init_uart(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, .source_clk = UART_SCLK_APB, }; uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0); uart_param_config(UART_NUM_1, &uart_config); uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); }
二、串口发送
串口发送,主要调用库里面的函数
int uart_write_bytes(uart_port_t uart_num, const void *src, size_t size)
例如发送版本号,如下:
#define SOFT_VER "Ver: 4.7.5" static void send_ver(void) { const char *data = SOFT_VER; const int len = strlen(data); uart_write_bytes(UART_NUM_1, data, len); }
发送8个字节具体的数据,如下:
static void send_info(uint8_t flag_info,uint8_t s0_info,uint16_t s1_info, uint16_t s2_info) { uint8_t buf_tx[8]; const int len = 8; buf_tx[0] = 0x23; buf_tx[1] = 0x66; buf_tx[2] = flag_info; buf_tx[3] = s0_info ; buf_tx[4] = s1_info; buf_tx[5] = s1_info >> 8; buf_tx[6] = s2_info; buf_tx[7] = s2_info >> 8; uart_write_bytes(UART_NUM_1, &buf_tx[0], len); }
三、串口接收
串口接收,主要调用uart_read_bytes函数:
int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, TickType_t ticks_to_wait)
可以通过建立单独的任务来接收数据。
static void rx_task(void *arg) { msg_uart_rdata_t msg_uart_rdata; static const char *RX_TASK_TAG = "RX_TASK"; esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO); uint8_t* udata = (uint8_t*) malloc(RX_BUF_SIZE+1); while (1) { const int rxBytes = uart_read_bytes(UART_NUM_1, udata, RX_BUF_SIZE, 100 / portTICK_PERIOD_MS); if (rxBytes > 0 ) { udata[rxBytes] = 0; //这里可以把接收的数据发送回串口1 //uart_write_bytes(UART_NUM_1, udata, rxBytes); msg_uart_rdata.u_rdata_len = rxBytes; msg_uart_rdata.u_rdata = (uint8_t*)malloc(rxBytes); for (int tmp_i = 0; tmp_i < rxBytes; tmp_i++) { /* 提取接收的数据,通过消息队列发送给其他任务来处理 */ msg_uart_rdata.u_rdata[tmp_i] = udata[tmp_i]; } if (xQueueSend(message_queue_uart, (void *)&msg_uart_rdata, (TickType_t)300) != pdTRUE) //消息队列发送 { free(msg_uart_rdata.u_rdata); } else { /* 消息队列发送成功,同时可以发送事件组标志位 */ xEventGroupSetBits(spi_event_group, EVENTBIT4_uart1); vTaskDelay(pdMS_TO_TICKS(50)); free(msg_uart_rdata.u_rdata); } } vTaskDelay(pdMS_TO_TICKS(20)); } free(udata); }
四、其他任务中处理串口接收数据
串口接收任务,负责接收数据,通过消息队列发送出去,同时发送事件组标志。
其他任务循环中通过查询事件组标志,判断有串口数据到来,然后接收串口消息队列中数据。
事件标志组
#define EVENTBIT0 (1 << 0)
#define EVENTBIT1 (1 << 1)
#define EVENTBIT2 (1 << 2)
#define EVENTBIT3_key (1 << 3) // 按键按下
#define EVENTBIT4_uart1 (1 << 4) // 串口接收
#define EVENTBIT5_i2c (1 << 5) // I2C总线
#define EVENTBIT6_bmq (1 << 6) // adc采样数据发送事件组
EventGroupHandle_t spi_event_group; // 事件标志组句柄
typedef struct
{
uint8_t *u_rdata;
uint16_t u_rdata_len;
} msg_uart_rdata_t; // 串口队列消息缓存
static QueueHandle_t message_queue_uart; //串口消息队列while(1) { uxBits = xEventGroupWaitBits(spi_event_group, EVENTBIT4_uart1 | EVENTBIT2 | EVENTBIT3_key | EVENTBIT4_am | EVENTBIT5_i2c | EVENTBIT6_adc, pdTRUE, pdFALSE, 10); if ((uxBits & EVENTBIT4_uart1) != 0) { msg_uart_rdata_t msg_uart_rdata; //串口初始化部分有定义 if (xQueueReceive(message_queue_uart, &msg_uart_rdata, (TickType_t)20) != pdPASS) { vTaskDelay(pdMS_TO_TICKS(100)); } else { temp_rdata_len = msg_uart_rdata.u_rdata_len; for (int tmp_i = 0; tmp_i < temp_rdata_len; tmp_i++) { /* 提取消息队列中的数据 */ udata[tmp_i] = msg_uart_rdata.u_rdata[tmp_i]; } /* 接下来的程序可以处理接收过来的数据udata[] */ } } }
总结
串口接收和发送,主要是函数调用,相对比较简单,这里贴出来,方便以后调用,不用重新编写了。