freeRTOS 新建任务

MDK移植freeRTOS及多任务创建 MDK移植freeRTOS及多任务创建准备freeROTS移植及多任务的创建 准备 去freeRTOS官网下载源码,下载后解压。 在电脑中新建一个freeRTOS文件夹,将之前解压文件夹中的 FreeROTS\Source\ 目录下的所有文件复制到新建文件夹中 将portable/RVDS中的CM3复制到keil文件夹中 将解压的源码文件夹中的FreeRTOS\Demo\CORTEX_stm32f103_keil中的FreeRTOSConfig.h复制到自己新建的文件夹中 freeROT 阅读详情
xTaskCreate创建任务
|
+----prvInitialiseNewTask 初始化任务TCB
|    |
|    +----pxPortInitialiseStack初始化任务堆栈
|
+----prvAddNewTaskToReadyList 把新任务加入到任务就绪列表

最后引用了一篇文章,里面介绍了freeRTOS里的全局变量的含义。

任务控制块TCB

 1 typedef struct tskTaskControlBlock
 2 {
 3     volatile StackType_t *pxTopOfStack; //任务堆栈栈顶
 4     #if ( portUSING_MPU_WRAPPERS == 1 )
 5     xMPU_SETTINGSxMPUSettings; //MPU 相关设置
 6     #endif
 7     ListItem_t xStateListItem; //状态列表项
 8     ListItem_t xEventListItem; //事件列表项
 9     UBaseType_t uxPriority; //任务优先级
10     StackType_t *pxStack;   //任务堆栈起始地址
11     char pcTaskName[ configMAX_TASK_NAME_LEN ];//任务名字
12 #if ( portSTACK_GROWTH > 0 ) 13 StackType_t *pxEndOfStack; //任务堆栈栈底 14 #endif
15 #if ( portCRITICAL_NESTING_IN_TCB == 1 ) 16 UBaseType_t uxCriticalNesting; //临界区嵌套深度 17 #endif 18
#if ( configUSE_TRACE_FACILITY == 1 ) //trace 或 debug 的时候用到 19 UBaseType_t uxTCBNumber; 20 UBaseType_t uxTaskNumber; configUSE_TRACE_FACILITY定义时,表示freertos开启了trace功能,支持trace task states,系统运行信息,每个任务的运行时间。uxTCBNumber协助进行信息打印。 21 #endif 22
#if ( configUSE_MUTEXES == 1 ) 23 UBaseType_t uxBasePriority; //任务基础优先级,优先级反转之前的优先级 24 UBaseType_t uxMutexesHeld; //任务获取到的互斥信号量个数 25 #endif
26 #if ( configUSE_APPLICATION_TASK_TAG == 1 ) //任务标签 27 TaskHookFunction_t pxTaskTag; 28 #endif 29 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) //与本地存储有关 30 void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; 32 #endif
33 #if( configGENERATE_RUN_TIME_STATS == 1 ) 34 uint32_t ulRunTimeCounter; //用来记录任务运行总时间,可以得到CPU占用率。 35 #endif
36 #if ( configUSE_NEWLIB_REENTRANT == 1 ) 37 struct _reent xNewLib_reent; //定义一个 newlib 结构体变量,可重入性方面的东东。。 38 #endif 39 #if( configUSE_TASK_NOTIFICATIONS == 1 )//任务通知相关变量 40 volatile uint32_t ulNotifiedValue; //任务通知值 41 volatile uint8_t ucNotifyState; //任务通知状态 42 #endif 43 #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 44 //用来标记任务是动态创建的还是静态创建的,如果是静态创建的此变量就为 pdTURE, 45 //如果是动态创建的就为 pdFALSE 46 uint8_t ucStaticallyAllocated; 47 #endif 48 #if( INCLUDE_xTaskAbortDelay == 1 ) 49 uint8_t ucDelayAborted; 50 #endif 51 } tskTCB; 52 //新版本的 FreeRTOS 任务控制块重命名为 TCB_t,但是本质上还是 tskTCB,主要是为了兼容 53 //旧版本的应用。 54 typedef tskTCB TCB_t;

任务创建函数

 1     BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
 2                             const char * const pcName,
 3                             const uint16_t usStackDepth,
 4                             void * const pvParameters,
 5                             UBaseType_t uxPriority,
 6                             TaskHandle_t * const pxCreatedTask ) 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */ 7 { 8 TCB_t *pxNewTCB; 9 BaseType_t xReturn; 10 11 /* If the stack grows down then allocate the stack then the TCB so the stack 12 does not grow into the TCB. Likewise if the stack grows up then allocate 13 the TCB then the stack. 向上增长堆栈*/ 14 #if( portSTACK_GROWTH > 0 ) 15 { 16 /* Allocate space for the TCB. Where the memory comes from depends on 17 the implementation of the port malloc function and whether or not static 18 allocation is being used. */ 19 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 20 21 if( pxNewTCB != NULL ) 22 { 23 /* Allocate space for the stack used by the task being created. 24 The base of the stack memory stored in the TCB so the task can 25 be deleted later if required. */ 26 pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
/*lint !e961 MISRA exception as the casts are only redundant for some ports. */ 27 28 if( pxNewTCB->pxStack == NULL ) 29 { 30 /* Could not allocate the stack. Delete the allocated TCB. */ 31 vPortFree( pxNewTCB ); 32 pxNewTCB = NULL; 33 } 34 } 35 } 36 #else /* portSTACK_GROWTH 只关注下边部分*/ 37 { 38 StackType_t *pxStack; //uint32_t 39 40 /* Allocate space for the stack used by the task being created. 申请堆栈空间*/ 41 pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
/*lint !e961 MISRA exception as the casts are only redundant for some ports. */ 42 43 if( pxStack != NULL ) 44 { 45 /* Allocate space for the TCB. 申请任务控制块空间*/ 46 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
/*lint !e961 MISRA exception as the casts are only redundant for some paths. */ 47 48 if( pxNewTCB != NULL ) 49 { 50 /* Store the stack location in the TCB. */ 51 pxNewTCB->pxStack = pxStack; 52 } 53 else 54 { 55 /* The stack cannot be used as the TCB was not created. Free 56 it again. */ 57 vPortFree( pxStack ); 58 } 59 } 60 else 61 { 62 pxNewTCB = NULL; 63 } 64 } 65 #endif /* portSTACK_GROWTH */ 66 67 if( pxNewTCB != NULL ) 68 { 69 #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 70 { 71 /* Tasks can be created statically or dynamically, so note this 72 task was created dynamically in case it is later deleted. 标记任务是动态创建的*/ 73 pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; 74 } 75 #endif /* configSUPPORT_STATIC_ALLOCATION */ 76 77 prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); 78 prvAddNewTaskToReadyList( pxNewTCB ); 79 xReturn = pdPASS; 80 } 81 else 82 { 83 xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 84 } 85 86 return xReturn; 87 }

