• 方案介紹
    • 一、前言
    • 二、實現(xiàn)思路
    • 三、 C語言實現(xiàn)驗證思路
    • 四、STM32串口接收
  • 附件下載
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

STM32單片機(jī)采用環(huán)形緩沖區(qū)實現(xiàn)串口中斷數(shù)據(jù)接收管理

14小時前
477
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

更多詳細(xì)資料請聯(lián)系.docx

共1個文件

一、前言

嵌入式系統(tǒng)開發(fā)中,與上位機(jī)進(jìn)行串口通信是非常常見的場景。上位機(jī)可以通過串口發(fā)送指令或者數(shù)據(jù)給嵌入式設(shè)備,而嵌入式設(shè)備需要可靠地接收并解析這些數(shù)據(jù),以執(zhí)行相應(yīng)的操作。然而,在串口通信過程中,上位機(jī)發(fā)送數(shù)據(jù)的速率往往與嵌入式設(shè)備接收和處理數(shù)據(jù)的速率不一致,這就可能導(dǎo)致數(shù)據(jù)的丟失或者誤解析。

為了解決這個問題,決定設(shè)計并實現(xiàn)一個環(huán)形緩沖區(qū)來進(jìn)行數(shù)據(jù)接收管理。環(huán)形緩沖區(qū)是一種高效的數(shù)據(jù)結(jié)構(gòu),適用于數(shù)據(jù)產(chǎn)生速率快于消費(fèi)速率的場景。它具有固定大小的緩沖區(qū),并且可以循環(huán)利用空間,保證數(shù)據(jù)的連續(xù)存儲和有效利用。

在本項目中,選擇使用STM32微控制器來實現(xiàn)串口數(shù)據(jù)接收功能。STM32具有豐富的外設(shè)資源和強(qiáng)大的性能,非常適合用于串口通信和數(shù)據(jù)處理。通過在STM32上實現(xiàn)環(huán)形緩沖區(qū),可以實現(xiàn)以下目標(biāo):

(1)數(shù)據(jù)穩(wěn)定接收:通過使用環(huán)形緩沖區(qū),確保即使在接收數(shù)據(jù)速率慢于發(fā)送速率的情況下,數(shù)據(jù)也能夠得到穩(wěn)定的接收,避免數(shù)據(jù)丟失。

(2)數(shù)據(jù)緩存和管理:環(huán)形緩沖區(qū)可以作為一個數(shù)據(jù)緩存區(qū),將接收到的數(shù)據(jù)暫時存儲起來,以便后續(xù)處理。這樣可以降低數(shù)據(jù)處理的延遲和復(fù)雜性。

(3)數(shù)據(jù)解析和應(yīng)用:通過從環(huán)形緩沖區(qū)中讀取數(shù)據(jù),并進(jìn)行解析和處理,嵌入式設(shè)備可以根據(jù)接收到的數(shù)據(jù)執(zhí)行相應(yīng)的操作,如控制外部設(shè)備或響應(yīng)上位機(jī)指令。

通過使用環(huán)形緩沖區(qū)管理串口接收的數(shù)據(jù),可以實現(xiàn)可靠的數(shù)據(jù)接收和處理,并提高系統(tǒng)的穩(wěn)定性和可靠性。同時,該方案也適用于其他嵌入式系統(tǒng)和通信場景。

image-20230902144017003

image-20230902144051473

二、實現(xiàn)思路

(1)定義環(huán)形緩沖區(qū)的結(jié)構(gòu)體:首先,需要定義一個表示環(huán)形緩沖區(qū)的結(jié)構(gòu)體,其中包含以下成員變量:

  • 緩沖區(qū)的大?。╟apacity):表示環(huán)形緩沖區(qū)的容量,即可以存儲的最大元素數(shù)量。
  • 寫指針(write_ptr):表示當(dāng)前可寫入數(shù)據(jù)的位置。
  • 讀指針(read_ptr):表示當(dāng)前可讀取數(shù)據(jù)的位置。
  • 數(shù)據(jù)數(shù)組(buffer):用于存儲實際的數(shù)據(jù)。

(2)初始化環(huán)形緩沖區(qū):在使用環(huán)形緩沖區(qū)之前,需要進(jìn)行初始化。初始化時,將緩沖區(qū)的大小、寫指針和讀指針都設(shè)置為初始位置,通常都是0。

