2013-07-16 99 views
0

我有一個隊列結構,我嘗試使用循環緩衝區來實現,我在網絡應用程序中使用它。我正在尋找一些指導和反饋。首先,讓我介紹一下相關的代碼。在C中使用指針的循環緩衝區

typedef struct nwk_packet_type 
{ 
    uint8_t dest_address[NRF24_ADDR_LEN]; 
    uint8_t data[32]; 
    uint8_t data_len; 
}nwk_packet_t; 

/* The circular fifo on which outgoing packets are stored */ 
nwk_packet_t nwk_send_queue[NWK_QUEUE_SIZE]; 
nwk_packet_t* send_queue_in; /* pointer to queue head */ 
nwk_packet_t* send_queue_out; /* pointer to queue tail */ 

static nwk_packet_t* nwk_tx_pkt_allocate(void) 
{ 
    /* Make sure the send queue is not full */ 
    if(send_queue_in == (send_queue_out - 1 + NWK_QUEUE_SIZE) % NWK_QUEUE_SIZE) 
     return 0; 

    /* return pointer to the next add and increment the tracker */ 
    return send_queue_in++;//TODO: it's not just ++, it has to be modular by packet size 
} 

/* External facing function for application layer to send network data */ 
// simply adds the packet to the network queue if there is space 
// returns an appropriate error code if anything goes wrong 
uint8_t nwk_send(uint8_t* address, uint8_t* data, uint8_t len) 
{ 
    /* First check all the parameters */ 
    if(!address) 
     return NWK_BAD_ADDRESS; 
    if(!data) 
     return NWK_BAD_DATA_PTR; 
    if(!len || len > 32) 
     return NWK_BAD_DATA_LEN; 

    //TODO: PROBABLY NEED TO START BLOCKING HERE 
    /* Allocate the packet on the queue */ 
    nwk_packet_t* packet; 
    if(!(packet = nwk_tx_pkt_allocate())) 
     return NWK_QUEUE_FULL; 

    /* Build the packet */ 
    memcpy(packet->dest_address, address, NRF24_ADDR_LEN); 
    memcpy(packet->data, data, len); 
    packet->data_len = len; 
    //TODO: PROBABLY SAFE TO STOP BLOCKING HERE 

    return NWK_SUCCESS; 
} 

/* Only called during NWK_IDLE, pushes the next item on the send queue out to the chip's "MAC" layer over SPI */ 
void nwk_transmit_pkt(void) 
{ 
    nwk_packet_t tx_pkt = nwk_send_queue[send_queue_out]; 
    nrf24_send(tx_pkt->data, tx_pkt->data_len); 
} 

/* The callback for transceiver interrupt when a sent packet is either completed or ran out of retries */ 
void nwk_tx_result_cb(bool completed) 
{ 
    if((completed) && (nwk_tx_state == NWK_SENDING)) 
     send_queue_out++;//TODO: it's not just ++, it has to be modular by packet size with in the buffer 

} 

好吧,現在快速解釋,然後我的問題。所以基本的想法是,我已經有了這個正在發送到網絡上的數據隊列。功能nwk_send()可以在應用程序代碼中的任何地方被調用,該應用程序代碼中的牆將是一個小型先發制人的基於任務的操作系統(FreeRTOS),因此可以從代碼中的很多地方發生,並被OS tick中斷中斷。

現在,因爲該函數正在修改指向全局隊列的指針,所以我知道它在做這件事時需要阻塞。我在關於我應該阻止的代碼的註釋中正確(即禁用中斷)嗎?使用全局布爾變量或其他東西來創建互斥鎖會更聰明,而不僅僅是禁用中斷?

另外,我認爲當事情從隊列中排除時,我應該阻擋第二個位置,但我不確定它到底在哪。它在nwk_transmit_pkt()哪裏我實際上是從隊列中複製數據並將其轉換爲本地ram變量?

最後的問題,我如何實現數組中指針的模數運算?我覺得它應該是這樣的:

send_queue_in = ((send_queue_in + 1) % (NWK_QUEUE_SIZE*sizeof(nwk_packet_t))) + nwk_send_queue; 

任何反饋非常感謝,謝謝。

+1

