Send a struct through Queue

xico2004 wrote on Tuesday, August 18, 2015:

Hi there

I am new to this forum and I am already bring some problems that I have been facing while exploring FreeRTOS on my STM32F4:

I want to pass a simple structure from one task to anoder. Actually am I followyig by the book (I think…) what is told on FreeRTOS API suport:
This is shortest example fo what I am doing:

	#define WHEEL_PERIMETER    100
    
    struct ENCODER_Motion 
	{
		float    Current_Speed;
		float    Current_Direction;
	} ENCODER_MOTION_Read;
    
    QueueHandle_t     xQueueENCODER_Readings;
    
    int main (void)
	{
		... Initiate system configuration ...

		xQueueENCODER_Readings  = xQueueCreate( 10  , sizeof( struct ENCODER_Motion* ) );
        
		if ( xQueueENCODER_Readings == 0)
			... flags with a flashing led;
        
        ... creates Task_A and Task_B and lauches scheduler....

		while (1);
    }
    
    void Task:A ( void *pvParameters ) 
	{
        struct		ENCODER_Motion   *ENCODER_MOTION_NOW;
        
        float RPM ;
        float DIRECTION ;
        
        while (1)
        {
            ... Reads Encoder to RPM and DIRECTION ...
        
            ENCODER_MOTION_Read.Current_Speed = RPM * WHEEL_PERIMETER;
            ENCODER_MOTION_Read.Current_Direction = DIRECTION;
        
            if ( xQueueENCODER_Readings != 0 ) 
            {
                ENCODER_MOTION_NOW = &ENCODER_MOTION_Read;	
                xQueueSend ( xQueueENCODER_Readings , (void *) &ENCODER_MOTION_NOW , 
                                         (TickType) 10 );
            }
        
        }
        
    void Task:B ( void *pvParameters ) 
	{
        struct		ENCODER_Motion   *ENCODER_Received;
        float SPEED_RECEIVED;
        float DIRCTION_RECEIVED;
        
        while (1)
        {
            if ( xQueueENCODER_Readings )
                {
                    if ( uxQueueMessagesWaiting ( xQueueENCODER_Readings ) )	
                    {
                        if (xQueueReceive ( xQueueENCODER_Readings , &(ENCODER_Received) , 
                                                        QUEUE_TIME_TO_WAIT_10 ) );
                        {
                            SPEED_RECEIVED = ENCODER_Received->Current_Speed;
                            DIRCTION_RECEIVED = ENCODER_Received->Current_Direction;
                        }

				}
			}
        }

My problems that; passing the struck though the queue or not (not calling the xQueueSend on Task_A) I still receive on TASK_B the information saved at this point of may code

            ENCODER_MOTION_Read.Current_Speed = RPM * WHEEL_PERIMETER;
            ENCODER_MOTION_Read.Current_Direction = DIRECTION;

It seems I am saving the data on a Global variable (structure) and not sending a pointer to a strcuture - one of the 10 that was created when I called xQueueCreat.

I tryed not using pointer on the TASK_B but it receives trash.

Any help will be highly appreciated
Thanks

rtel wrote on Tuesday, August 18, 2015:

In your code you are only creating one structure, then sending pointers to that one structure through the queue. All the pointers will be pointing to the same structure, and reading anything out from the structure will result in whatever value was last written to the structure by any task (all tasks are accessing the same one). Is this correct?

If the structure is small you might be better of sending the structure itself, so each time you send the structure to the queue you send a copy of a strcture, rather than a pointer to a single structure. So the queue would be created as:

// The * has been removed
xQueueENCODER_Readings = xQueueCreate( 10 , sizeof( struct ENCODER_Motion) );

Then Task a can do this to send a copy of its local variable, rather than a pointer to it, to the queue

void Task:A ( void *pvParameters ) 
{
    struct      ENCODER_Motion   ENCODER_MOTION_NOW;
    
    for( ;; )
    {
        ENCODER_MOTION_NOW.Current_Speed = // whatever
        ENCODER_MOTION_NOW.Current_Direction = // whatever
    
        // Send a copy of the queue to the other task.
        xQueueSend ( xQueueENCODER_Readings , 
                    (void *) &ENCODER_MOTION_NOW , 
                    (TickType) 10 );
        
        // Reset of code follows

And Task B can receive a copy into its own local variable, as follows:

void Task:B ( void *pvParameters ) 
{
struct      ENCODER_Motion   ENCODER_Received;

    while (1)
    {
        if (xQueueReceive ( xQueueENCODER_Readings , 
                           &(ENCODER_Received) , 
                           QUEUE_TIME_TO_WAIT_10 ) );
                           
        // Reset of code follows

I think if you wanted to queue pointers, maybe for efficiency reasons, then you would have to have a set of structures available that could be pointed to, so no two tasks point to the same structure at the same time - or perhaps dynamically allocate a structure then send a pointer to the dynamically allocated structure, and have the receiving task free the dynamically allocated structure again after it has received it.

Regards.

xico2004 wrote on Wednesday, August 19, 2015:

Thank you so much for your reply.
Your solution worked perfectly! Thank you again.

Indeed I was intending to use pointers due to efficiency issues. Actually the example above was the smallest and maybe the simpliest situation.

This is the first time I saw this kind of buggy behaviour - accessing globaly to the same structure through those pointers which were indeed pointing precisely to the same strucutre.

In my aplication I need to pass arge structures from TASK_A, TASK_B and TASK_C to TASK_D and this last one will gather all information and process it.

I was trying to use a single Queue, common to all tasks A, B and C, which will be used to send the data to task D.

The strucutre has a sort of task ID to identify from where that information came.

So please confirm if I undertood it right: it is recomended to create several structures associated to each task A, B and C. Then send each pointer through the same queue to task D.

Thank you once again for your important support
Regards

edwards3 wrote on Wednesday, August 19, 2015:

If you want to send pointers to structures then you can create a pool of structures, and tag each structure as available or in use

#define MAX_STRUCTS 5
// The pool of structs
ENCODER_MOTION_Read my_structs[ MAX_STRUCTS ];

// Available flags
bool is_in_use[ MAX_STRUCTS ] = { false };

ENCODER_MOTION_Read *get_struct(void){
int i;

// Find the next free struct
for(i=0; i<MAX_STRUCTS; i++){
    if(is_in_use[i]==false){
	   // Mark struct in use and return it
	   is_in_use[i]=true;
	   return &my_structs[i];
	}
}

// All in use
return NULL;

}

void free_struct(ENCODER_MOTION_Read *to_release){
int i;

// Find the struct being freed
for(i=0; i<MAX_STRUCTS; i++){
    if(to_release==&my_structs[i]){
	   // Mark struct not in use
	   is_in_use[i]=false;
	   return;
	}
}

}

To send a pointer to a structure, call get_struct to get a struct from the pool, fill the struct with data, then send a pointer to the struct to the queue.

At the other end of the queue, read out the data from the struct, then call free_struct() to return the stuct to the pool.

This implementation is very crude but ok if MAX_STRUCTS is small.

richard_damon wrote on Wednesday, August 19, 2015:

If each task is sending the same sort of data, there is no need for multiple queues, and in fact multiple queues can make you life harder. My general rules as each source of asyncronous data should have a single task dedicated to it.

Whether to send a structure or a pointer is largely dependent on how much data is in the structure. Two floats isn’t bad. Sending structures is simpler in that you normally don’t need to deal with life management of the data structures. Passing pointers may be quicker if there is much data in the structure, OR if the structure deals with pointers to itself or other data buffers.

(And when dealing wth pointers to buffers, I find the simplest way to keep track of the free buffers is to put the pointers into another queue, that you take from to get a buffer and put to in order to free a buffer)