This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CC3551E:UART 驱动程序读取问题

Guru**** 2928100 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/wireless-connectivity/wi-fi-group/wifi/f/wi-fi-forum/1642130/cc3551e-uart-driver-read-issue

器件型号: CC3551E

您好的团队、

我们遇到了 UART 驱动程序的问题、即观察到的行为与我们对文档的理解不完全一致。

 

相关文档

在 UART2_ReadReturnMode_Partial 中、UART2_READ () 将阻止、直到接收到请求的字节数或发生 UART 硬件读取超时。

...

如果读取返回模式为 UART2_ReadReturnMode_Partial、则 UART2_READ () 会在读取所有数据之前返回 、如果已接收到某些数据、但接收已处于非活动状态足够长的时间、导致硬件读取超时发生(例如,在 32 位周期内)。

 

我们观察到、对于特定的读取模式、驱动程序环缓冲区可能包含数据(如 UART2_getRxCount 所示)、但 UART2_read() UART2_Mode_BLOCKING UART2_ReadReturnMode_PARTIAL 始终调用 IN + MODE 块(或直到新数据通过串行线路进入)。 我们对文档(以及驱动程序声称模仿的 POSIX 行为)的解释是、此类读取会立即返回环缓冲区中的可用数据。

 

我们可能能够解决应用程序代码中的当前行为,但与直接修复驱动程序行为相比,这可能不太可靠。

 

重现此问题的步骤如下:

  • 启用硬件流控制
  • 激活 RX 环缓冲区(大小) R
  • 向 size 个 1.5 * R 字节的器件发送数据突发
  • UART2_read() 具有目标缓冲区大小的调用 R
    •  此处按预期返回 R 字节
  • UART2_read() R 再次调用目标缓冲区大小
    • 此调用将永远阻止
    • 我们期望它返回 R / 2 字节

 

您对此修复有何评论?

 

此致、

