FreeRTOS的队列使用拷贝传输,也就是要传输uint32_t时,把4字节的数据拷贝进队列;要传输一个8字节的结构体时,把8字节的数据拷贝进队列。如果要传输1000字节的结构体呢?写队列时拷贝1000字节,读队列时再拷贝1000字节?不建议这么做,影响效率!
这时候,我们要传输的是这个巨大结构体的地址:把它的地址写入队列,对方从队列得到这个地址,使用地址去访问那1000字节的数据。
使用地址来间接传输数据时,这些数据放在RAM里,对于这块RAM,要保证这几点:
RAM的所有者、操作者,必须清晰明了这块内存,就被称为"共享内存"。要确保不能同时修改RAM。比如,在写队列之前只有由发送者修改这块RAM,在读队列之后只能由接收者访问这块RAM。RAM要保持可用这块RAM应该是全局变量,或者是动态分配的内存。对于动然分配的内存,要确保它不能提前释放:要等到接收者用完后再释放。另外,不能是局部变量。创建一个队列,然后创建1个发送任务、1个接收任务:
创建的队列:长度为1,用来传输"char *"指针
发送任务优先级为1,在字符数组中写好数据后,把它的地址写入队列
接收任务优先级为2,读队列得到"char *"值,把它打印出来
这个程序故意设置接收任务的优先级更高,在它访问数组的过程中,接收任务无法执行、无法写这个数组。
main函数中创建了队列、创建了发送任务、接收任务,代码如下:
/* 定义一个字符数组 */
static char pcBuffer[100];
/* vSenderTask被用来创建2个任务,用于写队列
* vReceiverTask被用来创建1个任务,用于读队列
*/
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{
prvSetupHardware();
/* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */
xQueue = xQueueCreate( 1, sizeof(char *) );
发送任务的函数中,现在全局大数组pcBuffer中构造数据,然后把它的地址写入队列,代码如下:
if( xQueue != NULL )
{
/* 创建1个任务用于写队列
* 任务函数会连续执行,构造buffer数据,把buffer地址写入队列
* 优先级为1
*/
xTaskCreate( vSenderTask, "Sender", 1000, NULL, 1, NULL );
/* 创建1个任务用于读队列
* 优先级为2, 高于上面的两个任务
* 这意味着读队列得到buffer地址后,本任务使用buffer时不会被打断
*/
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );
/* 启动调度器 */
vTaskStartScheduler();
}
else
{
/* 无法创建队列 */
}
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
发送任务的函数中,现在全局大数组pcBuffer中构造数据,然后把它的地址写入队列,代码如下:
static void vSenderTask( void *pvParameters )
{
BaseType_t xStatus;
static int cnt = 0;
char *buffer;
/* 无限循环 */
for( ;; )
{
sprintf(pcBuffer, "www.100ask.net Msg %d\r\n", cnt++);
buffer = pcBuffer; // buffer变量等于数组的地址, 下面要把这个地址写入队列
/* 写队列
* xQueue: 写哪个队列
* pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列
* 0: 如果队列满的话, 即刻返回
*/
xStatus = xQueueSendToBack( xQueue, &buffer, 0 ); /* 只需要写入4字节, 无需
写入整个buffer */
if( xStatus != pdPASS )
{
printf( "Could not send to the queue.\r\n" );
}
}
}
接收任务的函数中,读取队列、得到buffer的地址、打印,代码如下;
static void vReceiverTask( void *pvParameters )
{
/* 读取队列时, 用这个变量来存放数据 */
char *buffer;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
BaseType_t xStatus;
/* 无限循环 */
for( ;; )
{
/* 读队列
* xQueue: 读哪个队列
* &xReceivedStructure: 读到的数据复制到这个地址
* xTicksToWait: 没有数据就阻塞一会
*/
xStatus = xQueueReceive( xQueue, &buffer, xTicksToWait); /* 得到buffer地
址,只是4字节 */
if( xStatus == pdPASS )
{
/* 读到了数据 */
printf("Get: %s", buffer);
}
else
{
/* 没读到数据 */
printf( "Could not receive from the queue.\r\n" );
}
}
}