Примерный скетч RP2040 для двухъядерной очереди

arduino-nano-rp2040

Я хотел бы использовать очередь, потому что я думаю, что это лучше всего подходит для моего приложения. У меня очень ограниченный опыт в двухъядерном программировании, все, что я хочу, это правильно передать 32-разрядные числа второму ядру. Очередь кажется хорошим решением, потому что она гарантирует, что второе ядро, получающее данные, ничего не пропустит в последовательности, если данные поступают очень быстро, а ядро 2 не готово к их обработке.

Но у меня возникли проблемы с поиском примеров. Я нашел один, но он предназначен для очереди runner, которая, я думаю, немного отличается от того, что я хочу

Кто-нибудь может показать мне примерный скетч? Что я действительно хотел бы видеть, так это добавление ядра 1 в очередь и извлечение ядром 2 и удаление данных из очереди

, 👍1

Обсуждение

Если все, что вы хотите передать,-это 32-разрядные числа, то [FIFO](https://raspberrypi.github.io/pico-sdk-doxygen/group__multicore__fifo.html) лучше соответствует вашим потребностям., @Majenko

Но, тем не менее, очередь или FIFO-это одно и то же (вам просто нужно создать очередь, прежде чем использовать ее). Надавите на один конец и снимите его с другого (оба с дополнительной блокировкой)., @Majenko

@Majenko Я действительно читал о FIFO, но решил этого не делать из-за примечания в sdk: "Рекомендуется не использовать FIFO в своих собственных целях, если только не применимо ни одно из вышеперечисленных соображений; в большинстве случаев передача данных между ядрами может быть эффективно обработана с помощью очереди". У вас есть несколько простых примеров очереди, на которые я могу посмотреть?, @DrakeJest

Да, я видел это *после того, как* опубликовал комментарий ;), @Majenko

@Majenko Как вы думаете, вы можете показать мне, как использовать очередь, сэр? :), @DrakeJest


2 ответа


Лучший ответ:

2

Покопавшись, я смог использовать queue_t из sdk. и вот оно

queue_t queue;

typedef struct queueItem {
  uint32_t value;
} item;

const int QUEUE_LENGTH = 128;


void setup() { //Настройка ЯДРА 1

  Serial.begin(9600);
  while (!Serial);
  queue_init(&queue, sizeof(item), QUEUE_LENGTH); //инициализировать очередь

}

void loop() { //ЦИКЛ ЯДРА 1

  queueItem temp;
  temp.value = random (1000);
  if (queue_try_add(&queue, &temp)) { //добавление элементов в очередь
    Serial.print("ADDING:\t");
    Serial.println(temp.value);
  }

  else
    Serial.println("FIFO был заполнен\n");
  delay(500);
}

void setup1() { // ЯДРО 2
}

void loop1() {  //ЦИКЛ ЯДРА 2

  queueItem temp;
  while (queue_try_remove(&queue, &temp)) { //извлечение элемента из очереди и удаление его из очереди
    Serial.print("ITEM RETRIEVED:\t");
    Serial.println(temp.value);
  }

  delay(500);


}
,

1

Для этого требуется потокобезопасная очередь, в которой один поток записывает данные в очередь, а другой поток считывает данные из очереди.

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

Что-то вроде этого:

#define MAXSZ 80
static int buff[MAXSZ];
static int head=0;
static int tail=0;

bool full(void)  { return (tail+1)%MAXSZ == head; }
bool empty(void) { return head==tail; }
int  usage(void) { int num = tail-head; return num<0 ? num+MAXSZ : num; }


int produce(int val)
{
   if ( full() ) return -1;
   buff[tail] = val;
   tail = (tail+1)%MAXSZ;
   return 0; // signal success.
}

int consume(void)
{
   if ( empty() ) return -1;
   int val = buff[head];
   head = (head+1)%MAXSZ;
}

Это будет работать до тех пор, пока ваш компилятор не будет делать сумасшедшего переупорядочения операций, и до тех пор, пока вы касаетесь только головы с потребителем и касаетесь только хвоста с производителем. Кроме того, назначения head/tail должны быть атомарными, а не разделяться следующим образом: tail++ ; tail = tail%MAXSZ;

Подумайте об этом: если достаточно небольшой емкости, например 80 из примера кода, я бы использовал int8_t для головы/хвоста, на всякий случай, если при записи int в память происходит переключение контекста между записями частей LSB и MSB. (Я недостаточно хорошо знаю RP2040, чтобы знать, является ли написание int атомарным.)

,

Таким образом, буфер будет просто увеличиваться и увеличиваться по мере передачи большего количества данных? Не будет ли это в конечном итоге съедать всю память, чем больше данных передается?, @DrakeJest

Нет, в буфере в примере зарезервировано 80 слотов, и он никогда не будет использовать больше памяти. Используемые слоты варьируются от 0 до 79, поэтому у вас не может быть слишком большой задержки в потреблении, так как буфер будет заполнен. И "голова`, и "хвост" будут разворачиваться, когда достигнут 80-го индекса. Как только вы потребляете значение, его слот теперь доступен для очереди в новом значении., @Bram

Как мне узнать, сколько человек в очереди? потому что, судя по тому, что я понял, глядя на это, данные в буфере никогда не удаляются, а просто перезаписываются, когда массив оборачивается., @DrakeJest

Вы можете проверить количество допустимых элементов, пройдя по очереди от начала до конца, считая необходимые итерации, пока не дойдете до конца. Элемент действителен только в том случае, если он находится между головой и хвостом (включающая голова, исключительный хвост)., @chrisl

@DrakeJest Просто посмотрите на расстояние между головой/хвостом для количества используемых слотов. Смотрите функцию " использование ()`, которую я добавил., @Bram