2013年12月17日 星期二

嵌入-1-迴圈緩衝區

最近,有同事在處理IR訊號的擷取及處理。有聽到對於緩衝區的部份,所以整理一下這部份的內容:

 

迴圈緩衝區

image

在某些DSP系統中,可以支援迴圈緩衝區的硬體,向其中放置資料時,不需考慮是否緩衝區填滿。而一般的嵌入式系統中並無這種的硬体機制。

可以簡單的推導,若要實現則必須在每次的都在迴圈中判斷緩衝區是否放滿了。

但會付出一些代價,而且在某些時候影響很大。(取樣處理問題)

以下,是以另一方式處理:

#define BUFFERSIZE 256
int x[BUFFERSIZE];//static ,stack and heap
 
unsigned int k; //mask 00001111
unsigned int i; //acc  xxxxxxxx =k只取i後n bit
 
while(1)
{
     k=i&;(BUFFERSIZE-1);
     x[k]=Imput();
     ........
     i++;
}


從程式中可見,x[]是程式用來做緩衝區的使用。而於是全域且非初始化(BSS)段。


 


在程式中,i用來做累加的作用。而k做為緩衝區陣例的下標。


因此,k應在0~BUFFERSIZE –1的範圍內。而每次變數i累加後再與k做 "&"來得其緩衝區的下標k。


image


k= i & (BUFFERSIZE-1);


使其k只取i後的n位元的資料。


使其緩衝區的大小必須為2的整數次冪,來使得可用位元"&及"來罩掉。(i大於256時,k得0)


 


由於上述程式不需要使用if 判斷,可以節省幾道程式。但在這一區塊中它所佔的比重還是很大。特別在程式需要指定時間內完成的及 執行頻率比較高的。


------------------------------------------------


在看了如何處理loop buffer後,應該要配合週邊裝置做結合才行。


剛好也找到一些資料,加以整理。


--------------------------------------------------


資料驅動系統(data-driven system),目標是取得資料加以處理,再對結果作動作週而復始。


所以它不存在事件(event),只有持續地增加等待系統處理的資烞。


所以系統處理應比產生的速度快。


"系統主要的特性在於資料量與產生速度"


任何重新啓動和錯誤會讓系統處理落後,能不能優雅地失敗對這類的系統十分重要,如果有錯誤會造成系統處理落後,系統


是否要跳過部份資料追上最新內容? 或是應該改為某種簡化過的處理等待系統恢復? 跳過部份會造成什麼後果?


 


環型緩衝區:


實作data -driven system中主要以環型緩衝區的策略(FIFO, First In First Out)。


生產者以某個速度將資料放入環型緩衝區,消費者以相同的平均速率取出資料,消費者可以一次取出大量資料,但生產者放入時只能一個個放入(也可相反),並讓處理器在資料達到演算法所需大小後進一步處理。


image


其中變數有:


使用的長度、下一個寫入位置(write or start)及下一讀取位置(read or end)。


在write = = read ==> empty or full?


解決方式有:


A. 設定當write 在落後read 一個位置時就表示滿了. read =0 , write 7 , Full


B.多一個變數,write +1 ; read –1 ,full var = buffer lenght –1, Empty var= 0.


 


而在data driver system中,大多其中一個必為中斷(生產或消費); 另一者則非中斷。所以為了讓生產及消費者使用的獨立,


必須要在"讀取及寫入指標"的更新是 單元操作(atomically)才能使用環型緩衝區不被中斷。


但在指標到緩衝區時會折回,這時以一般方使(餘數 %, if判斷)會使得無法使用 單元操作(atomically)


 


可以將其環型緩衝區大小設為 2的次方大小



struct sCircularBuffer {
tElement *buf; // block of memory
uint16_t size; // must be a power of two
uint16_t read; // holds current read position: 0 to (size-1) 
uint16_t write; // holds current write position: 0 to (size-1)
};
uint16_t CBLengthData(struct sCircularBuffer *cb) 
{
/* **********
|-----|----------|-------|
write read
*/
int32_t length = cb->;write - cb->read;
if (length >; 0) return length;
/*
bbbbbb aaaaaaaa 
|-----|----------|-------|
read write
*/
return (cb->;size – cb->write) + /* aaaa */
(cb->;read); /* bbbbb */
}
 
uint16_t CBLengthData(struct sCircularBuffer *cb) 
{
return ((cb->;write – cb->read) & (cb->size - 1));
}
其中需將其read /write設為

(cb->write – cb->read) & (cb->size - 1)
= ( 5 – 6 ) & ( 8 - 1)
= ( -1 ) & ( 7 )
= ( 1111 1111 ) & ( 0000 0111 )
= 0000 0111 = 7



enum eError CBWrite(struct sCircularBuffer *cb, tElement data)
{
if (CBLengthData(cb) == (cb->;size-1)) { return eErrorBufferFull;}
cb->;buf[cb->write] = data;
cb->;write = (cb->write + 1) & (cb->size - 1); // must be atomic 
}
 
enum eError CBRead(struct sCircularBuffer *cb, tElement *data)
{
if (CBLengthData(cb) == 0) { return eErrorBufferEmpty;}
*data = cb->;buf[cb->read];
cb->;read = (cb->read + 1) & ( cb->size - 1); 
}

而在setting "write/read"在不同系統32/16/8 bits. 通常只有16bits一定是單元操作其它的則需去確定。


 


在build Ring-FIFO後,使用者可以read其資料並加以複制memcpy並釋放free。


但這也會造成其浪費,若要將資料保留在原處並只有處理後才是真正釋放free時,則需新增一個 “Free”


image


image



enum eError CBFree(struct sCircularBuffer *cb)
{
if (CBLengthReadData(cb) == 0) { return eErrorBufferEmpty;}
cb->;free = (cb->free + 1) & (cb->size-1); 
}

 


---------------------------


image


To implement a system like that, you'd need to add one more pointer to the system
called processed. As the pointers move independently of on another, adding another is
fairly straightforward. The code is nearly the same as we added for CBReador CBWrite.
Don't forget to add a new length function(s) too.

 


硬体:buffer


檢查碼:


 


p.s:其它有


stack,BSS and heap

沒有留言:

張貼留言