PIC UART 命令解析器

问题描述

我有一个项目,我想在 dsPIC 上构建一个简单的接口,它允许我通过 UART 以 ASCII 格式向微控制器发送命令。它将仅用于调试,所以目前我不关心错误检查和确认。目的是能够使用终端,向 mcu 发送命令(可能是参数化的),并接收响应。

目前我在 UART1 RX ISR 中有一个状态机,它查找 FRAME_START 符号,然后将所有后续字符放入循环缓冲区,直到遇到 FRAME_END 字符。如果发送方在传输时有很大的延迟,我还使用计时器来重新启动状态机。

即使代码最初仅用于调试,它也可能需要扩展为更健壮的解决方案,以便进入生产环境,或作为其他项目的调试模板,所以我想制作它更健壮,我很乐意收到有关如何做到这一点的评论。我(目前)关注的要点是:

  • 我应该在 UART ISR 中使用循环缓冲区,还是简单地填充一个大小等于最大命令长度的线性缓冲区
  • 如何接收和存储多个命令 - 我应该吗
  • 如何解析传入命令的参数 - 例如,如果它是类似 $WRITE_REGISTER,100,4; 的东西,那么获取参数 100 和 4 (atoi?) 的好方法是什么?
  • 接收命令时调用函数的好模式是什么

代码示例如下。我也读过一些关于 herehere 类似主题的优秀帖子,但这些更关注协议实现,我离这还很远。

/* State machine states */
#define IDLE                0x01
#define COMMAND             0x02
#define AFTER_ESCAPE        0x03

/* Message  delimiters */
#define FRAME_START       0x24      // '$'    
#define ESCAPE_SYMBOL      0x2F     // '/'
#define FRAME_END         0x3B      // '/'

// UART ISR variables
#define SER_FIFO_MASK       0x10
volatile uint16_t rxfifo[8];
volatile uint16_t rxiptr = 0;
volatile uint16_t command_received = 0;          

// Static variables for the TMR2 ISR
static volatile uint16_t tmr2_isr_counter = 0;
static volatile uint16_t tmr2_char_timeout = 0;

/*
 * TMR2 ISR
 */
void __attribute__((__interrupt__,no_auto_psv)) _T2Interrupt(void)
{
    IFS0bits.T2IF = 0;                                          // Clear interrupt flag
    
    tmr2_isr_counter++;
    if (tmr2_isr_counter == 100)                                // 10 seconds
    {
        T2CONbits.TON = 0;                                      // Stop timer
        tmr2_char_timeout = 1;                                  // Set flag
    }
}


/*
 * UART1 TX ISR 
 */
void __attribute__((interrupt,no_auto_psv)) _U1RXInterrupt(void)
{
 static unsigned short state = IDLE;                            // A static state to keep track of the message
    uint8_t ch = U1RXREG;                                       // copy char from buffer to temporary variable
    if (U1STAbits.OERR) _OERR = 0;                              // If overflow,clear flag and buffer
    _U1RXIF = 0; // Clear IF    
    if (ch <= 0x1F || ch >= 0x60) return;                       // discard char if it's outside allowed charset

    // Reset state machine on timeout
    if (tmr2_char_timeout == 1)
    {
        state = IDLE;                                           // Set flag to be polled in main,which should send an error message? And reset state
        rxiptr = 0;
    }
    
    switch (state) {
        case IDLE:                                              // If state is IDLE,examine char; if char is equal to $,change state
            if (ch == (char) FRAME_START) {
                state = COMMAND;
                START_CHAR_TIMER();
            }
            break;
        case COMMAND:                                           // While COMMAND state,every char is put into the buffer,which is free to overflow.
            START_CHAR_TIMER();
            if (ch == (char) ESCAPE_SYMBOL) {                  // Escape char is #
                state = AFTER_ESCAPE;
            } else if (ch == (char) FRAME_END) {                // If FRAME_END (;) char is received,raise the flag and change state back to WAIT HEADER
                state = IDLE;
                command_received = 1;                           // main() should poll and clear that flag
            } else {
                rxfifo[rxiptr] = ch;
                rxiptr++;
                if(rxiptr == SER_FIFO_MASK) rxiptr = 0;         // Wrap-around buffer head
            }
            break;
        case AFTER_ESCAPE:                                      // Put the character following the escape symbol in the command buffer
            START_CHAR_TIMER();
            rxfifo[rxiptr] = ch;
            rxiptr++;
            if(rxiptr == SER_FIFO_MASK) rxiptr = 0;
            break;
        default: break;
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)