简体   繁体   English

嵌入式C UART约定

[英]Embedded C UART conventions

I need advice on proper way of handling UART communication. 我需要有关处理UART通信的正确方法的建议。 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. 我觉得我已经很好地处理了通过UART发送串行命令的过程,但是我不知道解析响应或接收串行数据的方法是否是最好的方法。 Any tips are appreciated but I just want to know if there's a better more and elegant way to parse UART RX. 任何提示都值得赞赏,但我只是想知道是否有更好的,更优雅的方式来解析UART RX。

This is for an MSP430 uC by the way... 顺便说一下,这是针对MSP430 uC的...

First I have these declared in the header file: 首先,我在头文件中声明了这些:

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;

Here is the function that is called once the flag UART_RX_Pkt_Complete is set in the ISR: 在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;
}

For reference here's the RX ISR: 作为参考,这里是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;
 }

Also here's the TX ISR and send UART command routine: 这也是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() and USCIA0RX_ISR() are tightly coupled, which is undesirable. Receive_Resp()USCIA0RX_ISR()紧密耦合,这是不希望的。 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). 他们都为另一个操作UART_RX_IndexUSCIA0RX_ISR()递增,而Receive_Resp()清除),并且它们在每个消息的成帧过程中都依赖于另一个( USCIA0RX_ISR()找到帧的结尾,而Receive_Resp()解释并为下一帧重置)。 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. ISR应该仅将字符添加到循环缓冲区中并前进头指针。 The ISR should also handle wrapping of the head pointer back to the beginning of the circular buffer. ISR还应处理头指针的回绕操作,使其返回到循环缓冲区的开头。 And the ISR should protect from overruns by making sure the head pointer doesn't pass the tail pointer. ISR应该通过确保头指针不通过尾指针来防止超限。
  • 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. Receive例程递增并包装尾指针。 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 (ie, separate the framing from the interpretation of the message). 为了减少耦合,您可以使用一个单独的函数来解释消息的内容(即,将框架与消息的解释分开)。
  • Your ReceiveResp() routine doesn't handle any errors such as a dropped character. 您的ReceiveResp()例程不处理任何错误,例如字符丢失。 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. UART_RX_Index >= 3减去3之前,至少应确保UART_RX_Index >= 3 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. 一个更健壮的串行协议在每个帧中都有一个校验和或CRC,以确保正确接收到该帧,但这可能对您的应用程序而言是过大的。
  • 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. TX ISR将字符从缓冲区复制到UART,并管理尾指针。

Do the calls to Delay in Send_CMD() mean that your application is totally stalled while it's waiting to finish the transmission? Send_CMD()Delay的调用是否意味着您的应用程序在等待完成传输时完全停止了? 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. 通常,您希望应用程序即使在等待UART准备发送时也能继续运行。 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. 这使我回到我的第一点,如果您的工作可以满足您的要求,那很好。

Personally, I'd write a completely separate UART module that contained methods for write and read operations. 就个人而言,我将编写一个完全独立的UART模块,其中包含用于writeread操作的方法。 Under the hood I'd create two circular buffers (of whatever the appropriate size may be) and use those for storing bytes as data comes in or goes out. 在后台,我将创建两个循环缓冲区(大小合适的缓冲区),并使用它们在数据进出时存储字节。 This would allow an interrupt driven solution with buffers. 这将允许具有缓冲区的中断驱动解决方案。

For instance, my interrupt for receive would do something like: 例如,我的接收中断将执行以下操作:

#pragma vector=USCIAB0RX_VECTOR
__interrupt void UartRxIsr()
{
    ...
    // Add new byte to my receive buffer.
    ... 
}

Then I can call my Uart.read() method to read out that byte. 然后,我可以调用Uart.read()方法来读取该字节。 The read method might look something like the following: read方法可能类似于以下内容:

char read()
{
    if (Uart.rxBuffer.length > 0)
    {
        return Uart.rxBuffer.buffer[Uart.rxBuffer.write++];
    }
}

This is assuming that you've implemented circular buffers using pointers. 假设您已经使用指针实现了循环缓冲区。

I have a solution lying around somewhere. 我有个解决方案。 I'll try to find it and post it. 我将尝试查找并将其发布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM