freeRTOS 队列2 ※※※ 发送消息 ※※※

向队列发送消息

BaseType_t xQueueSend                ( QueueHandle_t xQueue,  const void * pvItemToQueue,  TickType_t xTicksToWait 入队阻塞时间);
BaseType_t xQueueSendToBack    (QueueHandle_t xQueue,  const void* pvItemToQueue,  TickType_t xTicksToWait);
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue,  const void *pvItemToQueue,  TickType_t xTicksToWait);

BaseType_t xQueueOverwrite          (QueueHandle_t xQueue,  const void * pvItemToQueue); 【没有阻塞时间】

他们都用函数 xQueueGenericSend() 来实现。

类似的

xQueueSendFromISR()xQueueSendToBackFromISR()xQueueSendToFrontFromISR() 、

xQueueOverwriteFromISR() 

都通过函数 xQueueGenericSendFromISR() 实现。

下面先分析xQueueGenericSend() :

/*
xCopyPosition: 入队方式,有三种入队方式:
        queueSEND_TO_BACK:  后向入队
        queueSEND_TO_FRONT: 前向入队
        queueOVERWRITE:     覆写入队。
*/            
  1 BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
  2 {
  3 BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
  4 TimeOut_t xTimeOut;
  5 Queue_t * const pxQueue = ( Queue_t * ) xQueue;
  6 
  7     configASSERT( pxQueue );
  8     configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
  9     configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
 10     #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
 11     {
 12         configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
 13     }
 14     #endif
 15 
 16 
 17     /* This function relaxes the coding standard somewhat to allow return
 18     statements within the function itself.  This is done in the interest
 19     of execution time efficiency. 为了时间效率,允许函数本身的返回语句?*/
 20     for( ;; )
 21     {
 22         taskENTER_CRITICAL();
 23         {
 24             /* Is there room on the queue now?  The running task must be the
 25             highest priority task wanting to access the queue.  If the head item
 26             in the queue is to be overwritten then it does not matter if the
 27             queue is full. 队列是否有空间,覆盖写入就不用考虑空间问题了*/
 28             if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )  当前队列里的消息个数 < 队列长度(队列有空间);或者是覆盖写入
 29             {
 30                 traceQUEUE_SEND( pxQueue );  没有操作
 31                 xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );  //<--拷贝数据 见后 ##1
 32 
 33                 #if ( configUSE_QUEUE_SETS == 1 )
 34                 {  忽略队列集相关的 

81 } 82 #else /* configUSE_QUEUE_SETS */ 83 { 84 /* If there was a task waiting for data to arrive on the 85 queue then unblock it now. */ 86 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 如果有任务等待接受,激活这个任务 87 { 88 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) 从事件列表删除,加入就绪列表 或 Pending就绪列表 89 { 90 /* The unblocked task has a priority higher than 91 our own so yield immediately. Yes it is ok to do 92 this from within the critical section - the kernel 93 takes care of that. */ 94 queueYIELD_IF_USING_PREEMPTION(); //置位PendSV,切换任务 95 } 96 else 97 { 98 mtCOVERAGE_TEST_MARKER(); 99 } 100 } 101 else if( xYieldRequired != pdFALSE ) CopyDataToQueue后,由于互斥量的获取 和 互斥量的优先级继承问题,返回true 102 { 107 queueYIELD_IF_USING_PREEMPTION(); 请求PendSV 108 } 109 else 110 { 111 mtCOVERAGE_TEST_MARKER(); 112 } 113 } 114 #endif /* configUSE_QUEUE_SETS */ 115 116 taskEXIT_CRITICAL(); 117 return pdPASS; 118 } 119 else 【队列没有空余空间】 120 { 121 if( xTicksToWait == ( TickType_t ) 0 ) 122 { 123 /* The queue was full and no block time is specified (or 124 the block time has expired) so leave now. */ 125 taskEXIT_CRITICAL(); 126 127 /* Return to the original privilege level before exiting 128 the function. */ 129 traceQUEUE_SEND_FAILED( pxQueue ); 130 return errQUEUE_FULL; //返回 表示队列满了 131 } 132 else if( xEntryTimeSet == pdFALSE ) 133 { 134 /* The queue was full and a block time was specified so 135 configure the timeout structure. */ 136 vTaskSetTimeOutState( &xTimeOut ); //<--初始化时间结构体 137 xEntryTimeSet = pdTRUE; 138 } 139 else 140 { 141 /* Entry time was already set. */ 142 mtCOVERAGE_TEST_MARKER(); 143 } 144 } 145 } 146 taskEXIT_CRITICAL(); 147 148 /* Interrupts and other tasks can send to and receive from the queue 149 now the critical section has been exited. */ >>! 退出临界区,至此,中断 和 其它任务可以向这个队列执行入队(投递)或出队(读取)操作.
>>! 如果阻塞时间不为0,则本任务会因为等待入队而进入阻塞。
>>! 在将任务设置为阻塞的过程中,是不希望有其它任务和中断操作这个队列的事件列表的(WaitingToRcv列表和WaitingToSend列表),

>>! 因为操作队列事件列表可能引起其它任务解除阻塞,这可能会发生优先级翻转。
>>! 比如任务A的优先级低于本任务,但是在本任务进入阻塞的过程中,任务A却因为其它原因解除阻塞了,这显然是要绝对禁止的。
>>! 因此FreeRTOS使用挂起调度器来简单粗暴的禁止其它任务操作队列,
>>! 因为挂起调度器意味着任务不能切换并且不准调用可能引起任务切换的API函数。

>>! 但挂起调度器并不会禁止中断,中断服务函数仍然可以操作队列事件列表,可能会解除任务阻塞、可能会进行上下文切换,这是不允许的。
>>! 于是,解决办法是不但挂起调度器,还要给队列上锁!

操作事件列表的,可以是任务,可以是中断。调度器挂起来禁止任务切换,队列加锁来禁止中断对队列的操作。
151 vTaskSuspendAll(); //调度器上锁 152 prvLockQueue( pxQueue ); //队列上锁,队列的cRxLock和cTxLock锁定。 153 154 /* Update the timeout state to see if it has expired yet. */ 155 if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )【阻塞时间没到】 156 { 157 if( prvIsQueueFull( pxQueue ) != pdFALSE )【队列满】 158 { 159 traceBLOCKING_ON_QUEUE_SEND( pxQueue );
            
            /* 将任务添加到队列的 xTasksWaitingToSend 列表中 和 延时列表中,并且将任务从就绪列表中移除。
            注意!如果阻塞时间是 portMAX_DELAY 并且宏INCLUDE_vTaskSuspend 1 的话,
            函数 vTaskPlaceOnEventList()会将任务添加到列表 xSuspendedTaskList 上。 */
160 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); 函数见后 ##2 161 162 /* Unlocking the queue means queue events can effect the 163 event list. It is possible that interrupts occurring now 164 remove this task from the event list again - but as the 165 scheduler is suspended the task will go onto the pending 166 ready last instead of the actual ready list. */ 167 prvUnlockQueue( pxQueue ); 解除队列锁,如果有任务要解除阻塞,只能移动到挂起就绪列表,因为调度器还没开。 168 169 /* Resuming the scheduler will move tasks from the pending 170 ready list into the ready list - so it is feasible that this 171 task is already in a ready list before it yields - in which 172 case the yield will not cause a context switch unless there 173 is also a higher priority task in the pending ready list. */ 174 if( xTaskResumeAll() == pdFALSE ) 恢复调度器,将任务从挂起就绪列表,移动到就绪列表。 175 {
当任务成功阻塞在等待入队操作后,当前任务就没有必要再占用CPU了,所以接下来解除队列锁、恢复调度器、进行任务切换,下一个处于最高优先级的就绪任务就会被运行了。 176 portYIELD_WITHIN_API(); //portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; 置位PendSV 177 } 178 } 179 else 【队列有空闲了!】 180 { 181 /* Try again. */ 182 prvUnlockQueue( pxQueue ); 183 ( void ) xTaskResumeAll(); 184 } 185 } 186 else 【阻塞时间到了】 187 { 188 /* The timeout has expired. */ 189 prvUnlockQueue( pxQueue ); 解锁的时候,会处理队列上锁期间的入队操作。 190 ( void ) xTaskResumeAll(); 调度器解锁 191 192 traceQUEUE_SEND_FAILED( pxQueue ); 193 return errQUEUE_FULL; // 返回,表示队列满了 194 } 195 }//end of for(;;) 196 }

for(大循环)

临界区

>1  如果队列有空间,或者是覆盖写入

拷贝数据到队列,

@1 如果有任务等待接收信号量:

        从事件列表踢掉这个任务,加入就绪列表 或 Pending就绪列表(函数TaskRemoveFromEventList)。并请求任务切换。

@2 如果没有任务等待接收信号量:

        拷贝的是互斥量话,那么由于优先级继承问题,任务的优先级变了,所以要请求任务切换。

>2  队列没有空间

@1 如果没有入队阻塞时间,直接返回队列已满。

@2 设置了入队阻塞时间,启动一个定时器服务

临界区

调度器上锁

队列上锁

>1 阻塞时间没到

@1 队列还是满的

执行双加函数见##2,这个任务已经阻塞了,解锁队列、调度器,执行任务调度让下个任务去运行吧!

@2 队列有空了,解锁队列、调度器,返回到for大循环重来一套。

>2 阻塞时间到

解锁队列、调度器,返回队列已满错误

for(大循环结束)

##1  prvCopyDataToQueue 见信号量章节。

##2  加入到事件等待列表,加入到延时任务列表。双加。

void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait )
{
    configASSERT( pxEventList );

    /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE
    SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */

    /* Place the event list item of the TCB in the appropriate event list.
    This is placed in the list in priority order so the highest priority task
    is the first to be woken by the event.  The queue that contains the event
    list is locked, preventing simultaneous access from interrupts. */
    vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) );

    prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
}
与这个函数操作相反的是,xTaskRemoveFromEventList

