嵌入式C UART约定 [英] Embedded C UART conventions

查看:86
本文介绍了嵌入式C UART约定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要有关处理UART通信的正确方法的建议.我觉得我已经很好地处理了通过UART发送串行命令的过程,但是我不知道解析响应或接收串行数据的方法是否是最好的方法.任何提示都值得赞赏,但我只想知道是否有更好,更优雅的方法来解析UART RX.

I need advice on proper way of handling UART communication. I feel like I've done handling sending serial commands over UART well but I don't know if the way I'm parsing the response or receiving serial data is the best way to do it. Any tips are appreciated but I just want to know if there's a better more and elegant way to parse UART RX.

这是针对MSP430 uC的...

This is for an MSP430 uC by the way...

首先,我在头文件中声明了这些内容:

const unsigned char *UART_TX_Buffer;
unsigned char UART_TX_Index;
unsigned char UART_TX_Length;
unsigned char UART_TX_Pkt_Complete;

unsigned char UART_RX_Buffer[25];
unsigned char UART_RX_Pkt_Complete;
unsigned char UART_RX_Index;

以下是在ISR中设置了UART_RX_Pkt_Complete标志后调用的函数:

void Receive_Resp()
{
    switch (UART_RX_Buffer[UART_RX_Index - 3])
    {
    case 0x4B:
        break;
    case 0x56:
        P1OUT &= ~(tos_sel0 + tos_sel1);
        break;
    case 0x43:
        P1OUT |= tos_sel0;
        P1OUT &= ~tos_sel1;
        break;
    case 0x34:
        P1OUT |= tos_sel1;
        P1OUT &= ~tos_sel0;
        break;
    case 0x33:
        P1OUT |= tos_sel0 + tos_sel1;
        break;
    default:
        break;
    }
    UART_RX_Pkt_Complete = 0;
    UART_RX_Index = 0;
}

作为参考,这里是RX ISR:

#pragma vector=USCIAB0RX_VECTOR
 __interrupt void USCIA0RX_ISR(void)
 {
     UART_RX_Buffer[UART_RX_Index++] = UCA0RXBUF;

     if (UART_RX_Buffer[UART_RX_Index - 1] == 0x0A)
    {
        UART_RX_Pkt_Complete = 1;
        _BIC_SR_IRQ(LPM3_bits);
    }
    IFG2 &= ~UCA0RXIFG;
 }

这也是TX ISR并发送UART命令例程:

    if (UART_TX_Index < UART_TX_Length)                                 // Check if there are more bytes to be sent
    {
        UCA0TXBUF = UART_TX_Buffer[UART_TX_Index++];
    }

    else                                                                // Last byte has been sent
    {
        UART_TX_Pkt_Complete = 1;                                       // Set flag to show last byte was sent
        _BIC_SR_IRQ(LPM3_bits);
    }
    IFG2 &= ~UCA0TXIFG;



void Send_CMD (const unsigned char *Data, const unsigned char Length)
{                                                       
    UART_TX_Buffer = Data;                                                  // Move into global variables
    UART_TX_Length = Length;
    UART_TX_Pkt_Complete = 0;                                               // Starting values
    UART_RX_Pkt_Complete = 0;
    UART_TX_Index = 0;

    UCA0TXBUF = UART_TX_Buffer[UART_TX_Index++];

    while(!UART_TX_Pkt_Complete)
    {
        Delay(5,'u');
    }

    while(!UART_RX_Pkt_Complete)
    {
        Delay(5,'u');
    }
}

推荐答案

如果这有效并且满足您系统的要求,那就很好.但是有几种方法可以改进它.

