How to interrupt an xQueueReceive() API in FreeRTOS?

2k views Asked by At

In the following code

  • SendMessage() is the API called by the user to send a message over USB
  • UsbDataReceived() is the function called by another thread when data is received on the USB
  • Task() is the thread created in init() that reads from the queue filled by SendMessage() and calls the low level usb function to send a message
  • UsbConnected() is the function called by another thread when a USB device is connected.

I need to implement the following behaviour:

  • Once a message has been sent, no new messages can be sent up to when an answer for the last message has arrived
  • When UsbConnected() is called, all of the queues must be emptied and if the thread Task() is waiting on the queue AnswerQueue, it has to be unblocked so that it can go back to wait on the queue CommandQueue

An example of my best solution is listed below.

I had to post an EVENT_QUEUE_RESETTED message on both of the queues with a particular order.

Is it possible to achieve the same result in a less cumbersome way?

void UsbhOut(Message_t );
int UsbConnected;
QueueHandle_t MessageQueue;



typedef enum
{
    EVENT_QUEUE_RESETTED,
    EVENT_NEW_MESSAGE,
    EVENT_NEW_ANSWER,
} Event_t;

typedef struct
{
    Event_t Event;
    uint8_t Data[32];
} CommandQueue_t;

typedef struct
{
    Event_t Event;
    uint8_t Data[32];
} AnswerQueue_t;


void init(void)
{
  MessageQueue = xQueueCreate(sizeof(Message_t), 10);
  AnswerQueue = xQueueCreate(sizeof(Message_t), 10);
  xTaskCreate(Task, "", 200, NULL, 1, NULL);
}

void UsbConnected(void)
{
  CommandEvent.Event = EVENT_QUEUE_RESETTED;
  AnswerEvent.Event = EVENT_QUEUE_RESETTED;

  UsbConnected = 1;
  xQueueReset(MessageQueue);
  xQueueReset(AnswerQueue);

  /* The order here is fundmental */
  xQueueSend(AnswerQueue, &AnswerEvent, 0);
  xQueueSend(MessageQeueue, &CommandEvent, 0);

}


int SendMessage(uint8_t * Data)
{
  CommandQueue_t CommandEvent;

  CommandEvent.Event = EVENT_NEW_MESSAGE;
  memcpy(&CommandEvent, Message, 32);

  if(xQueueSend(MessageQueue, &CommandEvent, 0) == pdTRUE)
    return 0;
  else
    return -1;
}


void UsbDataReceived(uint8_t * Data)
{
  AnswerQueue_t AnswerEvent;
  AnswerEvent.Event = EVENT_NEW_ANSWER;
  memcpy(AnswerEvent.Data, Data, 32);

  xQueueSend(AnswerQueue, &AnswerEvent,0);
}


void Task(void *pvParameters)
{
  CommandQueue_t CommandEvent;
  AnswerQueue_t AnswerEvent;
  CommandCallback_t * Callback;

  while(1)
  {
     xQueueReceive(UsbhStaticData.CommandSenderQueue, &CommandEvent, portMAX_DELAY);

     if(CommandEvent.Event == CommandSenderQueueData_t::EVENT_QUEUE_RESETTED)
     {
       continue;
     }

     /* Low level Usb fucntion used to send Data */
     UsbdSendData(Command.Data);

     xQueueReceive(UsbhStaticData.AnswerReceivdQueue, &AnswerEvent, portMAX_DELAY);

     if(AnswerEvent.Event == AnswerReceivedQueueData_t::EVENT_QUEUE_RESETTED)
     {
         continue;
     }
     else if(AnswerEvent.Event == AnswerReceivedQueueData_t::EVENT_ANSWER_RECEIVED)
     {
       ParseData(AnswerEvent.Data);
     }
  }

}
1

There are 1 answers

0
stathisv On

You could use Queue Sets FreeRTOS Queue Set API That way you could create a Queue Set containing a queue and a semaphore. Your task would block on the Queue Set. It would then get unblocked by either receiving a message from the queue or you incrementing the semaphore.