2010-05-23 14 views
6

我正在開發一個項目,發送串行數據來控制LED燈的動畫,這需要與動畫引擎保持同步。似乎有一個大的串行寫入緩衝區(OSX(POSIX)+ FTDI芯片組usb串行設備),所以如果不手動調節write()的調用,軟件可以在燈光前幾秒鐘。串口:write()限制?

目前我手動將串行寫入速度限制爲波特率(8N1 = 10字節串行幀每8字節數據,19200bps串行 - > 1920字節每秒最大),但我遇到了動畫問題隨着時間流逝與燈光不同步 - 它開始很好,但在10分鐘後,動畫和燈光之間出現明顯的(100ms +)延遲。

這是真實限制串行寫入速度的代碼(每個動畫幀調用一次,「經過」是當前幀,「波特率」的持續時間是BPS(19200)):

void BufferedSerial::update(float elapsed) 
{ 
    baud_timer += elapsed; 

    if (bytes_written > 1024) 
    { 
     // maintain baudrate 
     float time_should_have_taken = (float(bytes_written)*10)/float(baudrate); 
     float time_actually_took = baud_timer; 
     // sleep if we have > 20ms lag between serial transmit and our write calls 
     if (time_should_have_taken-time_actually_took > 0.02f) 
     { 
      float sleep_time = time_should_have_taken - time_actually_took; 
      int sleep_time_us = sleep_time*1000.0f*1000.0f; 
      //printf("BufferedSerial::update sleeping %i ms\n", sleep_time_us/1000); 
      delayUs(sleep_time_us); 

      // subtract 128 bytes 
      bytes_written -= 128; 
      // subtract the time it should have taken to write 128 bytes 
      baud_timer -= (float(128)*10)/float(baudrate); 
     } 
    } 
} 

很明顯,某處出了問題。

一個更好的方法是能夠確定當前在傳輸隊列中的字節數,並嘗試將其保持在固定閾值以下,但我無法弄清楚如何在OSX上執行此操作( POSIX)系統。

任何意見讚賞。

+1

跨緩衝區同步是POSIX通常不適合處理的東西;在所有消費者操作系統上同步音頻和視頻的早期嘗試是多麼糟糕的一個很好的例子。您可能需要直接寫入串行UART或查找或寫入允許準同步ioctl的驅動程序,例如「不會早於時間發出此字節_n_ – msw 2010-05-23 15:07:30

+0

您是否可以控制動畫引擎? – 2010-05-23 15:12:09

+0

動畫幀率是否固定? – 2010-05-23 16:02:39

回答

3

如果你想減慢你的動畫以匹配你可以寫入LED的最大速度,你可以使用tcdrain();像這樣:

while (1) 
{ 
    write(serial_fd, led_command); 
    animate_frame(); 
    tcdrain(serial_fd); 
} 
+0

這就是我一直在尋找的!非常感謝。 – damian 2010-05-24 08:58:09

2

您可以使用硬件流量控制。

我不知道你在串口鏈路的另一端有什麼樣的硬件,但是通過RTS/CTS握手線可以同步和調節自己的硬件。

這就是他們打算的畢竟。

+0

我正在將UART輸出直接插入一個RS485線路驅動器,因此沒有機會。 – damian 2010-05-23 19:01:50

1

只要保持一個固定的波特率比它所需要稍微快一點,並同步發光二極管與動畫N個動畫幀的每個塊:

for each block 
{ 
    writeBlockSerialData(); 
    for each frame in block 
    { 
     animateFrame(); 
    } 
} 

的稍快的波特率將確保串行緩衝區不會逐漸溢出。

串行數據塊之間會有一個小的停頓(毫秒),但這不應該被察覺。

編輯:這是假設你有一個固定的動畫率。

+0

+1。通過您的解決方案,實際波特率是無關緊要的,除非它很慢,並且OP假定_serial port_是問題 - 它可能是_animation frame rate_。另一種可能性是UAR T時鐘:第一次谷歌命中'rs232時鐘準確度'表明時鐘可以在溫度和壽命上漂移+/- 0.5%,這與OP的1%漂移沒有太大差別。 – 2010-05-23 16:55:17

+0

動畫幀率不固定。我有時會閃爍所有的LED,有時會閃爍一個很好的淡入淡出 - 較低的幀率適合閃爍(因爲串行數據包較大,所以必要),但對於單脈衝,我更喜歡更高的幀率以獲得更好的視覺淡化。 – damian 2010-05-23 18:56:58

2

不得不將數據饋送到串行熱圖紙記錄器一次(非常像收據打印機)並且具有相同類型的問題。數據的任何延遲都會導致打印輸出中的跳過,這是不可接受的。

解決方案非常簡單:如果您始終將數據保存在內核串行緩衝區中,那麼輸出將精確地(波特率/(1 +數據位+停止位))每秒字符。因此,只需添加足夠的NUL字節填充以分隔數據。

如果某些設備在數據中看到NUL字節,那麼它可能會做非常糟糕的事情,在這種情況下,這不起作用。但是很多隻是忽略消息之間的額外NUL字節,這使您可以使用串口內非常準確的硬件定時器來控制您的定時。

+0

呵呵。好的想法,謝謝! 我正在通過一個RS485線路驅動器(半雙工)推送數據,雖然目前沒有必要從從屬設備的ACK消息;但如果這種變化(如果我需要開始做錯誤檢查,例如)這種方法將無法正常工作.. – damian 2010-05-23 18:55:15

+0

我的打印機實際上確實有一個反向通道與狀態信息(不完全確認/ NACK,但我不明白這是如何改變任何東西)。如果你正在寫另一端,那麼在允許填充的同時你應該沒有任何困難實現ACK/NACK。 – 2010-05-23 21:29:23

0

下面是一個方法,使用多線程,這是比我的其他回答不同:

ledThread() 
{ 
    while(animating) 
    { 
     queue.pop(packet); 
     writeSerialPacket(packet); 
     flushSerialPacket(); // Blocks until serial buffer is empty 
    } 
} 

animationThread() 
{ 
    time lastFrameTime = now(); 
    time_duration elapsed = 0; 
    while(animating) 
    { 
     buildLedPacket(packet); 
     queue.push(packet); 
     elapsed = lastFrameTime - now(); 
     lastFrameTime = now(); 
     animateNextFrame(elapsed); 
    } 
} 

在上述僞代碼中,隊列是阻塞producer-consumer queue,其中一個的容量。換句話說,生產者將在queue.push()期間阻塞,而隊列不爲空。您可以使用帶有條件變量或信號量的「ping-pong」緩衝區來代替阻塞隊列。

每個動畫幀都會在發送相應的LED數據後顯示。串行端口傳輸數據包所用的時間用於計算下一個動畫幀。

擁有兩條線程的好處是,您可以在等待串行數據傳輸(傳輸串行數據幾乎不使用任何CPU)時使用CPU進行動畫製作。

很難用單詞來描述這種多線程的東西。我希望我有一個白板可以塗寫。 :-)