ESP32 IDF开发 驱动篇⑦UART两个串口的使用


别迷路-导航栏
快速导航找到你想要的(文章目录)

此篇文章如果对你有用,请点赞收藏,您的支持就是博主坚持的动力。

1、博主写这篇技术文章的目的:

(1)熟悉掌握UART相关API;
(2)掌握esp32 串口的使用方法;

2、Uart库的介绍

ESP32最多只有3个串口上。
有关Uart详细函数请参考msys32\esp-idf\components\driver\include\driver\uart.h
在这里我只做几个重要经常使用的API函数讲解

Uart库函数分为:
(1)、填充uart结构体设置串口基本参数;
(2)、锁定串口引脚;
(3)发送接收串口数据
uart结构体设置串口API:

/**
  * @brief 设置UART配置参数。
  *
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param uart_config UART参数设置
  *
  * @return
  *-ESP_OK成功
  *-ESP_FAIL参数错误
  */
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);
/**
  * @brief 配置UART中断。
  *
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param intr_conf UART中断设置
  *
  * @return
  *-ESP_OK成功
  *-ESP_FAIL参数错误
  */
esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf);

锁定串口引脚API:

/**
  * @brief 设置UART引脚号
  * @note 内部信号可以输出到多个GPIO焊盘。
  *只有一个GPIO垫可以连接输入信号。
  * @note 可能会提供宏'UART_PIN_NO_CHANGE'代替GPIO号
          保持当前分配的引脚。
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param tx_io_num UART TX引脚GPIO编号。
  * @param rx_io_num UART RX引脚GPIO编号。
  * @param rts_io_num UART RTS引脚GPIO编号。
  * @param cts_io_num UART CTS引脚GPIO编号。
  * @return
  *-ESP_OK成功
  *-ESP_FAIL参数错误
  */
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);
/**
 * @brief 安装UART驱动程序并将UART设置为默认配置。
 *
 * UART ISR处理程序将连接到运行此函数的同一CPU内核。
 *
 * @note Rx_buffer_size应大于UART_FIFO_LEN。 Tx_buffer_size应该为零或大于UART_FIFO_LEN。
 *
 * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
 * @param rx_buffer_size UART RX环形缓冲区的大小。
 * @param tx_buffer_size UART TX环形缓冲区的大小。
 *如果设置为零,驱动程序将不使用TX缓冲区,TX功能将阻止任务,直到所有数据都已发送出去。
 * @param queue_size UART事件队列的大小/深度。
 * @param uart_queue UART事件队列句柄(无参数)。成功后,将在此处编写新的队列句柄以提供
 *访问UART事件。如果设置为NULL,驱动程序将不使用事件队列。
 * @param intr_alloc_flags用于分配中断的标志。一个或多个(ORred)
 * ESP_INTR_FLAG_ *值。有关更多信息,请参见esp_intr_alloc.h。不要在这里设置ESP_INTR_FLAG_IRAM
 *(驱动程序的ISR处理程序不在IRAM中)
 * @return
 *-ESP_OK成功
 *-ESP_FAIL参数错误
 */
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);

发送接收串口数据相关API函数

/**
  * @brief 从给定的缓冲区和长度将数据发送到UART端口,将数据发送到TX FIFO就返回
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param src数据缓冲区地址 要发送的参数大小数据长度
  * @return
  *-(-1)参数错误
  *-其他(> = 0)推送到TX FIFO的字节数
  */
int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size);
/**
  * @brief 等待直到UART TX FIFO为空。
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param ticks_to_wait超时,以RTOS滴答计数
  * @返回
  *-ESP_OK成功
  *-ESP_FAIL参数错误
  *-ESP_ERR_TIMEOUT超时
  */
esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait);
/**
  * @brief UART从UART缓冲区读取字节
  *
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  * @param buf指向缓冲区的指针。
  * @参数长度数据长度
  * @param ticks_to_wait sTimeout,以实时操作系统滴答计数
  *
  * @返回
  *-(-1)错误
  *-其他(> = 0)从UART FIFO读取的字节数
  */
int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait);
/**
  * @brief 清除输入缓冲区,丢弃所有在环形缓冲区中的数据。
  * @note 为了在tx FIFO中发送所有数据,我们可以使用uart_wait_tx_done函数。
  * @param uart_num UART端口号,最大端口号是(UART_NUM_MAX -1)。
  *
  * @返回
  *-ESP_OK成功
  *-ESP_FAIL参数错误
  */
