1

I would like to use queuing because i think its best for my application. I have very limited experience in dual core programming all i want is to hopefully pass 32 bit numbers to the second core properly. queuing seems to be good because it makes sure that the second core who is receiving the data wont miss anything in the sequence if data comes in very fast and core 2 is not ready to process it.

But Im having trouble finding examples. I did find one but its for a runner queue, which i think is a bit different to what i want

Can anyone show me an example sketch? what i would really like to see is core 1 adding to the queue and core 2 retrieving and removing data from the queue

5
  • If all you want to pass is 32-bit numbers the FIFO is better suited to your needs.
    – Majenko
    Commented Nov 15, 2021 at 17:16
  • But that said, Queue or FIFO it's the same (you just have to create a queue before you use it). Push stuff on one end, and pop it off the other (both with optional blocking).
    – Majenko
    Commented Nov 15, 2021 at 17:18
  • @Majenko I did read about FIFO but decided not to because of the note in the sdk, it is suggested that you do not use the FIFO for your own purposes unless none of the above concerns apply; the majority of cases for transferring data between cores can be eqaully well handled by using a queue Do you have some simple examples of the queue i can look at?
    – DrakeJest
    Commented Nov 15, 2021 at 19:00
  • Yeah, I saw that after I posted the comment ;)
    – Majenko
    Commented Nov 15, 2021 at 19:17
  • @Majenko Do you think you can show me how to use queue sir? :)
    – DrakeJest
    Commented Nov 15, 2021 at 19:48

2 Answers 2

2

After digging around i was able to use the queue_t from the sdk. and here it is

queue_t queue;

typedef struct queueItem {
  uint32_t value;
} item;

const int QUEUE_LENGTH = 128;


void setup() { //CORE 1 setup

  Serial.begin(9600);
  while (!Serial);
  queue_init(&queue, sizeof(item), QUEUE_LENGTH); //initialize the queue

}

void loop() { //CORE 1 TIGHTLOOP

  queueItem temp;
  temp.value = random (1000);
  if (queue_try_add(&queue, &temp)) { //adding items to the queue
    Serial.print("ADDING:\t");
    Serial.println(temp.value);
  }

  else
    Serial.println("FIFO was full\n");
  delay(500);
}

void setup1() { // CORE2
}

void loop1() {  //CORE 2 TIGHTLOOP

  queueItem temp;
  while (queue_try_remove(&queue, &temp)) { //retrieving item from the queue and deleting it from the queue
    Serial.print("ITEM RETRIEVED:\t");
    Serial.println(temp.value);
  }

  delay(500);


}
1

This requires a thread-safe queue, where one thread writes to the queue, and another thread reads from the queue.

But I find that with a little care, you can just use a circular buffer where the consumer will increase the head, and the producer will increase the tail.

Something like this:

#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;
}

This will work as long as your compiler doesn't do crazy re-ordering of the operations, and as long as you only touch the head with the consumer, and only touch the tail with the producer. Also, the assignments of head/tail need to be atomic, not split up as: tail++ ; tail = tail%MAXSZ;

Come to think of it: if a small capacity suffices, like the 80 from the example code, I would use int8_t for head/tail, just in case writing an int to memory gets context-switched between writing the LSB and MSB parts. (I don't know RP2040 well enough to know if writing an int is atomic.)

5
  • So the buffer will just grow larger and larger as more data is transfered? Wont that eventually eat all the memory the more data is passed?
    – DrakeJest
    Commented Nov 15, 2021 at 19:26
  • No, the buffer in the example has 80 slots reserved, and will never use more memory. The used slots vary between 0 and 79, so you can't have the consumption lag too much, as the buffer will be full. Both the head and tail will wrap-around as they reach 80th index. Once you consume a value, its slot is now available for queuing in a new value.
    – Bram
    Commented Nov 15, 2021 at 19:30
  • How do i know how many are in queue? because from what i undestand looking at it, data in the buffer never gets deleted, just overwritten when the array wraps around.
    – DrakeJest
    Commented Nov 15, 2021 at 19:42
  • You can check for the number of valid elements by iterating through the queue from head to tail, counting the needed iterations until you reach the tail. An element is only valid, when its between head and tail (inclusive head, exclusive tail)
    – chrisl
    Commented Nov 15, 2021 at 19:58
  • @DrakeJest Just look at the distance between head/tail for the number of used slots. See the usage() function that I added.
    – Bram
    Commented Nov 15, 2021 at 20:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.