个人网站建设报价,重庆装修公司哪家最好,外贸网站推广软件,节点网站目录 1 消息队列的概念和作用
2 应用
2.1功能需求
2.2接口函数API
2.3 功能实现
3 消息队列源码分析
3.1消息队列控制块
3.2消息队列创建
3.3消息队列删除
3.4消息队列在任务中发送
3.5消息队列在中断中发送
3.6消息队列在任务中接收
3.7消息队列在中断中接收 1 消…目录 1 消息队列的概念和作用
2 应用
2.1功能需求
2.2接口函数API
2.3 功能实现
3 消息队列源码分析
3.1消息队列控制块
3.2消息队列创建
3.3消息队列删除
3.4消息队列在任务中发送
3.5消息队列在中断中发送
3.6消息队列在任务中接收
3.7消息队列在中断中接收 1 消息队列的概念和作用
消息队列queue可以在任务与任务间、中断和任务间传递消息实现任务接收来自其他任务或中断的不固定长度的消息。后面的二值信号量、互斥信号量等都是基于消息队列衍生出来的。 队列是什么 解决三个问题
排队无序的问题先入先出有序不可能插队的问题在一端插入高效的问题只要入队都能买到票
FreeRTOS程序中的消息队列
中断和任务不断的发送消息
在固定时间内等待Timeout相当于osdelay挂起消息没有消息的时候把cpu交给其他任务。 “深度”。在队列创建时需要设定其深度和每个单元的大小。
通常情况下队列被作为 FIFO(先进先出)使用即数据由队列尾写入从队列首读出。当然由队列首写人也是有可能的。
向队列写入数据是通过字节拷贝把数据存储到队列中从队列读出数据使得把队列中的数据拷贝删除。
在FreeROTS中操作消息队列控制块只要对头有消息就会取直到去完为止。
2 应用
2.1功能需求
1、使用消息队列检测串口输入2、通过串口发送字符串openled6openled7openled8openled9分别打开板载led6led7led8 led93、通过串口发送字符串closeled6closeled7closeled8closeled9分别关闭板载led6led7led8 led9
2.2接口函数API 一般在调度器开启之前创建 可以发送在队头、队尾一般先入先出有紧急消息可以队头插队常用的是Send和SendtoBack 启动调度器之前是不能调用此函数的因为是在中断中触发的在中断中不能阻塞。第三个参数NULL已经不用了。 第三个参数NULL已经不用了。
2.3 功能实现 CubeMX功能配置
led端口配置、usart1中断配置、创建消息队列 消息队列接收和发送功能开发
串口中断使能
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct {0};if(uartHandle-InstanceUSART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO Configuration PA9 ------ USART1_TXPA10 ------ USART1_RX */GPIO_InitStruct.Pin GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode GPIO_MODE_AF_PP;GPIO_InitStruct.Pull GPIO_PULLUP;GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate GPIO_AF7_USART1;HAL_GPIO_Init(GPIOA, GPIO_InitStruct);/* USART1 interrupt Init */HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspInit 1 *///开启接收中断__HAL_UART_ENABLE_IT(uartHandle,UART_IT_RXNE);/* USER CODE END USART1_MspInit 1 */}
}
串口中断服务函数入队操作
void USART1_IRQHandler(void)
{uint8_t u8Data;//接收中断标志位if(__HAL_UART_GET_FLAG(huart1,UART_FLAG_RXNE) SET){//读取接收寄存器u8Data huart1.Instance-DR;//进行入队操作xQueueSendFromISR(CmdQueueHandle,u8Data,NULL);}HAL_UART_IRQHandler(huart1);
}从消息队列出队一直等待当接收到第一个消息循环从消息队列出队阻塞等待50ms完成消息接收
uint8_t u8CmdBuff[20]; //全局变量void Usart_Task(void const * argument)
{uint8_t u8Index;for(;;){//每次读取消息之前把索引初始化为0u8Index 0;//1、一直等待接收消息第一个消息应该放在消息缓冲区的第一个元素上if(xQueueReceive(CmdQueueHandle,u8CmdBuff[u8Index],portMAX_DELAY)pdPASS){while(xQueueReceive(CmdQueueHandle,u8CmdBuff[u8Index],50)){}u8CmdBuff[u8Index] \0;//保证一包完成字符串信息vParseString(u8CmdBuff);//完成解析以后要清空接收缓冲区memset(u8CmdBuff,0,20);}}
}
消息解析控制功能开发
根据板载LED端口数量循环遍历openledX字符串进行比较如果字符串比较成功打开相关led端口
根据板载LED端口数量循环遍历closeledX字符串进行比较如果字符串比较成功打开相关led端口
uint8_t *OpenString[LED_NUM] {openled6,
openled7,
openled8,
openled9,};uint8_t *CloseString[LED_NUM] {closeled6,
closeled7,
closeled8,
closeled9,};void vParseString(uint8_t *buff){uint8_t i;for(i0;iLED_NUM;i){if(strcmp((char const*)buff,(char const*)OpenString[i]) 0){HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_RESET);printf(Cmd is %s\n,OpenString[i]);return;}}for(i0;iLED_NUM;i){if(strcmp((char const*)buff,(char const*)CloseString[i]) 0){HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_SET);printf(Cmd is %s\n,CloseString[i]);return;}}}
3 消息队列源码分析
3.1消息队列控制块 3.2消息队列创建 //queue.h//队列实现实际是xQueueGenericCreate实现的
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )//队列的创建类型
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) //基本类型
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) //互斥锁
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) //计数信号量
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) //二值信号量
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) //递归互斥锁/*参数uxQueueLength 队列长度uxItemSize 队列项大小ucQueueType 队列类型返回值QueueHandle_t 队列的句柄 其实就是队列控制块地址
*/
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{Queue_t *pxNewQueue;size_t xQueueSizeInBytes;uint8_t *pucQueueStorage;//队列内存空间为空if( uxItemSize ( UBaseType_t ) 0 ){/*队列字节大小赋值为0 */xQueueSizeInBytes ( size_t ) 0;}else{/* 队列字节大小赋值为 长度*每个队列项大小 */xQueueSizeInBytes ( size_t ) ( uxQueueLength * uxItemSize ); }// 申请内存空间 消息队列控制块大小消息队列空间大小pxNewQueue ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) xQueueSizeInBytes );if( pxNewQueue ! NULL ){/* 找到消息队列操作空间的首地址 */pucQueueStorage ( ( uint8_t * ) pxNewQueue ) sizeof( Queue_t );//初始化一个新的消息队列prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );}return pxNewQueue;
}/*参数uxQueueLengthuxItemSizepucQueueStorage队列操作空间的首地址ucQueueType队列类型pxNewQueue队列的句柄
*/
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{( void ) ucQueueType;if( uxItemSize ( UBaseType_t ) 0 )//是否有队列空间{/* 把队列控制块首地址赋值到队列头指针 这是互斥信号使用后面分析*/pxNewQueue-pcHead ( int8_t * ) pxNewQueue;}else{/* 把队列空间的首地址赋值给队列头指针 */pxNewQueue-pcHead ( int8_t * ) pucQueueStorage;}/* 长度、单元大小 */pxNewQueue-uxLength uxQueueLength;pxNewQueue-uxItemSize uxItemSize;//队列重置函数( void ) xQueueGenericReset( pxNewQueue, pdTRUE );}/*参数xQueue 队列句柄xNewQueue 操作队列的状态是什么新建pdTRUE还是已经创建好了返回值*/
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{Queue_t * const pxQueue ( Queue_t * ) xQueue;//进入临界段这时候操作队列控制块不允许打断taskENTER_CRITICAL();{/*1、头地址赋值2、等待处理的消息个数为03、写入指针赋值为队列头指针4、读出指针写入最后一个可用消息5、赋值队列锁位解锁状态*/pxQueue-pcTail pxQueue-pcHead ( pxQueue-uxLength * pxQueue-uxItemSize );pxQueue-uxMessagesWaiting ( UBaseType_t ) 0U;pxQueue-pcWriteTo pxQueue-pcHead;pxQueue-u.pcReadFrom pxQueue-pcHead ( ( pxQueue-uxLength - ( UBaseType_t ) 1U ) * pxQueue-uxItemSize );pxQueue-cRxLock queueUNLOCKED;pxQueue-cTxLock queueUNLOCKED;//判断是否位新建队列if( xNewQueue pdFALSE )//不是新任务{/* 判断发送等待列表里面是否有任务 */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToSend ) ) pdFALSE ){// 移除事件列表中的任务如延时任务if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToSend ) ) ! pdFALSE ){//进行上下文切换queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else//新建队列直接初始化 发送和接收 列表项{/* Ensure the event queues start in the correct state. */vListInitialise( ( pxQueue-xTasksWaitingToSend ) );vListInitialise( ( pxQueue-xTasksWaitingToReceive ) );}}taskEXIT_CRITICAL();/* A value is returned for calling semantic consistency with previousversions. */return pdPASS;
}
3.3消息队列删除
void vQueueDelete( QueueHandle_t xQueue )
{Queue_t * const pxQueue ( Queue_t * ) xQueue;#if ( configQUEUE_REGISTRY_SIZE 0 ){vQueueUnregisterQueue( pxQueue );}#endif#if( ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) ( configSUPPORT_STATIC_ALLOCATION 0 ) ){/* 释放消息队列的内存空间 */vPortFree( pxQueue );}#elif( ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) ( configSUPPORT_STATIC_ALLOCATION 1 ) ){/* 释放消息队列的内存空间 */if( pxQueue-ucStaticallyAllocated ( uint8_t ) pdFALSE ){vPortFree( pxQueue );}else{mtCOVERAGE_TEST_MARKER();}}#else{( void ) pxQueue;}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
3.4消息队列在任务中发送 分支一
队列未满或覆盖入队那么看下是否有接收任务有的话解除一个接收任务退出临界段。 这队列已满不需要等待的话直接退出临界段。如果需要等待初始化超时结构体。最后退出临界段挂起调度器。
分支二
锁定队列判断超时等待是否溢出溢出解锁跌了恢复调度。没有溢出再次判断一下队列是否满没满解锁队列恢复调度器如果满了将任务插入等待发送列表解锁队列恢复调度。 问为什么在进入临界段
因为操作的消息是共享资源可以被多个任务或中断接收和发送那么操作的时候是不希望被别的任务打断的。
问为什么要挂起调度器
不让内核调度不让其他任务打断下面程序的处理。
问为什么要锁定队列
因为消息队列是可以从任务中发送的锁定队列是告诉任务不要打断下面程序的查询队列和插入队列否则整个程序会打乱。
/*queueSEND_TO_BACK ?由于信号量都是有消息队列实现的这个时候操作系统定义了消息队列的类型类型:#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) 1从队尾加入#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) 2从对头加入#define queueOVERWRITE ( ( BaseType_t ) 2 ) 3覆盖入队
*/
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )/*最终实现消息队列发送的是xQueueGenericSend接口参数xQueue 消息队列句柄pvItemToQueue 要发送的消息的地址xTicksToWait 超时时间xCopyPosition 队列操作类型返回值BaseTyp_t*/
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{BaseType_t xEntryTimeSet pdFALSE, xYieldRequired;TimeOut_t xTimeOut;Queue_t * const pxQueue ( Queue_t * ) xQueue;/* 使用for循环的目的为了快速处理消息拷贝消息处理功能*/for( ;; ){//进入临界段taskENTER_CRITICAL();{/* 1、判断消息队列是否满了2、判断是否允许覆盖入队任何一个成立执行入队操作*/if( ( pxQueue-uxMessagesWaiting pxQueue-uxLength ) || ( xCopyPosition queueOVERWRITE ) ){//拷贝数据到队列操作空间内xYieldRequired prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );#if ( configUSE_QUEUE_SETS 1 ){//宏定义不看}#else /* configUSE_QUEUE_SETS */{/* 判断等待接收的列表是否为空. */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){//移除等待接收任务的列表改变等待接收任务的状态为就绪态if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* 操作成功进行上下文切换让任务赶紧处理 */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}//如果在拷贝数据的时候提示需要调度else if( xYieldRequired ! pdFALSE ){/* 再次调度进行上下文切换*/queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_QUEUE_SETS *///退出临界段返回成功taskEXIT_CRITICAL();return pdPASS;}else //如果不允许入队{//是否需要阻塞if( xTicksToWait ( TickType_t ) 0 ){/* 不需要阻塞退出临界段之后返回队列队满 */taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}//超时结构体是否操作过else if( xEntryTimeSet pdFALSE ){/* 超时结构体初始化 */vTaskSetTimeOutState( xTimeOut );xEntryTimeSet pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}//退出临界段taskEXIT_CRITICAL();/* 后面的代码都是允许阻塞处理 *//* 1、挂起了调度器 ----不让其他任务打断2、队列上锁------不让中断打断 因为之前已经退出临界段了*/vTaskSuspendAll();prvLockQueue( pxQueue );/* 判断阻塞时间是否超时了 */if( xTaskCheckForTimeOut( xTimeOut, xTicksToWait ) pdFALSE ){//判断队列是否满if( prvIsQueueFull( pxQueue ) ! pdFALSE ){//队满把当前任务添加到等待发送的事件列表中内部还把任务添加到延时列表中去traceBLOCKING_ON_QUEUE_SEND( pxQueue );vTaskPlaceOnEventList( ( pxQueue-xTasksWaitingToSend ), xTicksToWait );/* 解锁*/prvUnlockQueue( pxQueue );/* 恢复调度器 */if( xTaskResumeAll() pdFALSE ){//进行上下文切换portYIELD_WITHIN_API();}}else{//队列未满 解锁恢复调度器重新进行入队操作/* Try again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}////已经超时了解锁开始调度器 返回队满else{/* The timeout has expired. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}}
}
3.5消息队列在中断中发送 问上锁的目的是什么
如果上锁了在中断中不会处理上面3.5在任务中发送的功能。如果中断能打断那么中断也能操作队列、中断也能操作队列那么优先级会出现混乱。没有上锁那么可以操作。
/*最终调用发送消息接口是xQueueGenericSendFromISR为什么由于信号量都是有消息队列实现的这个时候操作系统定义了消息队列的类型类型#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) 1、从队尾加入#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) 2、从队头加入#define queueOVERWRITE ( ( BaseType_t ) 2 ) 3、覆盖入队
*/
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
/*参数xQueuepvItemToQueueNULLqueueSEND_TO_BACK返回值BaseType_t */
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue ( Queue_t * ) xQueue;/*带返回值的关闭中断需要保存上次关闭中断的状态值恢复时候写入 */uxSavedInterruptStatus portSET_INTERRUPT_MASK_FROM_ISR();{//队满覆盖入队if( ( pxQueue-uxMessagesWaiting pxQueue-uxLength ) || ( xCopyPosition queueOVERWRITE ) ){//获取了队列发送锁的状态值const int8_t cTxLock pxQueue-cTxLock;//拷贝数据到队列操作空间内( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/* 判断队列是否上锁 *//*#define queueUNLOCKED ( ( int8_t ) -1 ) 解锁状态#define queueLOCKED_UNMODIFIED ( ( int8_t ) 0 ) 上锁状态初值*/if( cTxLock queueUNLOCKED ){#if ( configUSE_QUEUE_SETS 1 )#else /* configUSE_QUEUE_SETS */{//恢复等待消息任务中断内没有进行上下文切换会在开启调度器的时候进行if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority so record that acontext switch is required. */if( pxHigherPriorityTaskWoken ! NULL ){*pxHigherPriorityTaskWoken pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_QUEUE_SETS */}else //队列已经上锁{/* 发送锁加一 */pxQueue-cTxLock ( int8_t ) ( cTxLock 1 );}//返回成功xReturn pdPASS;}else{ //返回队满traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );xReturn errQUEUE_FULL;}}//开启中断保存上次状态值portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );return xReturn;
}
//队列上锁
把发送和接受锁都赋值为上锁的初始值
#define prvLockQueue( pxQueue ) \taskENTER_CRITICAL(); \{ \if( ( pxQueue )-cRxLock queueUNLOCKED ) \{ \( pxQueue )-cRxLock queueLOCKED_UNMODIFIED; \} \if( ( pxQueue )-cTxLock queueUNLOCKED ) \{ \( pxQueue )-cTxLock queueLOCKED_UNMODIFIED; \} \} \taskEXIT_CRITICAL()/*队列解锁参数消息队列句柄
*/
static void prvUnlockQueue( Queue_t * const pxQueue )
{/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. *//* 进入临界段 */taskENTER_CRITICAL();{//获取发送锁的状态值int8_t cTxLock pxQueue-cTxLock;/* 遍历解锁 直到解锁为止 */while( cTxLock queueLOCKED_UNMODIFIED ){/* Data was posted while the queue was locked. Are any tasksblocked waiting for data to become available? */#if ( configUSE_QUEUE_SETS 1 )#else /* configUSE_QUEUE_SETS */{/* 解除等待消息任务进行上下文切换 */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority so record thata context switch is required. */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}#endif /* configUSE_QUEUE_SETS *///队列发送锁减一--cTxLock;}//最后解除发送锁pxQueue-cTxLock queueUNLOCKED;}//退出临界段taskEXIT_CRITICAL();/* Do the same for the Rx lock. 接收锁也是一样的 */taskENTER_CRITICAL();{int8_t cRxLock pxQueue-cRxLock;while( cRxLock queueLOCKED_UNMODIFIED ){if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToSend ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToSend ) ) ! pdFALSE ){vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}--cRxLock;}else{break;}}pxQueue-cRxLock queueUNLOCKED;}taskEXIT_CRITICAL();
}
3.6消息队列在任务中接收 分支一
是出队删除还是出队不删除不删除是留个多个任务读取一个消息的情况使用的。
分支二
接收消息为空需要判断是否允许阻塞
/*最终调用发送消息接口是xQueueGenericReceive为什么队列出队有两种模式一种是出队后删除已经读取到队列项或者消息空间另一种是出队后不删除然后恢复出队记录地址让其他任务或者中断继续读取使用类型pdFALSE 删除pdTRUE 不删除*/
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait )
xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )//重点分析这一个其他流程和发送流程差不多//判断是否删除已经接收到的消息空间if( xJustPeeking pdFALSE ){traceQUEUE_RECEIVE( pxQueue );//更新消息等待的记录值让它减一pxQueue-uxMessagesWaiting uxMessagesWaiting - 1;}else{//不删除 就重新赋值未读取消息之前的地址到出队指针pxQueue-u.pcReadFrom pcOriginalReadPosition;}3.7消息队列在中断中接收 源码分析参考3.5中断中发送最后一部分。