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