FreeRTOS从入门到摔倒-List

背景交代:

在差不多熟练运用完STM32F103单片机的基本功能之后,比如GPIO口配置、IIC、SPI、PWM波等,想继续深入系统进行学习,当然学习阶段最终的目标是ARM+Linux,为了学习这些轻量级的操作系统,将开发板从F103换成了F429,因为后续想结合GUI一起学习。

去年了解过一些FreeRTOS的API,并走过一些基本的例程,但不入虎穴,焉得虎子,内核不了解有什么意思,所以这段时间的学习从内核入手,可能在学习的过程当中会复习一些《操作系统与编译原理》中的知识,希望自己尽快完成这方面的学习。

Day 1:

在新建工程之后开始了概念学习:

裸机系统:分为轮询系统和前后台系统。轮询系统:只适合哪些按顺序执行的功能代码,实时性差;前后台系统:在轮询系统中加入中断,中断被称之为前台,main函数的无限循环被称之为后台;

多任务系统:就是任务也拥有了优先级,相对前后台系统,能够提升实时性,用言语描述,太麻烦,直接用代码来表现好了:

int flag1 = 0;
int flag2 = 0;
int flag3 = 0;

void DoSomething1(void) 
void DoSomething2(void) 
void DoSomething3(void) 

int main(void)
{
    HardWareInit();  //硬件相关初始化;
    RTOSInit();      //OS初始化;
    RTOSStart()//OS开始启动,多任务调度;
}

void ISR1(void) 
{   
     flag1 = 1;      //置位标志位; 
}

void ISR2(void) 
{   
     flag2 = 2;      //置位标志位; 
}

void ISR3(void) 
{   
     flag3 = 1;      //置位标志位; 
}

void DoSomething1(void) 
{ 
     for (;;) {                        
      if (flag1) {}  // 任务实体 ;  
     }
}

void DoSomething2(void) 
{ 
     for (;;) {                        
      if (flag2) {}  // 任务实体 ;  
     }
}

void DoSomething3(void) 
{ 
     for (;;) {                        
      if (flag3) {}  // 任务实体 ;  
     }
}

用了嵌入式操作系统的好处是:不用精心设计程序执行流,坏处是需要额外的FLASH和RAM;
当然本着学习的心态,暂时搁置优缺点,就是它再不好,现在也得学;

List & List item:
FreeRTOS中与链表相关的操作均在list.h和list.c这两个文件中实现,列表项结构体展开为:

struct xLIST_ITEM
{
    portTickType xItemValue;                  //列表项的值 通常用来进行列表项排序
    volatile struct xLIST_ITEM * pxNext;      //指向下一个列表项 
    volatile struct xLIST_ITEM * pxPrevious;  //指向前一个列表项
    void * pvOwner;                           //指向拥有该列表项的内核对象,通常是即任务控制块
    void * pvContainer;                       //指向该节点所在的列表
}

其中的含义如图所示:
FreeRTOS从入门到摔倒-List_第1张图片
这里其实我一直有个疑问:pvOwner和pvContainer有什么区别:
这里的pvOwner指该链表属于哪个任务,而pvContainer指向该列表项属于哪个列表;

列表结构体展开为:

typedef struct xLIST
{
    volatile unsigned UBaseType_t uxNumberOfItems;   // 列表中链表项的数目
    ListItem_t * pxIndex;                            // 列表项索引指针,用于步寻列表项 
    MiniListItem_t xListEnd;                         // 列表中最后一个列表项
}

列表的作用是用来跟踪任务的。

初始化:vListInitialise () 和 vListInitialiseItem()

void vListInitialise( List_t * const pxList )
{
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );          //pxIndex 指向xListEnd
    pxList->xListEnd.xItemValue = portMAX_DELAY;       //xListEnd 的列表项值初始化为portMAX_DELAY
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );  //初始化xListEnd 的pxPrevious 变量,指向xListEnd 自身;
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );  //同上;
    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;                      //初始化列表项个数为0
}

为了方便理解:
FreeRTOS从入门到摔倒-List_第2张图片

void vListInitialiseItem( ListItem_t * const pxItem )
{
    pxItem->pvContainer = NULL;            //初始化pvContainer 为NULL
}

列表项成员的初始化会在任务创建函数中进行初始化。

列表项的插入:
对列表项值进行升序插入:vListInsert ()

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
    ListItem_t *pxIterator; 
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
    
    if( xValueOfInsertion == portMAX_DELAY )          //如果插入链表项值为允许最大值,则插入列表结尾
    {
        pxIterator = pxList->xListEnd.pxPrevious;
    }
    else
    {
        for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
        {
            //找到插入位置
        }
    }
    // 插入链表项
    pxNewListItem->pxNext = pxIterator->pxNext; 
    pxNewListItem->pxNext->pxPrevious = pxNewListItem; 
    pxNewListItem->pxPrevious = pxIterator; 
    pxIterator->pxNext = pxNewListItem;

    pxNewListItem->pvContainer = ( void * ) pxList;
    
    ( pxList->uxNumberOfItems )++;  //更新链表项值

列表项末尾插入函数:vListInsertEnd ()

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
    ListItem_t * const pxIndex = pxList->pxIndex;

    pxNewListItem->pxNext = pxIndex;    
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;

    mtCOVERAGE_TEST_DELAY(); 
    pxIndex->pxPrevious->pxNext = pxNewListItem; 
    pxIndex->pxPrevious = pxNewListItem;
    
    pxNewListItem->pvContainer = ( void * ) pxList;
    
    ( pxList->uxNumberOfItems )++;
}

列表项删除:uxListRemove ()

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
    List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;       //读取该列表项对应列表
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;     
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;            //和上面一行一起,将要删除的、与下面一行完成列表项的删除,其实就是将要前后两个“连接”在一起

    mtCOVERAGE_TEST_DELAY();
    
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;       
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
    
    pxItemToRemove->pvContainer = NULL;      // 被删除列表项的成员变量 pvContainer清
    ( pxList->uxNumberOfItems )--;           //列表项个数减一

    return pxList->uxNumberOfItems;          //返回新列表当前列表项数目
}

列表项遍历:listGET_OWNER_OF_NEXT_ENTRY ()

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{
    List_t * const pxConstList = ( pxList );
    ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;

    if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )
    {
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
    }
    
    ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;
}

本质是定义在list.h中的宏,每调用一次这个函数,pxIndex变量就会指向下一个列表项,并且返回这个列表项的pxOwner变量。

最后复习一下关于列表和列表项的API:
FreeRTOS从入门到摔倒-List_第3张图片

你可能感兴趣的:(知识点总结)