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.

[参考译文] RTOS:RTOS:MSP430F5438A UART 中断

Guru**** 2696775 points

Other Parts Discussed in Thread: MSP430F5438A

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

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/574377/rtos-rtos-msp430f5438a-uart-interrupt

主题中讨论的其他器件:MSP430F5438A

工具/软件:TI-RTOS

您好!

我在 MSP430F5438A 上使用 TI-RTOS。 我想在 MSP 和另一个器件之间进行 UART 通信。

我有一个任务、可以从 UART 中写入和读取消息。 在 UART_write()之后,设备会向我返回另一条消息。 通信包含从我的 MSP 发送到器件并立即由器件响应的消息。 对于我发送 到器件的每条消息、我需要等待一段时间、以便下一条消息从 MSP 发送到器件。

在这种情况下,我使用 TI-RTOS 驱动程序时没有问题,我只需要调用 UART_write(),然后在该 UART_read()之后。 我的问题是、设备会随机发送一些消息。 我不想停留在循环中,在循环中调用 UART_read()来捕获随机时间出现的缓冲区。 当我在 UART (上升沿)上有东西并读取消息时、是否有任何方法产生中断?

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

    我们一直在处理类似的问题(在 MSP432上、但 UART 驱动程序几乎相同)。
    如果您不知道何时接收字节或实际接收多少字节、如何使用 UART 驱动程序?

    可能的解决方案:

    将 UART 配置为在读取回调模式下一次接收单个字符。
    回调会将收到的字符放入 RingBufferand 并发布 SWI
    SWI 调用 UART_read()以获取下一个字符(RTOS 不允许您在回调中调用 UART read!)。
    然后、当获得时间时、外部任务可以从 RingBuffer 中处理字节。

    在 SWI 中调用 UART_read(),而不是在回调中略微增加了在较高波特率或 CPU 负载较重的情况下丢失字符的风险,除非您对优先级很小心。

    使用这种方法、即使您知道数据是以固定大小的数据包发送给您、也最好一次只能粘附一个字符、否则您必须开始考虑如何处理错误情况。
    例如,如果您在中途通过数据包或某个字符调用 UART_read(),会错过什么
    如果发生这种情况、您最终会与数据流不同步、并需要以某种方式重新建立同步。

    只需以较高的波特率观察处理器负载-例如、57600Baud 每(大约) 17uSec 您就会得到一个中断、发布一个 SWI、将字符加载到环形缓冲区、准备下一次读取。 这*可能*会*妨碍系统中的其他操作。

    最后、我们决定不使用 UART_READ 的驱动程序、并创建了我们自己的裸机驱动程序、该驱动程序基本上只是一个 ISR、每当 UART Rx 中断触发时、会将接收到的 charater 整合到 RingBuffer 中(并适当处理中断标志)。

    如果将 UART 驱动程序扩展为具有与我们的裸机驱动程序类似的模式、那将会更好。  即、提供启动"后台读取模式"的功能、该模式常量运行并将接收到的字符推入环形缓冲区、并提供 API 以从环形缓冲区中弹出字符(例如、在处理接收到的数据的任务中)。  


    谢谢
    Julian

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

    TI-RTOS 中的 UART 驱动程序创建编号为46的 Hwi。 在这种情况下、我可以使用 UART_WRITE 驱动程序并在裸机中创建用于 UART 读取的 ISR?

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

    我想我对我们所做的事情进行了误解。 最后、我们为读取和写入编写了自己的简单裸机 UART 驱动程序。

    对于给定的 UART、您不能使用 RTOS 进行写入、也不能使用自己的驱动程序进行读取(至少、在不更改和重新编译 RTOS UART 驱动程序的情况下是如此。)

    如果您的实现使用多个 UART、则将 RTOS 驱动程序用于一个 UART、将您自己的驱动程序用于另一个 UART 不会有任何问题。

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

    离子、

    随机入站消息的时间是多久? 您如何保证随机入站消息不会与 来自设备的 ACK 入站消息重叠? 我假设您有一个解决方案。

    简单的方法是在 MSP 上执行两项任务:一项任务用于写入、另一项任务用于读取。 写入任务会根据需要唤醒以将其消息发送到器件。 读取任务将接收 ACK 消息。 您可以在写入任务中设置一个标志、告知读取任务需要 ACK 消息。 如果读取任务接收到一条消息、并且标志未设置、则这是一条随机消息。 这假设 你有一个解决我上面所问问题的办法。

    如果时序使得 ACK 消息和随机消息永远不会重叠、则另一种方法是使用 UART 读取超时。 假设您 每5秒发送一次消息。 您可以调用 UART 读取、超时为5秒。 如果该调用返回时没有数据、则您知道是时候发出 UART 写入、然后进行 UART 读取。 然后您再次调用 UART 读取并再次等待。 如果 UART 读操作返回数据、则会显示随机消息。 其优点是您只需要一个任务。 但超时值是打开的调用的参数。 因此、您无法更改它、所有 UART 读取都将具有相同的超时。 这可能是一个问题。

    最后一个建议是使用 UART 回调模式。 这种方法也可以作为一项任务而不是两项任务来完成。 在此选项中、您在回调模式下调用 UART 读取。 发生的情况是、UART 读取立即返回、您只需向驱动程序发出缓冲区即可。 当缓冲区已满时、将调用回调。 现在您创建一个信标。 在 UART 读取回调中,您调用 Semaphore_post()。 您的任务会调用 semaphore_pend()并具有超时。 如果返回时出现超时、您就知道是时候进行写入和读取了。 如果返回时没有超时,则表示您收到了随机消息。

    ~Ramsey

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

    >读取任务将收到 ACK 消息。

    您能不能再解释一下您所谓的"阅读任务"。
    我有这种令人不安的担心、这实际上比您说的更难实现。

    如何处理不同大小的 ACK/Random 消息? [原始海报可能无法控制这些消息大小]

    您如何处理丢失的字符(例如帧错误-我的理解是在这种情况下会生成错误中断,但不会接收到字符,因此字符实际上会丢失,如果您执行 UART_READ(),则会出现一定数量的字符,这是一个问题。)

    在远程端发送 ACK/Random 消息时如何处理开始读取(这种情况可能发生、如果通信不是严格的消息响应、则更可能发生)

    我是不是太想了?!!!

    我的感觉是(并且我的实现方法已经是)允许读取是真正异步的(UART!) HWI 将字符送入环形缓冲器、并且任务会定期从环形缓冲器中快速拉出字符、从而避免溢出。 这不是 UART 驱动程序支持的操作模式、因此我必须自行滚动。

    任务需要定期运行、拉出字符、构建消息并验证消息是否正确(例如,格式似乎比较合理、CRC 校验等)。 格式错误的消息被丢弃。 找到正确的消息后、即可适当处理。 例如、如果它是一个 ACK、并且您正在等待一个 ACK、则发布一个信标或设置一个标志或其他内容。 如果是“随机消息”,则相应地处理它。

    *这里有一个假设,即有一些消息结构允许您将不同的消息彼此分开!

    我认为最好的方法不是每 X 个字符运行一次任务(这基本上就是在阻塞或回调模式下使用 UART_READ()时得到的结果)。 如果消息大小不是恒定的,并且存在接收错误或同步错误(在 Tx 发生时开始读取),则会出现问题。 超时很有用,但请记住,如果您为 N 个字符(阻止或回调)执行了 UART_READ(),超时意味着您收到的字符少于 N 个(这并不意味着您收到的字符为0个) 因此、如果发生超时、您仍然需要返回并查看缓冲区中是否有任何有用的内容。 [例如,您可能希望收到一条较长的“随机消息”,但却收到了一条较短的“ACK”消息–您仍然不需要处理 ACK,不是您?!]

    我不知道,也许我所提议的不仅仅是原始海报的需求......
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Ramsey、

    MSP 发送到器件的命令从文件中解释、并且每个命令都有延迟、以便随机消息不会与 ACK 消息重叠。 此时、我有一个管理 UART 的任务、还有另一个切换 LED 并递增变量的任务。 UART 处于阻塞模式、在该模式下、任务将被阻塞、直到 UART_READ 或 UART_WRITE 完成 wirte/read 并让其他任务完成其工作。  

    这是我的 UART 任务、优先级为3

    空 taskUART (UArg0、UArgarg1)
    {
    
    /*创建一个数据处理关闭的 UART。 *
    UART_PARAMS_INIT (uartParams);
    uartParams.writeDataMode= UART_DATA_BINARY;
    uartParams.readDataMode= UART_DATA_BINARY;
    uartParams.readReturnMode= UART_return_full;
    uartParams.readEcho= UART_ECHO_OFF;
    uartParams.baudrate= 9600;
    uartParams.readTimeout= 100;
    
    
    
    uartHandle = UART_OPEN (Board_UART0、uartParams);
    
    if (uartHandle == NULL){
    System_abort ("打开 UART 时出错");
    }
    
    字节 seqCount=0x00;
    字节 CMD_ON[3]={CMD_ON、0x01、0x00};
    字节 CMD_OFF[3]={CMD_OFF、0x01、seqCount};
    字节 CMD_LDP[31]={CMD_LDP、0x1D、seqCount、0x00、0x00、0xcc、0x04、0x66、 0x04、0x00、0x04、0x99、0x03、 0x00、0x00、0x00、0x00、0xe8、 0x03
    、0x10、0x00、0x00、0x00、0xEA、 0xFE、0xcc、0x0c、0x10、0x27、 0x00、0x08};
    
    
    字节命令列表[]={CMD_ON、CMD_LDP、CMD_OFF};
    字节 su _RESPONSE[174];
    时间长;
    超时1;
    
    
    int i;
    for (i=0;<sizeof(commandList);i++)     )
    {
    seqCount++;
    switch (commandList[i])
    {
    CASE CMD_ON:{
    CMD_OBC_su _ON[2]= seqCount;
    
    UART_WRITE (uartHandle、&cmd_OBC_su_on、sizeof (cmd_OBC_su_on));
    时间= getSystemTimestamp (&newTime、&Y2K、&y1k);
    while (time1 < time + 60)
    {
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    中断;
    
    
    CASE CMD_OFF:{
    CMD_OBC_SU_OFF[2]= seqCount;
    UART_WRITE (uartHandle、&cmd_OBC_su _off、sizeof (cmd_OBC_su _off));
    
    时间= getSystemTimestamp (&newTime、&Y2K、&y1k);
    while (time1 < time + 60)
    {
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    中断;
    
    
    Cmd_LDP 案例:{
    CMD_su _LDP[2]= seqCount;
    UART_WRITE (uartHandle、&cmd_su_ldp、sizeof (cmd_su_ldp));
    
    
    时间= getSystemTimestamp (&newTime、&Y2K、&y1k);
    while (time1 < time + 60)
    {
    UART_READ (uartHandle、&su_RESPONSE、sizeof (su_RESPONSE));
    
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    Time1 = getSystemTimestamp (&newTime、&Y2K、&y1k);
    }
    中断;
    
    };
    } 

    另一个任务具有优先级2



    空任务3 (UARg arg0、UARg arg1)
    {
    int i=0;
    while (1)
    {
    i++;
    if (i==255)
    i=0;
    GPIO_toggle (Board_LED0);
    }
    

    仅在发送 CMD_LDP 命令后、才会出现随机消息。 在本例中、我在作为命令之间延迟的循环中调用 UART_READ。 UART_WRITE 工作正常、但我无法读取器件发出的消息。

    需要注意的是:从 MSP 发送到器件的命令数据包大小因命令而异。 ACK 和随机消息的大小相同。

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

    Ion、Julian、

    这个主题中提出了很多问题。 可能太多让我无法回答。  我看到大家都在寻找一种从 UART 接收数据的方式。 可能该数据是异步的、并且消息大小可能会有所不同。 我假设必须有一些消息协议、否则您将无法对消息进行解码和处理。

    让我为接收数据路径建议一个解决方案。 我们稍后可以讨论传输数据路径。 我尚未对此进行构建和测试、但主要目标是为您提供构建适合您应用的解决方案的想法。

    请参阅随附的图表:

    我建议采用一种设计、在该设计中、任务是将收到的数据汇编为完整的消息以进行处理。 UART 应配置为回调模式。 数据队列将用于将数据从驱动程序传递到任务。

    一个关键因素是数据接收路径必须独立于处理任务运行。 换言之、数据到达时、必须将其存储在安全的地方。 这必须具有最高优先级。

    该任务将基于两个输入运行:1)接收到足够的数据以启动进程循环、2)在一段空闲时间后处理任何可用数据。

    UART 通过线接收数据。 当一个帧(例如字节)已经被接收时、它被放置在 RXBUF 缓冲区中。 如果 UART 驱动程序已被请求接收数据、则会产生中断。 否则、不会。

    调用 UART_read()将启用 UART 驱动程序。 每次接收到新的帧时、中断都会将数据复制到缓冲区中。 如果缓冲区已满、则将调用应用程序回调。 否则、不会。

    需要注意的是、当缓冲区已满时、UART 将不再保存接收到的数据。 因此、系统必须快速响应、以回收完整的缓冲区并发出新的空缓冲区、这将再次为 UART 驱动程序加注主文件。 当 UART 驱动程序调用回调时、它在最高优先级的中断上下文中运行。 ap_uartRxCb ()将数据计数存储在缓冲区对象中、并将缓冲区放置在 Swi 参数句柄中。 然后、它将布置 Swi。

    Swi 也以非常高的优先级运行。 ap_uartRxSwi()将从空闲队列中获取一个新的空缓冲区,并通过调用 UART_read()将其发送给 UART 驱动程序。 现在、我们可以安全地接收更多数据。 完整的缓冲区被放置在数据队列上、并且通过发布信标来通知任务。

    如果 Swi 被赋予一个空的数据缓冲区(见下面)、那么它只将缓冲区返回到 Free 队列、并且*不*发布信号量。

    该任务从数据队列获取数据、并将消息片段复制到命令缓冲区中。 数据缓冲区将返回到 Free 队列。 任务处理数据。 最终、任务通过在信标上挂起来等待更多数据。

    当 UART 变为空闲状态时、一段时间后、任务将运行以获取尚未处理的任何数据。 发生这种情况是因为任务在信号量上挂起时使用了超时(例如50毫秒)。 该任务调用 UART_readCancel()。 在此调用中、UART 驱动程序将使用已接收的字节计数来调用回调。 与之前一样、回调会将计数存储在数据对象中并布置 Swi。 Swi 用一个新的缓冲区作为 UART 的优先级、将数据缓冲区放置在数据队列中并布置信标。  在所有这一切发生后,对 UART_readCancel()的调用最终返回到任务中。 任务只是跳转到循环的开头并在信标上挂起。

    如果有数据需要处理、那么信号量就会被布置、任务不会被阻止。 它立即从信标挂起返回并从数据队列中提取缓冲区。 但是、如果没有任何数据、回调会将一个空的缓冲区传递给 Swi。 Swi 检查计数。 如果计数为零、它将空缓冲区返回到 Free 队列、并且不发布信标。 这将导致任务再次被阻止、等待更多数据。

    下面是一些有助于填写详细信息的伪代码。

    typedef 结构{
    字节数据[32];
    INT 计数;
    Queue_Elemm 链接;
    }AP_Buffer;
    
    内联 AP_Buffer * AP_BaseLink (Queue_Elem*链接)
    {
    int offset =(int)&(((AP_Buffer *) 0)-> link);
    外转 n (((AP_Buffer *)((char *) link - offset));
    }
    
    AP_Buffer * AP_swirxArg0;
    
    AP_uartRxCb (void * data、size_t count)
    {
    AP_Buffer * buf =(AP_Buffer *)数据;
    
    /*在缓冲区对象中存储计数*/
    buf->count = count;
    
    /*将缓冲区传递给 Swi */
    AP_swiArg0 =(UArg)buf;
    Swi_post (ap_uartRxSwifnl);
    }
    
    ap_uartRxSwifxn (UArg arg0、UArg arg1)
    {
    AP_Buffer *缓冲区;
    
    /*带有新缓冲区的主要 UART */
    ptr = que_get (freeQ);
    buf = AP_BaseLink (PTR);
    UART_READ (UART、buf->data、32);
    
    buf =(AP_Buffer *) arg0;
    如果(buf->count >0){
    /*将数据传递到任务以进行处理*/
    Que_Put (DataQ、&(buf->link));
    Semaphore_post (AP_workSem);
    }
    否则{
    /*将空缓冲区返回到空闲队列*/
    Q_Put (FreeQ、&(buf->link));
    }
    }
    
    ap_processFskxn()
    {
    bool doWork = true;
    AP_Buffer *缓冲区;
    
    while (doWork){
    
    /*等待信号量超时*/
    if (!Sempahore_pend (AP_workSem、50)){
    
    /*超时,获取未处理的数据*/
    UART_readCancel (UART);
    继续;
    }
    
    /*从数据队列获取数据*/
    ptr = que_get (DataQ);
    buf = AP_BaseLink (PTR);
    
    /*将数据复制到命令缓冲区中*/
    (笑声)
    
    /*将缓冲区返回空闲队列*/
    queue_put (freeQ、&(buf->link));
    
    /*进程命令缓冲区*/
    }
    } 

    让我们在下一个帖子中继续这一讨论。

    ~Ramsey

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

    这是一种与我所采用的方法类似的方法、但我选择了更多的裸机。

    我想我现在将退出本次讨论。 您可能更能够以正确的方向转向。

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

    Julian、

    好的。 感谢您的参与。 听说我们都采用了类似的解决方案是很好的。 这表明设计很有希望。

    ~Ramsey