本文是《ALIENTEK STM32F429 FreeRTOS 开发教程》第八章学习笔记-1
第一章笔记–FreeRTOS简介与源码下载
第二章笔记–FreeRTOS在STM32F4上移植
第三章笔记-FreeRTOS系统配置
第四章笔记-FreeRTOS中断分析
第四章笔记补充-FreeRTOS临界段代码
第五章笔记-FreeRTOS任务基础
第六章笔记-FreeRTOS任务API函数的使用
第七章笔记-FreeRTOS列表和列表项

任务创建

1. 任务创建函数

FreeRTOS使用函数xTaskCreate()和xTaskCreateStatic()分别动态和静态创建任务

xTaskCreate()的源代码在tasks.c中:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName,const uint16_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask ){TCB_t *pxNewTCB;BaseType_t xReturn;#if( portSTACK_GROWTH > 0 ){pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );if( pxNewTCB != NULL ){pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); if( pxNewTCB->pxStack == NULL ){vPortFree( pxNewTCB );pxNewTCB = NULL;}}}#else /* portSTACK_GROWTH */{StackType_t *pxStack;pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );if( pxStack != NULL ){pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );if( pxNewTCB != NULL ){pxNewTCB->pxStack = pxStack;}else{vPortFree( pxStack );}}else{pxNewTCB = NULL;}}#endif /* portSTACK_GROWTH */if( pxNewTCB != NULL ){#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ){pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;}#endif /* configSUPPORT_STATIC_ALLOCATION */prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );prvAddNewTaskToReadyList( pxNewTCB );xReturn = pdPASS;}else{xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;}return xReturn;}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ):可以看出使用此函数进行任务创建时必须将支持动态内存宏定为1

if( portSTACK_GROWTH > 0 ):如果宏定义堆栈向上增长则执行接下来的代码,portSTACK_GROWTH在portmacro.h里有宏定义

pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL ){pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); if( pxNewTCB->pxStack == NULL ){vPortFree( pxNewTCB );pxNewTCB = NULL;}}

先使用函数pvPortMalloc()给任务控制块申请内存,如果成功的话,再给人物的任务堆栈申请内存,如果申请失败释放任务控制块内存

#else /* portSTACK_GROWTH */{StackType_t *pxStack;pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );if( pxStack != NULL ){pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );if( pxNewTCB != NULL ){pxNewTCB->pxStack = pxStack;}else{vPortFree( pxStack );}}else{pxNewTCB = NULL;}}
#endif /* portSTACK_GROWTH */

这段代码表示若向下增长堆栈时,先申请任务堆栈内存,再申请任务控制块内存

pxNewTCB->ucStaticallyAllocated=tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB:标记任务堆栈和任务控制块是使用动态内存分配方法得到的

prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ):使用函数prvInitialiseNewTask()初始化任务,完成对任务控制块中各个字段的初始化工作

prvAddNewTaskToReadyList( pxNewTCB ):将新创建的任务加入到就绪表中

2. 任务初始化函数

static void prvInitialiseNewTask(   TaskFunction_t pxTaskCode,const char * const pcName,const uint32_t ulStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask,TCB_t *pxNewTCB,const MemoryRegion_t * const xRegions )

任务初始化代码较多,只列出了函数定义,之后重点分析当中代码,源代码在tasks.c中

参数:pxTaskCode:任务函数;pcName:任务名称;ulStackDepth:任务堆栈大小,从任务创建函数中强制转化成uint32_t格式传入任务初始化函数;pvParameters:传递给任务函数的参数,一直是NULL没看出作用;uxPriority:任务优先级;pxCreatedTask:任务句柄;pxNewTCB:任务控制块;xRegions:使用xTaskCreateRestricted()函数建立微处理器保护任务时传递的内存地址参数,普通人物建立时此参数为NULL

#if( portUSING_MPU_WRAPPERS == 1 )/* Should the task be created in privileged mode? */BaseType_t xRunPrivileged;if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ){xRunPrivileged = pdTRUE;}else{xRunPrivileged = pdFALSE;}uxPriority &= ~portPRIVILEGE_BIT;#endif /* portUSING_MPU_WRAPPERS == 1 */

如果宏定义portUSING_MPU_WRAPPERS为1,判断该任务是否在特权模式下创建,给xRunPrivileged赋值,并赋值优先级大小,由于我们将portUSING_MPU_WRAPPERS宏定义为0,所以不编译这段代码

    #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ){/* Fill the stack with a known value to assist debugging. */( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );}
#endif

如果使能了堆栈溢出检测功能或者追踪可视化就使用一个定值tskSTACK_FILL_BYTE来填充任务堆栈,值为0xa5U

    #if( portSTACK_GROWTH < 0 ){pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. *//* Check the alignment of the calculated top of stack is correct. */configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );}#else /* portSTACK_GROWTH */{pxTopOfStack = pxNewTCB->pxStack;/* Check the alignment of the stack buffer is correct. */configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );/* The other extreme of the stack space is required if stack checking isperformed. */pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );}#endif /* portSTACK_GROWTH */

