freertos任务调度器

任务调度器初始化过程:

1.创建空闲任务
/* The Idle task is being created using dynamically allocated RAM. */
xReturn = xTaskCreate( prvIdleTask,
“IDLE”, configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

2.如果使能软件定时器,创建定时器任务

#if ( configUSE_TIMERS == 1 )
{
	if( xReturn == pdPASS )
	{
		xReturn = xTimerCreateTimerTask();
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

3.关闭中断

4.初始化任务调度器相关变量:
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) 0U;

调用:xPortStartScheduler() ;
5.1设置PENDSV,SYSTICK中断优先级:
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

5,.2.设置一个系统定时器作为os的滴答节拍:

  • Setup the SysTick timer to generate the tick interrupts at the required

  • frequency.
    */
    #if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0

    void vPortSetupTimerInterrupt( void )
    {
    /* Calculate the constants required to configure the tick interrupt. */

     portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
     portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
    

    }

/-----------------------------------------------------------/

freertos任务调度器_第1张图片

port.c:

/* Constants required to manipulate the core. Registers first… /
#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) )
#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
/
…then bits in the registers. */
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )

5.3如果有FPU,使能FPU,开启惰性压栈

6.开始第一个任务:__asm void prvStartFirstTask( void )

  1. SVC 0;

freertos任务调度器_第2张图片

开始第一个任务:

__asm void prvStartFirstTask( void )
{
PRESERVE8

/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08    //向量表偏移量寄存器的地址
ldr r0, [r0]
ldr r0, [r0]
/* Set the msp back to the start of the stack. */
msr msp, r0   //MSR 存储通用寄存器的值到特殊功能寄存器   主堆栈指针(MSP),
/* Globally enable interrupts. */
cpsie i
cpsie f
dsb
isb
/* Call SVC to start the first task. */
svc 0
nop
nop

}
CPSID I ;PRIMASK=1, ;关中断
CPSIE I ;PRIMASK=0, ;开中断
CPSID F ;FAULTMASK=1, ;关异常
CPSIE F ;FAULTMASK=0 ;开异常

PRIMASK 这是个只有 1 个位的寄存器。当它置 1 时,就关掉所有可屏蔽的异常,只剩下 NMI
和硬 fault 可以响应。它的缺省值是 0,表示没有关中断。
FAULTMASK 这是个只有 1 个位的寄存器。当它置 1 时,只有 NMI 才能响应,所有其它的异常,
包括中断和 fault,通通闭嘴。它的缺省值也是 0,表示没有关异常。

对于时间‐关键任务而言,PRIMASK 和 BASEPRI 对于暂时关闭中断是非常重要的。而
FAULTMASK 则可以被 OS 用于暂时关闭 fault 处理机能,这种处理在某个任务崩溃时可能需
要。因为在任务崩溃时,常常伴随着一大堆 faults。在系统料理“后事”时,通常不再需要
响应这些 fault——人死帐清。总之 FAULTMASK 就是专门留给 OS 用的。
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10
2)|(3UL << 112)); / set CP10 and CP11 Full Access /
#endif
/
Reset the RCC clock configuration to the default reset state ------------/
/
Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;

/* Reset CFGR register */
RCC->CFGR = 0x00000000;

/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;

/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;

/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;

/* Disable all interrupts */
RCC->CIR = 0x00000000;

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

/* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------*/
SetSysClock();

/* Configure the Vector Table location add offset address ------------------/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /
Vector Table Relocation in Internal SRAM /
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /
Vector Table Relocation in Internal FLASH */
#endif
}

__asm void vPortSVCHandler( void )
{
PRESERVE8

/* Get the location of the current TCB. */
ldr	r3, =pxCurrentTCB//r3=任务控制块地址

ldr r1, [r3]//r3地址的值(栈顶指针)赋给r1
ldr r0, [r1]//
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14}//将
msr psp, r0
isb
mov r0, #0
msr	basepri, r0
bx r14

}

ldr r3, =pxCurrentTCB//r3=任务控制块地址
freertos任务调度器_第3张图片

ldr r1, [r3]//r3地址的值(栈顶指针)赋给r1
freertos任务调度器_第4张图片

ldr r0, [r1]//r1地址的值(栈顶指针)赋给r0
freertos任务调度器_第5张图片

ldmia r0!, {r4-r11, r14} 将r0指向的单元赋值给r4 r0++,将r0指向的单元赋值给r5,r6~r11 r14。

在这里插入图片描述

msr psp, r0// r0赋给psp

进程堆栈指针(PSP):由用户的应用程序代码使用
freertos任务调度器_第6张图片
freertos任务调度器_第7张图片

bx r14
跳转???

