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.

[参考译文] CC2642R:UART2和任务设计

Guru**** 2538955 points
Other Parts Discussed in Thread: SYSCONFIG

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

https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/992792/cc2642r-uart2-and-task-design

器件型号:CC2642R
Thread 中讨论的其他器件:SysConfig

大家好、

我已通过 系统配置工具使用 UART2驱动程序将本机 UART 接口分配给两个器件(115200和9600、两者均为8N1)。  除了"串行"任务之外、没有其他正在运行的任务。 我观察到在接收、处理和随后通过示波器上的 UART2发送应答之间存在很大的延迟(+1秒)。 这远远不是最佳的、而且即将出现功能失调的情况-我想将这种延迟至少减少800ms。  

通过主机显示禁用"Display_printf"进行调试可能会改善运行时间、但考虑到仅使用一个串行外设的延迟、我无法想象这两个外设会更好。   我需要优化处理串行外设的方法。  

我正在考虑的解决方案:

  1. (当前) 具有四个事件(COM1_RX、COM1_TX、COM2_RX、COM2_TX)的"串行"任务、具有回调和外部可访问的"serial_write"函数。  
    1. 事件 COMx_RX 调用 UART2_READ 以读取静态本地缓冲区。
    2. 事件 COMx_TX 调用 UART2_Write 来写入静态本地缓冲区。
    3. "serial_write"将远程缓冲区复制 到静态本地缓冲区、然后根据指定的端口布置事件 COMx_TX。
    4. callbackFxn_read 将缓冲区指针和长度传递给外部函数、用于解析和发布事件 COMx_RX、以重新启用串行 RX。
  2. (下一个解决方案)一个具有同等优先级的"COM1"和"COM2"任务、每个任务都演示了以下行为。
    1. 任务循环(串行读取和分析)
      1. 进入任务循环时调用 UART2_READ。
      2. 信号量块直到回调 Fxn_read 释放信号量。
      3. UART 消息在本地解析。  
    2. 可从外部访问的"serial_write"函数
      1. "serial_write"会将远程缓冲区复制到静态本地缓冲区、UART2_write 会立即复制。

我考虑的事项:

  • 没有系统可以排队或列出消息-消息的处理速度与接收或生成速度一样快。
  • 时钟用于(1)生成一些消息、(2)生成丢失消息的超时中断、时间至关重要。
  • 三个任务(简单外设、COM1和 COM2)将在它们之间通信信息、因此需要平衡线程安全性和延迟。

在推进总体项目开发之前、我正在寻找任何和所有建议来提高绩效。

