freeRTOS最吸引我的地方,就是他的任務間通信、任務間同步所用的概念,全部都建立在“隊列”的基礎之上。
只要抓住隊列的實現,對其他的就比較清晰了。
對任務狀態的管理,建立在“列表”的基礎之上。
Queue_t
queue.h
步驟:
xQueueCreate( uxQueueLength, uxItemSize ) ->
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) ->
prvInitialiseNewQueue ->
xQueueGenericReset
|
+-- vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
+-- vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
本節只有創建隊列、初始化隊列兩部分。參考地址:http://blog.csdn.net/zhzht19861011/article/details/51510384
Queue_t
typedef struct QueueDefinition { int8_t *pcHead; /* 指向隊列存儲區起始位置,即第一個隊列項 */ int8_t *pcTail; /* 指向隊列存儲區結束后的下一個字節 */ int8_t *pcWriteTo; /* 指向下隊列存儲區的下一個空閑位置 */ union /* 使用聯合體用來確保兩個互斥的結構體成員不會同時出現 */ { int8_t *pcReadFrom; /* 當結構體用於隊列時,這個字段指向出隊項目中的最后一個. */ UBaseType_t uxRecursiveCallCount;/* 當結構體用於互斥量時,用作計數器,保存遞歸互斥量被"獲取"的次數. */ } u; List_t xTasksWaitingToSend; /* 因為等待入隊而阻塞的任務列表,按照優先級順序存儲 */ List_t xTasksWaitingToReceive; /* 因為等待隊列項而阻塞的任務列表,按照優先級順序存儲 */ volatile UBaseType_t uxMessagesWaiting;/*< 當前隊列的隊列項數目 */ UBaseType_t uxLength; /* 隊列項的數目 */ UBaseType_t uxItemSize; /* 每個隊列項的大小 */ volatile BaseType_t xRxLock; /* 隊列上鎖后,存儲從隊列收到的列表項數目,如果隊列沒有上鎖,設置為queueUNLOCKED */ volatile BaseType_t xTxLock; /* 隊列上鎖后,存儲發送到隊列的列表項數目,如果隊列沒有上鎖,設置為queueUNLOCKED */ #if ( configUSE_QUEUE_SETS == 1 ) struct QueueDefinition *pxQueueSetContainer; #endif #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxQueueNumber; uint8_t ucQueueType; #endif #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) uint8_t ucStaticAllocationFlags; #endif } xQUEUE; typedef xQUEUE Queue_t;
queue.h
/** * Type by which queues are referenced. 【queue被引用的句柄】 For example, a call to xQueueCreate() * returns an QueueHandle_t variable that can then be used as a parameter to * xQueueSend(), xQueueReceive(), etc. */ typedef void * QueueHandle_t; /* For internal use only. */ #define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) #define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) #define queueOVERWRITE ( ( BaseType_t ) 2 ) /* For internal use only. These definitions *must* match those in queue.c. */ #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 ) /** QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ); * Creates a new queue instance, and returns a handle by which the new queue * can be referenced. * * Internally, within the FreeRTOS implementation, queues use two blocks of * memory. The first block is used to hold the queue's data structures. The * second block is used to hold items placed into the queue.
+ If a queue is * created using xQueueCreate() then both blocks of memory are automatically * dynamically allocated inside the xQueueCreate() function.
+ If a queue is created using * xQueueCreateStatic() then the application writer must provide the memory that * will get used by the queue. xQueueCreateStatic() therefore allows a queue to * be created without using any dynamic memory allocation. * @param uxQueueLength The maximum number of items that the queue can contain. * * @param uxItemSize The number of bytes each item in the queue will require. * Items are queued by copy, not by reference, so this is the number of bytes * that will be copied for each posted item. Each item on the queue must be * the same size. * * @return If the queue is successfully create then a handle to the newly * created queue is returned. If the queue cannot be created then 0 is * returned. * * Example usage: struct AMessage { char ucMessageID; char ucData[ 20 ]; }; void vATask( void *pvParameters ) { QueueHandle_t xQueue1, xQueue2; // Create a queue capable of containing 10 uint32_t values. xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); if( xQueue1 == 0 ) { // Queue was not created and must not be used. } // Create a queue capable of containing 10 pointers to AMessage structures. // These should be passed by pointer as they contain a lot of data. xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); if( xQueue2 == 0 ) { // Queue was not created and must not be used. } } */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define xQueueCreate( uxQueueLength, uxItemSize )
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) #endif /** QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxQueueBuffer ); * @param pucQueueStorageBuffer 【保存隊列存儲區】
If uxItemSize is not zero then * pucQueueStorageBuffer must point to a uint8_t array that is at least large * enough to hold the maximum number of items that can be in the queue at any * one time - which is ( uxQueueLength * uxItemsSize ) bytes.
If uxItemSize is * zero then pucQueueStorageBuffer can be NULL.
* @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which
* will be used to hold the queue's data structure.【保存隊列結構體】
* Example usage: struct AMessage { char ucMessageID; char ucData[ 20 ]; }; #define QUEUE_LENGTH 10 #define ITEM_SIZE sizeof( uint32_t ) // xQueueBuffer will hold the queue structure. StaticQueue_t xQueueBuffer; // ucQueueStorage will hold the items posted to the queue. Must be at least // [(queue length) * ( queue item size)] bytes long. uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ]; void vATask( void *pvParameters ) { QueueHandle_t xQueue1; // Create a queue capable of containing 10 uint32_t values. xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold. ITEM_SIZE // The size of each item in the queue &( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue. &xQueueBuffer ); // The buffer that will hold the queue structure. // The queue is guaranteed to be created successfully as no dynamic memory // allocation is used. Therefore xQueue1 is now a handle to a valid queue. // ... Rest of task code. } */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer )
xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) #endif /* configSUPPORT_STATIC_ALLOCATION */
分析動態創建函數 xQueueGenericCreate()
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) { Queue_t *pxNewQueue; size_t xQueueSizeInBytes; uint8_t *pucQueueStorage; configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); if( uxItemSize == ( UBaseType_t ) 0 ) { /* There is not going to be a queue storage area. */ xQueueSizeInBytes = ( size_t ) 0; } else { /* Allocate enough space to hold the maximum number of items that can be in the queue at any time. */ xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 */ } pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); 【動態創建 隊列存儲區 和 隊列結構體】 if( pxNewQueue != NULL ) { /* Jump past the queue structure to find the location of the queue storage area. */ pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); 【隊列存儲區地址】 #if( configSUPPORT_STATIC_ALLOCATION == 1 ) { /* Queues can be created either statically or dynamically, so note this task was created dynamically in case it is later deleted. 【pdFALSE 標記他是動態創建的】*/ pxNewQueue->ucStaticallyAllocated = pdFALSE; } #endif /* configSUPPORT_STATIC_ALLOCATION */ prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); //<-- } return pxNewQueue; }
插一下
freeRTOSconfig.h 斷言機制 #define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int) #define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
繼續看prvInitaliseNewQueue
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) { /* Remove compiler warnings about unused parameters should configUSE_TRACE_FACILITY not be set to 1. */ ( void ) ucQueueType; if( uxItemSize == ( UBaseType_t ) 0 ) { /* No RAM was allocated for the queue storage area, but PC head cannot be set to NULL because NULL is used as a key to say the queue is used as a mutex. Therefore just set pcHead to point to the queue as a benign(good) value that is known to be within the memory map. */ pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; 【做個標記而已,沒有實際存儲區】 } else { /* Set the head to the start of the queue storage area. */ pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; } /* Initialise the queue members as described where the queue type is defined. */ pxNewQueue->uxLength = uxQueueLength; pxNewQueue->uxItemSize = uxItemSize; ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); //<-- 復位隊列 #if ( configUSE_TRACE_FACILITY == 1 )【略】 { pxNewQueue->ucQueueType = ucQueueType; } #endif /* configUSE_TRACE_FACILITY */ #if( configUSE_QUEUE_SETS == 1 )【略】 { pxNewQueue->pxQueueSetContainer = NULL; } #endif /* configUSE_QUEUE_SETS */ traceQUEUE_CREATE( pxNewQueue ); }
繼續:
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) { Queue_t * const pxQueue = ( Queue_t * ) xQueue; configASSERT( pxQueue ); taskENTER_CRITICAL(); { 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 there are tasks blocked waiting to read from the queue, then the tasks will remain blocked as after this function exits the queue will still be empty.
If there are tasks blocked waiting to write to the queue, then one should be unblocked as after this function exits it will be possible to write to it. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) 如果有任務等待寫入隊列,這個任務可以被Unblock { 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 previous versions. */ return pdPASS; }
## xTaskRemoveFromEventList
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) { TCB_t *pxUnblockedTCB; BaseType_t xReturn; /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be called from a critical section within an ISR. */ /* The event list is sorted in priority order, so the first in the list can be removed as it is known to be the highest priority. Remove the TCB from the delayed list, and add it to the ready list. If an event is for a queue that is locked then this function will never get called - the lock count on the queue will get modified instead. This means exclusive access to the event list is guaranteed here. This function assumes that a check has already been made to ensure that pxEventList is not empty. */ pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) 如果調度器沒有關閉,將任務放入就緒任務列表 { ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); } else 調度器關閉,將任務放入PendingReadyList,不排序的插入 { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); } if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) Unblock的任務優先級比當前的任務高 { /* Return true if the task removed from the event list has a higher priority than the calling task. This allows the calling task to know if it should force a context switch now. */ xReturn = pdTRUE; /* Mark that a yield is pending in case the user is not using the "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ xYieldPending = pdTRUE; } else { xReturn = pdFALSE; } #if( configUSE_TICKLESS_IDLE != 0 ) { /* If a task is blocked on a kernel object then xNextTaskUnblockTime might be set to the blocked task's time out time. If the task is unblocked for a reason other than a timeout xNextTaskUnblockTime is normally left unchanged, because it is automatically reset to a new value when the tick count equals xNextTaskUnblockTime. However if tickless idling is used it might be more important to enter sleep mode at the earliest possible time - so reset xNextTaskUnblockTime here to ensure it is updated at the earliest possible time. */ prvResetNextTaskUnblockTime(); } #endif return xReturn; }
留白