При разработке встраиваемых систем последовательная связь с главным компьютером является очень распространенным сценарием. Главный компьютер может отправлять инструкции или данные на встроенное устройство через последовательный порт, и встроенному устройству необходимо надежно получать и анализировать эти данные для выполнения соответствующих операций. Однако во время последовательной связи скорость, с которой главный компьютер отправляет данные, часто не соответствует скорости, с которой встроенное устройство получает и обрабатывает данные, что может привести к потере данных или неправильной интерпретации.
Для решения этой проблемы было решено спроектировать и реализовать кольцевой буфер управления приемом данных. Кольцевой буфер — это эффективная структура данных, подходящая для сценариев, в которых скорость создания данных превышает скорость их потребления. Он имеет буфер фиксированного размера и может повторно использовать пространство для обеспечения непрерывного хранения и эффективного использования данных.
В этом проекте мы решили использовать микроконтроллер STM32 для реализации функции приема данных через последовательный порт. STM32 обладает богатыми периферийными ресурсами и высокой производительностью и очень подходит для последовательной связи и обработки данных. Реализуя кольцевой буфер на STM32, можно достичь следующих целей:
(1) Стабильный прием данных: использование кольцевого буфера гарантирует, что даже если скорость приема данных ниже, чем скорость отправки, данные могут быть получены стабильно, чтобы избежать потери данных.
(2) Кэширование и управление данными. Кольцевой буфер можно использовать в качестве кэша данных для временного хранения полученных данных для последующей обработки. Это уменьшает задержку и сложность обработки данных.
(3) Анализ и применение данных: считывая данные из кольцевого буфера, анализируя и обрабатывая их, встроенное устройство может выполнять соответствующие операции на основе полученных данных, например, управлять внешними устройствами или отвечать на инструкции главного компьютера.
Используя кольцевой буфер для управления данными, полученными через последовательный порт, можно добиться надежного приема и обработки данных, а также повысить стабильность и надежность системы. В то же время это решение подходит и для других встроенных систем и сценариев связи.
(1) Определите структуру кольцевого буфера. Во-первых, вам необходимо определить структуру, представляющую кольцевой буфер, которая содержит следующие переменные-члены:
(2) Инициализируйте кольцевой буфер: перед использованием кольцевого буфера его необходимо инициализировать. Во время инициализации размер буфера, указатель записи и указатель чтения устанавливаются в исходное положение, обычно 0.
(3) Запись данных. Когда в буфер необходимо записать новые данные, необходимо выполнить следующие операции:
(4) Чтение данных. Когда вам нужно прочитать данные из буфера, вам необходимо выполнить следующие операции:
(5) Определите состояние буфера. Чтобы облегчить использование и управление буфером, вы можете реализовать некоторые функции для оценки состояния буфера, такие как:
При реализации кольцевого буфера необходимо обратить внимание на:
Благодаря вышеизложенным идеям на языке C можно реализовать простой и эффективный кольцевой буфер для хранения данных и управления ими, повышая стабильность и надежность системы во время процесса отправки и получения данных.
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
typedef struct {
int* buffer; // указатель буферного массива
int size; // размер буфера
int head; // Индекс заголовка
int tail; // хвостовой индекс
} CircularBuffer;
// Создать кольцевой буфер
CircularBuffer* createCircularBuffer(int size) {
CircularBuffer* cb = (CircularBuffer*)malloc(sizeof(CircularBuffer)); // Выделить пространство памяти
cb->buffer = (int*)malloc(sizeof(int) * size); // Выделить пространство памяти для буферных данных
cb->size = size; // настраиватьразмер буфера
cb->head = 0; // Инициализировать индекс заголовкадля0
cb->tail = 0; // инициализацияхвостовой индексдля0
return cb;
}
// Уничтожить кольцевой буфер
void destroyCircularBuffer(CircularBuffer* cb) {
free(cb->buffer); // Освободите пространство памяти буфера данных.
free(cb); // Освободите пространство памяти буферной структуры.
}
// Определить, заполнен ли кольцевой буфер
int isCircularBufferFull(CircularBuffer* cb) {
return ((cb->tail + 1) % cb->size == cb->head);
}
// Определить, пуст ли кольцевой буфер
int isCircularBufferEmpty(CircularBuffer* cb) {
return (cb->head == cb->tail);
}
// Записать данные в кольцевой буфер
void writeData(CircularBuffer* cb, int data) {
if (isCircularBufferFull(cb)) { // данные не могут быть записаны, если буфер заполнен
printf("Circular buffer is full. Data cannot be written.\n");
return;
}
cb->buffer[cb->tail] = data; // Записать данные в конец буфера
cb->tail = (cb->tail + 1) % cb->size; // возобновлятьхвостовой индекс,Перезагрузить буферное пространство
}
// Чтение данных из кольцевого буфера
int readData(CircularBuffer* cb) {
if (isCircularBufferEmpty(cb)) { // Если буфер пуст, нет данных для чтения.
printf("Circular buffer is empty. No data to read.\n");
return -1; // Возвращает значение по умолчанию, указывающее на ошибку чтения.
}
int data = cb->buffer[cb->head]; // Чтение данных из головы буфера
cb->head = (cb->head + 1) % cb->size; // возобновлять Индекс заголовка,Перезагрузить буферное пространство
return data;
}
int main() {
CircularBuffer* cb = createCircularBuffer(BUFFER_SIZE); // Создайте кольцевой буфер размером BUFFER_SIZE.
writeData(cb,1); // писатьданные1 writeData(cb,2); // писатьданные2 writeData(cb,3); // писатьданные3 printf("Read data: %d\n", readData(cb)); // Считайте данные и распечатайте
printf("Read data: %d\n", readData(cb));
writeData(cb, 4);
writeData(cb, 5);
printf("Read data: %d\n", readData(cb));
printf("Read data: %d\n", readData(cb));
printf("Read data: %d\n", readData(cb));
destroyCircularBuffer(cb); // Уничтожить кольцевой буфер
return 0;
}
#define BUFFER_SIZE 256
typedef struct {
uint8_t buffer[BUFFER_SIZE];
uint16_t head;
uint16_t tail;
} CircularBuffer;
// Инициализировать кольцевой буфер
void CircularBuffer_Init(CircularBuffer* cb) {
cb->head = 0;
cb->tail = 0;
}
// Определить, заполнен ли кольцевой буфер
bool CircularBuffer_IsFull(const CircularBuffer* cb) {
return (cb->head + 1) % BUFFER_SIZE == cb->tail;
}
// Определить, пуст ли кольцевой буфер
bool CircularBuffer_IsEmpty(const CircularBuffer* cb) {
return cb->head == cb->tail;
}
// Запись данных в кольцевой буфер
bool CircularBuffer_Write(CircularBuffer* cb, uint8_t data) {
if (CircularBuffer_IsFull(cb)) { // Буфер заполнен и запись в него невозможна.
return false;
}
cb->buffer[cb->head] = data;
cb->head = (cb->head + 1) % BUFFER_SIZE;
return true;
}
// Чтение данных из кольцевого буфера
bool CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
if (CircularBuffer_IsEmpty(cb)) { // Буфер пуст и читать нечего.
return false;
}
*data = cb->buffer[cb->tail];
cb->tail = (cb->tail + 1) % BUFFER_SIZE;
return true;
}
// Получить оставшийся размер кольцевого буфера
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;
}
}
// Получить записанный размер кольцевого буфера
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);
}
}
// Считайте указанную длину данных из кольцевого буфера.
bool CircularBuffer_ReadData(CircularBuffer* cb, uint8_t* data, uint16_t length) {
if (CircularBuffer_GetWrittenSize(cb) < length) {
return false; // Недостаточно данных в буфере
}
for (uint16_t i = 0; i < length; ++i) {
if (!CircularBuffer_Read(cb, &data[i])) {
return false; // Ошибка чтения данных
}
}
return true;
}
// Записывает указанную длину текста в кольцевой буфер.
bool CircularBuffer_WriteData(CircularBuffer* cb, const uint8_t* data, uint16_t length) {
if (CircularBuffer_GetRemainingSize(cb) < length) {
return false; // Недостаточно места в буфере
}
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)) {
// Буфер заполнен, ошибка обработки
}
}
в коде,определяет имядляCircularBuffer
Структура представляет собой кольцевой буфер。Содержит массив фиксированного размераbuffer
используется дляхранилищеданные,и головауказательhead
и хвостуказательtail
используется дляуправлятьданные Положение для чтения и письма。
Следующий,Реализованы функции для работы с кольцевыми буферами.。CircularBuffer_Init
функцияиспользуется для Инициализировать кольцевой буфер;CircularBuffer_IsFull
иCircularBuffer_IsEmpty
функция Определите, заполнен ли буфер отдельноилидлянулевой;CircularBuffer_Write
функцияиспользуется длябуферизоватьписатьданные;CircularBuffer_Read
функцияиспользуется длячитать из буфераданные。
CircularBuffer_GetRemainingSize
функцияиспользуется для Получить оставшийся размер кольцевого буфера,То есть, оно еще можетписатьсколько байтданные;CircularBuffer_GetWrittenSize
функцияиспользуется для Получите ужеписать Количество байтов в буфере;CircularBuffer_ReadData
функцияиспользуется для Считайте указанную длину данных из кольцевого буфера.,Сохраните его в предусмотренномданныев массиве;CircularBuffer_WriteData
функцияиспользуется для Записывает указанную длину текста в кольцевой буфер.,скопировать соответствующий байт из предоставленного массива данных.
Используя эти удобные функции, вы сможете более удобно управлять кольцевым буфером и осуществлять чтение и запись данных.
наконец,Приведен пример,Показывает, как записать полученные данные в кольцевой буфер в функции обработки прерывания приема последовательного порта STM32. в функции обработчика прерываний,проходитьUSART_ReceiveData
функция Получить полученные данные,вызовCircularBuffer_Write
функция Воляданныеписатьбуфер。