Ken

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

    跟进:

    我尝试了"下一个解决方案"、其中涉及使用信标控制任务执行的单个任务和单个外设。 我能够将响应时间修整为280ms。

    我已从系统配置中禁用"显示模块"、以查看它在最终系统中的工作程度。 平均而言、我观察到响应时间为3.15ms。

    注释和致谢:

    1. 我基于传感器内核示例代码开发了 TI-RTOS 信号量(因为缺少更好的完整参考)。
    2. 系统怠速更平稳。 将尝试运行两个串行任务。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Ken、

    您是否在 uart2callback uart2echo 示例上进行了评估和扩展?  您能否共享 UART2参数?  TI 驱动程序似乎 在处理接收到的数据之前超时、这可以通过 修改实现来改进。  您不需要使用单独的任务或传感器控制器。   

    此致、
    Ryan

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

    您好、Ryan、

    我评估了 uart2callback 和 uart2echo 示例、并基于这些示例进行了源代码计算。 从代码可读性的角度来看、我实际上更喜欢使用具有自己信标的单独串行任务来调节 RX 处理。 到目前为止、我使用单个"串行"任务来处理器件驱动程序、这些驱动程序将器件特定源与驱动程序本身分离。 将每个外设分离到自己的任务中使我能够在该任务中隔离与该器件相关的所有源。

    我很高兴不再将传感器控制器用作 UART 仿真器、并且对传感器控制器示例中包含的信标示例通常感到满意(我充其量只精通 C 语言-我绝不是嵌入式专业人员)。  

    连接到下面的 IF 我的 UART 任务和回调函数。 它非常轻-我唯一的问题是我是否应该实施缓冲区。 据我所知、外部外设仅在先发送 TX 时发送 RX。 我认为这使我不必实施环形缓冲器,但我希望得到第二个意见。

    void COM1_TaskFxn(UArg a0, UArg a1) {
        uint32_t          status = UART2_STATUS_SUCCESS;
    
        // Initialize the UART driver.
        UART2_Params_init(&UART_Params_COM1);
        UART_Params_COM1.readMode     = UART2_Mode_CALLBACK;
        UART_Params_COM1.readCallback = callbackFxn_COM1_read;
        UART_Params_COM1.dataLength   = UART2_DataLen_8;
        UART_Params_COM1.stopBits     = UART2_StopBits_1;
        UART_Params_COM1.parityType   = UART2_Parity_NONE;
        UART_Params_COM1.baudRate     = 115200;
    
        UART_COM1 = UART2_open(UART2_COM1, &UART_Params_COM1);
    
        if (UART_COM1 == NULL) {
            Display_printf(dispHost, 0, 0, "COM1 Task: Failure to Initialize\n");
            while(1);
        } else {
            Display_printf(dispHost, 0, 0, "COM1 Task: Initialized Successfully\n");
        }
    
        while(1) {
    
            uint8_t numBytesRead = 0;
    
            status = UART2_read(UART_COM1, &RX_COM1, 256, numBytesRead);
    
            if (status != UART2_STATUS_SUCCESS) {
                /* UART2_read() failed */
                Display_printf(dispHost, 0, 0, "COM1: Failed to Read\n");
                while (1);
            }
    
            // Wake up the OS task
            Semaphore_pend(Semaphore_handle(&COM1_TaskSemaphore), BIOS_WAIT_FOREVER);
    
            COM1_RX_Handler(RX_COM1, numBytesRead);
    
            memset(RX_COM1, 0x00, sizeof(RX_COM1));
        }
    }
    
    void callbackFxn_COM1_read(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() */
            Display_printf(dispHost, 0, 0, "COM1: Failed to Read\n");
        }
    
        // Wake up the OS task
        Semaphore_post(Semaphore_handle(&COM1_TaskSemaphore));
    
    }

    最棒的

    Ken

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

    在 UART2_Mode_callback 中、回读回调(当缓冲区大小(256)已填充(UART2_ReadReturnMode_Full)或由于接收不活动(UART2_ReadReturnMode_Partial)而发生超时时时时、返回 callbackFxn_COM1_Read。  Display TI 驱动程序对延迟的影响令人惊讶、因为它仅传输数据、不应阻止其他任务操作。

    此致、
    Ryan

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

    我也是这么认为的。 最初、我打算使用 UART (UART1?) 驱动程序、但我发现基于换行符的返回要求的文本模式。 我在这里发布了相关信息。  

    不过、我更确切地说、在一些情况下、缓冲器是在 RX 流量较高的不太可能发生的情况下的一个好主意。 您是否有针对此问题的推荐方法、例如邮箱或列表? 或者我应该只写一个 ringbuffer 吗? 我想最大程度地减少延迟、由于没有数据离开此线程、我怀疑线程安全性目前不是问题。

    最棒的

    Ken

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

    感谢您链接原始主题。  UART1和 UART2 TI 驱动程序之间当然存在明显差异。  对于邮箱或列表是否有利、我没有任何经验可供评论、通常 现有的 UART2环缓冲器已经足够了。

    此致、
    Ryan

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

    嗯。

    作为外设初始化过程的一部分、我依次请求一系列值(与12个单独的写入命令相关联)。 我注意到、间隔为100ms 时、我收到2个与 UART 解析函数默认情况对应的"未知响应"。 在单步执行函数时、这些误差消失。  

    我非常喜欢 UART2的换行/自动返回、 但需要进行某种控制。 对于每个传输、都有一个预期的响应。 我计划的方法是创建一个 bool "WaitingForRx"、该 bool 通过串行写入操作设置为 true。 后续写入被阻止、直到该值为 false。 由于我对协议时序有很好的了解、如果在设定的时间段后没有收到响应、我应该能够添加一个时钟、将其分配为 false。 这应最大限度地减少与此任务相关的中断次数。

    我愿意听取您关于如何更好地实施此发送/侦听系统的建议。

    最棒的

    Ken

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

    我注意到、有时缓冲器不会返回值(或至少不会返回所有值)。 为了诊断这一点、我添加了一些调试代码来检查读取操作返回的值:

    memset(RX_Buff, 0x00, sizeof(RX_Buff));
    size_t numBytesRead = 0;
    Display_printf(dispHost, 0, 0, "COM1(i)(%u): Read %lu bytes", RefreshValue,(long unsigned int)numBytesRead);
    
    // Read each byte, callback writes byte to Riung
    status = UART2_read(uart, RX_Buff, 32, &numBytesRead);
    Display_printf(dispHost, 0, 0, "COM1(R)(%u): Read %lu bytes", RefreshValue,(long unsigned int)numBytesRead);
    
    if (status != UART2_STATUS_SUCCESS) {
        /* UART2_read() failed */
        Display_printf(dispHost, 0, 0, "COM1: Failed to Read\n");
        while (1);
    }
    
    // Wake up the OS task
    Semaphore_pend(Semaphore_handle(&com1TaskSemaphore), BIOS_WAIT_FOREVER);
    Display_printf(dispHost, 0, 0, "C0M1(S)(%u): Read %lu bytes", RefreshValue,(long unsigned int)numBytesRead);
    RX_Handler(RX_Buff, numBytesRead);
    WaitingForRX = false;

    我在控制台中看到的是困惑:

    COM1(i)(0): Read 0 bytes
    COM1(R)(0): Read 0 bytes
    COM1(S)(1): Read 0 bytes
    // No Error
    
    COM1(i)(1): Read 0 bytes
    COM1(R)(1): Read 0 bytes
    COM1(S)(2): Read 0 bytes
    // No Error
    
    COM1(i)(2): Read 0 bytes
    COM1(R)(2): Read 0 bytes
    COM1(S)(3): Read 0 bytes
    // No Error
    
    COM1(i)(3): Read 0 bytes
    COM1(R)(3): Read 0 bytes
    COM1(S)(4): Read 0 bytes
    // Error
    
    COM1(i)(4): Read 0 bytes
    COM1(R)(4): Read 7 bytes
    COM1(S)(4): Read 7 bytes
    // Error
    
    COM1(i)(4): Read 0 bytes
    COM1(R)(4): Read 0 bytes
    COM1(S)(5): Read 0 bytes
    // Error
    
    COM1(i)(5): Read 0 bytes
    COM1(R)(5): Read 0 bytes
    COM1(S)(6): Read 0 bytes
    // No Error
    
    COM1(i)(6): Read 0 bytes
    COM1(R)(6): Read 0 bytes
    COM1(S)(7): Read 0 bytes
    // Error
    
    COM1(i)(7): Read 0 bytes
    COM1(R)(7): Read 4 bytes
    COM1(S)(7): Read 4 bytes
    // No Error
    
    COM1(i)(7): Read 0 bytes
    COM1(R)(7): Read 0 bytes
    COM1(S)(8): Read 0 bytes
    // No Error
    
    COM1(i)(8): Read 0 bytes
    COM1(R)(8): Read 0 bytes
    COM1(S)(9): Read 0 bytes
    // Error
    
    COM1(i)(9): Read 0 bytes
    COM1(R)(9): Read 4 bytes
    COM1(S)(9): Read 4 bytes
    // Error
    
    COM1(i)(9): Read 0 bytes
    COM1(R)(9): Read 0 bytes
    COM1(S)(10): Read 0 bytes
    // No Error
    
    COM1(i)(10): Read 0 bytes
    COM1(R)(10): Read 0 bytes
    COM1(S)(11): Read 0 bytes
    // No Error
    
    COM1(i)(11): Read 0 bytes
    COM1(R)(11): Read 0 bytes
    COM1(S)(12): Read 0 bytes
    // No Error

    从我自己的代码 中、没有任何关于写入的字节为什么通常为零的解释。 我注意到一些有趣的行为:

    1. 在第一个缓冲区截断之前读取32个字节。
    2. 读取的字节= 7发生在(1)之后、这是一个准确的字节计数。
    3. 32个字节由下一个缓冲区截断点读取
    4. 读取的字节=4发生在(2)之后,这是一个准确的字节计数。
    5. 32个字节由下一个缓冲区截断点读取
    6. 读取的字节= 4发生在(3)之后、这是一个准确的字节计数。
    7. 最后3次读取不使用32个字节。

    这使我非常清楚、我正在 UART2驱动程序本身内部的32字节缓冲区中运行。 这与存储多达256个值的 RX_Buffer 不同。 那么、现在这个时刻最重要的问题是、可以对这个问题做些什么?

    我的直觉是使用单字节迷你缓冲区存储 UART2驱动程序读取的数据、然后使用读取回调函数将该值插入环形缓冲区。 如果检测到终止序列、则缓冲器将传递给 RX_handler 函数。

    该解决 方案完全挫败了 UART2驱动器对多字节读取的自动终止。  我看到 API 中的"UART2_flushRx (UART_Handle)"函数似乎与我需要的内容相匹配-它看起来工作正常。  

    最棒的

    Ken

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

    您好 Ken、

    很抱歉、我想联系 TI 驱动程序开发专家、评论您描述的行为。

    此致、
    Ryan

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

    您好、Ryan、

    感谢您的更新。  读取的字节数的奇怪行为 值得关注。 我还注意到、无法尝试从缓冲 区中读取超过32个字节。 虽然这在原始 UART 驱动程序中得到了确认、但它似乎也适用于 UART2。

    // GLOBAL VARIABLES
        // Read Buffers
        const  size_t   RX_Size = 256;
        static uint8_t  RX_Read [RX_Size];
    
    // Task Function
        memset(RX_Read, 0x00, RX_Size);
        
        status = UART2_read(COM_1, &RX_Read, RX_Size, NULL);
    
        if (status != UART2_STATUS_SUCCESS) {
            /* UART2_read() failed */
            Display_printf(dispHost, 0, 0, "COM1: Failed to Read\n");
        }
        
        // Wake up the OS task
        Semaphore_pend(Semaphore_handle(&com1TaskSemaphore), BIOS_WAIT_FOREVER);
        vesc_RX_Handler(RX_Read);
        UART2_flushRx(COM_1);

    我将 UART2 RX_READ 缓冲器与为 UART1开发的环形缓冲器进行了比较、并观察 到缓冲器内容和长度存在差异。  我对 TI 驱动程序开发专家的想法很好奇。

    最棒的

    Ken

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

    您好、Ryan、

    我不熟悉 SysConfig 工具、只是意识到在 UART2驱动程序的配置中、我可以指定 RX 和 TX 缓冲区长度。

    我将对此进行拍摄、看看它是否起作用。 如果读取的字节数不正确、我仍然认为驱动程序有问题。

    跟进:

    这无法解决4.40 SDK 上的问题。 但是、我更新为5.10、它突然接收到更多看起来正确的字节! 哇!

    UART_READ()准备就绪的字节数仍然为零。 因此、肯定存在一个错误。

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

    您好 Ken、

    我从 TI 驱动程序团队获得了一些反馈、我想与大家分享这些反馈:

    " 看到 UART2中的换行处理让我有点惊讶、因为我们有意删除了与 UART2中实际有效负载内容相关的任何功能。

    在回调模式下,在设置 UART HW 和 DMA 事务并从环形缓冲区中读取任何已有的数据之后,对 UART2_READ()的调用会立即返回。 除非我们已经在循环缓冲区中存储了立即复制回的字节,否则该 UART2_READ()调用将始终向 numBytesRead 写入0。 这是有道理的、因为在这一点上、没有任何内容被读出。

    在回调函数中读取的总字节数可用作 count 参数。 这就是应该打印的内容。 但是,这会是一个问题,因为它是在 HWI 环境中执行的,Display_printf()当时不可用。

    建议不要 使用回调模式来模拟阻塞模式。  阻断模式专为其用例而设计 、如果他们使用、开发门槛会明显更简单。 回调模式适用于需要异步操作的用户。"

    我希望这对您有所帮助、
    Ryan

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


    感谢 Ryan -我从这个过程中学到了很多、感谢您的帮助。 请向司机团队致谢-我现在了解了打印方面的行为。  

    最棒的

    Ken