使用条件编译,在向上堆栈和向下堆栈不同时计算堆栈栈顶pxTopOfStack

    for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){pxNewTCB->pcTaskName[ x ] = pcName[ x ];/* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter thanconfigMAX_TASK_NAME_LEN characters just in case the memory after thestring is not accessible (extremely unlikely). */if( pcName[ x ] == 0x00 ){break;}else{mtCOVERAGE_TEST_MARKER();}}

保存任务的任务名

pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = ‘\0’:在任务名数组添加字符串结束符’\0’

    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;}else{mtCOVERAGE_TEST_MARKER();}

判断任务优先级是否大于预先定义的最大优先级configMAX_PRIORITIES宏,若大于则将任务优先级设置为configMAX_PRIORITIES-1

pxNewTCB->uxPriority = uxPriority:将得到的uxPriority值赋给任务控制块的优先级(即初始化任务控制块的优先级)

    #if ( configUSE_MUTEXES == 1 ){pxNewTCB->uxBasePriority = uxPriority;pxNewTCB->uxMutexesHeld = 0;}#endif /* configUSE_MUTEXES */

如果宏定义configUSE_MUTEXES为1,即使用了互斥信号量,这里初始化相应变量

vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

这里初始化列表项xStateListItem和xEventListItem,任务控制块结构体中有两个列表项,对其做初始化操作

listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )  ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )   ( ( pxListItem )->xItemValue = ( xValue ) )

这里设置了列表项xStateListItem和xEventListItem属于当前任务控制块,即设置两个列表项的成员变量pvOwner为新创建的任务的任务控制块;并且设置了列表项xEventListItem的变量xItemValue为configMAX_PRIORITIES-uxPriority,意味着xItemValue的值越大优先级越小(列表插入按照xItemValue的值升序排列)

#if ( portCRITICAL_NESTING_IN_TCB == 1 )
{pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
}
#endif /* portCRITICAL_NESTING_IN_TCB */
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
{pxNewTCB->pxTaskTag = NULL;
}
#endif /* configUSE_APPLICATION_TASK_TAG */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{pxNewTCB->ulRunTimeCounter = 0UL;
}
#endif /* configGENERATE_RUN_TIME_STATS */

分别条件编译,portCRITICAL_NESTING_IN_TCB:临界区嵌套宏定义;configUSE_APPLICATION_TASK_TAG:任务标签功能;configGENERATE_RUN_TIME_STATS:时间统计功能;若使能这些功能宏定义,则给任务控制块的相关变量初始化赋值

#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ){for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ){pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;}}
#endif

如果使能了宏configNUM_THREAD_LOCAL_STORAGE_POINTERS为1,初始化线程本地存储指针

#if ( configUSE_TASK_NOTIFICATIONS == 1 ){pxNewTCB->ulNotifiedValue = 0;pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;}
#endif#if ( configUSE_NEWLIB_REENTRANT == 1 ){/* Initialise this task's Newlib reent structure. */_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );}
#endif#if( INCLUDE_xTaskAbortDelay == 1 ){pxNewTCB->ucDelayAborted = pdFALSE;}
#endif

条件编译,configUSE_TASK_NOTIFICATIONS:使能任务通知功能(默认开启);configUSE_NEWLIB_REENTRANT:使能NEWLIB;INCLUDE_xTaskAbortDelay:使能函数INCLUDE_xTaskAbortDelay();如果使能了相应功能,则给任务控制块的相关变量初始化赋值

#if( portUSING_MPU_WRAPPERS == 1 ){pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );}
#else /* portUSING_MPU_WRAPPERS */{pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );}
#endif /* portUSING_MPU_WRAPPERS */

调用了函数pxPortInitialiseStack()初始化任务堆栈

