赞
踩
int main (void) { /* 硬件初始化 */ HardWare_Init(); /* RTOS 系统初始化 */ RTOS_Init(); /* 创建任务 1,但任务 1 不会执行,因为调度器还没有开启 */ RTOS_TaskCreate(Task1); /* 创建任务 2,但任务 2 不会执行,因为调度器还没有开启 */ RTOS_TaskCreate(Task2); /* ......继续创建各种任务 */ /* 启动 RTOS,开始调度 */ RTOS_Start(); } void Task1( void *arg ) { while (1) { /* 任务实体,必须有阻塞的情况出现 */ } } void Task1( void *arg ) { while (1) { /* 任务实体,必须有阻塞的情况出现 */ } }
int main (void) { /* 硬件初始化 */ HardWare_Init(); /* RTOS 系统初始化 */ RTOS_Init(); /* 创建一个任务 */ RTOS_TaskCreate(AppTaskCreate); /* 启动 RTOS,开始调度 */ RTOS_Start(); } /* 起始任务,在里面创建任务 */ void AppTaskCreate( void *arg ) { /* 创建任务 1,然后执行 */ RTOS_TaskCreate(Task1); /* 当任务 1 阻塞时,继续创建任务 2,然后执行 */ RTOS_TaskCreate(Task2); /* ......继续创建各种任务 */ /* 当任务创建完成,删除起始任务 */ RTOS_TaskDelete(AppTaskCreate); } void Task1( void *arg ) { while (1) { /* 任务实体,必须有阻塞的情况出现 */ } } void Task2( void *arg ) { while (1) { /* 任务实体,必须有阻塞的情况出现 */ } }
任务创建:
在任务创建中,FreeRTOS 会帮我们进行一系列的系统初始化,在创建任务的时候,会帮我们自动进行堆内存的初始化。
/* 任务创建函数 */ BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ) { if ( pxStack != NULL ) { /* 分配任务控制块内存 */ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if ( pxNewTCB != NULL ) { /* 将堆栈位置存储在 TCB 中。*/ pxNewTCB->pxStack = pxStack; } } /* 省略代码 ...... */ } /* 分配内存函数 */ void *pvPortMalloc( size_t xWantedSize ) { BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; void *pvReturn = NULL; vTaskSuspendAll(); { /* 如果这是对 malloc 的第一次调用,那么堆将需要初始化来设置空闲块列表 */ if ( pxEnd == NULL ) { prvHeapInit(); } else { mtCOVERAGE_TEST_MARKER(); } /* 省略代码 ...... */ } }
开启任务调度器函数:
当创建完了所有任务之后,我们只是把任务添加到了系统之中,还没真正调度,并且空闲任务也没实现,定时器任务也没实现,这些都是在开启调度函数 vTaskStartScheduler() 中实现的。需要空闲任务的原因是因为FreeRTOS 一旦启动,就必须要保证系统中每时每刻都有一个任务处于运行态(Runing),并且空闲任务不可以被挂起与删除,空闲任务的优先级是最低的,以便系统中其他任务能随时抢占空闲任务的 CPU 使用权。
void vTaskStartScheduler( void ) { BaseType_t xReturn; /* 添加空闲任务 */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) //使能创建静态任务标志位 { StaticTask_t *pxIdleTaskTCBBuffer = NULL; StackType_t *pxIdleTaskStackBuffer = NULL; uint32_t ulIdleTaskStackSize; /* 空闲任务是使用用户提供的 RAM 创建的 然后利用 RAM 的地址创建空闲任务。这是静态创建任务 */ vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, "IDLE", ulIdleTaskStackSize, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), pxIdleTaskStackBuffer, pxIdleTaskTCBBuffer ); if( xIdleTaskHandle != NULL ){ xReturn = pdPASS;} else{ xReturn = pdFAIL;} } #else //使能创建动态任务标志位 { /* 使用动态分配的 RAM 创建空闲任务 */ xReturn = xTaskCreate( prvIdleTask, "IDLE", configMINIMAL_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); } #endif /* 如果使能了 configUSE_TIMERS 宏定义表明使用定时器,需要创建定时器任务 */ #if ( configUSE_TIMERS == 1 ) { if( xReturn == pdPASS ){ xReturn = xTimerCreateTimerTask();} else{ mtCOVERAGE_TEST_MARKER();} } #endif /* configUSE_TIMERS */ if( xReturn == pdPASS ) { /* 此处关闭中断,以确保中断不会发生在调用 xPortStartScheduler()之前或期间。 创建堆栈的任务包含打开中断的状态,因此当第一个任务时,中断将自动重新启用开始运行。*/ portDISABLE_INTERRUPTS(); #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ xNextTaskUnblockTime = portMAX_DELAY; //表示调度器开始运行了 xSchedulerRunning = pdTRUE; //将xTickCount初始化为0,这是用于记录系统的时间,在节拍定时器中断服务函数中自加 xTickCount = ( TickType_t ) 0U; /* 如果定义了 configGENERATE_RUN_TIME_STATS,则以下内容必须 定义宏以配置用于生成的计时器/计数器运行时计数器时基。目前没启用该宏定义 */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); /* 调用 xPortStartScheduler 函数配置相关硬件如滴答定时器、FPU、pendsv 等 */ if( xPortStartScheduler() != pdFALSE ) //启动系统节拍定时器 { /* 如果 xPortStartScheduler 函数启动成功,则不会运行到这里 */ } else { /* 不会运行到这里,除非调用 xTaskEndScheduler() 函数 */ } } else { /* 只有在内核无法启动时才会到达此行,因为没有足够的堆内存来创建空闲任务或计时器任务。 此处使用了断言,会输出错误信息,方便错误定位 */ configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); } /* 如果 INCLUDE_xTaskGetIdleTaskHandle 设置为 0,则防止编译器警告, 这意味着在其他任何地方都不使用 xIdleTaskHandle。暂时不用理会 */ ( void ) xIdleTaskHandle; }
如果在 FreeRTOSConfig.h 中使能了 configUSE_TIMERS 这个宏定义,那么需要创建一个定时器任务。
BaseType_t xTimerCreateTimerTask( void ) { BaseType_t xReturn = pdFAIL; /* 检查使用了哪些活动计时器的列表,以及用于与计时器服务通信的队列,已经初始化。*/ prvCheckForValidListAndQueue(); if( xTimerQueue != NULL ) { #if( configSUPPORT_STATIC_ALLOCATION == 1 ) //静态创建定时器任务 { StaticTask_t *pxTimerTaskTCBBuffer = NULL; StackType_t *pxTimerTaskStackBuffer = NULL; uint32_t ulTimerTaskStackSize; vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); xTimerTaskHandle = xTaskCreateStatic(prvTimerTask, "Tmr Svc", ulTimerTaskStackSize, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, pxTimerTaskStackBuffer, pxTimerTaskTCBBuffer ); if( xTimerTaskHandle != NULL ) { xReturn = pdPASS; } } #else //动态创建定时器任务 { xReturn = xTaskCreate( prvTimerTask, "Tmr Svc", configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, &xTimerTaskHandle ); } #endif /* configSUPPORT_STATIC_ALLOCATION */ } else { mtCOVERAGE_TEST_MARKER(); } configASSERT( xReturn ); return xReturn; }
在 Cortex-M3 架构中,FreeRTOS 为了任务启动和任务切换使用了三个异常:SVC、PendSV 和 SysTick
**SVC(系统服务调用,亦简称系统调用):**用于任务启动,有些操作系统不允许应用程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件,它就会产生一个SVC 异常。
**PendSV(可挂起系统调用):**用于完成任务切换,它是可以像普通的中断一样被挂起的,它的最大特性是如果当前有优先级比它高的中断在运行,PendSV 会延迟执行,直到高优先级中断执行完毕,这样子产生的 PendSV 中断就不会打断其他中断的运行。
SysTick: 用于产生系统节拍时钟,提供一个时间片,如果多个任务共享同一个优先级,则每次 SysTick 中断,下一个任务将获得一个时间片。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。