当前位置:   article > 正文

FreeRTOS(三)启动流程_freertos创建任务后main函数还运行吗

freertos创建任务后main函数还运行吗

启动流程

FreeRTOS的主要两种启动流程

  1. 在 main 函数中将硬件初始化,RTOS 系统初始化,所有任务的创建这些都弄好。最后启动 RTOS 的调度器,开始多任务的调度。(先创建,再调度)
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)
    {
        /* 任务实体,必须有阻塞的情况出现 */
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  1. 在 main 函数中将硬件和 RTOS 系统先初始化好,然后创建一个启动任务后就启动调度器,然后在启动任务里面创建各种应用任务,当所有任务都创建成功后,启动任务把自己删除。
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)
    {
        /* 任务实体,必须有阻塞的情况出现 */
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

xTaskCreate函数

任务创建:

​ 在任务创建中,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();
        }
         /*
         省略代码
         ......
         */
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

vTaskStartScheduler函数

开启任务调度器函数:

​ 当创建完了所有任务之后,我们只是把任务添加到了系统之中,还没真正调度,并且空闲任务也没实现,定时器任务也没实现,这些都是在开启调度函数 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

xTimerCreateTimerTask函数

如果在 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

在 Cortex-M3 架构中,FreeRTOS 为了任务启动和任务切换使用了三个异常:SVC、PendSV 和 SysTick

**SVC(系统服务调用,亦简称系统调用):**用于任务启动,有些操作系统不允许应用程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件,它就会产生一个SVC 异常。

**PendSV(可挂起系统调用):**用于完成任务切换,它是可以像普通的中断一样被挂起的,它的最大特性是如果当前有优先级比它高的中断在运行,PendSV 会延迟执行,直到高优先级中断执行完毕,这样子产生的 PendSV 中断就不会打断其他中断的运行。

SysTick: 用于产生系统节拍时钟,提供一个时间片,如果多个任务共享同一个优先级,则每次 SysTick 中断,下一个任务将获得一个时间片。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/677301
推荐阅读
相关标签
  

闽ICP备14008679号