ESP-C3入门6. 使用UART串口

news2024/11/16 17:57:10

ESP-C3入门6. 使用UART串口

  • 一、简介
  • 二、UART使用的一般步骤
  • 三、使用的API
    • 1. `uart_config_t`结构体和设置参数函数`uart_param_config()`
    • 2. 专用函数设置参数
    • 3. 设置通信管脚`uart_set_pin()`
    • 4. 安装驱动程序`uart_driver_install()`
    • 5. 运行UART通信 `uart_write_bytes()`和`uart_read_bytes()`
      • (1)发送数据
        • 3.5.1.1 `uart_write_bytes()函数`
        • 3.5.1.2 `uart_write_bytes_with_break()函数`
        • 3.5.1.3 `uart_tx_chars()`
        • 3.5.1.4 `uart_wait_tx_done()`
      • (2) 接收数据`uart_read_bytes()`
    • 6. 软件流控
    • 7. 使用中断
      • (1)中断列表
      • (2)启用和禁用中断函数
      • (3)安装中断
      • (4)专用函数包装中断
        • 3.7.4.1 事件检测
        • 3.7.4.2 达到FIFO空间阈值或传输超时
        • 3.7.4.3 模式检测
    • 8. 删除驱动程序
  • 四、示例程序

在这里插入图片描述

一、简介

ESP32有三个UART控制器:

  • UART0
  • UART1
  • UART2

其中UART0用作下载、调试串口,引脚不可改变,
UART1和UART2的引脚是可以设置的。

本文使用的ESP32-C3芯片,只有一组UART0资源,开发板型号选用: ESP32-C3-DevKitM-1 v1.0,管脚资源如下图所示:

在这里插入图片描述

二、UART使用的一般步骤

  • 初始化串口,设置通讯参数
  • 设置通信管脚
  • 安装驱动程序
  • 运行UART通信
  • 使用中断
  • 任务中阻塞等待串口队列
  • 如果不再使用串口,删除驱动程序

三、使用的API

1. uart_config_t结构体和设置参数函数uart_param_config()

用来初始化串口使用。

/**
 * @brief UART configuration parameters for uart_param_config function
 */
typedef struct {
	// 波特率
    int baud_rate;                      /*!< UART baud rate*/
    // 字节长度
    uart_word_length_t data_bits;       /*!< UART byte size*/
    // 校验
    uart_parity_t parity;               /*!< UART parity mode*/
    // 停止位
    uart_stop_bits_t stop_bits;         /*!< UART stop bits*/
    // 硬件流控模式
    uart_hw_flowcontrol_t flow_ctrl;    /*!< UART HW flow control mode (cts/rts)*/
    uint8_t rx_flow_ctrl_thresh;        /*!< UART HW RTS threshold*/
    union {
    	// 时钟源
        uart_sclk_t source_clk;         /*!< UART source clock selection */
        bool use_ref_tick  __attribute__((deprecated)); /*!< Deprecated method to select ref tick clock source, set source_clk field instead */
    };
} uart_config_t;

使用示例:

const int uart_num = UART_NUM_2;
uart_config_t uart_config = {
    .baud_rate = 115200,
    .date_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_thress = 122,
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));

2. 专用函数设置参数

  • 波特率 uart_set_baudrate()
  • 传输位 uart_set_wod_length()
  • 奇偶控制 uart_set_parity()
  • 停止位 :uart_set_stop_bits()
  • 硬件流控模式: uart_set_hw_flow_ctrl()
  • 通信模式: uart_set_mode()

如果要查询参数,可以把上面的_set_改成_get_

3. 设置通信管脚uart_set_pin()

参数顺序: Tx,Rx,RTS,CTS。
保持不变的参数,使用宏: UART_PIN_NO_CHANGE
使用示例:

// 设置TX=IO4, RX=IO5, RTS=IO18, CTS=IO19
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 4, 5, 18, 19));

4. 安装驱动程序uart_driver_install()

参数:

  • Tx 环形缓冲区的大小
  • Rx 环形缓冲区的大小
  • 事件队列句柄和大小
  • 分配中断的标志
    示例:
// 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));

5. 运行UART通信 uart_write_bytes()uart_read_bytes()

串行通信由每个 UART 控制器的有限状态机 (FSM) 控制。发送数据的过程分为以下步骤:

  1. 将数据写入 Tx FIFO 缓冲区
  2. FSM 序列化数据
  3. FSM 发送数据