一、初始化新任务的TCB:

  1 static void prvInitialiseNewTask(   TaskFunction_t pxTaskCode,
  2                                     const char * const pcName,
  3                                     const uint32_t ulStackDepth,
  4                                     void * const pvParameters,
  5                                     UBaseType_t uxPriority,
  6                                     TaskHandle_t * const pxCreatedTask,
  7                                     TCB_t *pxNewTCB,
  8                                     const MemoryRegion_t * const xRegions ) 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */ 9 { 10 StackType_t *pxTopOfStack; 11 UBaseType_t x; 12 13 #if( portUSING_MPU_WRAPPERS == 1 ) 【略】 14 /* Should the task be created in privileged mode? */ 15 BaseType_t xRunPrivileged; 16 if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) 17 { 18 xRunPrivileged = pdTRUE; 19 } 20 else 21 { 22 xRunPrivileged = pdFALSE; 23 } 24 uxPriority &= ~portPRIVILEGE_BIT; 25 #endif /* portUSING_MPU_WRAPPERS == 1 */ 26 27 /* Avoid dependency on memset() if it is not required. 堆栈溢出检测、追踪功能打开时,使用已知值填充stack*/ 28 #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) 29 { 30 /* Fill the stack with a known value to assist debugging. */ 31 ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); 32 } 33 #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ 34 35 /* Calculate the top of stack address. This depends on whether the stack 36 grows from high memory to low (as per the 80x86) or vice versa. 37 portSTACK_GROWTH is used to make the result positive or negative as required 38 by the port. */ 39 #if( portSTACK_GROWTH < 0 ) 【略】 40 { 41 pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); 42 pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 */ 43 44 /* Check the alignment of the calculated top of stack is correct. */ 45 configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); 46 } 47 #else /* portSTACK_GROWTH */ 48 { 49 pxTopOfStack = pxNewTCB->pxStack; 50 51 /* Check the alignment of the stack buffer is correct. */ 52 configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); 53 54 /* The other extreme of the stack space is required if stack checking is 55 performed. */ 56 pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); //计算栈顶地址 57 } 58 #endif /* portSTACK_GROWTH */ 59 60 /* Store the task name in the TCB. 拷贝任务名字*/ 61 for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) 62 { 63 pxNewTCB->pcTaskName[ x ] = pcName[ x ]; 64 65 /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than 66 configMAX_TASK_NAME_LEN characters just in case the memory after the 67 string is not accessible (extremely unlikely). */ 68 if( pcName[ x ] == 0x00 ) 69 { 70 break; 71 } 72 else 73 { 74 mtCOVERAGE_TEST_MARKER(); 75 } 76 } 77 78 /* Ensure the name string is terminated in the case that the string length 79 was greater or equal to configMAX_TASK_NAME_LEN. */ 80 pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; 81 82 /* This is used as an array index so must ensure it's not too large. First 83 remove the privilege bit if one is present. */ 任务优先级 84 if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) 85 { 86 uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; 87 } 88 else 89 { 90 mtCOVERAGE_TEST_MARKER(); 91 } 92 93 pxNewTCB->uxPriority = uxPriority; 94 #if ( configUSE_MUTEXES == 1 ) 【略】 95 { 96 pxNewTCB->uxBasePriority = uxPriority; 97 pxNewTCB->uxMutexesHeld = 0; 98 } 99 #endif /* configUSE_MUTEXES */ 100 101 vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); 初始化两个列表项 102 vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); 103 104 /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get 105 back to the containing TCB from a generic item in a list. */ 106 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); 设置列表项的Owner 107 108 /* Event lists are always in priority order. */ 事件列表是按优先级排序的? 109 listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 */ 110 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); 111 112 #if ( portCRITICAL_NESTING_IN_TCB == 1 ) 【临界区嵌套深度】 113 { 114 pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; 115 } 116 #endif /* portCRITICAL_NESTING_IN_TCB */ 117 118 #if ( configUSE_APPLICATION_TASK_TAG == 1 ) 【任务标签】 119 { 120 pxNewTCB->pxTaskTag = NULL; 121 } 122 #endif /* configUSE_APPLICATION_TASK_TAG */ 123 124 #if ( configGENERATE_RUN_TIME_STATS == 1 ) 【运行时间统计】 125 { 126 pxNewTCB->ulRunTimeCounter = 0UL; 127 } 128 #endif /* configGENERATE_RUN_TIME_STATS */ 129 130 #if ( portUSING_MPU_WRAPPERS == 1 ) 【MPU保护】 131 { 132 vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); 133 } 134 #else 135 { 136 /* Avoid compiler warning about unreferenced parameter. */ 137 ( void ) xRegions; 138 } 139 #endif 140 141 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) 【线程本地存储指针】 142 { 143 for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) 144 { 145 pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; 146 } 147 } 148 #endif 149 150 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 【任务通知】 151 { 152 pxNewTCB->ulNotifiedValue = 0; 153 pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; 154 } 155 #endif 156 157 #if ( configUSE_NEWLIB_REENTRANT == 1 ) 【略】 158 { 159 /* Initialise this task's Newlib reent structure. */ 160 _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); 161 } 162 #endif 163 164 #if( INCLUDE_xTaskAbortDelay == 1 ) 【略】 165 { 166 pxNewTCB->ucDelayAborted = pdFALSE; 167 } 168 #endif 169 170 /* Initialize the TCB stack to look as if the task was already running, 171 but had been interrupted by the scheduler. The return address is set 172 to the start of the task function. Once the stack has been initialised 173 the top of stack variable is updated. */ 174 #if( portUSING_MPU_WRAPPERS == 1 ) 175 { 176 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); 177 } 178 #else /* portUSING_MPU_WRAPPERS */ 179 { 180 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); 初始化任务堆栈 181 } 182 #endif /* portUSING_MPU_WRAPPERS */ 183 184 if( ( void * ) pxCreatedTask != NULL ) 185 { 186 /* Pass the handle out in an anonymous way. The handle can be used to 187 change the created task's priority, delete the created task, etc.*/ 188 *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; 转换为任务句柄 189 } 190 else 191 { 192 mtCOVERAGE_TEST_MARKER(); 193 } 194 } 195 /*-----------------------------------------------------------*/

初始化新任务TCB  调用  初始化堆栈:

 1 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
 2 {
 3     /* Simulate the stack frame as it would be created by a context switch
 4     interrupt. */  
 5 
 6     /* Offset added to account for the way the MCU uses the stack on entry/exit
 7     of interrupts, and to ensure alignment. */
 8     pxTopOfStack--;
 9 
10     *pxTopOfStack = portINITIAL_XPSR;    /* xPSR */
11     pxTopOfStack--;
12     *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;    /* PC */
13     pxTopOfStack--;
14     *pxTopOfStack = ( StackType_t ) prvTaskExitError;    /* LR */
15 
16     /* Save code space by skipping register initialisation. */
17     pxTopOfStack -= 5;    /* 跳过 R12, R3, R2 and R1. */
18     *pxTopOfStack = ( StackType_t ) pvParameters;    /* R0 */
19 
20     /* A save method is being used that requires each task to maintain its
21     own exec return value. */
22     pxTopOfStack--;
23     *pxTopOfStack = portINITIAL_EXEC_RETURN;
24 
25     pxTopOfStack -= 8;    /* 跳过 R11, R10, R9, R8, R7, R6, R5 and R4. */
26 
27     return pxTopOfStack;
28 }

在port.c中

原子freeRTOS资料:

堆栈是用来在进行上下文切换的时候保存现场的,一般在新创建好一个堆栈以后会对其先
进行初始化处理,即对 Cortex-M 内核的某些寄存器赋初值。这些初值就保存在任务堆栈中,保
存的顺序按照: xPSR、 R15(PC)、 R14(LR)、 R12、 R3~R0、 R11~R14。(1)、寄存器 xPSR 值为 portINITIAL_XPSR,其值为 0x01000000。 xPSR 是 Cortex-M4 的一
个内核寄存器,叫做程序状态寄存器,0x01000000 表示这个寄存器的 bit24 为 1,表示处于 Thumb状态,即使用的 Thumb 指令。(2)、寄存器 PC 初始化为任务函数 pxCode。(3)、寄存器 LR 初始化为函数 prvTaskExitError。(4)、跳过 4 个寄存器, R12, R3, R2, R1,这四个寄存器不初始化。(5)、寄存器 R0 初始化为 pvParameters,一般情况下,函数调用会将 R0~R3 作为输入参数,R0 也可用作返回结果,如果返回值为 64 位,则 R1 也会用于返回结果,这里的 pvParameters 是
作为任务函数的参数,保存在寄存器 R0 中。(6)、保存 EXC_RETURN 值,用于设置退出 SVC 或 PendSV 中断时,处理器应该处于什么状态。处理器进入异常或中断服务程序(ISR)时,链接寄存器 R14(LR)的数值会被更新为EXC_RETURN 数值,之后该数值会在异常处理结束时触发异常返回。这里人为的设置为0XFFFFFFD,表示退出异常以后 CPU 进入线程模式并且使用进程栈!关于 EXC_RETURN 的
更多信息请参考《权威指南》“第 8 章 深入了解异常处理”。(7)、跳过 8 个寄存器, R11、 R10、 R8、 R7、 R6、 R5、 R4。
经过上面的初始化之后,此时的堆栈结果如图所示:

 

 去看看一下EXC_RETURN是什么东西。

       (1)有中断请求时,中断控制器NVIC获取中断源硬件设备的中断向量号,并通过识别的中断向量号,将对应硬件中断源模块的中断状态寄存器中的“中断活动位”置位。   (2)响应中断的时候,首先要做的就是保存中断现场:依次将PC、xPSR、R0~R3、R12、LR这个8个寄存器压入任务堆栈(任务栈的栈顶指针使用PSP),这一步是由硬件压入的,注意这里的顺序。   (3)堆栈指针切换为使用MSP进行堆栈操作,系统计算EXC_RETURN值①(EXC_RETURN值只是用于区分返回后换入Handler模式还是线程模式。②以及栈操作使用MSP还是PSP)并将它赋值给LR,此时的LR值已经被修改为EXC_RETURN供异常在返回时判断使用,更新中断号程序状态寄存器IPSR中的低8位为新的中断号,更换程序计数器PC值为中断服务例程的入口地址。   (4)执行中断服务例程,当执行PUSH {LR}语句时将EXC_RETURN值压入堆栈,中断服务例程结束时,更新NVIC寄存器,将中断状态寄存器中对应的“中断活动位”清除,接着执行POP {PC}就是将EXC_RETURN弹出到PC,此时会将MSP切换为PSP,CM4中使用把EXC_RETURN写入PC来触发中断返回动作序列,中断返回动作序列就是将之前压入的8个寄存器弹出。把EXC_RETURN写入PC有还有两种形式,一种是当LR寄存器中存放了EXC_RETURN时,使用BX LR即可返回,另外一种就是将PC作为目的寄存器,使用LDR或者LDM指令也可以启动中断返回序列。此时会将之前压入堆栈的PC、xPSR、R0~R3、R12、LR这8个寄存器已经全部恢复。

地址:http://jeremybai.github.io/blog/2014/03/03/mqx-interrupt-3

二、初始化完TCB,初始化完任务Stack,接下来是:把任务加入到任务列表中。

FreeRTOS 使用不同的列表,表示任务的不同状态,在文件 tasks.c 中就定义了多个列表和指向列表的指针来完成不同的功能 

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
PRIVILEGED_DATA static List_t xDelayedTaskList1;  两个延时任务列表,一个用于记录已经延时溢出的任务列表
PRIVILEGED_DATA static List_t xDelayedTaskList2;  
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;
PRIVILEGED_DATA static List_t xPendingReadyList;
列表数组 pxReadyTasksLists[]就是任务就绪列表,数组大小为 configMAX_PRIORITIES,
也就是说一个优先级一个列表,这样相同优先级的任务就使用一个列表。

把新建的任务加入到就绪任务列表中。

 1 static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
 2 {
 3     /* Ensure interrupts don't access the task lists while the lists are being updated. */
 5     taskENTER_CRITICAL();
 6     {
 7         uxCurrentNumberOfTasks++;
 8         if( pxCurrentTCB == NULL )  全局变量,当前没有其他任务
 9         {
10             /* There are no other tasks, or all the other tasks are in
11             the suspended state - make this the current task. */
12             pxCurrentTCB = pxNewTCB;  更新这个全局变量
         更新之后的动作,应该是调度器进行的操作。 13 14 if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) 15 { 16 /* This is the first task to be created so do the preliminary 初步 17 initialisation required. We will not recover if this call 18 fails, but we will report the failure. */ 19 prvInitialiseTaskLists(); 初始化任务列表 20 } 21 else 22 { 23 mtCOVERAGE_TEST_MARKER(); 24 } 25 } 26 else 已经有其他任务 27 { 28 /* If the scheduler is not already running, make this task the 29 current task if it is the highest priority task to be created 30 so far. */ 31 if( xSchedulerRunning == pdFALSE ) 调度器没运行 32 { 33 if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) 34 { 35 pxCurrentTCB = pxNewTCB; 新加入的任务优先级高的话,则更新全局变量为pxNewTCB 36 } 37 else 38 { 39 mtCOVERAGE_TEST_MARKER(); 40 } 41 } 42 else 43 { 44 mtCOVERAGE_TEST_MARKER(); 45 } 46 } 47 48 uxTaskNumber++; trace和debug相关的,TCB内有此变量。 49 50 #if ( configUSE_TRACE_FACILITY == 1 ) 51 { 52 /* Add a counter into the TCB for tracing only. */ 53 pxNewTCB->uxTCBNumber = uxTaskNumber; 54 } 55 #endif /* configUSE_TRACE_FACILITY */ 56 traceTASK_CREATE( pxNewTCB ); 57 58 prvAddTaskToReadyList( pxNewTCB ); 加入任务就绪列表 59 60 portSETUP_TCB( pxNewTCB ); 没动作 61 } 62 taskEXIT_CRITICAL(); 63 64 if( xSchedulerRunning != pdFALSE ) 调度器正在运行 65 { 66 /* If the created task is of a higher priority than the current task 67 then it should run now. */ 68 if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) 69 { 70 taskYIELD_IF_USING_PREEMPTION(); 而且新加入的任务优先级高,则置位PendSV,完成任务切换 71 } 72 else 73 { 74 mtCOVERAGE_TEST_MARKER(); 75 } 76 } 77 else 78 { 79 mtCOVERAGE_TEST_MARKER(); 80 } 81 }

如果新建的任务要加入的任务就绪列表中,没有其他任务:则把新建任务 更新到 pxCurrentTCB中。并初始化所有的几个任务列表。

如果新建的任务要加入的任务就绪列表中,有其他任务&&

1. 新建任务优先级最高:则把新建任务 更新到pxCurrentTCB中。

2. 新建任务优先级不是最高:不更新pxCurrentTCB

新建任务加入任务就绪表。

最后,调度器正在运行的话,

1. 新建任务优先级最高,则触发任务切换。

2. 新建任务优先级不是最高,不动作。

其中涉及到的:初始化所有的几个任务列表。

static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;

    for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );  初始化所有优先级对应的“任务就绪列表”
    }

    vListInitialise( &xDelayedTaskList1 );  初始化两个延时任务列表
    vListInitialise( &xDelayedTaskList2 );
    vListInitialise( &xPendingReadyList );  初始化已经就绪,但还没加入到就绪任务列表的,任务列表

    #if ( INCLUDE_vTaskDelete == 1 )
    {
        vListInitialise( &xTasksWaitingTermination );  等待销毁的任务列表
    }
    #endif /* INCLUDE_vTaskDelete */

    #if ( INCLUDE_vTaskSuspend == 1 )
    {
        vListInitialise( &xSuspendedTaskList );  挂起任务列表
    }
    #endif /* INCLUDE_vTaskSuspend */

    /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList
    using list2. */
    pxDelayedTaskList = &xDelayedTaskList1;           初始化两个指针
    pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
