2011-07-06 22 views
8

我通過460Kbaud UART將數據從我的PIC24H微控制器傳輸到藍牙無線電模塊。在大多數情況下,此流程工作正常,藍牙模塊在其內部數據緩衝區已滿時使用CTS和RTS線路管理流量控制。但是,藍牙模塊中存在某種類型的錯誤,當數據連續發送到藍牙模塊時不會發生任何中斷,如果我的數據在另一個瓶頸中備份,就會發生這種錯誤。如何節制中斷驅動的UART傳輸PIC24H?

如果模塊能夠正常工作,那很好,但那不在我的控制範圍之內。所以看來,我唯一的選擇是在我的一端做一些數據調節,以確保不超過數據吞吐量限制(我大致通過實驗知道這一點)。

我的問題是如何實現數據速率的限制?

我當前UART實現是長1024個字節,主迴路中的數據寫入RAM圓形的FIFO緩衝器。當UART硬件發出最後一個字節時,PIC會觸發外設中斷,並且ISR從緩衝區讀取下一個字節並將其發送到UART硬件。

這裏的源代碼的一個想法:

uart_isr.c

//*************** Interrupt Service routine for UART2 Transmission 
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void) 
{  
//the UART2 Tx Buffer is empty (!UART_TX_BUF_FULL()), fill it 
//Only if data exists in data buffer (!isTxBufEmpty()) 
while(!isTxBufEmpty()&& !UART_TX_BUF_FULL()) { 
    if(BT_CONNECTED) 
    { //Transmit next byte of data 
     U2TXREG = 0xFF & (unsigned int)txbuf[txReadPtr]; 
     txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE; 
    }else{ 
     break; 
    } 
} 
IFS1bits.U2TXIF = 0; 
} 

uart_methods.c

//return false if buffer overrun 
BOOL writeStrUART(WORD length, BYTE* writePtr) 
{ 
    BOOL overrun = TRUE; 
    while(length) 
    { 
     txbuf[txWritePtr] = *(writePtr); 
     //increment writePtr 
     txWritePtr = (txWritePtr + 1) % TX_BUFFER_SIZE; 
     if(txWritePtr == txReadPtr) 
     { 
      //write pointer has caught up to read, increment read ptr 
      txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE; 
      //Set overrun flag to FALSE 
      overrun = FALSE; 
     } 

     writePtr++; 
     length--; 
    } 

    //Make sure that Data is being transmitted 
    ensureTxCycleStarted(); 

    return overrun; 
} 


void ensureTxCycleStarted() 
{ 
    WORD oldPtr = 0; 
    if(IS_UART_TX_IDLE() && !isTxBufEmpty()) 
    { 
     //write one byte to start UART transmit cycle 
     oldPtr = txReadPtr; 
     txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;//Preincrement pointer 
     //Note: if pointer is incremented after U2TXREG write, 
     //  the interrupt will trigger before the increment 
     //  and the first piece of data will be retransmitted. 
     U2TXREG = 0xFF & (unsigned int)txbuf[oldPtr]; 
    } 
} 

編輯
有一些限制可以實現兩種方式正如我所見:

  1. 在強制之間要被寫入UART字節,用於把上的數據吞吐量的上限的時間延遲。

  2. 保持在某個時間幀中發送的字節流水賬並且如果超過該時間跨度的最大字節數創建稍長的繼續傳輸之前延遲。

任何一個選項理論上都可以工作,我想知道它的實現。

+0

描述模塊需要什麼樣的「休息」來工作...... –

+0

@Ben Jackson如果我知道的話,這將是很好的。這是一個不是功能的bug,所以沒有記錄。基本上它會在持續吞吐量約3秒後重置,即使它是流量控制的。我平均可以獲得大約25kbps的吞吐量。 – CodeFusionMobile

回答

2

也許配額方式是你想要的。 使用相關時間表的週期性中斷,將一個「要傳輸的字節」的配額添加到全局變量中,直到您沒有爲相關的洪水調整某個級別。 然後在你發送一個字節之前檢查是否有配額。在新的傳輸中,將會出現最初的洪水,但稍後配額將限制傳輸速率。