freertos任务调度器_第8张图片
freertos任务调度器_第9张图片

freertos任务调度器_第10张图片

typedef struct tskTaskControlBlock

{
volatile StackType_t pxTopOfStack; /< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */

#if ( portUSING_MPU_WRAPPERS == 1 )
	xMPU_SETTINGS	xMPUSettings;		/*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif

ListItem_t			xStateListItem;	/*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
ListItem_t			xEventListItem;		/*< Used to reference a task from an event list. */
UBaseType_t			uxPriority;			/*< The priority of the task.  0 is the lowest priority. */
StackType_t			*pxStack;			/*< Points to the start of the stack. */
char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

#if ( portSTACK_GROWTH > 0 )
	StackType_t		*pxEndOfStack;		/*< Points to the end of the stack on architectures where the stack grows up from low memory. */
#endif

#if ( portCRITICAL_NESTING_IN_TCB == 1 )
	UBaseType_t		uxCriticalNesting;	/*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
#endif

#if ( configUSE_TRACE_FACILITY == 1 )
	UBaseType_t		uxTCBNumber;		/*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
	UBaseType_t		uxTaskNumber;		/*< Stores a number specifically for use by third party trace code. */
#endif

#if ( configUSE_MUTEXES == 1 )
	UBaseType_t		uxBasePriority;		/*< The priority last assigned to the task - used by the priority inheritance mechanism. */
	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;	/*< Stores the amount of time the task has spent in the Running state. */
#endif

#if ( configUSE_NEWLIB_REENTRANT == 1 )
	/* Allocate a Newlib reent structure that is specific to this task.
	Note Newlib support has been included by popular demand, but is not
	used by the FreeRTOS maintainers themselves.  FreeRTOS is not
	responsible for resulting newlib operation.  User must be familiar with
	newlib and must provide system-wide implementations of the necessary
	stubs. Be warned that (at the time of writing) the current newlib design
	implements a system-wide malloc() that must be provided with locks. */
	struct	_reent xNewLib_reent;
#endif

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

/* See the comments above the definition of
tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
	uint8_t	ucStaticallyAllocated; 		/*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif

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

} tskTCB;

/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;

LR (R14)
freertos任务调度器_第11张图片

freertos任务调度器_第12张图片

连接寄存器 R14
R14 是连接寄存器(LR)。在一个汇编程序中,你可以把它写作 both LR 和 R14。LR 用于
在调用子程序时存储返回地址。例如,当你在使用 BL(分支并连接,Branch and Link)指令时,
就自动填充 LR 的值。
main ;主程序

BL function1 ; 使用“分支并连接”指令呼叫 function1
; PC= function1,并且 LR=main 的下一条指令地址

Function1
… ; function1 的代码
BX LR ; 函数返回(如果 function1 要使用 LR,必须在使用前 PUSH,
; 否则返回时程序就可能跑飞了——译注)
尽管 PC 的 LSB 总是 0(因为代码至少是字对齐的),LR 的 LSB 却是可读可写的。这是历
史遗留的产物。在以前,由位 0 来指示 ARM/Thumb 状态。因为其它有些 ARM 处理器支持
ARM 和 Thumb 状态并存,为了方便汇编程序移植,CM3 需要允许 LSB 可读可写。
程序计数器 R15
R15 是程序计数器,在汇编代码中你也可以使用名字“PC”来访问它。因为 CM3 内部
使用了指令流水线,读 PC 时返回的值是当前指令的地址+4。比如说:
0x1000: MOV R0, PC ; R0 = 0x1004
如果向 PC 中写数据,就会引起一次程序的分支(但是不更新 LR 寄存器)。CM3 中的指
令至少是半字对齐的,所以 PC 的 LSB 总是读回 0。然而,在分支时,无论是直接写 PC 的值
还是使用分支指令,都必须保证加载到 PC 的数值是奇数(即 LSB=1),用以表明这是在
Thumb 状态下执行。倘若写了 0,则视为企图转入 ARM 模式,CM3 将产生一个 fault 异
常。

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void pvParameters )
{
/
Simulate the stack frame as it would be created by a context switch
interrupt. */

/* Offset added to account for the way the MCU uses the stack on entry/exit
of interrupts, and to ensure alignment. */
pxTopOfStack--;

*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

/* Save code space by skipping register initialisation. */
pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

/* A save method is being used that requires each task to maintain its
own exec return value. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXEC_RETURN;

pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

return pxTopOfStack;

}

BASEPRI 这个寄存器最多有 9 位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈
值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号
越大,优先级越低)。但若被设成 0,则不关闭任何中断,0 也是缺省值。

你可能感兴趣的:(freertos任务调度器)