/*-----------------------------------------------------------*/

和,任务加入就绪任务列表

/*
 * Place the task represented by pxTCB into the appropriate ready list for
 * the task.  It is inserted at the end of the list.
 */
#define prvAddTaskToReadyList( pxTCB )                                                                \
    traceMOVED_TASK_TO_READY_STATE( pxTCB );                                                          \
    taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );      见后                                      \
把任务状态列表项,加入到“相应优先级的”任务就绪列表中。 vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) /*-----------------------------------------------------------*/

taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );   

“删除任务”分析里面,也会调用这个API,目的是维护一个变量,

这个变量的功能:

1. 在使用通用方法找到最高优先级任务时,它用来记录最高优先级任务的优先级。

2. 在使用硬件方法找到最高优先级任务时,它的每一位(共32bit)的状态代表这个优先级上边,有没有就绪的任务。

找到最高优先级任务的方法,见“任务切换”一节。

========================================================

找到一篇文章对于freeRTOS内核的解析非常的到位,这里贴出地址:

http://www.tk4479.net/jorhai

以下内容来自上边的地址:

首先介绍下任务存在下面几种状态 

运行:此时CPU正在运行任务。 

就绪:ready,已经具备执行条件,但是需要CPU进行调度,才能成为运行任务。 

阻塞:如果任务当前正在等待某个时间点、事件或信号量,这个任务处于阻塞状态。

  比如一个任务调用vTaskDelay()后会阻塞到延时周期到为止。

  任务也可能阻塞在队列或信号量事件上。

  进入阻塞状态的任务通常有一个“超时”周期,当事件超时后解除阻塞。 