接收数据的过程类似,只是步骤相反:

  1. FSM 处理且并行化传入的串行流
  2. FSM 将数据写入 Rx FIFO 缓冲区
  3. 从 Rx FIFO 缓冲区读取数据

应用程序参考读写缓冲区即可进行UART通信。

(1)发送数据

3.5.1.1 uart_write_bytes()函数

写入缓冲区,空间不足时会阻塞,示例代码:

// 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));

3.5.1.2 uart_write_bytes_with_break()函数

传输结束时添加串行中断信号,示例代码:

// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);

3.5.1.3 uart_tx_chars()

空间不足时不会阻塞,运行后立刻返回写入的字节数。

3.5.1.4 uart_wait_tx_done()

监听Tx FIFO缓冲区的状态,在缓冲区为空时返回。

(2) 接收数据uart_read_bytes()

uart_get_buffered_data_len() 用于查看Rx FIFO 缓冲区中可用的字节数,示例代码:

// Read data from UART.
const uart_port_t uart_num = UART_NUM_2;
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);

6. 软件流控

如果硬件流控被禁用,您可使用函数 uart_set_rts() 和 uart_set_dtr() 分别手动设置 RTS 和 DTR 信号电平。

7. 使用中断

(1)中断列表

  • UART_AT_CMD_CHAR_DET_INT: 接收到at_cmd字符时触发;
  • UART_RS485_CLASH_INT: RS-485 模式下检测到发送、接收有冲突时触发;
  • UART_RS485_FRM_ERR_INT: RS-485检测到数据帧错误;
  • UART_RS485_PARITY_ERR_INT: RS-485 模式下检测到奇偶校验错误;
  • UART_TX_DONE_INT: 发送完FIFO数据时触发;
  • UART_TX_BRK_IDLE_DONE_INT: 发送的空闲状态在发送完最后一个数据后保持在最低限值时触发;
  • UART_TX_BRK_DONE_INT: FIFO发送完后,完成发送NULL时触发;
  • UART_GLITCH_DET_INT: 当接收检测到 START 位时触发;
  • UART_SW_XOFF_INT: UART_SW_FLOW_CON_EN设置为1时收到Xon字符时触发;
  • UART_SW_XON_INT:UART_SW_FLOW_CON_EN设置为1时收到Xoff字符时触发;
  • UART_RXFIFO_TOUT_INT: 接收字节超出RX_TOUT_THRHD 时间触发;
  • UART_BRK_DET_INT: STOP位后检测到低电平时触发;
  • UART_CTS_CHG_INT: 当接收检测到 CTSn 信号的边沿变化时触发;
  • UART_DSR_CHG_INT: 当接收检测到 DSRn 信号的边沿变化时触发;
  • UART_RXFIFO_OVF_INT: 当接收获取的数据多于 FIFO 可存储的数据时触发;
  • UART_FRM_ERR_INT: 当接收检测到数据帧错误时触发 ;
  • UART_PARITY_ERR_INT: 当接收检测到数据中的奇偶校验错误时触发;
  • UART_TXFIFO_EMPTY_INT: 当传输 FIFO 中的数据量小于 tx_mem_cnttxfifo_cnt 指定的值时触发;
  • UART_RXFIFO_FULL_INT: 接收获得的数据多于 (rx_flow_thrhd_h3, rx_flow_thrhd) 指定的数据时触发。

(2)启用和禁用中断函数

调用 uart_enable_intr_mask() 或 uart_disable_intr_mask() 能够分别启用或禁用特定中断。

(3)安装中断

uart_driver_install() 函数可以安装驱动程序的内部中断处理程序,用以管理 Tx 和 Rx 环形缓冲区,并提供事件等高级 API 函数。

(4)专用函数包装中断

3.7.4.1 事件检测

uart_event_type_t定义多个事件,FreeRTOS队列功能上报事件。

3.7.4.2 达到FIFO空间阈值或传输超时

Tx和Rx FIFO缓冲区在填充特定数量的字符和在发送或接收数据超时时触发中断。使用此类中断的操作是:

  • 配置缓冲区长度和超时阈值:在结构体uart_intr_config_t中输入阈值并调用uart_intr_config()
  • 启用中断: uart_enable_tx_intr()uart_enable_rx_intr()
  • 禁用中断: uart_disable_tx_intr()uart_disable_rx_intr()

3.7.4.3 模式检测