(3)寫入數(shù)據(jù):當(dāng)有新的數(shù)據(jù)要寫入緩沖區(qū)時,需要執(zhí)行以下操作:

  • 檢查緩沖區(qū)是否已滿,如果已滿則無法寫入新的數(shù)據(jù)。
  • 將數(shù)據(jù)寫入當(dāng)前寫指針?biāo)赶虻奈恢谩?/li>
  • 更新寫指針的位置,通常是將其加1,并考慮到環(huán)形特性,需要進(jìn)行取模運(yùn)算。

(4)讀取數(shù)據(jù):當(dāng)需要從緩沖區(qū)中讀取數(shù)據(jù)時,需要執(zhí)行以下操作:

  • 檢查緩沖區(qū)是否為空,如果為空則無數(shù)據(jù)可讀取。
  • 讀取當(dāng)前讀指針?biāo)赶虻臄?shù)據(jù)。
  • 更新讀指針的位置,通常是將其加1,并考慮到環(huán)形特性,需要進(jìn)行取模運(yùn)算。

(5)判斷緩沖區(qū)狀態(tài):為了方便使用和管理緩沖區(qū),可以實現(xiàn)一些用于判斷緩沖區(qū)狀態(tài)的函數(shù),例如:

  • is_full():判斷緩沖區(qū)是否已滿。
  • is_empty():判斷緩沖區(qū)是否為空。

實現(xiàn)環(huán)形緩沖區(qū)時,需要注意:

  • 寫指針和讀指針的位置計算要考慮到環(huán)形特性,即超過緩沖區(qū)容量時需要進(jìn)行取模運(yùn)算。
  • 緩沖區(qū)大小要合理選擇,根據(jù)實際需求確定,以充分利用內(nèi)存資源并避免數(shù)據(jù)丟失。
  • 多線程或中斷環(huán)境下的并發(fā)訪問要考慮數(shù)據(jù)同步和互斥操作,以避免競爭條件和數(shù)據(jù)不一致的問題。

通過以上思路,可以在C語言中實現(xiàn)一個簡單高效的環(huán)形緩沖區(qū),用于存儲和管理數(shù)據(jù),在數(shù)據(jù)收發(fā)過程中提高系統(tǒng)的穩(wěn)定性和可靠性。

三、 C語言實現(xiàn)驗證思路

#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 10

typedef struct {
    int* buffer;      // 緩沖區(qū)數(shù)組指針
    int size;         // 緩沖區(qū)大小
    int head;         // 頭部索引
    int tail;         // 尾部索引
} CircularBuffer;

// 創(chuàng)建環(huán)形緩沖區(qū)
CircularBuffer* createCircularBuffer(int size) {
    CircularBuffer* cb = (CircularBuffer*)malloc(sizeof(CircularBuffer));  // 分配內(nèi)存空間
    cb->buffer = (int*)malloc(sizeof(int) * size);  // 分配緩沖區(qū)數(shù)據(jù)的內(nèi)存空間
    cb->size = size;   // 設(shè)置緩沖區(qū)大小
    cb->head = 0;      // 初始化頭部索引為0
    cb->tail = 0;      // 初始化尾部索引為0
    return cb;
}

// 銷毀環(huán)形緩沖區(qū)
void destroyCircularBuffer(CircularBuffer* cb) {
    free(cb->buffer);  // 釋放緩沖區(qū)數(shù)據(jù)的內(nèi)存空間
    free(cb);          // 釋放緩沖區(qū)結(jié)構(gòu)體的內(nèi)存空間
}

// 判斷環(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);
}

// 寫入數(shù)據(jù)到環(huán)形緩沖區(qū)
void writeData(CircularBuffer* cb, int data) {
    if (isCircularBufferFull(cb)) {  // 如果緩沖區(qū)已滿,則無法寫入數(shù)據(jù)
        printf("Circular buffer is full. Data cannot be written.n");
        return;
    }
    cb->buffer[cb->tail] = data;  // 將數(shù)據(jù)寫入緩沖區(qū)的尾部
    cb->tail = (cb->tail + 1) % cb->size;  // 更新尾部索引,循環(huán)利用緩沖區(qū)空間
}

