江红之乡

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

列表的插入 初始化

 

 

插入步骤

 

 

 

 

 

 

 

 

  在图3-1中我们看到寄存器xPSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令;寄存器PC被初始化为任务函数指针vTask_A,这样当某次任务切换后,任务A获得CPU控制权,任务函数vTask_A被出栈到PC寄存器,之后会执行任务A的代码;LR寄存器初始化为函数指针prvTaskExitError,这是由移植层提供的一个出错处理函数。当中断发生时,LR被设置成中断要返回的地址,但是每个任务都是一个死循环,正常情况下不应该退出任务函数,所以一旦从任务函数退出,说明那里出错了,这个时候会调用寄存器LR指向的函数来处理这个错误,即prvTaskExitError;根据ATPCS(ARM-Thumb过程调用标准),我们知道子函数调用通过寄存器R0~R3传递参数,在文章的最开始讲xTaskCreate()函数时,提到这个函数有一个空指针类型的参数pvParameters,当任务创建时,它作为一个参数传递给任务,所以这个参数被保存到R0中,用来向任务传递参数。

 

3-1:初始化任务堆栈

vApplicationMallocFailedHook()

在很多场合中,某个硬件资源只有一个,当低优先级任务占用该资源的时候,即便高优先级任务也只能乖乖的等待低优先级任务释放资源。这里高优先级任务无法运行而低优先级任务可以运行的现象称为优先级翻转”。

 

 

 

 

嵌套的中断
在CM3内核以及NVIC的深处,就已经内建了对中断嵌套的全力支持,根本无需使用用汇编写封皮代码(wrapper code)。事实上,我们要做的就只是为每个中断适当地建立优先级,
不用再操心别的。表现在:
第一、 NVIC和CM3处理器会为我们排出优先级解码的顺序。因此,在某个异常正在响
应时,所有优先级不高于它的异常都不能抢占之,而且它自己也不能抢占自己。
第二、 有了自动入栈和出栈,就不用担心在中断发生嵌套时,会使寄存器的数据损毁,
从而可以放心地执行服务例程。
然而,有一件事情却必须更加一丝不苟地处理了,否则有功能紊乱甚至死机的危险,这
就是计算主堆栈容量的最小安全值。我们已经知道,所有服务例程都只使用主堆栈。所以当
中断嵌套加深时,对主堆栈的压力会增大:每嵌套一级,就至少再需要8个字,即32字节的堆栈空间——而且这还没算上ISR对堆栈的额外需求,并且何时嵌套多少级也是不可预料的。如果主堆栈的容量本来就已经所剩无几了,中断嵌套又突然加深,则主堆栈有被用穿的凶险。这就好像已经表现出了高血压危象的时候,情绪又一激动,就容易导致中风一般。在这里,堆栈溢出同样是很致命的,它会使入栈数据与主堆栈前面的数据区发生混迭,使这些数据被破坏;若服务例程又更改了混迭区的数据,则堆栈内容被破坏。这么一来在执行中断返回后,系统极可能功能紊乱,甚至当场被一击必杀——程序跑飞/死机!另一个要注意的,是相同的异常是不允许重入的。因为每个异常都有自己的优先级,并且在异常处理期间,同级或低优先级的异常是要阻塞的,因此对于同一个异常,只有在上次实例的服务例程执行完毕后,方可继续响应新的请求。由此可知,在SVC服务例程中,就不
得再使用SVC指令,否则将fault伺候 

另一个要注意的,是相同的异常是不允许重入的。因为每个异常都有自己的优先级,并
且在异常处理期间,同级或低优先级的异常是要阻塞的,因此对于同一个异常,只有在上次实例的服务例程执行完毕后,方可继续响应新的请求。由此可知,在SVC服务例程中,就不得再使用SVC指令,否则将fault伺候。 

 

 

这个是对于低优先级的中断,高优先级的中断处理时候,有低优先级的进来时,高优先级完成后,不再执行POP指令,而是直接执行中断指令,指令执行完成,再POP节约中断完成时间,

 

 

LR的值被自动更新为特殊的EXC_RETURN 

前面已经讲到,在进入异常服务程序后, LR的值被自动更新为特殊的EXC_RETURN,这是一个高28位全为1的值,只有[3:0]的值有特殊含义,如表9.3所示。当异常服务例程把这个值送往PC时,就会启动处理器的中断返回序列。因为LR的值是由CM3自动设置的,所以只要没有特殊需求 

 

 

 


模式分为两种:处理者模式(handler)和线程模式(thread);