在检测到重复接收/发送同一字符的模式时触发中断。使用中断的步骤:

  • 配置并启用此中断: uart_enable_pattern_det_intr()
  • 禁用中断: uart_disable_pattern_det_intr()

8. 删除驱动程序

uart_driver_delete()

四、示例程序

基本的发送接收示例程序,不使用中断

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

const int RX_BUF_SIZE = 1024;

#define TXD_PIN (GPIO_NUM_0)
#define RXD_PIN (GPIO_NUM_1)

/**
 * 初始化串口
 */
void uart_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,
            .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);
}

/**
 * 发送数据
 * @param logName 
 * @param data 
 * @return 
 */
int sendData(const char* logName, const char* data)
{
    const int len = strlen(data);
    const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
    ESP_LOGI(logName, "Wrote %d bytes: %s", txBytes, data);
    return txBytes;
}

/**
 * 发送数据任务
 * @param arg 
 */
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);
    }
}

/**
 * 接收数据任务
 * @param arg 
 */
void rx_task(void *arg)
{
    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
    while (1) {
        const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_RATE_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
        }
        vTaskDelay(1);
    }
    free(data);
}

void app_main(void)
{
    uart_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);
    while(1){
        vTaskDelay(1);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187092.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Batch 批处理-执行上下文

引言 接着上篇&#xff1a;Spring Batch 批处理-作业监听器&#xff0c;了解作业监听器后&#xff0c;本篇就来了解一下Spring Batch执行上下文&#xff0c;来看看Spring Batch 如何在作业与步骤间进行数据共享。 作业与步骤上下文 语文中有个词叫上下文&#xff0c;比如&am…

设置mysql远程链接

一、 进入mysql数据库 mysql -uroot –p密码 二、 创建用户进行远程链接 Mysql>grant all privileges on *.* to duan%identified by 111111 with grant option; ‘duan’:用户名 ‘%’&#xff1a;表示所有主机都可以登录&#xff0c;如果‘ip’表示该主机可…

[GXYCTF2019]luck_guy1题解

无论风暴把我带到什么样的岸边&#xff0c;我都将以主人的身份上岸。 ——贺拉斯 目录 1.查壳 2.静态分析 分析信息 猜测 3.wp 1.查壳 x86-64 拖入64位IDA 2.静态分析 找到main函数&#xff0c;按下F5反编译 int __cdecl main(int argc, const char **argv, const cha…

Python Windows Apache部署Django项目运行环境

目录 一、安装Apache服务器 1、在官网http://httpd.apache.org/download.cgi或网上搜索下载zip压缩包 2、Apache相关配置 3、创建Apache服务器 解决报错AH00369: Failed to open the Windows service manager, perhaps you forgot to log in as Adminstrator? 4、启动ap…

【每周Java技术】2023.01.26 周四 到 01.29 周日

文章目录一、01.26 周四 大年初五1.1&#xff09;Python的一道算法题目1.1.1) 题目1.1.2) 解答1.1.3) 知识点一、01.26 周四 大年初五 1.1&#xff09;Python的一道算法题目 1.1.1) 题目 2437. 有效时间的数目 给你一个长度为 5 的字符串 time &#xff0c;表示一个电子时钟…

产线工控设备安全经验分享

工控设备安全现状 工业控制系统是支撑国民经济的重要设施&#xff0c;是工业领域的神经中枢。现在工业控制系统已经广泛应用于电力、通信、化工、交通、航天等工业领域&#xff0c;支撑起国计民生的关键基础设施。 随着传统的工业转型&#xff0c;数字化、网络化和智能化的工…

机器学习算法竞赛实战--3,数据探索

数据挖掘是竞赛的核心模块之一&#xff0c;贯彻竞赛始终也是很多竞赛胜利的关键那么数据探索又是什么呢&#xff1f;可以解决哪些问题&#xff1f;首先应该明确3点&#xff0c;即如何确保自己准备好竞赛使用的算法模型如何为数据集选择最合适的算法如何定义可用于算法模型的特征…

安装DevStack稳定版本zed

STEP1: 创建stack用户(一定要&#xff01;) sudo useradd -s /bin/bash -d /opt/stack -m stack sudo chmod x /opt/stack echo "stack ALL(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack sudo -u stack -i //用stack用户登陆 STEP2: git clone devsta…

SAP中物料价格改变导致的库存价值变动业务分析

基于审计的需求要看看物料在标准成本价格变更前后的库存成本变化情况。找了下资料&#xff0c;需要用到 CKMPCSEARCH这个事务&#xff0c;相当于是一个用于价格修改凭证查询的事务。但试了一下在本公司的系统中并没有从搜索到其菜单位置。 尝试直接输入Tcode后&#xff0c;是可…

CSRF 伪造跨域请求

文章目录一、什么是伪造跨域请求二、攻击方式三、防御措施1、检查Referer字段2、添加校验token一、什么是伪造跨域请求 伪造跨域请求&#xff08;英语&#xff1a;Cross-site request forgery&#xff09;&#xff0c;通常缩写为 CSRF&#xff0c; 是一种挟制用户在当前已登录…

IntelliJ IDEA 2021.2(Community Edition)安装阿里编码规约插件,亲测有效

1.背景阿里巴巴java开发手册不断完善并出了很多版本到目前2023年1月截止&#xff0c;已经出了黄山版&#xff0c;在文章最后有参考资料可根据需要下载&#xff1b;随着企业对项目质量的要求&#xff0c;对开发代码也有了更高的要求和规范&#xff0c;借助阿里巴巴编码规约插件&…

redis geo 没有迁移手工插入数据

1、docker ps |grep redis2、docker exec -it qinghai-sc-xining-redis-single/eadd4cc4eefe bin\sh --进入redis容器[rootlocalhost ~]# [rootlocalhost ~]# [rootlocalhost ~]# [rootlocalhost ~]# docker ps|grep rediseadd4cc4eefe redis "docker-entrypoint.s…"…

python 气体扩散,在 Python中用数值模拟研究气体扩散

在 Python 中&#xff0c;可以使用数值模拟来研究气体扩散。 模拟气体扩散需要解决两个问题&#xff1a;流体动力学方程&#xff08;如 Navier-Stokes 方程&#xff09;和扩散方程。 文章目录Python 代码模拟气体扩散计算并显示气体浓度的均值和标准差研究气体扩展的高级方法Py…

Word控件Spire.Doc 【Table】教程(7): 如何在C#中用表格替换word文档中的文本

Spire.Doc for .NET 是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

【金三银四系列】之Java基础面试(2023版)

Java基础面试题 一: Java基础 1: 简单说说Java中对象如何拷贝? 一、浅拷贝clone&#xff08;&#xff09;如果对象中的所有数据域都是数值或者基本类型&#xff0c;使用clone&#xff08;&#xff09;即可满足需求&#xff0c;如&#xff1a;Person p new Person();Person…

30天自制操作系统(Mac版)读书笔记(day9)

day7和day8都是鼠标和32位的操作&#xff0c;看起来都是理论&#xff0c;先略过。 检测内存块数量 使用中断去检测内存&#xff0c;把这个当成一个函数调用吧。这里面di寄存器给的地址就是结果存在的内存位置。 ComputeMemory:mov ebx, 0mov di, MemChkBuf .loop:mov e…

linux安装vnc服务

1、 如操作系统是最小化安装&#xff0c;那么需要安装GNOME桌面&#xff0c;安装参考&#xff1a;https://blog.csdn.net/carefree2005/article/details/119417234 2、 安装vnc-server yum -y install tigervnc-server3、 复制配置文件模板&#xff0c;将无关的内容清除 cat…

图灵 | 计算机器与智能

【“计算机器与智能”选自《Mind》&#xff0c;no.2236&#xff08;1950.10&#xff09;&#xff0c;P433-460。牛津大学出版社允许重印。刘西瑞、王汉琦 翻译】1. 模仿游戏我建议来考虑这个问题 &#xff1a;“机器能够思维吗&#xff1f;” 这可以从定义 “机 器” 和 “思维…

Docker数据目录迁移

背景在CentOS中安装了Docker&#xff0c;默认Docker Root目录是/var/lib/docker。但是该目录磁盘空间很有限&#xff0c;后期很容易导致系统盘满了&#xff0c;所以考虑迁移到更大磁盘目录下&#xff0c;比如下面的/fsc目录下。解决在Docker官方文档https://docs.docker.com/co…

excel查找定位:INDEX函数——精确制导导弹

一、认识INDEX函数Index函数&#xff1a;在给定的单元格区域中&#xff0c;返回特定行列交叉处单元格的值或引用。函数结构&#xff1a;index&#xff08;单元格区域,行号,列号&#xff09;区域&#xff0c;行号&#xff0c;列号&#xff0c;很像通过坐标瞄准打靶呀。就像下面动…