// 從環(huán)形緩沖區(qū)讀取數(shù)據(jù)
int readData(CircularBuffer* cb) {
    if (isCircularBufferEmpty(cb)) {  // 如果緩沖區(qū)為空,則無數(shù)據(jù)可讀取
        printf("Circular buffer is empty. No data to read.n");
        return -1;  // 返回一個默認(rèn)值表示讀取失敗
    }
    int data = cb->buffer[cb->head];  // 從緩沖區(qū)的頭部讀取數(shù)據(jù)
    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);  // 寫入數(shù)據(jù)1
    writeData(cb, 2);  // 寫入數(shù)據(jù)2
    writeData(cb, 3);  // 寫入數(shù)據(jù)3

    printf("Read data: %dn", readData(cb));  // 讀取數(shù)據(jù)并打印
    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ū)寫入數(shù)據(jù)
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ū)讀取數(shù)據(jù)
bool CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
  if (CircularBuffer_IsEmpty(cb)) {  // 緩沖區(qū)為空,無數(shù)據(jù)可讀取
    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ū)讀取指定長度的數(shù)據(jù)
bool CircularBuffer_ReadData(CircularBuffer* cb, uint8_t* data, uint16_t length) {
  if (CircularBuffer_GetWrittenSize(cb) < length) {
    return false;  // 緩沖區(qū)中的數(shù)據(jù)不足
  }
  
  for (uint16_t i = 0; i < length; ++i) {
    if (!CircularBuffer_Read(cb, &data[i])) {
      return false;  // 讀取數(shù)據(jù)出錯
    }
  }
  
  return true;
}

// 向環(huán)形緩沖區(qū)寫入指定長度的數(shù)據(jù)
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;  // 寫入數(shù)據(jù)出錯
    }
  }
  
  return true;
}



// 示例:STM32串口接收中斷處理函數(shù)
void USART_Receive_IRQHandler(void) {
  uint8_t data = USART_ReceiveData(USART1);  // 獲取接收到的數(shù)據(jù)
  if (!CircularBuffer_Write(&rxBuffer, data)) {
    // 緩沖區(qū)已滿,處理錯誤
  }
}


在代碼中,定義了一個名為CircularBuffer的結(jié)構(gòu)體來表示環(huán)形緩沖區(qū)。包含了一個具有固定大小的數(shù)組buffer用于存儲數(shù)據(jù),以及頭部指針head和尾部指針tail用于管理數(shù)據(jù)的讀寫位置。

接下來,實現(xiàn)了一些函數(shù)來對環(huán)形緩沖區(qū)進(jìn)行操作。CircularBuffer_Init函數(shù)用于初始化環(huán)形緩沖區(qū);CircularBuffer_IsFullCircularBuffer_IsEmpty函數(shù)分別判斷緩沖區(qū)是否已滿和是否為空;CircularBuffer_Write函數(shù)用于向緩沖區(qū)寫入數(shù)據(jù);CircularBuffer_Read函數(shù)用于從緩沖區(qū)讀取數(shù)據(jù)。

CircularBuffer_GetRemainingSize函數(shù)用于獲取環(huán)形緩沖區(qū)的剩余大小,即還能寫入多少個字節(jié)的數(shù)據(jù);CircularBuffer_GetWrittenSize函數(shù)用于獲取已經(jīng)寫入到緩沖區(qū)的字節(jié)數(shù);CircularBuffer_ReadData函數(shù)用于從環(huán)形緩沖區(qū)讀取指定長度的數(shù)據(jù),將其存儲到提供的數(shù)據(jù)數(shù)組中;CircularBuffer_WriteData函數(shù)用于向環(huán)形緩沖區(qū)寫入指定長度的數(shù)據(jù),從提供的數(shù)據(jù)數(shù)組中復(fù)制相應(yīng)的字節(jié)。

使用這些方便函數(shù),可以更方便地管理環(huán)形緩沖區(qū),實現(xiàn)數(shù)據(jù)的讀取和寫入。

最后,給出了一個示例,展示在STM32串口接收中斷處理函數(shù)中將接收到的數(shù)據(jù)寫入環(huán)形緩沖區(qū)。在中斷處理函數(shù)中,通過USART_ReceiveData函數(shù)獲取接收到的數(shù)據(jù),調(diào)用CircularBuffer_Write函數(shù)將數(shù)據(jù)寫入緩沖區(qū)。

  • 更多詳細(xì)資料請聯(lián)系.docx
    下載

相關(guān)推薦