权限级别也分为两种:特权级别、用户级别

两种模式为handler模式和线程(thread)模式,这两种模式是为了区别正在执行代码的类型;handler模式为异常处理例程的代码;线程模式为普通应用程序的代码

 

 

相对延时:总体时间不是一定的 只是延时部分一定,总体时间会根据上下变动(前边函数的执行时间),更具执行的函数来判断

计算唤醒时间点:进入延时函数开始计算延时时间

 

 

绝对延时:保证while()里边的延时是一定的,除了中断打断外,基本是准确的。

一共有两个参数,函数开始时候的时间点和结束时间点,具体的延时函数会变化,

计算唤醒时间点:上一次时间点+延时时间

 

 

 

 

 

 

 


if( xConstTickCount == ( TickType_t ) 0U )// 发生溢出

{

taskSWITCH_DELAYED_LISTS(); 进行交换

}

else

{

mtCOVERAGE_TEST_MARKER();

}

 

 

 

 

中断

 

#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )

 

任务创建

 


任务创建过程:先申请任务堆栈,堆栈申请成功后,给任务控制块申请内存。

1任务堆栈和任务控制块的联系是什么? 先创建一块大的地址(堆栈),然后把新的任务控制块地址指向推栈,即可申请成功

任务控制块:

 

 

任务堆栈:

 

3-1:初始化任务堆栈

创建两个任务时候的流程

( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )=512  start_tast  128*4

( sizeof( TCB_t )=92

( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )=520  空闲任务 130*4

( sizeof( TCB_t )=92

( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )=1040  时间任务 260*4

( sizeof( TCB_t )=92

( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )=512   task1_task  128*4

( sizeof( TCB_t )=92

( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) )=512    task2_task   128*4

( sizeof( TCB_t )=92

 

 

添加到就绪列表函数()

 

初始化后的就绪列表

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ){}

 

for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )

{

vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );

}

 每一个优先级都用一个列表来表示:

vListInitialise( &xDelayedTaskList1 );

vListInitialise( &xDelayedTaskList2 );

vListInitialise( &xPendingReadyList );

 

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;

void * configLIST_VOLATILE pvContainer;

listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE

}

 

 

 

 

void vListInitialise( List_t * const pxList )

{

pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );

pxList->xListEnd.xItemValue = portMAX_DELAY;

pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );

listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );

}

 


时间片调度

时间片调度:在FreeRTOS中,相同优先级的任务,采用时间片调度,第一个任务用100ms  时间用完后,滴答定时器产生中断,回会检查相同优先级下边是否还有任务,如果有,则任务切换;

 

#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 )

#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 )  //其他状态处于运行当中

#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 )

 

void SysTick_Handler(void)

{

    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行

    {

        xPortSysTickHandler();

    }

}

BaseType_t xTaskGetSchedulerState( void )

{

BaseType_t xReturn;

 

if( xSchedulerRunning == pdFALSE )// 调度器没有运行,则不做任何切换,

{

xReturn = taskSCHEDULER_NOT_STARTED;

}

Else//调度器开始运行,则可以做切换,不是很明白这个逻辑

{

if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )

{

xReturn = taskSCHEDULER_RUNNING;

}

else

{

xReturn = taskSCHEDULER_SUSPENDED;

}

}

 

return xReturn;

}

 

FreeRTOS内存部分

 

 

 

 

 

需要加上结构体的大小

 

 


Heap_4

 

通过首地址 找到对其地址;  设置开始链表指向,设置完成后,地址更新为END地址

 

END地址减去结构体地址,

 

 

 

队列部分:

 

pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );

获取的首地址:队列结构体大小+新队列的首地址

 

 

 

 

 

 


相同优先级的内存处理;

任务的切换:滴答定时器:查找延时列表

延时函数,delay_ms(),引起任务切换,delay_us(),不会引起任务切换

 

if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) 被挂起

{

xYieldPending = pdTRUE;//

}

else

{

//执行任务

 

 

}

 

 

注意:同优先级的调用

 

 

 

#define NV_DATA    ((NvData_stu_t *)EEPROM_BASE_ADDR)

定义为一个结构体指针,调用地址即可;

void EEPROM_WriteMultiBytes(uint32_t addr, void *pData, uint16_t len)

EEPROM_WriteMultiBytes((uint32_t)&(NV_DATA->sysParam),pSysParam,sizeof(NvSystemParam_stu_t));

 

 

前导零

 

posted on 2019-02-21 14:09  江红之乡  阅读(1099)  评论(0编辑  收藏  举报