一、前言
在嵌入式系統(tǒng)開發(fā)中,與上位機進行串口通信是非常常見的場景。上位機可以通過串口發(fā)送指令或者數據給嵌入式設備,而嵌入式設備需要可靠地接收并解析這些數據,以執(zhí)行相應的操作。然而,在串口通信過程中,上位機發(fā)送數據的速率往往與嵌入式設備接收和處理數據的速率不一致,這就可能導致數據的丟失或者誤解析。
為了解決這個問題,決定設計并實現一個環(huán)形緩沖區(qū)來進行數據接收管理。環(huán)形緩沖區(qū)是一種高效的數據結構,適用于數據產生速率快于消費速率的場景。它具有固定大小的緩沖區(qū),并且可以循環(huán)利用空間,保證數據的連續(xù)存儲和有效利用。
在本項目中,選擇使用STM32微控制器來實現串口數據接收功能。STM32具有豐富的外設資源和強大的性能,非常適合用于串口通信和數據處理。通過在STM32上實現環(huán)形緩沖區(qū),可以實現以下目標:
(1)數據穩(wěn)定接收:通過使用環(huán)形緩沖區(qū),確保即使在接收數據速率慢于發(fā)送速率的情況下,數據也能夠得到穩(wěn)定的接收,避免數據丟失。
(2)數據緩存和管理:環(huán)形緩沖區(qū)可以作為一個數據緩存區(qū),將接收到的數據暫時存儲起來,以便后續(xù)處理。這樣可以降低數據處理的延遲和復雜性。
(3)數據解析和應用:通過從環(huán)形緩沖區(qū)中讀取數據,并進行解析和處理,嵌入式設備可以根據接收到的數據執(zhí)行相應的操作,如控制外部設備或響應上位機指令。
通過使用環(huán)形緩沖區(qū)管理串口接收的數據,可以實現可靠的數據接收和處理,并提高系統(tǒng)的穩(wěn)定性和可靠性。同時,該方案也適用于其他嵌入式系統(tǒng)和通信場景。
二、實現思路
(1)定義環(huán)形緩沖區(qū)的結構體:首先,需要定義一個表示環(huán)形緩沖區(qū)的結構體,其中包含以下成員變量:
- 緩沖區(qū)的大?。╟apacity):表示環(huán)形緩沖區(qū)的容量,即可以存儲的最大元素數量。
- 寫指針(write_ptr):表示當前可寫入數據的位置。
- 讀指針(read_ptr):表示當前可讀取數據的位置。
- 數據數組(buffer):用于存儲實際的數據。
(2)初始化環(huán)形緩沖區(qū):在使用環(huán)形緩沖區(qū)之前,需要進行初始化。初始化時,將緩沖區(qū)的大小、寫指針和讀指針都設置為初始位置,通常都是0。
(3)寫入數據:當有新的數據要寫入緩沖區(qū)時,需要執(zhí)行以下操作:
- 檢查緩沖區(qū)是否已滿,如果已滿則無法寫入新的數據。
- 將數據寫入當前寫指針所指向的位置。
- 更新寫指針的位置,通常是將其加1,并考慮到環(huán)形特性,需要進行取模運算。
(4)讀取數據:當需要從緩沖區(qū)中讀取數據時,需要執(zhí)行以下操作:
- 檢查緩沖區(qū)是否為空,如果為空則無數據可讀取。
- 讀取當前讀指針所指向的數據。
- 更新讀指針的位置,通常是將其加1,并考慮到環(huán)形特性,需要進行取模運算。
(5)判斷緩沖區(qū)狀態(tài):為了方便使用和管理緩沖區(qū),可以實現一些用于判斷緩沖區(qū)狀態(tài)的函數,例如:
- is_full():判斷緩沖區(qū)是否已滿。
- is_empty():判斷緩沖區(qū)是否為空。
實現環(huán)形緩沖區(qū)時,需要注意:
- 寫指針和讀指針的位置計算要考慮到環(huán)形特性,即超過緩沖區(qū)容量時需要進行取模運算。
- 緩沖區(qū)大小要合理選擇,根據實際需求確定,以充分利用內存資源并避免數據丟失。
- 多線程或中斷環(huán)境下的并發(fā)訪問要考慮數據同步和互斥操作,以避免競爭條件和數據不一致的問題。
通過以上思路,可以在C語言中實現一個簡單高效的環(huán)形緩沖區(qū),用于存儲和管理數據,在數據收發(fā)過程中提高系統(tǒng)的穩(wěn)定性和可靠性。
三、 C語言實現驗證思路
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
typedef struct {
int* buffer; // 緩沖區(qū)數組指針
int size; // 緩沖區(qū)大小
int head; // 頭部索引
int tail; // 尾部索引
} CircularBuffer;
// 創(chuàng)建環(huán)形緩沖區(qū)
CircularBuffer* createCircularBuffer(int size) {
CircularBuffer* cb = (CircularBuffer*)malloc(sizeof(CircularBuffer)); // 分配內存空間
cb->buffer = (int*)malloc(sizeof(int) * size); // 分配緩沖區(qū)數據的內存空間
cb->size = size; // 設置緩沖區(qū)大小
cb->head = 0; // 初始化頭部索引為0
cb->tail = 0; // 初始化尾部索引為0
return cb;
}
// 銷毀環(huán)形緩沖區(qū)
void destroyCircularBuffer(CircularBuffer* cb) {
free(cb->buffer); // 釋放緩沖區(qū)數據的內存空間
free(cb); // 釋放緩沖區(qū)結構體的內存空間
}
// 判斷環(huán)形緩沖區(qū)是否已滿
int isCircularBufferFull(CircularBuffer* cb) {
return ((cb->tail + 1) % cb->size == cb->head);
}
// 判斷環(huán)形緩沖區(qū)是否為空
int isCircularBufferEmpty(CircularBuffer* cb) {
return (cb->head == cb->tail);
}
// 寫入數據到環(huán)形緩沖區(qū)
void writeData(CircularBuffer* cb, int data) {
if (isCircularBufferFull(cb)) { // 如果緩沖區(qū)已滿,則無法寫入數據
printf("Circular buffer is full. Data cannot be written.n");
return;
}
cb->buffer[cb->tail] = data; // 將數據寫入緩沖區(qū)的尾部
cb->tail = (cb->tail + 1) % cb->size; // 更新尾部索引,循環(huán)利用緩沖區(qū)空間
}
// 從環(huán)形緩沖區(qū)讀取數據
int readData(CircularBuffer* cb) {
if (isCircularBufferEmpty(cb)) { // 如果緩沖區(qū)為空,則無數據可讀取
printf("Circular buffer is empty. No data to read.n");
return -1; // 返回一個默認值表示讀取失敗
}
int data = cb->buffer[cb->head]; // 從緩沖區(qū)的頭部讀取數據
cb->head = (cb->head + 1) % cb->size; // 更新頭部索引,循環(huán)利用緩沖區(qū)空間
return data;
}
int main() {
CircularBuffer* cb = createCircularBuffer(BUFFER_SIZE); // 創(chuàng)建大小為BUFFER_SIZE的環(huán)形緩沖區(qū)
writeData(cb, 1); // 寫入數據1
writeData(cb, 2); // 寫入數據2
writeData(cb, 3); // 寫入數據3
printf("Read data: %dn", readData(cb)); // 讀取數據并打印
printf("Read data: %dn", readData(cb));
writeData(cb, 4);
writeData(cb, 5);
printf("Read data: %dn", readData(cb));
printf("Read data: %dn", readData(cb));
printf("Read data: %dn", readData(cb));
destroyCircularBuffer(cb); // 銷毀環(huán)形緩沖區(qū)
return 0;
}
四、STM32串口接收
#define BUFFER_SIZE 256
typedef struct {
uint8_t buffer[BUFFER_SIZE];
uint16_t head;
uint16_t tail;
} CircularBuffer;
// 初始化環(huán)形緩沖區(qū)
void CircularBuffer_Init(CircularBuffer* cb) {
cb->head = 0;
cb->tail = 0;
}
// 判斷環(huán)形緩沖區(qū)是否已滿
bool CircularBuffer_IsFull(const CircularBuffer* cb) {
return (cb->head + 1) % BUFFER_SIZE == cb->tail;
}
// 判斷環(huán)形緩沖區(qū)是否為空
bool CircularBuffer_IsEmpty(const CircularBuffer* cb) {
return cb->head == cb->tail;
}
// 向環(huán)形緩沖區(qū)寫入數據
bool CircularBuffer_Write(CircularBuffer* cb, uint8_t data) {
if (CircularBuffer_IsFull(cb)) { // 緩沖區(qū)已滿,無法寫入
return false;
}
cb->buffer[cb->head] = data;
cb->head = (cb->head + 1) % BUFFER_SIZE;
return true;
}
// 從環(huán)形緩沖區(qū)讀取數據
bool CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
if (CircularBuffer_IsEmpty(cb)) { // 緩沖區(qū)為空,無數據可讀取
return false;
}
*data = cb->buffer[cb->tail];
cb->tail = (cb->tail + 1) % BUFFER_SIZE;
return true;
}
// 獲取環(huán)形緩沖區(qū)剩余大小
uint16_t CircularBuffer_GetRemainingSize(const CircularBuffer* cb) {
if (cb->head >= cb->tail) {
return BUFFER_SIZE - (cb->head - cb->tail);
} else {
return cb->tail - cb->head - 1;
}
}
// 獲取環(huán)形緩沖區(qū)已寫入大小
uint16_t CircularBuffer_GetWrittenSize(const CircularBuffer* cb) {
if (cb->head >= cb->tail) {
return cb->head - cb->tail;
} else {
return BUFFER_SIZE - (cb->tail - cb->head - 1);
}
}
// 從環(huán)形緩沖區(qū)讀取指定長度的數據
bool CircularBuffer_ReadData(CircularBuffer* cb, uint8_t* data, uint16_t length) {
if (CircularBuffer_GetWrittenSize(cb) < length) {
return false; // 緩沖區(qū)中的數據不足
}
for (uint16_t i = 0; i < length; ++i) {
if (!CircularBuffer_Read(cb, &data[i])) {
return false; // 讀取數據出錯
}
}
return true;
}
// 向環(huán)形緩沖區(qū)寫入指定長度的數據
bool CircularBuffer_WriteData(CircularBuffer* cb, const uint8_t* data, uint16_t length) {
if (CircularBuffer_GetRemainingSize(cb) < length) {
return false; // 緩沖區(qū)剩余空間不足
}
for (uint16_t i = 0; i < length; ++i) {
if (!CircularBuffer_Write(cb, data[i])) {
return false; // 寫入數據出錯
}
}
return true;
}
// 示例:STM32串口接收中斷處理函數
void USART_Receive_IRQHandler(void) {
uint8_t data = USART_ReceiveData(USART1); // 獲取接收到的數據
if (!CircularBuffer_Write(&rxBuffer, data)) {
// 緩沖區(qū)已滿,處理錯誤
}
}
在代碼中,定義了一個名為CircularBuffer
的結構體來表示環(huán)形緩沖區(qū)。包含了一個具有固定大小的數組buffer
用于存儲數據,以及頭部指針head
和尾部指針tail
用于管理數據的讀寫位置。
接下來,實現了一些函數來對環(huán)形緩沖區(qū)進行操作。CircularBuffer_Init
函數用于初始化環(huán)形緩沖區(qū);CircularBuffer_IsFull
和CircularBuffer_IsEmpty
函數分別判斷緩沖區(qū)是否已滿和是否為空;CircularBuffer_Write
函數用于向緩沖區(qū)寫入數據;CircularBuffer_Read
函數用于從緩沖區(qū)讀取數據。
CircularBuffer_GetRemainingSize
函數用于獲取環(huán)形緩沖區(qū)的剩余大小,即還能寫入多少個字節(jié)的數據;CircularBuffer_GetWrittenSize
函數用于獲取已經寫入到緩沖區(qū)的字節(jié)數;CircularBuffer_ReadData
函數用于從環(huán)形緩沖區(qū)讀取指定長度的數據,將其存儲到提供的數據數組中;CircularBuffer_WriteData
函數用于向環(huán)形緩沖區(qū)寫入指定長度的數據,從提供的數據數組中復制相應的字節(jié)。
使用這些方便函數,可以更方便地管理環(huán)形緩沖區(qū),實現數據的讀取和寫入。
最后,給出了一個示例,展示在STM32串口接收中斷處理函數中將接收到的數據寫入環(huán)形緩沖區(qū)。在中斷處理函數中,通過USART_ReceiveData
函數獲取接收到的數據,調用CircularBuffer_Write
函數將數據寫入緩沖區(qū)。