if( ( void * ) pxCreatedTask != NULL )
{*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{mtCOVERAGE_TEST_MARKER();
}

生成任务句柄,返回给参数pxCreatedTask,任务句柄其实就是任务控制块

3. 任务堆栈初始化函数

堆栈用来进行上下文切换时候保存现场,新创建好一个堆栈后会对其进行初始化处理,即对Cortex-M内核的某些寄存器赋初值。初值保存在任务堆栈中,保存顺序为:xPSR,R15(PC),R14(LR),R12,R3\~R0,R11~R14

函数pxPortInitialiseStack()即为堆栈初始化函数,函数源码:

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{/* Simulate the stack frame as it would be created by a context switchinterrupt. *//* Offset added to account for the way the MCU uses the stack on entry/exitof 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 itsown exec return value. */pxTopOfStack--;*pxTopOfStack = portINITIAL_EXEC_RETURN;pxTopOfStack -= 8;  /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}

参数:pxTopOfStack:堆栈栈顶;pxCode:任务函数;pvParameters:传递参数(null)

*pxTopOfStack = portINITIAL_XPSR: 寄存器xPSR值为portINITIAL_XPSR(0x01000000),此时表示xPSR寄存器的bit24为1,即处于Thumb状态(Cortex-m系列没有ARM状态)

*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK:寄存器PC初始化为任务函数pxCode

*pxTopOfStack = ( StackType_t ) prvTaskExitError:寄存器LR初始化为函数prvTaskExitError()

pxTopOfStack -= 5;  /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters

跳过4个寄存器R12,R3,R2和R1(这四个寄存器不初始化);并把寄存器R0初始化为pvParameters

*pxTopOfStack = portINITIAL_EXEC_RETURN:保存EXC_RETURN值,用于退出SVC和PendSV中断的时候处理器该处于什么状态。当处理器进入异常或中断服务程序时,链接寄存器R14(LR)的数值更新为EXC_RETURN,这里宏定义为0xfffffffd

pxTopOfStack -= 8:跳过8个寄存器,R11,R10,R8,R7,R6,R5,R4

初始化之后,堆栈结果为:

4. 添加任务到就绪表

任务创建后被添加到就绪列表中,FreeRTOS使用不同的列表表示任务的不同状态,tasks.c中定义了多个列表完成不同功能:

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList1;                /*< Delayed tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList2;                /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;     /*< Points to the delayed task list currently being used. */
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t xPendingReadyList;                /*< Tasks that have been readied while the scheduler was suspended.  They will be moved to the ready list when the scheduler is resumed. */

其中pxReadyTasksLists[]即是任务就绪列表,数组大小为configMAX_PRIORITIES即可使用的最大优先级,所以一个优先级一个列表,相同优先级的任务则使用一个列表。

函数prvAddNewTaskToReadyList()完成将一个新创建的任务添加到就绪列表中

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{taskENTER_CRITICAL();{uxCurrentNumberOfTasks++;if( pxCurrentTCB == NULL ){pxCurrentTCB = pxNewTCB;if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ){prvInitialiseTaskLists();}else{mtCOVERAGE_TEST_MARKER();}}else{if( xSchedulerRunning == pdFALSE ){if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ){pxCurrentTCB = pxNewTCB;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}uxTaskNumber++;#if ( configUSE_TRACE_FACILITY == 1 ){pxNewTCB->uxTCBNumber = uxTaskNumber;}#endif /* configUSE_TRACE_FACILITY */traceTASK_CREATE( pxNewTCB );prvAddTaskToReadyList( pxNewTCB );portSETUP_TCB( pxNewTCB );}taskEXIT_CRITICAL();if( xSchedulerRunning != pdFALSE ){if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ){taskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}
}

taskENTER_CRITICAL():首先先进入临界区,关闭中断,使接下来函数里代码完整运行不被打断

uxCurrentNumberOfTasks++:全局变量uxCurrentNumberOfTasks加1,统计任务数量

if( pxCurrentTCB == NULL ){pxCurrentTCB = pxNewTCB:如果正在运行任务控制块为NULL,即为没有任务运行,则将新任务的任务控制块赋值给pxCurrentTCB,新创建的任务便是第一个任务

if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ){prvInitialiseTaskLists();}:如果任务数量为1,则说明创建的任务是第一个任务,此时通过函数prvInitialiseTaskLists()来初始化相应的列表(函数prvInitialiseTaskLists()即调用列表初始化函数vListInitialise()来初始化几个列表)

if( xSchedulerRunning == pdFALSE ){if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ){pxCurrentTCB = pxNewTCB;}:如果调度器尚未运行,新任务的优先级比正在运行的任务优先级高,则修改pxCurrentTCB为新建任务的任务控制块

uxTaskNumber++:uxTaskNumber加1,用作任务控制块编号

if ( configUSE_TRACE_FACILITY == 1 ){pxNewTCB->uxTCBNumber = uxTaskNumber;}#endif:条件编译如果启动了可视化跟踪调试,则将任务控制块编号赋值给新任务的任务控制块的uxTCBNumber成员变量

prvAddTaskToReadyList( pxNewTCB ):调用函数吧任务添加到就绪表中,这个函数其实是个宏:

#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 )

traceMOVED_TASK_TO_READY_STATE( pxTCB )和tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )都没什么用;taskRECORD_READY_PRIORITY用来记录处于就绪态任务,接下来使用函数vListInsertEnd()将任务添加到就绪列表末尾

taskEXIT_CRITICAL():之后退出临界区

if( xSchedulerRunning != pdFALSE ){if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ){
taskYIELD_IF_USING_PREEMPTION();}:如果调度器已经开始运行,并且新任务的优先级最高,则调用函数taskYIELD_IF_USING_PREEMPTION()完成一次任务切换

FreeRTOS任务创建相关推荐

  1. FreeRTOS任务创建和删除

    任务创建和删除的API函数 xTaskCreate():使用动态方法创建一个任务 xTaskCreateStatic():使用静态方法创建一个任务 xTaskCreateRestricated():创 ...

  2. FreeRTOS动态创建任务

    一.动态方式创建任务和删除任务 1>测试环境 系统:WIN7 MDK:keil v5.26 开发板:GD32F303C-EVAL 固件库:V1.0.2 FreeRTOS版本:V10.4.0 2& ...

  3. 【STM32】FreeRTOS创建和删除任务示例(静态方法)(了解)

    00. 目录 文章目录 00. 目录 01. 概述 02. 功能描述 03. 任务设计 04. 程序设计 05. 结果验证 06. 附录 07. 参考 01. 概述 FreeRTOS中创建和删除任务A ...

  4. 【STM32】FreeRTOS创建和删除任务示例(动态方法)

    00. 目录 文章目录 00. 目录 01. 概述 02. 功能描述 03. 任务设计 04. 程序设计 05. 结果验证 06. 附录 07. 参考 01. 概述 FreeRTOS中创建和删除任务A ...

  5. esp32 怎么分配freertos 堆栈大小_深度解剖~ FreeRtos阅读笔记2 任务创建、内核链表初始化...

    2.FREERTOS任务创建.内核链表初始化 硬件环境:cortex m4 FreeRTOS版本:v8.0.1 今天开始阅读freertos,阅读同时做下笔记,等哪天碰到移植问题再翻出来看看. 2.1 ...

  6. 【FreeRTOS】02 任务的创建

    上一节我们利用cubemx自动生成了一个实例,它建立了两个用户任务和一个默认任务,并成功运行起来了.本节我们将分析一下FreeRTOS任务创建的过程,并结合创建时需要的参数讲解一下FreeRTOS任务 ...

  7. FreeRtos源码分析之任务创建和管理(一)

    一.任务控制块TCB 在开始学习FreeRtos任务相关源码之前,我们需要先了解一个重要的结构体-任务控制块.FreeRtos的每一个任务都有一个独立的任务控制块TCB,TCB中记录了任务的优先级.名 ...

  8. freeRtos学习笔记 (6)软件定时器

    freeRtos学习笔记 freeRtos软件定时器 软件定时器需要注意事项 软件定时器的精度基于时钟节拍,例如系统时钟节拍为10ms, 软件定时器定时时间必须是10ms的整数倍,因此软件定时器一般用 ...

  9. FreeRTOS高级篇7---FreeRTOS内存管理分析

    内存管理对应用程序和操作系统来说都非常重要.现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关.         FreeRTOS操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理 ...

最新文章

  1. day_06、面向对象
  2. Android性能调优篇之探索垃圾回收机制
  3. [BUUCTF-pwn]——picoctf_2018_buffer overflow 0
  4. WebLogic下WAR方式部署获取文件路径问题
  5. 使用参数来防止SQL注入
  6. 【英语学习】【Level 07】U06 First Time L6 Not your typical experience
  7. kafka自定义序列化器
  8. 初创IT公司里开发者最容易犯的九种错误
  9. 三进制计算机在线计算,计算器在线
  10. 视频播放控制:防盗链设置与视频加密及Android中的基础应用
  11. 日本作家将编程语言变成了动漫人物,你猜C语言是萝莉还是御姐?不得不说脑洞实在太大了!
  12. PNG格式图片怎么弄
  13. ARM GCC浮点相关总结
  14. 火车采集 PHP插件 post,火车采集器2010版PHP插件增加扩展的方法
  15. 安装黑群晖找不到局域网电脑_组建家庭存储群晖NAS(一)——详细安装篇
  16. 逻辑回归实例,特征预处理
  17. Elasticsearch学习(二)————搜索
  18. zimbra邮箱服务器迁移
  19. 【RDMA】RoCE网络QoS|应用层设置PFC等级|Tos|Priority|TC
  20. Navicate无法连接,提示is not allowed to connect to this mysql server

热门文章

  1. Java项目:图书进销存管理系统(java+SSM+JSP+bootstrap+Mysql)
  2. 计算机视觉在小机器人中应用,计算机视觉系统在工业机器人上的应用研究.doc...
  3. 微信公众号配置及测试
  4. 利用 Xunsearch 搭建搜索引擎、内容搜索实战
  5. Animator视图方法缩小
  6. Animator自定义动画播放
  7. Ossim java_ossim之添加资产全方法
  8. uglifyjs使用
  9. Android控件晃动效果实现
  10. 小型ssh工具dropbear 安装配置及使用详解