当前位置: 首页 > article >正文

【ESP32】ESP-IDF开发 | UART通用异步收发传输器+串口收发例程

1. 简介

        UART可以说是开发者使用得最多的外设之一了,打印log几乎都是使用串口来实现的。UART是一种异步全双工的通信方式,异步传输的特性使得它仅需2根线就可以完成全双工的传输,但这也要求发送端和接收端的速率、停止位、奇偶校验位等都要相同,通信才能成功。

        一个典型的UART帧开始于一个起始位,紧接着是有效数据,然后是奇偶校验位(可有可无),最后是停止位。 ESP32上的UART控制器支持多种字符长度和停止位。另外,控制器还支持软硬件流控和 DMA,可以实现无缝高速的数据传输。

2. 硬件架构

        ESP32中的UART有两个时钟源:80MHz APB_CLK和参考时钟REF_TICK,时钟源进来后会通过分频器调整到对应的频率,分频后的时钟会分别进到发送块和接收块。

        发送器和接收器都各自有一个FIFO来缓存数据,每个FIFO有128字节的空间,ESP32中3个UART设备会共享一块1KB大小的内存作为FIFO空间。获取FIFO数据除了通过CPU来实现,还可以通过内部DMA实现,以提高效率。

        UART设备也支持硬件流控和软件流控,以支持像RS-485、红外遥控等协议的开发。

3. 流控

3.1 硬件流控

        硬件流控主要通过输出信号rtsn_out和输入信号dsrn_in进行数据流控制。

        输出信号rtsn_out为高电平表示请求对方发送数据,rtsn_out为低电平表示通知对方中止数据发送直到rtsn_out恢复高电平。

        当UART检测到输入信号ctsn_in的沿变化时会产生UART_CTS_CHG_INT中断并且在发送完当前数据后停止接下来的数据发送。

        输出信号dtrn_out为高电平表示发送方数据已经准备完毕,UART在检测到输入信号dsrn_in 的沿变化时会产生UART_DSR_CHG_INT中断。软件在检测到中断后,通过读取UART_DSRN可以获取dsrn_in的输入信号电平,从而判断当前是否可以接收数据。

3.2 软件流控 

        软件流控主要通过在发送数据流中插入特殊字符以及在接收数据流中检测特殊字符来实现数据流控功能。

        软件可以通过置位UART_FORCE_XOFF来强制停止发送器发送数据,也可以通过置位UART_FORCE_XON来强制发送器发送数据。

        UART还可以通过传输特殊字符进行软件流控,当UART 接收的数据字节数超过UART_XOFF的阈值时,可以通过发送UART_XOFF_CHAR来告知对方停止发送数据。

        软件也可以在任意时候发送流控字符。置位UART_SEND_XOFF,发送器会在发送完当前数据之后插入发送一个UART_XOFF_CHAR;置位 UART_SEND_XON,发送器会在发送完当前数据之后插入发送一个UART_XON_CHAR。

4. 例程

        例程中使用ESP32的串口1和串口2,相互连接并实现数据收发。

#include "driver/gpio.h"
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "esp_log.h"

#include <string.h>

#define TAG "app"

void app_main()
{
    uart_config_t uart_cfg = {0};

    uart_cfg.baud_rate = 115200,
    uart_cfg.data_bits = UART_DATA_8_BITS;  // 8位数据
    uart_cfg.parity = UART_PARITY_DISABLE;  // 无校检
    uart_cfg.stop_bits = UART_STOP_BITS_1;  // 1位停止位
    uart_cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;  // 无硬件流控
    uart_cfg.source_clk = UART_SCLK_DEFAULT;  // 默认时钟源,APB时钟

    /* 初始化串口1 */
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 1024 * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_cfg));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 17, 18, -1, -1));

    /* 初始化串口2 */
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, 1024 * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_cfg));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 21, 22, -1, -1));

    const char *str = "Hello, World!";
    while (1) {
        /* 串口1发送 */
        int send_len = uart_write_bytes(UART_NUM_1, str, strlen(str));
        ESP_LOGI(TAG, "Send %d bytes, data: %s", send_len, str);
        uart_wait_tx_done(UART_NUM_1, portMAX_DELAY);
        /* 串口2接收 */
        char buf[128] = {0};
        int read_len = uart_read_bytes(UART_NUM_2, buf, 128, 100 / portTICK_PERIOD_MS);
        if (read_len >= 0) {
            ESP_LOGI(TAG, "[UART2] Receive %d bytes, data: %s", read_len, buf);
        } else {
            ESP_LOGE(TAG, "[UART2] Receive data timeout");
        }

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

        先初始化uart_config_t结构体,我设置的是常见的115200波特率,无校检,1位停止位,时钟为APB时钟。

        uart_driver_install注册串口设备,参数1为串口号;参数2和3是接收和发送缓冲区大小,缓冲区的大小必须大于FIFO的大小,发送缓冲区可以为0,我设置接收缓冲区2KB,无发送缓冲;参数4和5是消息队列的大小和句柄,可以通过它来接收串口的事件数据;参数6是注册标志,一般默认为0即可。

        uart_param_config配置串口通信参数;uart_set_pin配置串口引脚,参数依次为发送管脚、接收管脚、RTS管脚和CTS管脚,管脚不用的话设置为-1

        uart_write_bytes发送数据,如果发送缓冲区为0,那么所有数据压入发送FIFO就会返回;如果发送缓冲区不为0,那么所有数据复制进缓冲区时就会返回。其实无论哪一种情况,函数返回都不能保证数据一定完全发送了,所以最好后面调uart_wait_tx_done等串口完全发送再执行下一步操作。

        使用uart_read_bytes接收串口数据,最后一个参数可以设置等待时长,我这里是等待100ms。


http://www.kler.cn/a/314456.html

相关文章:

  • 黄色校正电容102j100
  • 中文书籍对《人月神话》的引用(161-210本):微软的秘密
  • 测试实项中的偶必现难测bug--验证码问题
  • 应对JSON解析键值对乱序问题的实用解决方案
  • #渗透测试#SRC漏洞挖掘#云技术基础02之容器与云
  • 回归分析学习
  • 深度学习-图像处理篇1.3pytorch神经网络例子
  • 【数据仓库】数据仓库层次化设计
  • vue3(整合版)
  • docker入门总结(附错误处理,持续更新)
  • 如何使用 Python 的 sqlite3 模块操作 SQLite 数据库?
  • mac命令行分卷压缩与合并
  • 长列表加载性能优化
  • python画图1
  • springboot实战学习(6)(用户模块的登录认证)(初识令牌)(JWT)
  • python:给1个整数,你怎么判断是否等于2的幂次方?
  • java.nio.ByteBuffer的 capacity, limit, position, mark
  • 如何打造高效的远程开发团队:最佳实践与挑战
  • 大话C++:第11篇 类的定义与封装
  • Redis——redispluspls库通用命令以及String类型相关接口使用
  • 每日一题--打印闰年
  • 如何使用 Python 连接 MySQL 数据库?什么是 ORM(对象关系映射),如何使用
  • fasterRCNN模型实现飞机类目标检测
  • 果蔬识别系统架构+流程图
  • Hadoop的安装
  • JVM 调优篇7 调优案例2-元空间的优化解决