esp_err_t uart_flush_input(uart_port_t uart_num);

3、软件设计

首先我们来分析一下uart的结构体:
第一步:

/**
 * @brief UART uart_param_config函数的配置参数
 */
typedef struct {
    int baud_rate;                      /*!< UART 波特率*/
    uart_word_length_t data_bits;       /*!< UART 数据位,一般8位*/
    uart_parity_t parity;               /*!< UART 奇偶校验模式*/
    uart_stop_bits_t stop_bits;         /*!< UART 停止位*/
    uart_hw_flowcontrol_t flow_ctrl;    /*!< UART 硬件流控制模式(cts / rts)*/
    uint8_t rx_flow_ctrl_thresh;        /*!< UART 硬件流控*/
    union {
        uart_sclk_t source_clk;         /*!< UART 源时钟选择 */
        bool use_ref_tick  __attribute__((deprecated));
    };
} uart_config_t;

从上面的结构我们可以看出我们只需要填充串口的一些基本参数然后使用

esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)

将参数配置到对应的串口中。
第二步:设置引脚,IO映射,一般只需要设置tx,rx引脚

uart_set_pin(UART_NUM_1, TXD1_PIN, RXD1_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

注册串口主要实现的功能就是为tx FIFO和rx FIFO分配内存,同时创建消息队列

uart_driver_install(UART_NUM_1, RX1_BUF_SIZE * 2, TX1_BUF_SIZE * 2, 0, NULL, 0);

第三部就是数据发送与接收使用函数uart_write_bytes,uart_read_bytes

4、实例分析

在任务中读写串口:
复制上一个工程改名字为 idf_uart即可 文件名字改为idf_uart.C makefile文件也改成 PROJECT_NAME := idf_uart即可,然后复制一下代码测试。

/**********************************************************************
*               文件名:            idf_uart.c
*               创建人:            
*               创建日期:           
*               修改人:
*               修改日期:           
*               版本号:            V1.1
*               备注:
*               公司:
********************************************************************/
#include <stdio.h>
#include "string.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
//UART1
#define RX1_BUF_SIZE        (1024)
#define TX1_BUF_SIZE        (512)
#define TXD1_PIN            (GPIO_NUM_5)
#define RXD1_PIN            (GPIO_NUM_4)

//UART0
#define RX0_BUF_SIZE        (1024)
#define TX0_BUF_SIZE        (512)
#define TXD0_PIN            (GPIO_NUM_1)//系统默认的下载口
#define RXD0_PIN            (GPIO_NUM_3)

//任务句柄 
static TaskHandle_t xHandleTaskUart0 = NULL;
static TaskHandle_t xHandleTaskUart1 = NULL;

static void vTaskUart0(void *pvParameters);
static void vTaskUart1(void *pvParameters);
/***********************************************************************
* 函数:  
* 描述:   串口配置初始化
* 参数:
* 返回: 无
* 备注:
************************************************************************/
void uart_init(void)
{
    //串口配置结构体
    uart_config_t uart0_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,
    };
    //注采用以下方式初始化参数,系统启动或会出现rx flow thresh error的错误,但不影响使用
    //串口参数配置->uart0
    // uart0_config.baud_rate = 115200;                 //波特率
    // uart0_config.data_bits = UART_DATA_8_BITS;           //数据位
    // uart0_config.parity = UART_PARITY_DISABLE;           //校验位
    // uart0_config.stop_bits = UART_STOP_BITS_1;           //停止位
    // uart0_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;   //硬件流控
    uart_param_config(UART_NUM_0, &uart0_config);       //设置串口
    //IO映射-> T:IO1  R:IO3;系统默认的下载就就是1,3所以可以不用设置管脚
    //uart_set_pin(UART_NUM_0, TXD0_PIN, RXD0_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //注册串口服务即使能+设置缓存区大小
    uart_driver_install(UART_NUM_0, RX0_BUF_SIZE * 2, TX0_BUF_SIZE * 2, 0, NULL, 0);
    uart_config_t uart1_config;
        //串口参数配置->uart1
    uart1_config.baud_rate = 115200;                    //波特率
    uart1_config.data_bits = UART_DATA_8_BITS;          //数据位
    uart1_config.parity = UART_PARITY_DISABLE;          //校验位
    uart1_config.stop_bits = UART_STOP_BITS_1;          //停止位
    uart1_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;  //硬件流控
    uart_param_config(UART_NUM_1, &uart1_config);       //设置串口
    //IO映射-> T:IO4  R:IO5
    uart_set_pin(UART_NUM_1, TXD1_PIN, RXD1_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //注册串口服务即使能+设置缓存区大小
    uart_driver_install(UART_NUM_1, RX1_BUF_SIZE*2, TX1_BUF_SIZE*2, 0, NULL, 0);
}
/***********************************************************************
* 函数:  
* 描述:   主函数
* 参数:
* 返回: 无
* 备注:
************************************************************************/
void app_main()
{    
    //串口初始化
    uart_init();
    //创建串口0任务
    xTaskCreate(vTaskUart0,         // 任务函数              
            "vTaskUart0",           // 任务名
            4096,                   // 任务栈大小,单位 word,也就是 4 字节 
            NULL,                   // 任务参数  
            2,                      // 任务优先级
            xHandleTaskUart0);      // 任务句柄 

    //创建串口1任务
    xTaskCreate(vTaskUart1,         // 任务函数              
            "vTaskUart1",           // 任务名
            4096,                   // 任务栈大小,单位 word,也就是 4 字节 
            NULL,                   // 任务参数  
            3,                      // 任务优先级
            xHandleTaskUart1);      // 任务句柄 

    //串口0数据发送测试     

    uart_write_bytes(UART_NUM_0, "vTaskUart0 Task Create OK ", strlen("vTaskUart0 Task Create OK "));
    
    //串口1数据发送测试     
    uart_write_bytes(UART_NUM_1, "vTaskUart1 Task Create OK ", strlen("vTaskUart1 Task Create OK "));
}
/***********************************************************************
* 函数:  
* 描述:   串口0回调函数
* 参数:
* 返回: 无
* 备注:
************************************************************************/
static void vTaskUart0(void *pvParameters)
{
    uint8_t* data = (uint8_t*) malloc(RX0_BUF_SIZE+1);
    for (;;) 
    {
        //获取串口1接收的数据
        int rxBytes = uart_read_bytes(UART_NUM_0, data, RX0_BUF_SIZE, 10 / portTICK_RATE_MS);//10ms读一次,一个系统滴答
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            //将接收到的数据发出去
            uart_write_bytes(UART_NUM_0, (char *)data, rxBytes);
        }
    }
    free(data);
}
/***********************************************************************
* 函数:  
* 描述:   串口1回调函数
* 参数:
* 返回: 无
* 备注:
************************************************************************/
static void vTaskUart1(void *pvParameters)
{
    uint8_t* data = (uint8_t*) malloc(RX1_BUF_SIZE+1);
    for (;;)  
    {
        int rxBytes = uart_read_bytes(UART_NUM_1, data, RX1_BUF_SIZE, 10 / portTICK_RATE_MS);//10ms读一次,一个系统滴答
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            //将接收到的数据发出去
            uart_write_bytes(UART_NUM_1, (char *)data, rxBytes);
        }
    }
    free(data);
}

