天天看点

freertos 源码学习一 ------ xTaskCreate()参数部分解析。

今天开始有决心来学习freertos 的源码,因为我感觉非开始不行了。

        我从freertos 官网下载了源代码(10.1.1)。下载源代码很简单,只需要在搜索网站上输入“freertos”  在下面就出现主页面。点击进去下载就好了。这个就不多说了,这是我的第一篇学习freertos 的文章,所以提了一下上面的话。谢谢大家提出宝贵意见。

今天的主角:xTaskCreate()

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
		        const char * const pcName,		
		        const configSTACK_DEPTH_TYPE usStackDepth,
		        void * const pvParameters,
		        UBaseType_t uxPriority,
		        TaskHandle_t * const pxCreatedTask 
                    )
           

当我看到这个函数的时候,我第一个想法就是: 它的参数是什么意思?有什么作用?

所以我下面的就逐一去找这些参数的定义。

(1)TaskFunction_t   : typedef   void   (*TaskFunction_t)( void * );    很显然是函数指针

(2)const  char  *  const  pcName  :  任务名字

(3)configSTACK_DEPTH_TYPE   :   #define  configSTACK_DEPTH_TYPE   uint16_t   是无符号的2字节数值

(4)void  *  const  pvParameters  :从名字看是要传入的参数

(5)UBaseType_t   uxPriority   :    typedef  unsigned   short   UBaseType_t;  是一个无符号的整形数

(6)TaskHandle_t  *  const   pxCreatedTask :这里面有一个结构体指针。下面列出来

typedef struct tskTaskControlBlock 
{
	volatile StackType_t	*pxTopOfStack;	

	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		
	#endif

	ListItem_t	xStateListItem;	
	ListItem_t	xEventListItem;		
	UBaseType_t	uxPriority;			
	StackType_t	*pxStack;			/*< Points to the start of the stack. */
	char		pcTaskName[ configMAX_TASK_NAME_LEN ];

	#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
		StackType_t	*pxEndOfStack;		
	#endif

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t	uxCriticalNesting;	
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t	uxTCBNumber;		
		UBaseType_t	uxTaskNumber;		
	#endif

	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t	uxBasePriority;		
		UBaseType_t	uxMutexesHeld;
	#endif

	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
		TaskHookFunction_t pxTaskTag;
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
		void  *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
	#endif

	#if( configGENERATE_RUN_TIME_STATS == 1 )
		uint32_t ulRunTimeCounter;	
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
		struct	_reent xNewLib_reent;
	#endif

	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif

	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
		uint8_t	ucStaticallyAllocated; 		
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif

	#if( configUSE_POSIX_ERRNO == 1 )
		int iTaskErrno;
	#endif

} tskTCB;
           

上面的代码,我删除了很多的注释。不删除注释的话,显示的会很乱。看到这个结构体这么大,我感觉肯定很重要。认真去看他的每一个成员。

(1)volatile StackType_t    *pxTopOfStack;   :pxTopOfStack  从名字看,是指向栈的最顶端。

         这个里面还用了一个很重要的关键字:volatile  

(2)xMPU_SETTINGS    xMPUSettings;  xMPU_SETTINGS 是一个结构体,

typedef struct MPU_SETTINGS
{

	xMPU_REGION_REGISTERS xRegion[ portTOTAL_NUM_REGIONS ];

} xMPU_SETTINGS;


typedef struct MPU_REGION_REGISTERS
{

	uint32_t ulRegionBaseAddress;
	uint32_t ulRegionAttribute;

} xMPU_REGION_REGISTERS;

           

     从代码看到xRegion  是一个带有区域基地址和区域属性的数组结构体。那么我又想到了一个问题,这个区域是做什么的啊? 这个问题就暂且保留吧,后面肯定会揭发它。

(3)ListItem_t            xStateListItem;   这个里面我想着重研究明白,ListItem_t 这个结构体。

struct xLIST;
struct xLIST_ITEM
{
	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			
	configLIST_VOLATILE TickType_t xItemValue;			
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;		
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;	
	void * pvOwner;										
	struct xLIST * configLIST_VOLATILE pxContainer;		
	listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE			
};
typedef struct xLIST_ITEM ListItem_t;					
           

<1>就是一层跟着一层的出现未知的内容。listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE   这个宏又是干什么的呢?

宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE用于检查列表项数据是否完整。

<2>configLIST_VOLATILE TickType_t xItemValue;      typedef   uint16_t    TickType_t;  这仅是一个值,但是我还不知道用来做什么?

 xItemValue是列表项值,通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。

<3>struct xLIST_ITEM * configLIST_VOLATILE pxNext;  和   struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  这个就很容易理解了,双向链表的pre 和 next。

<4>void * pvOwner;  指向一个任务TCB。

<5>struct   xLIST  *   configLIST_VOLATILE   pxContainer;

struct xMINI_LIST_ITEM
{
	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			
	configLIST_VOLATILE TickType_t xItemValue;
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

/*
 * Definition of the type of queue used by the scheduler.
 */
typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE	
	volatile UBaseType_t uxNumberOfItems;
	ListItem_t * configLIST_VOLATILE pxIndex;			
	MiniListItem_t xListEnd;							
	listSECOND_LIST_INTEGRITY_CHECK_VALUE				
} List_t;
           

从xLIST 结构体中看到xListEnd 中有MiniListItem 子集,而这个子集不难发现是把双向链表的元素选择出来了。也就是说链表索引时只需要读取这个元素就可以了。

(4)剩下的内容会在遇到的时候,再添加。

到这里参数就分析完了。但这只是刚开始,因为读完这么多参数功能,我们还需要静下心来想一想这个函数的参数到底布下了一张什么样的网?? 有函数指针,有任务名字字符串指针,有整型数,有操作句柄--> 在句柄中有列表项,有双向链表,在结构体中有宏检查数据完整,。。。。。   我们静下心来思考吧,每个人都有自己的体会。