Микроконтроллер STM32 использует кольцевой буфер для реализации управления приемом данных прерываний последовательного порта.
Микроконтроллер STM32 использует кольцевой буфер для реализации управления приемом данных прерываний последовательного порта.

1. Предисловие

При разработке встраиваемых систем последовательная связь с главным компьютером является очень распространенным сценарием. Главный компьютер может отправлять инструкции или данные на встроенное устройство через последовательный порт, и встроенному устройству необходимо надежно получать и анализировать эти данные для выполнения соответствующих операций. Однако во время последовательной связи скорость, с которой главный компьютер отправляет данные, часто не соответствует скорости, с которой встроенное устройство получает и обрабатывает данные, что может привести к потере данных или неправильной интерпретации.

Для решения этой проблемы было решено спроектировать и реализовать кольцевой буфер управления приемом данных. Кольцевой буфер — это эффективная структура данных, подходящая для сценариев, в которых скорость создания данных превышает скорость их потребления. Он имеет буфер фиксированного размера и может повторно использовать пространство для обеспечения непрерывного хранения и эффективного использования данных.

В этом проекте мы решили использовать микроконтроллер STM32 для реализации функции приема данных через последовательный порт. STM32 обладает богатыми периферийными ресурсами и высокой производительностью и очень подходит для последовательной связи и обработки данных. Реализуя кольцевой буфер на STM32, можно достичь следующих целей:

(1) Стабильный прием данных: использование кольцевого буфера гарантирует, что даже если скорость приема данных ниже, чем скорость отправки, данные могут быть получены стабильно, чтобы избежать потери данных.

(2) Кэширование и управление данными. Кольцевой буфер можно использовать в качестве кэша данных для временного хранения полученных данных для последующей обработки. Это уменьшает задержку и сложность обработки данных.

(3) Анализ и применение данных: считывая данные из кольцевого буфера, анализируя и обрабатывая их, встроенное устройство может выполнять соответствующие операции на основе полученных данных, например, управлять внешними устройствами или отвечать на инструкции главного компьютера.

Используя кольцевой буфер для управления данными, полученными через последовательный порт, можно добиться надежного приема и обработки данных, а также повысить стабильность и надежность системы. В то же время это решение подходит и для других встроенных систем и сценариев связи.

2. Идеи реализации

(1) Определите структуру кольцевого буфера. Во-первых, вам необходимо определить структуру, представляющую кольцевой буфер, которая содержит следующие переменные-члены:

  • Размер буфера (емкость): указывает емкость кольцевого буфера, то есть максимальное количество элементов, которые можно сохранить.
  • Указатель записи (write_ptr): указывает текущее место, куда можно записать данные.
  • Указатель чтения (read_ptr): указывает текущую позицию, где данные могут быть прочитаны.
  • Массив данных (буфер): используется для хранения фактических данных.

(2) Инициализируйте кольцевой буфер: перед использованием кольцевого буфера его необходимо инициализировать. Во время инициализации размер буфера, указатель записи и указатель чтения устанавливаются в исходное положение, обычно 0.

(3) Запись данных. Когда в буфер необходимо записать новые данные, необходимо выполнить следующие операции:

  • Проверьте, заполнен ли буфер,Если он заполнен, новые данные не могут быть записаны.
  • Запишите данные в то место, на которое в данный момент указывает указатель.
  • Обновите место, где написан указатель,Обычно к нему добавляют 1,и с учетом тороидальных свойств,Требуется операция по модулю.

(4) Чтение данных. Когда вам нужно прочитать данные из буфера, вам необходимо выполнить следующие операции:

  • Проверьте, пуст ли буфер,Если он пуст, никакие данные не могут быть прочитаны.
  • Считайте данные, на которые указывает текущий указатель чтения.
  • Обновить местоположение указателя,Обычно к нему добавляют 1,и с учетом тороидальных свойств,Требуется операция по модулю.