挂起:调用vTaskSuspend()可以使任务进入挂起状态。这些任务无法被调度器调度到,除非调用xTaskResume()将任务从挂起状态移除。

Freertos使用TaskHandle_t 标识任务句柄:

typedef void * TaskHandle_t;

TaskHandle_t 其实指向tskTCB,标识一个任务的详细信息和运行时的堆栈等。 

task.c中的关键全局变量:

  • pxCurrentTCB:记录现在运行的任务;
  • pxReadyTasksLists:记录处于ready状态,等待被调度运行的任务,这是一个链表数组。就绪任务列表的数组个数,就是优先级的个数,在调度时可以从优先级高的readylist中先进行调度。
  • xDelayedTaskList1:定义了一个delay的task链表。
  • xDelayedTaskList2:定义了一个delay的task链表,delay的task链表是指调用了taskdelay()或者因为阻塞动作被延时的任务,延时的单位为tick。Delaylist按照delay的tick时间进行排序,之所以定义了两个,是为了防止xTickCount发生反转时,一个list无法完全标识。
  • xTickCount:无符号数字,标识运行后到现在的系统产生的tick数,每个tick发生时,xTickCount++,当xTickCount发生翻转时,pxDelayedTaskList和pxOverflowDelayedTaskList进行对调,Overflowlist变为正常的delay list。时间管理。
  • pxDelayedTaskList和pxOverflowDelayedTaskList是链表指针,分包指向xDelayedTaskList1和xDelayedTaskList2。
  • xPendingReadyList:任务进入就绪状态,但是没有放入readylist链表。这种情况发生在调度器被停止时,有些任务进入到ready状态,这时就讲任务加入到xPendingReadyList,等待调度器开始时,从新进行一次调度。 
    任何操作系统都有临界区或者在执行某段代码时不想被打断,防止破坏某些关键操作的完整性。Freertos可以采取多种方式避免,如果是不希望被中断打断,需要调用:
task.h
#define taskENTER_CRITICAL() portENTER_CRITICAL() #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL() #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
portmacro.h
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() 设置BASEPRI的值为最大 #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) 设置BASEPRI的值为0 #define portENTER_CRITICAL() vPortEnterCritical() 调用↑边第一个,并且CriticalNesting++(临界段嵌套次数) #define portEXIT_CRITICAL() vPortExitCritical() 先CriticalNesting--,如果这个值为0,调用↑边第二个
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() 设置BASEPRI的值为最大,返回原来BASEPRI的值 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) 设置BASEPRI的值为x

归根结底,这几个临界区的设置,都跟BASEPRI寄存器杠上了。(设置到寄存器里的值如果是0,则是屏蔽所有中断。设为10,那么中断优先级0~9的异常和中断都可以执行。)

  • xTasksWaitingTermination:表示等待结束的任务,注意是为了释放这些任务的资源(动态申请的TCB和栈)。
  • xSuspendedTaskList:表示被挂起的任务列表。
  • xIdleTaskHandle:表示空闲任务句柄,优先级为0,即最低。
  • uxCurrentNumberOfTasks:目前所有的任务数。
  • uxTopReadyPriority:记录当前ready list中优先级最高的任务。(重要)
  • xSchedulerRunning:调度器是否运行。
  • uxPendedTicks:vTaskSuspendAll暂停了调度器,如果这期间tick的timer发送中断,这时uxPendedTicks记录了未被处理的ticks个数。见时间管理。
  • xYieldPending:在某种临界状态下,任务状态发生改变,需要等待从新调度。
  • xNumOfOverflows:记录tick计数翻转的次数。
  • uxTaskNumber:用来记录全局任务数,为新建的任务分配一个task number。(不大懂哎~)
  • xNextTaskUnblockTime:记录了延时任务列表中,第一个需要被唤醒的任务时间点。系统运行的每一个节拍,都会跟这个变量比对,如果满足任务延时完成,则进行相应操作。见时间管理。
  • uxSchedulerSuspended:调度器挂起标志。

