STM32 - FreeRTOS xQueue 接收不完整的数组
STM32 - FreeRTOS xQueue receiving incomplete array
我在 FreeRTOS v8 中实现 xQueue 时遇到问题。
开发板基于 STM32F4,我正在尝试将数据从 ISR(串行)发送到主线程。
唯一的问题是并非所有数据都在主线程上接收到。我在发送前检查了缓冲区,它是完整的。在主线程上,无论我发送多少字符,我总是收到缓冲区的前 5 个值。
缓冲区的结构(我试过[10]的缓冲区,结果是一样的):
typedef struct SerialBuffer
{
uint8_t Buffer[100];
} SerialBuffer;
创建队列:
xQueueSerialDataReceived= xQueueCreate( 10, sizeof( SerialBuffer * ) );
在串行端口接收处理程序上:
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);
}
主线任务:
SerialBuffer SerialBufferReceived;
void ReceiveAndSendSerialData()
{
if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
xQueueReceive(xQueueSerialDataReceived,&SerialBufferReceived,1);
if(SerialBufferReceived.Buffer[0] != 0)
{
...
}
}
我试过从一个任务发送到另一个任务而不使用 ISR,结果是一样的!
编辑:
解释 CDC_Receive_FS (uint8_t *Buf, uint32_t *Len)
:
如果我从 PC 发送字符串 "abcdefg",*Buf
将是:
Buf[0]='a' ... until Buf[6]='g'
并且 *Len
将是一个 uint8_t,值为 7
所以,for(int i =0;i<100;i++)
只是为了确保 SerialBufferRec.Buffer
的所有 100 个位置都将被覆盖。如果它小于接收到的缓冲区的长度,则复制接收到的缓冲区,否则用零填充。它还有助于清空数组中的最后一条消息。
调用 xQueueSendFromISR
之前的 SerialBufferRec
将是:
SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[6]='g'
SerialBufferRec.Buffer[7]=0
...
SerialBufferRec.Buffer[99]=0
SerialBufferRecived
on receive 任务是这样到达的('f' 和 'g' 缺失):
SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[4]='e'
SerialBufferRec.Buffer[5]=0
...
SerialBufferRec.Buffer[99]=0
...根据原始 post 中的新信息,以前的内容因不相关而被删除。
EDIT:基于 OP 的新内容,并取决于以下函数的工作方式:
xQueueSendFromISR(xQueueSerialDataReceived,(void *)&SerialBufferRec,&xHigherPriorityTaskWoken);
基于示例 at FreeRTOS regarding usage of xQueueSendFromISR,
看来,与其将 100 个元素打包到循环外传递的参数中,不如在循环内每次迭代只传递 1 个元素:
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 ();
}
}
然后,在您的代码(的缩写描述)中,这将转化为一次发送一个字节:
(注意,为了简洁起见,我将 Buf[i]
的赋值简化为 SerialBufferRec.Buffer[i]
)
...
char cIn;
for(int i =0;i<100;i++)
{
xQueueSendFromISR(xQueueSerialDataReceived,&Buf[i],&xHigherPriorityTaskWoken);
}
好了,重构代码,现在可以了。
我不知道是不是出了什么问题,还是缺少了什么。
我在这里发布我的代码是如何结束的,以帮助将来的人。
感谢@tgregory 和@ryyker 的努力!
创建了一个共享结构(customEnum.h):
typedef struct
{
char Buffer[100];
} SerialBuffer;
在主任务中:
xQueueSerialDataReceived= xQueueCreate( 2, sizeof( SerialBuffer) );
if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
{
xQueueReceive(xQueueSerialDataReceived,&(SerialBufferReceived),1);
...
}
在 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);
}
我从 PC 发送了一个 99 个字符的字符,并在主线程上完美地接收了所有字符。
我在 FreeRTOS v8 中实现 xQueue 时遇到问题。
开发板基于 STM32F4,我正在尝试将数据从 ISR(串行)发送到主线程。
唯一的问题是并非所有数据都在主线程上接收到。我在发送前检查了缓冲区,它是完整的。在主线程上,无论我发送多少字符,我总是收到缓冲区的前 5 个值。
缓冲区的结构(我试过[10]的缓冲区,结果是一样的):
typedef struct SerialBuffer
{
uint8_t Buffer[100];
} SerialBuffer;
创建队列:
xQueueSerialDataReceived= xQueueCreate( 10, sizeof( SerialBuffer * ) );
在串行端口接收处理程序上:
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);
}
主线任务:
SerialBuffer SerialBufferReceived;
void ReceiveAndSendSerialData()
{
if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
xQueueReceive(xQueueSerialDataReceived,&SerialBufferReceived,1);
if(SerialBufferReceived.Buffer[0] != 0)
{
...
}
}
我试过从一个任务发送到另一个任务而不使用 ISR,结果是一样的!
编辑:
解释 CDC_Receive_FS (uint8_t *Buf, uint32_t *Len)
:
如果我从 PC 发送字符串 "abcdefg",*Buf
将是:
Buf[0]='a' ... until Buf[6]='g'
并且 *Len
将是一个 uint8_t,值为 7
所以,for(int i =0;i<100;i++)
只是为了确保 SerialBufferRec.Buffer
的所有 100 个位置都将被覆盖。如果它小于接收到的缓冲区的长度,则复制接收到的缓冲区,否则用零填充。它还有助于清空数组中的最后一条消息。
调用 xQueueSendFromISR
之前的 SerialBufferRec
将是:
SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[6]='g'
SerialBufferRec.Buffer[7]=0
...
SerialBufferRec.Buffer[99]=0
SerialBufferRecived
on receive 任务是这样到达的('f' 和 'g' 缺失):
SerialBufferRec.Buffer[0]='a'
...
SerialBufferRec.Buffer[4]='e'
SerialBufferRec.Buffer[5]=0
...
SerialBufferRec.Buffer[99]=0
...根据原始 post 中的新信息,以前的内容因不相关而被删除。
EDIT:基于 OP 的新内容,并取决于以下函数的工作方式:
xQueueSendFromISR(xQueueSerialDataReceived,(void *)&SerialBufferRec,&xHigherPriorityTaskWoken);
基于示例 at FreeRTOS regarding usage of xQueueSendFromISR,
看来,与其将 100 个元素打包到循环外传递的参数中,不如在循环内每次迭代只传递 1 个元素:
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 ();
}
}
然后,在您的代码(的缩写描述)中,这将转化为一次发送一个字节:
(注意,为了简洁起见,我将 Buf[i]
的赋值简化为 SerialBufferRec.Buffer[i]
)
...
char cIn;
for(int i =0;i<100;i++)
{
xQueueSendFromISR(xQueueSerialDataReceived,&Buf[i],&xHigherPriorityTaskWoken);
}
好了,重构代码,现在可以了。
我不知道是不是出了什么问题,还是缺少了什么。
我在这里发布我的代码是如何结束的,以帮助将来的人。
感谢@tgregory 和@ryyker 的努力!
创建了一个共享结构(customEnum.h):
typedef struct
{
char Buffer[100];
} SerialBuffer;
在主任务中:
xQueueSerialDataReceived= xQueueCreate( 2, sizeof( SerialBuffer) );
if(uxQueueMessagesWaitingFromISR(xQueueSerialDataReceived)>0)
{
xQueueReceive(xQueueSerialDataReceived,&(SerialBufferReceived),1);
...
}
在 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);
}
我从 PC 发送了一个 99 个字符的字符,并在主线程上完美地接收了所有字符。