Luke

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Luke、

    我的实验略有不同、但请参阅下面的。

    e2e.ti.com/.../uart_5F00_returnpartial.mp4

    打印的字符对应于我要粘贴到 UART 终端的字符数。 它是 uart2echo 工程的自旋图、其中默认参数会用到  

    readReturnMode = UART2_ReadReturnMode_Partial;
     
    打印的字符为 48 + bytesRead(因此我引用 ASCII 表的原因)。  
     
    如果您有任何问题、请告诉我。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我对 UART 样片进行了以下更改。 将 40 个字符粘贴到终端会立即打印“32“、然后暂停以进行更多输入。 键入任何键 tat 这一点然后打印“9“(从上一次粘贴的 8 个多余的字符加上新的)。 我的预期行为是立即打印“32“和“8“。

    diff --git a/uart2echo_LP_EM_CC35X1 freertos_ticlang/uart2echo.c b/uart2echo_LP_EM_freertos_ticlang/uart2echo.c
    索引 70755af..ab7e79b 100644
    
    
    @@@@
    
    
    
    
    
    
    
    @@@@
    
    
    
    
    
    const char echoPrompt[]=“回显字符:\r\n“;
    UART2_Handle UART;
    UART2_Params uartParams;
    @@–65、6 +66、7 @@ void *mainThread (void *arg0)
    /*创建默认读写模式阻塞的 UART */
    UART2_Params_init (&uartParams);
    uartParams.baudrate = 115200;
    + uartParams.readReturnMode = UART2_ReadReturnMode_Partial;
    
    UART = UART2_OPEN (CONFIG_UART2_0、&uartParams);
    
    @@–85、7 +87、7 @@ void *mainThread (void *arg0)
    bytesRead = 0;
    while (bytesRead = 0)
    {
    - 状态= UART2_READ (UART、&INPUT、1、&bytesRead);
    + 状态= UART2_READ(UART、&INPUT、sizeof(输入)、&bytesRead);
    
    IF (STATUS!= UART2_STATUS_SUCCESS)
    {
    @@–94,10 +96,16 @@ void *mainThread(void *arg0)
    }
    }
    
    + //延迟后再读一点、以模拟使用输入数据执行某些操作、
    + //导致过多的数据被存储在环缓冲区中。
    + uleep(1000);
    ++
    sprint f(输入,“%d\r\n“、(int) bytesRead);
    +
    bytesWritten = 0;
    while (bytesWritten == 0)
    {
    - 状态= UART2_WRITE (UART、&INPUT、1、&bytesWritten);
    + 状态= UART2_WRITE (UART、&INPUT、strlen (INPUT)、&bytesWritten);
    
    IF (STATUS!= UART2_STATUS_SUCCESS)
    {
    
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Edward、我正在查看您的 UART2_READ、它看起来与 Luke Dawdy 描述的场景略有不同。 我个人将其设置为振铃缓冲区的大小 (32)。  

    void *mainThread(void *arg0)
    {
        char input;
        char output;
        const char echoPrompt[] = "Echoing characters :) \r\n";
        const char buttonPressed[] = "Button pressed:\n\r";
        UART2_Handle uart;
        UART2_Params uartParams;
        size_t bytesRead;
        size_t bytesWritten = 0;
        int_fast16_t status = UART2_STATUS_SUCCESS;
    
        /* Call driver init functions */
        GPIO_init();
    
        /* Configure the LED pin */
        GPIO_setConfig(CONFIG_GPIO_LED_0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
        /* Create a UART where the default read and write mode is BLOCKING */
        UART2_Params_init(&uartParams);
        uartParams.baudRate = 115200;
    
        uart = UART2_open(CONFIG_UART2_0, &uartParams);
    
        if (uart == NULL)
        {
            /* UART2_open() failed */
            while (1) {}
        }
    
        /* Turn on user LED to indicate successful initialization */
        GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
    
        UART2_write(uart, echoPrompt, sizeof(echoPrompt), &bytesWritten);
    
        // bytesWritten;
        while (!GPIO_read(CONFIG_BUTTON_0));
        UART2_write(uart, buttonPressed, sizeof(buttonPressed), &bytesWritten);
    
        /* Loop forever echoing */
        while (1)
        {
            bytesRead = 0;
            while (bytesRead == 0)
            {
                status = UART2_read(uart, &input, 32, &bytesRead);
    
                if (status != UART2_STATUS_SUCCESS)
                {
                    /* UART2_read() failed */
                    while (1) {}
                }
            }
    
            output = 48 + bytesRead;
    
            bytesWritten = 0;
            while (bytesWritten == 0)
            {
                status = UART2_write(uart, &output, 1, &bytesWritten); // originally 32
    
                if (status != UART2_STATUS_SUCCESS)
                {
                    /* UART2_write() failed */
                    while (1) {}
                }
            }
        }
    }

    您可以忽略“按钮按压“的内容。 我只是在四处玩耍。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    卢克代表我问。

    在我的示例中、`uSleep`μ s 是 100%可靠重现的必要条件。 否则、读取循环的重复速度太快、无法到达特定的驱动程序代码路径(环形缓冲区中的数据,但硬件 FIFO 中没有数据)。

    在我的硬件设置中、通过将波特率提高到 1.5Mb 并在 33 到 55 字节之间发送突发、它在没有睡眠的情况下也始终可以重现。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的、明天来看一下。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Edward:

    我首先在 while (bytesRead = 0) 块之后添加了 uSleep、没有卡住。 能够得到这个打印输出。

    我将在下周旅行、但如果有差距、我可以尝试其他方法来解决问题。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    请注意、需要睡眠(或高波特率)和大型数据突发才能重现该问题。 看起来您的重复尝试仅执行一项或另一项操作。 使用你的代码(与睡眠)我可以点击它:

    1. 更正`input`的大小、使读取不会破坏堆栈 (->char input char input[32])。
    2. 将 33 个字符粘贴到终端“33b33b33b33b33b33b33b33b33b33b33b33b33b33b33b33b33b33b“中。
    3. 这会导致“P“、而不是预期的“P1“
    4. 在此之后键入单个字符将产生“2“、而不是预期的“1“
    5. 这说明了粘贴的字符串的最后一个字节会卡在环形缓冲区中、直到新数据通过硬件 FIFO 传入。

    e2e.ti.com/.../Screencast-from-2026_2D00_05_2D00_11-13_2D00_35_2D00_15.mp4

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Edward:

    很抱歉误解。 是、我只添加了睡眠模式、没有更大的数据突发。 当我从出差回来时、我会再试一次。

    感谢您的耐心。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Edward:

    我使用了您的较高波特率、并且重现了这个问题。 我们会在最后看到为什么会出现这种情况、因为 UART 部分读取的原始功能似乎没有按照预期完全正常工作。

    感谢您提请我们注意这一点。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Edward:

    这是我做的另一件事、我非常感谢您的反馈:

    /* Loop forever echoing */
        while (1)
        {
            size_t totalBytes = 0;
    
            /* Block until burst arrives — hardware PARTIAL idle fires when line goes quiet */
            UART2_read(uart, input, sizeof(input), &bytesRead);
            totalBytes = bytesRead;
    
            /* Drain ring-buffer leftovers; 10ms timeout >> 87µs inter-char gap at 115200 */
            do {
                bytesRead = 0;
                status = UART2_readTimeout(uart, input, sizeof(input), &bytesRead, 10);
                if (status != UART2_STATUS_SUCCESS && status != UART2_STATUS_ETIMEOUT)
                {
                    while (1) {}
                }
                totalBytes += bytesRead;
            } while (bytesRead > 0);
    
            output = '0' + totalBytes;
            bytesWritten = 0;
            UART2_write(uart, &output, 1, &bytesWritten);
        }

    以较低的波特率粘贴字符串打印件 P1、以较高的波特率粘贴超过 60 个字符会产生'm'+/-几个字符。

    这对您有用吗? 我执行 64 最大值、因为这是我正在读取的大小+我的环状缓冲区的大小。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我们有一个替代的解决方法来避免这种繁忙的轮询。 但这并不是一个完美的解决方案、因为它没有充分利用驱动程序中零复制 DMA 路径的目标缓冲区大小。

    静态 int_fast16_t UART2_readPartial (UART2_handle 句柄、void *缓冲器、size_t size、size_t *bytesRead)

      //实验中只有以下读取模式始终能够返回部分数据:
      // 1。 读取 1 个字节
      // 2. 读取的字节数应小于或等于 RX 振铃缓冲区中当前的数量。
      // 3. 当振铃缓冲区为空时读取任意数量的字节(注意:这不能通过应用代码以无竞争方式完成)。
      size_t ringBufferCount = UART2_getRxCount (handle);
      if (ringBufferCount > 0)
      {
        //机会性快速路径在安全的情况下进行批量读取
        返回 UART2_READ (handle、buffer、min (size、ringBufferCount)、bytesRead);
      }
      暴露
      {
        //否则回退到低效但安全的单字节读取
        返回 UART2_READ(句柄,缓冲器,MIN (SIZE、1)、bytesRead);
      }
    }
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Edward:

    我们的开发团队仍在调查此问题、但我最终发现了一个新发现:

    /*
     * Copyright (c) 2020-2025, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdint.h>
    #include <stddef.h>
    
    /* POSIX Header files */
    #include <semaphore.h>
    
    /* Driver Header files */
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/UART2.h>
    
    /* Driver configuration */
    #include "ti_drivers_config.h"
    
    static sem_t sem;
    static volatile size_t numBytesRead;
    
    /*
     *  ======== callbackFxn ========
     */
    void callbackFxn(UART2_Handle handle, void *buffer, size_t count, void *userArg, int_fast16_t status)
    {
        if (status != UART2_STATUS_SUCCESS)
        {
            /* RX error occured in UART2_read() */
            while (1) {}
        }
    
        numBytesRead = count;
        sem_post(&sem);
    }
    
    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        char input;
        const char echoPrompt[] = "Echoing characters:\r\n";
        UART2_Handle uart;
        UART2_Params uartParams;
        int32_t semStatus;
        uint32_t status = UART2_STATUS_SUCCESS;
    
        /* Call driver init functions */
        GPIO_init();
    
        /* Configure the LED pin */
        GPIO_setConfig(CONFIG_GPIO_LED_0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
        /* Create semaphore */
        semStatus = sem_init(&sem, 0, 0);
    
        if (semStatus != 0)
        {
            /* Error creating semaphore */
            while (1) {}
        }
    
        /* Create a UART in CALLBACK read mode */
        UART2_Params_init(&uartParams);
        uartParams.readMode     = UART2_Mode_CALLBACK;
        uartParams.readCallback = callbackFxn;
        // uartParams.baudRate     = 115200;
        uartParams.baudRate = 1500000;
    
        uart = UART2_open(CONFIG_UART2_0, &uartParams);
    
        if (uart == NULL)
        {
            /* UART2_open() failed */
            while (1) {}
        }
    
        /* Turn on user LED to indicate successful initialization */
        GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
    
        /* Pass NULL for bytesWritten since it's not used in this example */
        UART2_write(uart, echoPrompt, sizeof(echoPrompt), NULL);
    
        /* Loop forever echoing */
        while (1)
        {
            numBytesRead = 0;
    
            /* Pass NULL for bytesRead since it's not used in this example */
            status = UART2_read(uart, &input, 32, NULL);
    
            if (status != UART2_STATUS_SUCCESS)
            {
                /* UART2_read() failed */
                while (1) {}
            }
    
            /* Do not write until read callback executes */
            sem_wait(&sem);
    
            input = 48 + numBytesRead;
    
            if (numBytesRead > 0)
            {
                status = UART2_write(uart, &input, 1, NULL);
    
                if (status != UART2_STATUS_SUCCESS)
                {
                    /* UART2_write() failed */
                    while (1) {}
                }
            }
        }
    }

    在本项目中、RX 和 TX 振铃缓冲区大小的 SYSCFG 设置设置设置为 32。

    在 115200 波特率下、字节不会卡在振铃缓冲区中。

    这是你目前可以使用的东西吗?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我之前发布的解决方法足以满足我们当前的使用、因此我们不需要任何其他类型的临时修复。