留白

ESP32 U8g2的常用函数,FreeRTOS新建3个任务,loop里单独运行oled u8g2基础函数,下载之后就可以进行编辑 阅读详情

相关推荐

在win10-wsl2(ubuntu)中安装jlink

在 官方下载linux版本的deb包拷贝到ubuntu的/home目录下(其他目录也可以),进行安装然后提示很多依赖缺失的错误,此时执行自动安装需要的依赖包再次重新安装就可以啦查看是否安装成功,输入JLink 再按下两次Table键,是否如下所示这样表示安装成功了。

yaq_30401的博客 3140

11-FreeRTOS配置函数 FreeRTOSConfig.h

这个表示是否启用调度函数,1表示使用抢占式RTOS调度程序,0是表示使用协程RTOS调度程序。

南山府的博客 2375

【electron】- 从零到一:在electron-vite项目中集成better-sqlite3的工程化实践

本文详细介绍了在electron-vite项目中集成better-sqlite3数据库的完整工程化实践。针对原生模块的编译挑战,提供了从环境配置、路径规划、服务封装到IPC通信的解决方案,并重点解决了开发与生产环境的数据库路径分离及打包部署等核心问题,帮助开发者构建稳定高效的Electron桌面应用数据层。

weixin_29306571的博客 205

关于 FreeRTOS和 newlib库共存问题

写在前面: 本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。 目录一、版本二、FreeRTOS 对 newlib 的支持三、当在 FreeRTOS中直接使用 newlib库的 printf/sprintf/snprintf/vsprintf/vsnprintf时出现的问题四、调用 printf/sprintf/snprintf/vsprintf/vsnprintf的线程安全处理 一、版本 以下说明仅对于: 1

夏沫的杂物间 8075

Fortran基础编程(3)——格式化输出

本篇推文将进一步讲解Fortran基础语法中格式化输出与声明部分,让我们的代码更加美观起来吧~

qq_45715554的博客 3827

SAP 系统和银行系统通过 SM2 加密算法进行集成

SM2 是基于椭圆曲线离散对数问题(ECDLP)的一种公钥密码算法。椭圆曲线加密的核心原理,是利用了一个数学难题:在椭圆曲线群上,已知曲线上两点的运算结果,逆向推导出这两个点的具体信息是非常困难的。这样的复杂度使得基于椭圆曲线的密码体制比传统的 RSA 更加难以破解。在 SM2 加密中,公钥和私钥的生成与传统的椭圆曲线加密类似。选择一个椭圆曲线和一个基点 P。使用私钥 d,计算公钥 P’ = d * P。私钥 d 保密,而公钥 P’ 可公开分发,用于加密数据。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1212

freertos源码解析-1启动过程分析

freertos启动过程分析 启动过程流程图 启动过程完成的事情 1.创建必要的任务:空闲任务和软件定时器任务(后者可选) 2.初始化系统的全局变量 3.初始化systick中断 PendSV中断 SVC中断 4.启动调度器 5.触发SVC中断, 启动第一个任务 代码分析 ...

weixin_38812277的博客 1881

python基础知识补充

1. jupyter notebook 解释 2. ipython 魔法命令 3. latex in Markdown cell(在markdown里面编写数学公式) 4. jupyter notebook功能扩展:安装Nbextension 5. python解释与运行 6. 垃圾回收(计数机制与分代回收机制) 7. 名字空间与局部作用域L和全局作用域G 8. 小整数缓存区与字符驻留 9. Statement &Expression 10. 调试

weixin_43178406的博客 1万+

FreeRTOS第二节——了解FreeRTOS的任务

1.FreeRTOS任务特点 FreeRTOS的任务使用是比较简单的,任务的数量是没有限制的,可以设置优先级,支持抢占,每个任务都有一个堆栈,用来保存任务的数据,任务多的话,内存的心态就会崩溃,如果抢占要考虑重新进入的问题。 2.为什么任务需要优先级和堆栈 首先什么是优先级,优先级呢就像是VIP,谁的级别高谁就牛,而且越高越牛,所以在FreeRTOS中最LOW的就是0级任务,空闲任务就是0级的。我...

生活不止眼前的苟且 737

Cadence PSpice 仿真0:绘制电路图方法图文教程

在使用PSpice进行电路的仿真时,首先需要使用带有PSpice模型的元器件来绘制电路图,本文简单介绍进行电路图绘制是常用的几种需要掌握的方法,以满足PSpice仿真绘制简单电路图所用。

fydar的博客 4707

ubuntu linux虚拟机安装部署openclaw详细教程(安装、问题处理)

本文详细介绍了在Ubuntu系统下安装OpenClaw AI助手的完整流程及常见问题解决方案。主要内容包括:1) 通过GIT、Node.js等基础环境配置;2) 使用官方脚本或源码编译两种方式安装OpenClaw;3) 配置向导中的关键选项说明;4) 针对systemctl服务启动失败等常见错误的处理方法。文章特别强调了权限管理、网络依赖和访问配置等易错环节,提供了修改service文件等具体解决方案,帮助开发者顺利完成部署并实现Web UI访问。教程实操性强,适合本地和云服务器环境部署参考。

少说,多做 2032

reentrant in newlib

The Newlib library maps standard C functions to a specific implementation environment through a chain of functions, for example: write() invokes _write_r() with the current reentrant context (e.g. thread/task-unique errno); _write_r() invokes _wr.

qq_38963393的博客 426

【cmd】Win10家庭版找不到 gpedit.msc (组策略)的解决方法

目录 一、创建cmd文件 二、以“管理员身份”运行“gpedit.cmd”文件 三、运行“gpedit.msc”命令查看组策略 一、创建cmd文件 (1)新建一个文件名为“gpedit.txt”的txt文本文档: (2)文本中写入以下内容: @echo off pushd "%~dp0" dir /b C:\Windows\servicing\Packages\Micros...

顾三殇 —— 博客空间(软件测试) 4万+

freeRTOS实战讲解之新建一个任务

一.准备工作: 单片机:STM32107VC 工程:已经移植到107工程的freeRTOS的工程 PS:如果没有可以看我之前写的播客自己移植 下面是链接。https://mp.youkuaiyun.com/postedit/101160362 二.开始新建自己的第一个任务 1. 首先我们新建任务之前,都要新建一个任务句柄。任务句柄就是每个任务的身份证,每一个任务都有一个不同的任务句柄。 static...

qq_40831778的博客 1516

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

198_FreeRTOS新建任务

1 #include <stdio.h> ...

sanbanzui2008的博客 348

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

01.新建FreeRTOS工程 及 裸机系统和多任务系统介绍

⭐最近在学习FreeRTOS,将学习过程记录为本系列文章,如果有错误,欢迎指正,大家一起进步🌈 目录 1.新建FreeRTOS工程 1.1.新建本地文件夹 1.2.使用Keil新建工程 1.3.在Keil创建文件分组 1.4.添加文件 1.5.编写main函数 1.6.配置仿真时钟 1.7.添加头文件路径 2.裸机系统与多任务系统 2.1.裸机系统 2.2.多任务系统 2.3.裸机系统与多任务系统的对比 1.新建FreeRTOS工程 1.1.新建本地文件夹 ...

小浪宝宝的博客 904

FreeRTOS新建任务

读手册就好了,不难 r_task_priority.h #define TASK_BLINK_TASK_PRI (5) main.h int_t os_blink_task_t(void); main.c int_t os_blink_task_t(void) { /* never exits */ while (1) { printf("hello world\r\n" ); R_OS_TaskSleep(500); } ret

weixin_44737922的博客 165

21.0.1-为什么要讲RTOS?第21章-FreeRTOS项目实战--基础知识之新建任务、启动流程、编码风格、系统配置 21.0-为什么要讲FreeRTOS(掌握)

这个是全网最详细的STM32项目教学视频。第一篇视频在这里:STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随)]

weixin_45922644的博客 974

FreeRtos新建任务不能运行

把#define configTOTAL_HEAP_SIZE 从15k改到48k就可以 ( ( size_t ) ( 48 * 1024 ) ) //15*1024 操作系统堆栈太小

hlt008的博客 8429

FreeRTOS新建任务不执行问题解决办法

FreeRTOS新建任务不执行问题解决办法

William_Zhang_优快云的博客 5727

新建FreeRTOS工程——软件仿真/裸机系统与多任务系统

一、新建FreeRTOS工程——软件仿真 1、新建本地工程文件 在开始新建工程之前,我们先在本地电脑端新建一个文件夹用于存放工程。文件夹名字我们取为“” FreeRTOS ”(名字可以随意取) ,然后再在该文件夹下面新建各个文件夹和文件: Doc:用于存放对整个工程的说明文件.readme.txt。 Project:用于存放新建的工程文件。 freertos: include:存放头文件 por...

Eleven的博客 1417

第一篇:新建Free RTOS工程以及新建任务

关于新建FreeRTOS工程和新建任务,包含编译缺少freertos_mpool.h问题的解决方式

super_axin的博客 677

使用STM32CubeMx创建FreeRTOS工程

使用STM32CubeMx创建FreeRTOS工程 保姆级教程

2301_80226516的博客 748

STM32CubeMX学习笔记(34)——FreeRTOS实时操作系统使用(任务通知)

一、FreeRTOS简介 FreeRTOS 是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS 提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。 FreeRTOS 是用 C 和汇编来写的,其中绝大部分都是用 C 语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS 结构简洁,可读性很强!最主要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。 最新版本 V9.0.0(2016年),尽管现在 FreeRTOS 的

Leung的博客 3577

使用cube_MX新建FreeRTOS文件——STM32单片机

在朋友的推荐下,新建一个嵌入FreeRTOS的单片机程序,熟悉一下建立过程,方便以后查阅。 选择完单片机,进入界面之后,在Middleware->FREERTOS进入以上界面,接口选择 CMSIS_V1。 点击 Tasks and Queues,拉长配置框,会看到一个默认的任务,点击defaultTask,可以看见参数,如上图所示,然后将入口函数改成你想用的名字,我改成了LED_Task。如下: 点击优先级可以切换任务的优先级,优先级有七种之多,其中实时的优先级最高。 改完之后..

吾日三省吾身的博客 735
上一篇: freeRTOS 队列4 读取消息
下一篇: freeRTOS 删除任务
WM_CH
博客等级 码龄17年 24粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值