這個問題更適合在[CodeReview](http://www.codereview.stackexchange.com) – tay10r

+0

我認爲你可能需要一個互斥量而不是依靠禁止中斷。是否有多個線程或進程可以調用send函數? – Will

+0

@TaylorFlores啊,有趣。我看到該網站只處於測試階段。也許你是對的,我會研究這個。 – NickHalden

回答

1

關於鎖定最好使用您使用的操作系統中的一些現有的互斥體原語。我對FreeRTOS並不熟悉,但它應該具有用於​​鎖定中斷和用戶上下文的內置原語。

對於圓形緩衝器你可以使用這些:

檢查空隊列

send_queue_in == send_queue_out 

支票滿的隊列

(send_queue_in + 1) % NWK_QUEUE_SIZE == send_queue_out 

推動元件[僞代碼]

if (queue is full) 
    return error; 
queue[send_queue_in] = new element; 
send_queue_in = (send_queue_in + 1) % NWK_QUEUE_SIZE; 

pop元素[僞代碼]

if (queue is empty) 
    return error; 
element = queue[send_queue_out]; 
send_queue_out = (send_queue_out + 1) % NWK_QUEUE_SIZE; 

它看起來像你複製,而不是在發送之前引用分組數據。這意味着您可以持續鎖定,直到完成複製。

+0

感謝您的回答!我同意你的建議,使用FreeRTOS的內置原語,我將不得不查看這些原語。另外,在數組中使用索引而不是指向數組內的元素有什麼好處?另外,我不明白你最後一句話,你能解釋一下嗎? – NickHalden

+0

具有索引更容易進行檢查。用指針檢查必須以不同的方式進行,並且更容易出錯。關於訪問我的意思是,在你發送數據包之前,在你的代碼中複製它。這一點很重要 - 如果你已經使用了指向數據的指針,那麼你必須鎖定隊列,直到發送完成。 – bbonev

+0

我同意使用索引時更容易的邊界檢查,那麼缺點是什麼?爲什麼有人會使用指針?這對我來說似乎相當自然,而且我很確定在某處之前我已經看到它這樣做了。 – NickHalden

0

如果沒有一個全面的驅動程序框架來開發,並且與uC上的中斷狀態進行通信時,您需要非常小心。

您不能使用OS同步原語與中斷狀態進行通信。這樣做肯定會導致操作系統崩潰,因爲中斷處理程序無法阻止。

應該避免複製實際批量數據。

在一個8位的uC上,我建議將一個索引排隊到一個緩衝區數組池中,其中緩衝區的數量是< 256.這意味着只有一個字節需要排隊,所以使用適當的隊列類它在更新內部字節大小索引之前存儲該值,因此可以將緩衝區安全地通信到tx處理程序,而不會出現過多的中斷禁用。

訪問池數組應該是線程安全的,'插入/刪除'應該很快 - 我在每個緩衝區結構中都有'succ/pred'字節字段,因此形成一個雙鏈表,訪問受到保護一個互斥體。和I/O一樣,我使用這個緩衝池來處理所有的線程間通信。

對於tx,從teh池中獲取緩衝區結構,填充數據,將索引推入到tx隊列中,禁用中斷足夠長的時間以確定tx中斷是否需要'primimg'。如果需要灌注,在重新啓用中斷之前,先插入一個充滿FIFO的數據。

當tx中斷處理程序發送了緩衝區時,它可以將'used'索引重新推回到'scavenge'隊列併發出一個信號量來使一個處理程序線程運行。然後該線程可以從清除隊列中獲取條目並將其返回到池中。

只有中斷處理程序不使用相同的緩衝方案重新啓用高優先級中斷時,該方案纔有效。

+0

好的,所以我理解並同意你的陳述,你不能對中斷環境中發生的任何事情使用阻塞調用,但我不能遵循你的答案的任何其他部分。此外,似乎使用我的方法,我不需要在我的中斷環境中阻止。當收發信號(通過中斷)發出信號時,我的信息被刪除,因此準備從隊列中刪除,我所要做的就是send_queue_out =(send_queue_out + 1)%NWK_QUEUE_SIZE吧?我是否錯過了你在答案的其餘部分中解釋的我正努力遵循的內容? – NickHalden