If this works and meets your system's requirements then it's fine. But there are several ways it could be improved.

  • Receive_Resp() USCIA0RX_ISR()紧密耦合,这是不希望的.它们都为另一个操作 UART_RX_Index ( USCIA0RX_ISR()递增它,而 Receive_Resp()清除它),并且两者都依赖于另一个每条消息的帧数( USCIA0RX_ISR()查找帧的末尾,而 Receive_Resp()解释并重置下一帧).如果这些例程解耦,那会更好.
  • 字符缓冲区应该是一个循环缓冲区,带有一个头指针(添加字符)和一个尾指针(删除字符).ISR应该仅将字符添加到循环缓冲区中并前进头指针.ISR还应处理头指针的回绕操作,使其返回到循环缓冲区的开头.ISR应该通过确保头指针不通过尾指针来防止超限.
  • 接收例程应负责对消息进行成帧.这包括从尾部指针中提取字符,并标识消息的开头和结尾.Receive例程递增并包装尾指针.通常,Receive例程被实现为状态机,该状态机具有用于标识帧的开始,正文和结束的状态.
  • 对于更少的耦合,您可能具有一个单独的函数来解释消息的内容(即,将框架与消息的解释分开).
  • 您的 ReceiveResp()例程不处理任何错误,例如字符丢失.假定正确接收了所有三个字符.也许这对您的应用程序和要求很好.但通常应在此处执行一些错误检查.至少应确保从 UART_RX_Index> = 3 减去3.换句话说,请确保消息长度合理.更加健壮的串行协议在每个帧中都有校验和或CRC,以确保正确接收到帧,但这可能对您的应用程序来说是多余的.
  • 您可以使用一些相同的建议来改进发送端.通常,有一个用于传送字符的循环缓冲区.发送例程将字符添加到缓冲区并管理头指针.TX ISR将字符从缓冲区复制到UART,并管理尾指针.
  • Receive_Resp() and USCIA0RX_ISR() are tightly coupled, which is undesirable. They both manipulate UART_RX_Index for the other (USCIA0RX_ISR() increments it and Receive_Resp() clears it) and they both rely on the other for part of the framing of each message (USCIA0RX_ISR() finds the end of the frame while Receive_Resp() interprets and resets for the next frame). It would be better if these routines were decoupled.
  • The character buffer should be a circular buffer with a head pointer (where characters get added) and a tail pointer (where characters get removed). The ISR should only add chars to the circular buffer and advance the head pointer. The ISR should also handle wrapping of the head pointer back to the beginning of the circular buffer. And the ISR should protect from overruns by making sure the head pointer doesn't pass the tail pointer.
  • The Receive routine should be responsible for framing the message. This includes pulling chars from the tail pointer and identifying the beginning and end of the message. The Receive routine increments and wraps the tail pointer. Typically the Receive routine is implemented as a state machine with states for identifying the start, body, and end of a frame.
  • For even less coupling you might have a separate function that interprets the content of the message (i.e., separate the framing from the interpretation of the message).
  • Your ReceiveResp() routine doesn't handle any errors such as a dropped character. It assumes that all three characters were received correctly. Maybe that's fine for your application and requirements. But typically there should be some error checking performed here. At the very least you should ensure that UART_RX_Index >= 3 before you subtract 3 from it. In other words, make sure the message length is sane. A more robust serial protocol would have a checksum or CRC in each frame to ensure the frame was received correctly but that is probably overkill for your application.
  • Your Transmit side could be improved with some of the same advice. Typically there is a circular buffer for transmit chars. The Transmit routine adds chars to the buffer and manages the head pointer. The TX ISR copies chars from the buffer to the UART and manages the tail pointer.

Send_CMD()中对 Delay 的调用是否意味着您的应用程序在等待完成传输时完全停滞了?同样,对于您的应用程序可能没问题,但是通常这是不希望的.通常,您希望应用程序即使在等待UART准备发送时也能继续运行.通过使用循环发送缓冲区,可能有多个消息在发送缓冲区中排队,而且您不必等待上一条消息完成就可以排队另一条消息.但是,然后您应该为缓冲区溢出添加保护,这可能会使您不必要地复杂化.这使我回到我的第一点,如果您的工作可以满足您的要求,那就很好了.

Do the calls to Delay in Send_CMD() mean that your application is totally stalled while it's waiting to finish the transmission? Again, maybe that's OK for your application but typically that is undesirable. Typically you want the application to continue to function even while it's waiting for the UART to be ready to transmit. By using a circular transmit buffer, it would be possible for multiple messages to queue up in the transmit buffer and you wouldn't have to wait for the previous message to finish before queuing up another. But then you should add protection for a buffer overrun and this may complicate things unnecessarily for you. Which brings me back to my first point, if what you have works and meets your requirements then it is fine.

这篇关于嵌入式C UART约定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