1
votes

I'm having trouble to implement xQueue in FreeRTOS v8.

The board is based on STM32F4, and I'm trying to send data from ISR (Serial) to main Thread.

The only problem is that not all data is received on the main Thread. I checked the buffer before send and it's complete. On the main Thread, no matter how much chars I send, I always receive the first 5 values of the buffer.

The struct of the buffer (I've tried with a buffer of [10], and the result is the same):

typedef struct SerialBuffer
{
    uint8_t Buffer[100];
} SerialBuffer;

The creation of the queue:

xQueueSerialDataReceived= xQueueCreate( 10, sizeof( SerialBuffer * ) );

On SerialPort Receive Handler:

SerialBuffer SerialBufferRec;

static int8_t CDC_Receive_FS (uint8_t *Buf, uint32_t *Len)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    uint32_t length = *Len -1;
    //Copy the buffer
    for(int i =0;i<100;i++)
    {
        if(i<=length)SerialBufferRec.Buffer[i]=Buf[i];
        else SerialBufferRec.Buffer[i]=0;
    }

xQueueSendFromISR(xQueueSerialDataReceived,(void *)&SerialBufferRec,&xHigherPriorityTaskWoken);

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

    return (USBD_OK);
}

On main Task:

SerialBuffer SerialBufferReceived;


void ReceiveAndSendSerialData()
{

if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
            xQueueReceive(xQueueSerialDataReceived,&SerialBufferReceived,1);
        if(SerialBufferReceived.Buffer[0] != 0)
        {
           ...
        }

}

I've tried send from a Task to other Task and not using ISR, and the result is the same!

EDIT:

Explaining CDC_Receive_FS (uint8_t *Buf, uint32_t *Len) :

If I send from PC the string "abcdefg", *Buf will be:

Buf[0]='a' ... until Buf[6]='g' 

And *Len will be a uint8_t with value 7

So, the for(int i =0;i<100;i++) is there just to make sure that all 100 positions of the SerialBufferRec.Buffer will be overwritten. If its minor than the length of the buffer received, copy the buffer received, otherwise fill with zero. It also helps to empty the last message that was in the array.

The SerialBufferRec just before calling xQueueSendFromISR will be:

SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[6]='g'
SerialBufferRec.Buffer[7]=0
...
SerialBufferRec.Buffer[99]=0

The SerialBufferRecived on receive Task arrives like this ('f' and 'g' missing):

SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[4]='e'
SerialBufferRec.Buffer[5]=0
...
SerialBufferRec.Buffer[99]=0
2
It is not known from what you have provided how CDC_Receive_FS(...) is called, specifically how uint8_t *Buf is created and populated before being passed as an argument.ryyker
Hi ryyker, CDC_Receive_FS() is a event handler of the serial, when I send data from PC to the board, this method is fired. I create a global variable, only use *Buf to feed the SerialBufferRec, that will be sent through Queue. Adding a breakpoint just before xQueueSendFromISR, I can check that SerialBufferRec is complete.Matheus Stumpf
You've created a queue of SerialBuffer pointers. Try changing xQueueCreate( 10, sizeof( SerialBuffer * ) ); to xQueueCreate( 10, sizeof( SerialBuffer) );.tgregory
I still cannot see how the variable passed as an argument is created. I can see you are passing it as a pointer, as is appropriate, but I do not know if it has been created as a char buffer[100]; or as a char *buffer;. And if the second one, has it been initialized with memory? Showing these in your code example is paramount to any ability for another person to provide any useful feedback.ryyker
In addition to @tgregory comment, the first argument in: xQueueSerialDataReceived= xQueueCreate( 10, sizeof( SerialBuffer * ) ); should be 100, should it not? Eg. xQueueSerialDataReceived= xQueueCreate( 100, sizeof( SerialBuffer ) );ryyker

2 Answers

0
votes

...Previous content removed as not relevant in light of new information in original post.

EDIT: based on new content to OP, and depending on how the following function works:

xQueueSendFromISR(xQueueSerialDataReceived,(void *)&SerialBufferRec,&xHigherPriorityTaskWoken);

Based on the example at FreeRTOS regarding usage of xQueueSendFromISR,

it appears that instead of packaging 100 elements into the argument you pass out side of the loop, you should be passing only 1 element per iteration inside the loop:

void vBufferISR( void )
{
char cIn;
BaseType_t xHigherPriorityTaskWoken;

    /* We have not woken a task at the start of the ISR. */
    xHigherPriorityTaskWoken = pdFALSE;

    /* Loop until the buffer is empty. */
    do
    {
        /* Obtain a byte from the buffer. */
        cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );

        /* Post the byte. */
        xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );

    } while( portINPUT_BYTE( BUFFER_COUNT ) );

    /* Now the buffer is empty we can switch context if necessary. */
    if( xHigherPriorityTaskWoken )
    {
        /* Actual macro used here is port specific. */
        taskYIELD_FROM_ISR ();
    }
}

In (an abbreviated depiction of) your code then, this would translate to sending one byte at a time: (Note, I short cut the assignment of Buf[i] into SerialBufferRec.Buffer[i] for brevity)

...
char cIn;
for(int i =0;i<100;i++)
{
      xQueueSendFromISR(xQueueSerialDataReceived,&Buf[i],&xHigherPriorityTaskWoken);

}
0
votes

Well, refactoring the code, now it's working.

I don't know if was some bug, or some thing was missing.

I'm posting here how my code ended, to help someone in future.

Thanks for the effort @tgregory and @ryyker!

Created a shared struct (customEnum.h) :

typedef struct
{
    char Buffer[100];
} SerialBuffer;

In main Task:

xQueueSerialDataReceived= xQueueCreate( 2, sizeof( SerialBuffer) );

if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
{
    xQueueReceive(xQueueSerialDataReceived,&(SerialBufferReceived),1);
    ...
}

In ISR:

static int8_t CDC_Receive_FS (uint8_t *Buf, uint32_t *Len)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    uint32_t length = *Len -1;
    //Copy the buffer
    for(int i =0;i<100;i++)
    {
        if(i<=length)SerialBufferRec.Buffer[i]=Buf[i];
        else SerialBufferRec.Buffer[i]=0;
    }
    xQueueSendFromISR(xQueueSerialDataReceived,(void *)&SerialBufferRec,&xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken)portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

    return (USBD_OK);
}

I sent a char of 99 chars from PC, and received perfectly all of them on main thread.