~~some periodic interrupt 
if(bytes_to_send < MAX_LEVEL){ 
    bytes_to_send = bytes_to_send + BYTES_PER_PERIOD; 
    } 
~~in uart_send_byte 
if(bytes_to_send){ 
    bytes_to_send = bytes_to_send - 1; 
    //then send the byte 
+0

我喜歡這種方法。仍然發送數據塊,但允許在不延遲每個字節的情況下管理流程 – CodeFusionMobile

2

如果你有一個免費的計時器,或者您可以使用現有的一個,你可以做某種發送的字節數的「反跳」的。

想象一下,你有這個全局變量,byte_interval和你有一個定時器溢出(並觸發ISR)每微秒。然後,它會是這個樣子:

timer_usec_isr() { 
    // other stuff 
    if (byte_interval) 
     byte_interval--; 
} 

然後在「的putchar」功能,你可以有這樣的:

uart_send_byte(unsigned char b) { 
    if (!byte_interval) { // this could be a while too, 
          // depends on how you want to structure the code 
     //code to send the byte 
     byte_interval = AMOUNT_OF_USECS; 
    } 

} 

對不起,沒有太大的看着你的代碼,所以我可能更具體。 這只是一個想法,我不知道它是否適合你。

+0

我不想在每個字節之間加一個延遲,因爲這需要頻繁地重新啓動tx週期。有沒有一種方法可以適應這種情況,以檢測在最後y個usecs中何時發送了x個(太多)字節並觸發定時器延遲?這會適合我的代碼結構好一點。 – CodeFusionMobile

+0

即使在沒有真人角色的情況下,您是否可以讓UART設置txdone?如果可以的話,那麼它應該發送停止位,如果它沒有傳送實際的數據。然後,你可以每隔1024(假設)字符消除2或3個字符,然後在2或3個字符時間過後再次提取字符。如果你能做到的話,那就是我會先嚐試的。 –

+0

@Pete Wilson我不能設置txdone而沒有啓動一個傳輸週期來推出空白數據。我不得不做一個計時器延遲或什麼來觸發中斷。 – CodeFusionMobile

1

首先,有兩種常用的串行流量控制。

你說CTS是對的,但你可能想看看XON/XOFF可以啓用某種程度上來說。

另一種方法,如果你可以配置它只是使用較低的波特率。這顯然取決於您可以在鏈接的另一端配置什麼,但當設備無法應對更高速度的傳輸時,它通常是解決問題的最簡單方法。

+0

啓用硬件流量控制,它由藍牙模塊驅動。我嘗試過實現較低的波特率設置,但這導致了太多其他問題(儘管它完全解決了這個特定問題)。對於給出的數據,+1表示良好的答案。 – CodeFusionMobile

+0

作爲一個方面說明,其他問題源於這樣的事實,即我有兩種運行時配置的數據速度,並且要切換波特率,我必須配置BT模塊,然後重置它,這是一個2秒的過程,再加上之後重新連接。所以降低波特率並不適用於特定應用的觀點。 – CodeFusionMobile

1

定時器的做法,在特定的時間增加了延遲TX:

  • 配置在適當的週期率自由運行的定時器。
  • 在計時器ISR,切換在一個全局狀態變量(delayBit)
  • 在UART ISR位,如果delayBit高且delayPostedBit是低的,然後退出TX ISR而不清除TX中斷標誌,並設置一個位在全局狀態變量(delayPostedBit)中。如果delayBit低,則清除delayPostedBit。其結果是導致延遲等於一個ISR調度延遲,因爲ISR將再次進入。這不是一個忙等待延遲,所以不會影響系統其餘部分的時間。
  • 調整定時器的週期以適當的時間間隔添加延遲。
+0

在不清除中斷標誌的情況下退出TX ISR導致ISR鎖定CPU,並鎖定主循環,這是我的應用程序無法接受的。 – CodeFusionMobile

+0

它應該在第二次通過ISR時退出ISR。這就是意圖(增加延遲等於1 ISR延遲),這將允許最好的粒度計時。我假設期望的延遲是微秒,而不是毫秒。 –