(5) Определите состояние буфера. Чтобы облегчить использование и управление буфером, вы можете реализовать некоторые функции для оценки состояния буфера, такие как:

  • is_full(): определяет, заполнен ли буфер.
  • is_empty(): определяет, пуст ли буфер.

При реализации кольцевого буфера необходимо обратить внимание на:

  • Расчет положения указателя записи и чтения указателя учитывает круговые свойства.,То есть при превышении емкости буфера Требуется операция по модулю.
  • размер буфер следует выбирать рационально,Определитесь исходя из реальных потребностей,Чтобы полностью использовать ресурсы памяти и избежать потери данных.
  • Параллельный доступ в многопоточных средах или средах с прерываниями должен учитывать операции синхронизации данных и взаимного исключения.,Чтобы избежать условий гонки и несоответствия данных.

Благодаря вышеизложенным идеям на языке C можно реализовать простой и эффективный кольцевой буфер для хранения данных и управления ими, повышая стабильность и надежность системы во время процесса отправки и получения данных.

3. Идеи проверки реализации языка C

Язык кода:javascript
копировать
#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;
}

4. Прием последовательного порта STM32.

Язык кода:javascript
копировать
#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функция Воляданныеписатьбуфер。

boy illustration
Артефакт, который делает код элегантным и лаконичным: программирование на Java8 Stream
boy illustration
Spring Boot(06): Spring Boot в сочетании с MySQL создает минималистскую и эффективную систему управления данными.
boy illustration
Как использовать ArrayPool
boy illustration
Интегрируйте iText в Spring Boot для реализации замены контента на основе шаблонов PDF.
boy illustration
Redis реализует очередь задержки на основе zset
boy illustration
Получить текущий пакет jar. path_java получает файл jar.
boy illustration
Краткое обсуждение высокопроизводительного шлюза Apache ShenYu
boy illustration
Если вы этого не понимаете, то на собеседовании даже не осмелитесь сказать, что знакомы с Redis.
boy illustration
elasticsearch медленный запрос, устранение неполадок записи, запрос с подстановочными знаками
boy illustration
По какому стандарту взимается плата за обслуживание программного обеспечения?
boy illustration
IP-адрес Получить
boy illustration
【Java】Решено: org.springframework.web.HttpRequestMethodNotSupportedException
boy illustration
Native js отправляет запрос на публикацию_javascript отправляет запрос на публикацию
boy illustration
.net PDF в Word_pdf в Word
boy illustration
[Пул потоков] Как Springboot использует пул потоков
boy illustration
Подробное объяснение в одной статье: Как работают пулы потоков
boy illustration
Серия SpringCloud (6) | Поговорим о балансировке нагрузки
boy illustration
IDEA Maven может упаковать все импортное полностью красное решение — универсальное решение.
boy illustration
Последний выпуск 2023 года, самое полное руководство по обучению Spring Boot во всей сети (с интеллект-картой).
boy illustration
[Решено — Практическая работа] SaTokenException: запрос не может быть получен в контексте, отличном от Интернета. Решение проблем — Практическая работа.
boy illustration
HikariPool-1 - Connection is not available, request timed out after 30000ms
boy illustration
Power Query: автоматическое суммирование ежемесячных данных с обновлением одним щелчком мыши.
boy illustration
установка Ubuntu в среде npm
boy illustration
3 Бесплатные системы управления складом (WMS) .NET с открытым исходным кодом
boy illustration
Глубокое погружение в библиотеку Python Lassie: мощный инструмент для автоматизации извлечения метаданных
boy illustration
Объяснение прослушивателя серии Activiti7 последней версии 2023 года
boy illustration
API-интерфейс Jitu Express для электронных счетов-Express Bird [просто для понимания]
boy illustration
Каковы архитектуры микросервисов Java. Серверная часть плавающей области обслуживания
boy illustration
Описание трех режимов жизненного цикла службы внедрения зависимостей Asp.net Core.
boy illustration
Java реализует пользовательские аннотации для доступа к интерфейсу без проверки токена.