Start process
There are two main startup processes for FreeRTOS
- In the main function, initialize the hardware, RTOS system and create all tasks. Finally, start the RTOS scheduler to start multi task scheduling. (create first, then schedule)
int main (void) { /* Hardware initialization */ HardWare_Init(); /* RTOS System initialization */ RTOS_Init(); /* Create task 1, but task 1 will not be executed because the scheduler has not been turned on */ RTOS_TaskCreate(Task1); /* Create task 2, but task 2 will not be executed because the scheduler has not been turned on */ RTOS_TaskCreate(Task2); /* ......Continue to create various tasks */ /* Start RTOS and start scheduling */ RTOS_Start(); } void Task1( void *arg ) { while (1) { /* The task entity must be blocked */ } } void Task1( void *arg ) { while (1) { /* The task entity must be blocked */ } }
- Initialize the hardware and RTOS system in the main function, and then create a startup task to start the scheduler, and then create various application tasks in the startup task. When all tasks are created successfully, the startup task deletes itself.
int main (void) { /* Hardware initialization */ HardWare_Init(); /* RTOS System initialization */ RTOS_Init(); /* Create a task */ RTOS_TaskCreate(AppTaskCreate); /* Start RTOS and start scheduling */ RTOS_Start(); } /* Start a task and create a task in it */ void AppTaskCreate( void *arg ) { /* Create task 1 and execute it */ RTOS_TaskCreate(Task1); /* When task 1 is blocked, continue to create task 2, and then execute */ RTOS_TaskCreate(Task2); /* ......Continue to create various tasks */ /* When the task is created, delete the starting task */ RTOS_TaskDelete(AppTaskCreate); } void Task1( void *arg ) { while (1) { /* The task entity must be blocked */ } } void Task2( void *arg ) { while (1) { /* The task entity must be blocked */ } }
xTaskCreate function
Task creation:
During task creation, FreeRTOS will help us carry out a series of system initialization. When creating tasks, FreeRTOS will help us automatically initialize heap memory.
/* Task creation function */ 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 ) { /* Allocate task control block memory */ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if ( pxNewTCB != NULL ) { /* Store stack position in TCB.*/ pxNewTCB->pxStack = pxStack; } } /* Omit code ...... */ } /* Allocate memory function */ void *pvPortMalloc( size_t xWantedSize ) { BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; void *pvReturn = NULL; vTaskSuspendAll(); { /* If this is the first call to malloc, the heap will need to be initialized to set the free block list */ if ( pxEnd == NULL ) { prvHeapInit(); } else { mtCOVERAGE_TEST_MARKER(); } /* Omit code ...... */ } }
vTaskStartScheduler function
Enable task scheduler function:
After all the tasks are created, we just add them to the system and haven't really scheduled them, and the idle tasks and timer tasks haven't been implemented. These are implemented in the start scheduling function vTaskStartScheduler(). The reason why idle tasks are needed is that once FreeRTOS is started, it must ensure that a task in the system is running all the time, and idle tasks cannot be suspended or deleted. The priority of idle tasks is the lowest, so that other tasks in the system can seize the CPU use right of idle tasks at any time.
void vTaskStartScheduler( void ) { BaseType_t xReturn; /* Add idle task */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) // Enable to create static task flag bit { StaticTask_t *pxIdleTaskTCBBuffer = NULL; StackType_t *pxIdleTaskStackBuffer = NULL; uint32_t ulIdleTaskStackSize; /* Idle tasks are created using user supplied RAM Then use the address of RAM to create idle tasks. This is a static creation task */ 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 // Enables the creation of dynamic task flag bits { /* Create idle tasks using dynamically allocated RAM */ xReturn = xTaskCreate( prvIdleTask, "IDLE", configMINIMAL_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); } #endif /* If configure is enabled_ The timers macro definition indicates that a timer task needs to be created to use a timer */ #if ( configUSE_TIMERS == 1 ) { if( xReturn == pdPASS ){ xReturn = xTimerCreateTimerTask();} else{ mtCOVERAGE_TEST_MARKER();} } #endif /* configUSE_TIMERS */ if( xReturn == pdPASS ) { /* Turn off interrupts here to ensure that interrupts do not occur before or during the call to xPortStartScheduler(). The task that creates the stack contains the state of opening the interrupt, so when the first task, the interrupt will automatically re enable and start running.*/ portDISABLE_INTERRUPTS(); #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ xNextTaskUnblockTime = portMAX_DELAY; //Indicates that the scheduler is running xSchedulerRunning = pdTRUE; //Initialize xTickCount to 0, which is used to record the time of the system and is added in the beat timer interrupt service function xTickCount = ( TickType_t ) 0U; /* If configgenerate is defined_ RUN_ TIME_ Stats, the following must be Define macros to configure timer / counter runtime timebases for generation. The macro definition is not currently enabled */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); /* Call the xPortStartScheduler function to configure related hardware, such as tick timer, FPU, pendsv, etc */ if( xPortStartScheduler() != pdFALSE ) //Start the system beat timer { /* If the xPortStartScheduler function is started successfully, it will not run here */ } else { /* It will not run here unless the xTaskEndScheduler() function is called */ } } else { /* This line is reached only when the kernel cannot start because there is not enough heap memory to create idle or timer tasks. Assertion is used here, and error information will be output to facilitate error location */ configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); } /* If include_ If xtaskgetidletaskhandle is set to 0, compiler warnings are prevented, This means that xIdleTaskHandle is not used anywhere else. Ignore it for the time being */ ( void ) xIdleTaskHandle; }
xTimerCreateTimerTask function
If in freertosconfig Configure is enabled in H_ Timers is a macro definition, so you need to create a timer task.
BaseType_t xTimerCreateTimerTask( void ) { BaseType_t xReturn = pdFAIL; /* Check that the list of active timers used and the queue used to communicate with the timer service have been initialized.*/ prvCheckForValidListAndQueue(); if( xTimerQueue != NULL ) { #if( configSUPPORT_STATIC_ALLOCATION == 1 ) // Create timer task statically { 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 // Dynamically create timer tasks { 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; }
In the Cortex-M3 architecture, FreeRTOS uses three exceptions for task startup and task switching: SVC, PendSV and SysTick
**SVC (system service call, also referred to as system call): * * used for task startup. Some operating systems do not allow applications to directly access the hardware, but provide some system service functions. The user program uses SVC to send a call request to the system service functions, and calls them in this way to indirectly access the hardware, which will produce an SVC exception.
**PendSV (system call can be suspended): * * used to complete task switching. It can be suspended like an ordinary interrupt. Its biggest feature is that if an interrupt with higher priority is running, PendSV will delay the execution until the execution of the high priority interrupt is completed, so that the generated PendSV interrupt will not interrupt the operation of other interrupts.
SysTick: used to generate the system beat clock and provide a time slice. If multiple tasks share the same priority, each time SysTick is interrupted, the next task will get a time slice.