2013-07-09 18 views
0

外部模塊通過中斷一次將字符串「CMD \ n」發送給我的程序一個字符。瞭解模塊序列中的哪些位置以便我可以排除故障很重要。這是我目前正在處理模塊跟蹤的方式:如何在C中整潔地處理這種中斷驅動的狀態?

// Enumeration describing the different states 
typedef enum { 
    BTSTATE_ENTERING_CMD_C, // awaiting "C" 
    BTSTATE_ENTERING_CMD_M, 
    BTSTATE_ENTERING_CMD_D, 
    BTSTATE_ENTERING_CMD_EOL, 
    BTSTATE_CMD 
} btstate_t; 

// State variable 
btstate_t btstate = BTSTATE_ENTERING_CMD_C; 

// function called every time a new character is sent 
ISR(USART_RX_vect) { 
    uint8_t rcv = UDR0; // the received character 
    if (  btstate == BTSTATE_ENTERING_CMD_C && rcv == 'C') { 
     btstate = BTSTATE_ENTERING_CMD_M; 
    } else if (btstate == BTSTATE_ENTERING_CMD_M && rcv == 'M') { 
     btstate = BTSTATE_ENTERING_CMD_D; 
    } else if (btstate == BTSTATE_ENTERING_CMD_D && rcv == 'D') { 
     btstate = BTSTATE_ENTERING_CMD_EOL; 
    } else if (btstate == BTSTATE_ENTERING_CMD_EOL && rcv == '\n') { 
     btstate = BTSTATE_CMD; 
    } else { 
     // error handling here 
    } 
} 

直觀上,代碼中似乎有很多冗餘。是否有更好或更經典的方法來達到相同的結果?

+0

您希望在將來如何擴展此功能?你是否想要開始一個除'CMD \ n'之外的多個潛在字符串的路徑,並以一個狀態結束,告訴你輸入了哪個命令? – sh1

+0

@ sh1會有幾條路要走,但程序會知道哪一條路要走。 (這很適合你的解決方案) – Andreas

回答

1

這是怎麼回事?它非常易讀並且易於修改。

// Enumeration describing the different states 
typedef enum { 
    BTSTATE_ENTERING_CMD_C, // awaiting "C" 
    BTSTATE_ENTERING_CMD_M, 
    BTSTATE_ENTERING_CMD_D, 
    BTSTATE_ENTERING_CMD_EOL, 
    BTSTATE_CMD 
} btstate_t; 

// State variable 
btstate_t btstate = BTSTATE_ENTERING_CMD_C; 

struct  cmp 
{ 
    btstate_t state; 
    btstate_t next_state; 
    uint8_t c; 
}   t_cmp; 

ISR(USART_RX_vect) { 
    static t_cmp cmp_array[] = { 
     {BTSTATE_ENTERING_CMD_C, BTSTATE_ENTERING_CMD_M, 'C'}, 
     {BTSTATE_ENTERING_CMD_M, BTSTATE_ENTERING_CMD_D, 'M'}, 
     {BTSTATE_ENTERING_CMD_D, BTSTATE_ENTERING_CMD_EOL, 'D'}, 
     {BTSTATE_ENTERING_CMD_EOL, BTSTATE_CMD, '\n'} 
    }; 
    static int array_size = sizeof(cmp_array)/sizeof(cmp_array[0]); 

    uint8_t rcv = UDR0; // the received character 
    int  i; 
    for (i = 0; i < array_size; ++i) 
    { 
     if (btstate == cmp_array[i].state && rcv == cmp_array[i].c) 
     { 
      btstate = cmp_array[i].next_state; 
      break ; 
     } 
    } 
    if (i == array_size) 
     // error handling here 
} 
+0

既簡潔又可擴展! – Andreas

+0

是的,但它可以優化:你可以使用二分搜索('O(log n)')而不是使用循環('O(n)')來找到良好的狀態,但是對於一個小的數組,它會是很好:) – nouney

+0

然而,如果你在一個微控制器上,我會建議在ISR中保持循環(雖然這仍然很短)......這一切都取決於時間和你的uC在做什麼在那ISR – LostBoy

2

類似下面的測試將進入流,以確認其 一個字符串匹配:

static const char leader[] = "CMD\n"; 
uint8_t btstate = 0; 

ISR(USART_RX_vect) { 
    uint8_t rcv = UDR0; 
    if (btstate < 4) 
    { 
     if (rcv == leader[btstate]) 
      btstate++; 
     else 
     { 
      // error handling here 
      btstate = 0; 
     } 
    } 
} 

(未經測試,很明顯)

如果(btstate == 4)那麼你有你的領導字符串,並現在收到 之後會發生什麼。

這裏沒有很好地處理錯誤情況,這可能會影響到 設計,這是您在正確的字符串之前收到一些前導垃圾的地方。 如往年一樣,我們將進入// error handling here和復位btstate, 但如果rcv現在等於第一'C'發送者真的打算 然後,我們已經錯過了它,並在下一次,我們會想到'C'但收到'M' 並提出另一個錯誤,並完全錯過正確的字符串。

這裏有兩個選項。一種方法是發信號通知發送者自行復位(其中 在高延遲鏈路上可能會很麻煩),另一種是在錯誤處理程序中重新檢查 (rcv == 'C')

如果你的命令字符串爲"GABBAGABBAHEY",以及您所期望的'H' 但你反而得到了'G',那麼它可能是所有的前述 字符被錯誤地發送,的字符的一些數量被送往 故意作爲另一個字符串(或當前字符串)的前綴。

處理這種情況,並處理存在多個可能的 字符串的情況下,需要一個結構,根據接收到的字符,可以採用不同的路徑。在你想容忍領先垃圾的情況下, 該結構可以循環回自己 - 指向與當前狀態匹配的最長前綴 - 在這種情況下,您並不真的想要 構建桌子手。

你說過你知道你期望的字符串,所以我不會詳述, 但我認爲值得一提的是完整性。

相關問題