简体   繁体   中英

STM32 - FreeRTOS xQueue receiving incomplete array

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

...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);

}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM