In the following code
SendMessage()
is the API called by the user to send a message over USBUsbDataReceived()
is the function called by another thread when data is received on the USBTask()
is the thread created ininit()
that reads from the queue filled bySendMessage()
and calls the low level usb function to send a messageUsbConnected()
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 threadTask()
is waiting on the queueAnswerQueue
, it has to be unblocked so that it can go back to wait on the queueCommandQueue
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);
}
}
}
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.