Реализация циклического буфера и Serial.print()
Я пытаюсь реализовать простейший кольцевой буфер в Arduino. Я думаю, что я в состоянии реализовать это, потому что мои функции push и pop не возвращают никаких ошибок. Однако, когда я хочу распечатать данные в буфере с помощью Serial.print()
, код переходит в бесконечный цикл. Вот мой код:
#include <stdio.h>
typedef struct circular_buffer
{
int buffer_arr[5];
int head;
int tail;
} circular_buffer;
void setup()
{
Serial.begin(9600);
Serial.println("Circular buffer started");
}
void loop()
{
circular_buffer *cb;
cb_init(cb);
if(Serial.available()){
char instruction = Serial.read();
Serial.println(instruction);
if(instruction == 'a'){
if(Serial.available()){
char instruction = Serial.read();
Serial.println("here");
}
char key = Serial.read();
cb_push_front(cb, key);
Serial.print(String(key));
Serial.println(" is entered");
}
else if(instruction == 'd'){
cb_pop_back(cb);
Serial.println("last item removed");
}
// for(int i=0; i<5; i++){
// Serial.print(cb->buffer_arr[i]);
//
// }
Serial.println("-");
}
}
void cb_init(circular_buffer *cb)
{
// cb->buffer_arr = {0,0,0,0,0};
for(int i=0; i<5; i++){
cb->buffer_arr[i] = 'c';
// Serial.print(sizeof(*cb));
}
// Serial.print(cb->buffer_arr[0]);
cb->head = 0;
cb->tail = 0;
}
void cb_push_front(circular_buffer *cb, char item)
{
cb->head++;
cb->buffer_arr[cb->head] = item;
}
void cb_pop_back(circular_buffer *cb)
{
cb->tail++;
cb->buffer_arr[cb->tail] = 0;
}
Цикл for
, который я закомментировал, является проблемным - или, по крайней мере, я так считаю. Моя проблема может быть во многом связана с моими плохими навыками владения языком Си. Я думал, что cb-> buffer_arr = {0,0,0,0,0};
фрагмент кода в функции инициализации должен был сработать (очень похоже на Python-ic), но этого не произошло.
Так что, если кто-нибудь сможет увидеть проблему и помочь мне ее устранить, я был бы вам очень признателен. Заранее благодарю.
Редактировать
После ответа Маенко я обновил код таким образом. Однако у меня все еще есть проблемы с печатью. Я не уверен, печатаю ли я адрес циклического буфера в формате ASCII таким образом. Я попытался передать cb
в функцию печати двумя способами. Хотя, похоже, ни один из них не работает ...
#include <stdio.h>
typedef struct circular_buffer
{
int buffer_arr[5];
int head;
int tail;
} circular_buffer;
circular_buffer cb;
void setup()
{
Serial.begin(9600);
cb_init(cb);
Serial.println("Circular buffer started");
}
void loop()
{
if(Serial.available()){
char instruction = Serial.read();
Serial.println(instruction);
if(instruction == 'a'){
Serial.print("a pressed");
cb_push_front(cb, instruction);
}
else if(instruction == 'd'){
Serial.print("d pressed");
}
else{
Serial.print("UNKNOWN");
}
Serial.println("-");
}
}
void cb_init(circular_buffer &cb)
{
for(int i=0; i<5; i++){
cb.buffer_arr[i] = 'c';
}
Serial.println("here init");
cb_print1(&cb);
cb_print2(cb);
cb.head = 0;
cb.tail = 0;
}
void cb_push_front(circular_buffer &cb, char item)
{
cb.head++;
cb.buffer_arr[cb.head] = item;
cb_print1(&cb);
cb_print2(cb);
}
void cb_pop_back(circular_buffer &cb)
{
cb.tail++;
cb.buffer_arr[cb.tail] = 0;
}
void cb_print1(circular_buffer *cb)
{
Serial.println("here print1");
for(int i=0; i<5; i++){
Serial.print(cb->buffer_arr[i]);
}
}
void cb_print2(circular_buffer cb)
{
Serial.println("here print2");
for(int i=0; i<5; i++){
Serial.print(cb.buffer_arr[i]);
}
}
И возвращаемый результат выглядит следующим образом:
here init
here print1
9999999999here print2
9999999999Circular buffer started
a
a pressedhere print1
9997999999here print2
9997999999-
a
a pressedhere print1
9997979999here print2
9997979999-
@TheClem, 👍1
Обсуждение2 ответа
Вы создаете переменную, которая может указывать на кольцевой буфер (*cb
), но ни в коем случае вы на самом деле не указываете ее на кольцевой буфер.
Либо вам нужно создать новый
циклический буфер, либо не создавать указатель, а создать статически выделенный циклический буфер. Последний метод является предпочтительным.
Просто удалите *
из вашего объявления cb, которое создает стандартную структурную переменную, затем возьмите ее адрес (с
&
) для передачи в качестве указателя на другие ваши функции. Или измените другие функции, чтобы они передавались по ссылке.
Например:
circular_buffer *cb;
становится
circular_buffer cb;
А потом
cb_init(cb);
становится:
cb_init(&cb);
Как и все другие функции, которым вы передаете переменную cb
.
Кроме того, каждый раз, когда вы проходите через loop()
, вы будете стирать и создавать новую переменную circular_buffer . Вместо этого cb
действительно должен быть глобальным, а вызов cb_init()
должен быть в setup()
.
Спасибо вам за ответ. Я обновил код в соответствии с вашим ответом. Однако, похоже, у меня все еще есть проблемы с печатью. Извините, что беспокою вас этими вопросами для начинающих., @TheClem
С этим кодом связано несколько проблем. Маенко уже показал основное, я добавлю здесь еще несколько.
Во-первых, о реализации циклического буфера. Как было указано в комментарии, для того чтобы буфер считался циклическим, индексы должны обтекаться. Я бы добавил небольшую придирку: в C ++ принято начинать заполнение массива с индекса 0. Таким образом, вы должны постинкрементировать (а не предварительно инкрементировать) индексы:
void cb_push_front(circular_buffer *cb, char item)
{
cb->buffer_arr[cb->head] = item;
cb->head++; // последующее приращение
if(cb->head == circular_buffer_size) // с обтеканием
cb->head = 0;
}
Однако все еще существует проблема: буфер может переполниться. Вы должны добавлять элемент только в том случае, если для этого есть место. И вы должны удалить элемент только в том случае, если буфер не пуст. Вы знаете, что буфер пуст, когда tail=head , но как вы узнаете, когда он заполнен? Если вы полностью заполните буфер и индексы будут обтекаться так, как должны, вы получите head= tail , и вы не сможете отличить полный буфер от пустого буфера. Есть два способа обойти эту проблему:
Вы можете сохранить в
структуре дополнительную переменную
, которая поможет вам определить разницу. Это может быть количество элементов в буфере, или логическое значение, сообщающее вам, когда буфер пуст, или логическое значение, сообщающее вам, когда он заполнен.Вы можете воздержаться от полного заполнения массива и считать, что буфер заполнен, когда он заполнен до полной емкости, где “емкость” определяется как размер массива минус единица. Затем буфер заполняется, когда
(голова + 1) = хвост [по модулю длины массива].
Вот реализация второй стратегии:
void cb_push_front(circular_buffer *cb, char item)
{
int next_head = cb->head + 1; // увеличить
if(next_head >= circular_buffer_size) // с переносом вокруг
next_head = 0;
if(next_head == cb->tail){
Serial.println("buffer overflow");
return;
}
cb->buffer_arr[cb->head] = item;
cb->head = next_head;
}
int cb_pop_back(circular_buffer *cb)
{
if(cb->tail == cb->head) {
Serial.println("buffer underflow");
return -1;
}
char item = cb->buffer_arr[cb->tail];
cb->tail++;
if(cb->tail >= circular_buffer_size)
cb->tail = 0;
return item;
}
Обратите внимание, что в cb_pop_back() нет смысла
перезаписывать буфер.
С другой стороны, часто бывает очень полезно, чтобы эта функция возвращала
элемент, который только что был извлечен.
Теперь несколько случайных комментариев:
Вместо того, чтобы жестко кодировать размер массива (
5
) в нескольких местах, определите константу в начале программы (circular_buffer_size
в моих примерах) и используйте его везде. Это делает более понятным, что означает число, и облегчает изменение размера, если вам когда-нибудь понадобится.Поскольку вы храните символы, используйте массив
символов
.Ваш код для обработки
Serial
плохо справляется с ситуацией , когда существует небольшая задержка междуинструкцией 'a'
и добавляемым символом. Если вы можете позволить себе блокировать код, самое простое решение - блокировать до тех пор, пока пользователь не предоставит символ:
if(instruction == 'a'){
while(!Serial.available())
; // дождитесь ввода ключа
char key = Serial.read();
cb_push_front(&cb, key);
Serial.print(key);
Serial.println(" is entered");
}
Очень важные моменты, спасибо! У меня были проблемы с правильной передачей cb
функциям, поэтому я не прилагал особых усилий к оптимизации, так сказать. У меня все еще есть некоторые проблемы с печатью. Я обновил вопрос. Но я буду реализовывать эти пункты после этого. Большое вам спасибо., @TheClem
Re “_ Я буду реализовывать эти пункты после этого_”: это не очень хорошая идея. Устраните эти проблемы прямо сейчас, и тогда ваша программа будет работать должным образом. Или, если этого не произойдет, тогда будет намного легче понять, что происходит не так., @Edgar Bonet
- Как остановить цикл в последовательном мониторе?
- Как заставить переключатель работать до тех пор, пока не будет обнаружен другой последовательный вход?
- Операторы If/Else на Arduino не выполняются
- Застрял в цикле While
- Как долго должен длиться цифровой импульс, чтобы его можно было прочитать?
- Программа для умножения чисел на самом деле не будет их умножать
- Выполнение вычислений только по запросу через последовательный
- Как разделить входящую строку?
На самом деле это не кольцевой буфер. Индексы должны быть обернуты вокруг предела буфера (5). В противном случае вы будете записывать сообщения за пределы буфера достаточно долго., @chrisl
Возможно, вам это покажется полезным: https://github.com/MajenkoLibraries/CircularBuffer, @Majenko
Большое вам спасибо за помощь. У меня, конечно, есть проблемы с тем, как я передаю адреса и указываю на переменные. Вы правы в отношении ограничения размера буфера, но в этом конкретном приложении нет способа заполнить буфер - если я не ошибаюсь в коде :) Вот почему я хотел, чтобы все было как можно проще., @TheClem