5、以下是调试的结果:

在这里插入图片描述

在这里插入图片描述
我发现系统启动后出现了如下的错误不影响使用,但是这个错误看着令人很不爽。
经过博主研究一番,发现通过一下方式初始化参数时就会出现错误

//串口参数配置->uart0
uart0_config.baud_rate = 115200;                    //波特率
uart0_config.data_bits = UART_DATA_8_BITS;          //数据位
uart0_config.parity = UART_PARITY_DISABLE;          //校验位
uart0_config.stop_bits = UART_STOP_BITS_1;          //停止位
uart0_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;  //硬件流控

换成这种方式初始化参数就没有问题

uart_config_t uart0_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,
};

在这里插入图片描述
在这里插入图片描述

串口1多打印了“vTaskUart1 Task Create OK ”原因是在下载过程中系统重启的原因导致。
所有文章源代码:https://download.csdn.net/download/lu330274924/88518092

<think>嗯,用户的问题是关于在ESP32-S3芯片使用ESP-IDF框架编写驱动代码的示例或教程。我需要先理解用户的具体需求。他们可能是在开发某个项目,需要控制外设,比如GPIO、I2C、SPI等,但不知道从哪里找到相关的示例代码或者教程。 首先,我应该回顾一下ESP-IDF的官方资源。ESP-IDF作为乐鑫官方的开发框架,应该提供了丰富的示例代码。用户提到的引用[1]、[2]、[3]中提到了几个示例,比如a2dp_sink、coap_client等,但这些可能更多是应用层的例子,用户可能需要更基础的驱动代码示例。 接下来,我需要考虑用户可能需要的驱动类型。常见的外设包括GPIO、UART、I2C、SPI、ADC、PWM等。ESP-IDF的examples目录下应该有针对这些外设的示例。比如在peripherals子目录中,通常会有各个外设的示例代码,比如gpio、i2c、spi等。 用户提到的引用[1]中提到了esp-skainet和TTS相关的示例,但可能存在音频编解码器移植的问题。这可能意味着用户对音频驱动感兴趣,但官方示例可能不够完善。这时候,可能需要引导用户参考已有的外设示例,或者社区资源。 另外,用户可能对如何配置工程、使用CMake等构建工具有疑问。引用[1]提到ESP-IDF使用CMake,编译速度在Linux上更快,这或许也是用户需要注意的点。在编写驱动代码时,正确的工程配置非常重要,可能需要指导用户如何添加组件、配置外设参数等。 然后,用户可能需要具体的步骤来查找和使用示例。例如,导航到ESP-IDF的examples目录下的具体外设示例,如bluetooth、peripherals等。比如引用[2]中的a2dp_sink示例路径是examples/bluetooth/bluedroid/classic_bt/a2dp_sink,这可能对用户有帮助,但用户需要的是驱动代码,所以应该优先推荐peripherals下的示例。 此外,用户可能不知道如何开始一个新项目,或者如何将示例代码集成到自己的工程中。需要建议他们复制示例到项目目录,并进行必要的配置,比如设置正确的芯片型号、串口参数等。 考虑到用户可能遇到的困难,比如找不到特定芯片ESP32-S3)的示例,需要指出大多数示例支持多芯片,但需要检查sdkconfig中的配置是否正确,确保启用了ESP32-S3的支持。 另外,社区资源如CSDN博客、乐鑫官方论坛、GitHub仓库可能有更多的教程和解决方案。用户提到的引用[3]中有一个CSDN博客链接,这可能是一个有用的资源,可以推荐用户参考。 最后,生成相关问题时要考虑用户可能的后续疑问,比如外设配置、调试方法、驱动移植、性能优化等。这些问题可以帮助用户进一步深入学习和解决问题。</think>### ESP32-S3 ESP-IDF 驱动代码开发指南 #### 一、官方资源定位 1. **外设驱动示例** ESP-IDF 框架在 `examples/peripherals` 目录下提供完整的外设驱动示例,覆盖 GPIO、I2C、SPI、UART、ADC 等常用外设。例如: - `gpio` 目录包含 GPIO 输入输出中断示例 - `i2c` 目录提供 I²C 主从模式通信案例 - `spi` 目录展示 SPI 主设备与存储器交互实现 通过以下命令可快速定位示例: ```bash cd $IDF_PATH/examples/peripherals ``` 2. **芯片专用配置** ESP32-S3 需在 `sdkconfig` 中启用芯片支持: ```python CONFIG_IDF_TARGET_ESP32S3=y # 设置目标芯片ESP32-S3 ``` #### 二、典型驱动开发流程 以 GPIO 驱动为例: ```c // 初始化 GPIO 配置结构体 gpio_config_t io_conf = { .pin_bit_mask = (1ULL << GPIO_NUM_4), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; gpio_config(&io_conf); // 应用配置 // 控制 GPIO 输出电平 gpio_set_level(GPIO_NUM_4, 1); // 设置高电平 ``` #### 三、音频驱动参考方案 对于 TTS 等音频应用,建议结合以下资源: 1. **官方音频框架** `esp-adf` (音频开发框架) 提供编解码器驱动实现,包含 MP3、WAV 等格式支持[^1] 2. **硬件抽象层** 使用 `i2s` 驱动实现数字音频接口配置: ```c i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_TX, .sample_rate = 44100, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT }; i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); ``` #### 四、调试建议 1. **实时日志分析** 启用 `idf.py monitor` 查看实时运行日志 2. **信号测量** 使用逻辑分析仪验证 SPI/I2C 时序: ```bash # 查看 I2C 总线状态 i2c-tools 工具包提供 i2cdetect 等诊断命令 ``` #### 五、扩展学习路径 1. **外设寄存器编程** 通过 `soc/esp32s3/include/soc` 目录查看芯片寄存器定义 2. **中断管理** 参考 `intr_alloc` 示例实现高效中断处理
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物联网程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值