将当前任务从就绪列表中移除,并根据当前系统节拍计数器值计算唤醒时间,然后将任务加入延时列表。

函数 prvAddCurrentTaskToDelayedList 时间管理

 xQueueGenericSendFromISR

  1 BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
  2 {
  3 BaseType_t xReturn;
  4 UBaseType_t uxSavedInterruptStatus;
  5 Queue_t * const pxQueue = ( Queue_t * ) xQueue;
  6 
  7     configASSERT( pxQueue );
  8     configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
  9     configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
 10 
 11     /* RTOS ports that support interrupt nesting have the concept of a maximum
 12     system call (or maximum API call) interrupt priority.  Interrupts that are
 13     above the maximum system call priority are kept permanently enabled, even
 14     when the RTOS kernel is in a critical section, but cannot make any calls to
 15     FreeRTOS API functions.  If configASSERT() is defined in FreeRTOSConfig.h
 16     then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
 17     failure if a FreeRTOS API function is called from an interrupt that has been
 18     assigned a priority above the configured maximum system call priority.
 19     Only FreeRTOS functions that end in FromISR can be called from interrupts
 20     that have been assigned a priority at or (logically) below the maximum
 21     system call    interrupt priority.  FreeRTOS maintains a separate interrupt
 22     safe API to ensure interrupt entry is as fast and as simple as possible.
 23     More information (albeit Cortex-M specific) is provided on the following
 24     link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */
 25     portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
 26 
 27     /* Similar to xQueueGenericSend, except without blocking if there is no room
 28     in the queue.  Also don't directly wake a task that was blocked on a queue
 29     read, instead return a flag to say whether a context switch is required or
 30     not (i.e. has a task with a higher priority than us been woken by this
 31     post). */
 32     uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();  //记录BasePRI的值,并屏蔽所有系统可管理的中断
 33     {
 34         if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
 35         {
 36             const int8_t cTxLock = pxQueue->cTxLock;  //读取cTxlock,用于判断是否上锁
 37 
 38             traceQUEUE_SEND_FROM_ISR( pxQueue );
 39 
 40             /* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a
 41             semaphore or mutex.  That means prvCopyDataToQueue() cannot result
 42             in a task disinheriting a priority and prvCopyDataToQueue() can be
 43             called here even though the disinherit function does not check if
 44             the scheduler is suspended before accessing the ready lists. */
 45             ( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );  数据拷贝,见信号量章节。
 46 
 47             /* The event list 事件列表 is not altered if the queue is locked.  This will
 48             be done when the queue is unlocked later. */
 49             if( cTxLock == queueUNLOCKED )  【没上锁】
 50             {
 51                 #if ( configUSE_QUEUE_SETS == 1 )
 52                 {101                 }
102                 #else /* configUSE_QUEUE_SETS */
103                 {
104                     if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )  【有任务在等待这个消息】
105                     {
106                         if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )  
107                         {
108                             /* The task waiting has a higher priority so record that a
109                             context    switch is required. */
110                             if( pxHigherPriorityTaskWoken != NULL )
111                             {
112                                 *pxHigherPriorityTaskWoken = pdTRUE;  置位这个标记变量,进行任务切换
113                             }
114                             else
115                             {
116                                 mtCOVERAGE_TEST_MARKER();
117                             }
118                         }
119                         else
120                         {
121                             mtCOVERAGE_TEST_MARKER();
122                         }
123                     }
124                     else
125                     {
126                         mtCOVERAGE_TEST_MARKER();
127                     }
128                 }
129                 #endif /* configUSE_QUEUE_SETS */
130             }
131             else  【如果队列上锁的话,将队列成员cTxLock+1,表示进行了一次入队操作】
132             {
133                 /* Increment the lock count so the task that unlocks the queue
134                 knows that data was posted while it was locked. */
135                 pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
136             }
137 
138             xReturn = pdPASS; 【入队完成】
139         }
140         else
141         {
142             traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
143             xReturn = errQUEUE_FULL;  【队列满,直接返回】
144         }
145     }
146     portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
147 
148     return xReturn;
149 }
150 /*-----------------------------------------------------------*/

因为没有阻塞的处理,所以代码简单了很多。

留白

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值