赞
踩
目录
1.添加FreeRTOS源码到工程文件的Middlewares目录
2.在MDK移植内核管理算法(heap_x.c)连接桥梁(port.c)
编辑 4.添加FreeRTOSConfig.h(可以裁剪出需要的功能)
添加任务到就绪列表 prvAddNewTaskToReadyList
在中断中恢复被挂起的任务 xTaskResumeFromISR()
关中断函数 portDISABLE_INTERRUPTS()
入队堵塞:队列已满 该任务挂载到等待写入事件xTaskWaittingToSend()
出队堵塞:队列已控 该任务挂载到等待读取事件xTaskWaittingToReceive()
任务调度器:决定当前执行哪个任务。
优先级高的可以抢占优先级低的任务。(优先级数值越大,优先级越高)当前高优先级任务遇到阻塞直接切换到下一个低优先级任务
相同优先级在每个系统节拍切换任务(一个时间片等于SysTick滴答定时器的中断周期)当前任务遇到阻塞直接切换到下一个任务
当前任务一直执行,高优先级无法抢占
类似暂停,调用vTaskSuspend()进入挂起态,需要调用vTaskResune()才能进入就绪态
相同优先级会在同一个就绪列表上
调度器总是从就绪列表中选出最高优先级任务进行执行
在官方源码的demo里面有
然后添加文件路径
- #ifndef FREERTOS_CONFIG_H
- #define FREERTOS_CONFIG_H
-
- /* 头文件 */
- #include "./SYSTEM/sys/sys.h"
- #include "./SYSTEM/usart/usart.h"
- #include <stdint.h>
-
- extern uint32_t SystemCoreClock;
-
- /* 基础配置项 */
- #define configUSE_PREEMPTION 1 /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
- #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
- #define configUSE_TICKLESS_IDLE 0 /* 1: 使能tickless低功耗模式, 默认: 0 */
- #define configCPU_CLOCK_HZ SystemCoreClock /* 定义CPU主频, 单位: Hz, 无默认需定义 */
- #define configSYSTICK_CLOCK_HZ (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
- #define configTICK_RATE_HZ 1000 /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
- #define configMAX_PRIORITIES 32 /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
- #define configMINIMAL_STACK_SIZE 128 /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
- #define configMAX_TASK_NAME_LEN 16 /* 定义任务名最大字符数, 默认: 16 */
- #define configUSE_16_BIT_TICKS 0 /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
- #define configIDLE_SHOULD_YIELD 1 /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
- #define configUSE_TASK_NOTIFICATIONS 1 /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
- #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 定义任务通知数组的大小, 默认: 1 */
- #define configUSE_MUTEXES 1 /* 1: 使能互斥信号量, 默认: 0 */
- #define configUSE_RECURSIVE_MUTEXES 1 /* 1: 使能递归互斥信号量, 默认: 0 */
- #define configUSE_COUNTING_SEMAPHORES 1 /* 1: 使能计数信号量, 默认: 0 */
- #define configUSE_ALTERNATIVE_API 0 /* 已弃用!!! */
- #define configQUEUE_REGISTRY_SIZE 8 /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
- #define configUSE_QUEUE_SETS 1 /* 1: 使能队列集, 默认: 0 */
- #define configUSE_TIME_SLICING 1 /* 1: 使能时间片调度, 默认: 1 */
- #define configUSE_NEWLIB_REENTRANT 0 /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
- #define configENABLE_BACKWARD_COMPATIBILITY 0 /* 1: 使能兼容老版本, 默认: 1 */
- #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* 定义线程本地存储指针的个数, 默认: 0 */
- #define configSTACK_DEPTH_TYPE uint16_t /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
- #define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
-
- /* 内存分配相关定义 */
- #define configSUPPORT_STATIC_ALLOCATION 0 /* 1: 支持静态申请内存, 默认: 0 */
- #define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
- #define configTOTAL_HEAP_SIZE ((size_t)(10 * 1024)) /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
- #define configAPPLICATION_ALLOCATED_HEAP 0 /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
- #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */
-
- /* 钩子函数相关定义 */
- #define configUSE_IDLE_HOOK 0 /* 1: 使能空闲任务钩子函数, 无默认需定义 */
- #define configUSE_TICK_HOOK 0 /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
- #define configCHECK_FOR_STACK_OVERFLOW 0 /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
- #define configUSE_MALLOC_FAILED_HOOK 0 /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
- #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */
-
- /* 运行时间和任务状态统计相关定义 */
- #define configGENERATE_RUN_TIME_STATS 0 /* 1: 使能任务运行时间统计功能, 默认: 0 */
- #if configGENERATE_RUN_TIME_STATS
- #include "./BSP/TIMER/btim.h"
- #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats()
- extern uint32_t FreeRTOSRunTimeTicks;
- #define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
- #endif
- #define configUSE_TRACE_FACILITY 1 /* 1: 使能可视化跟踪调试, 默认: 0 */
- #define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */
-
- /* 协程相关定义 */
- #define configUSE_CO_ROUTINES 0 /* 1: 启用协程, 默认: 0 */
- #define configMAX_CO_ROUTINE_PRIORITIES 2 /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */
-
- /* 软件定时器相关定义 */
- #define configUSE_TIMERS 1 /* 1: 使能软件定时器, 默认: 0 */
- #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
- #define configTIMER_QUEUE_LENGTH 5 /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
- #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */
-
- /* 可选函数, 1: 使能 */
- #define INCLUDE_vTaskPrioritySet 1 /* 设置任务优先级 */
- #define INCLUDE_uxTaskPriorityGet 1 /* 获取任务优先级 */
- #define INCLUDE_vTaskDelete 1 /* 删除任务 */
- #define INCLUDE_vTaskSuspend 1 /* 挂起任务 */
- #define INCLUDE_xResumeFromISR 1 /* 恢复在中断中挂起的任务 */
- #define INCLUDE_vTaskDelayUntil 1 /* 任务绝对延时 */
- #define INCLUDE_vTaskDelay 1 /* 任务延时 */
- #define INCLUDE_xTaskGetSchedulerState 1 /* 获取任务调度器状态 */
- #define INCLUDE_xTaskGetCurrentTaskHandle 1 /* 获取当前任务的任务句柄 */
- #define INCLUDE_uxTaskGetStackHighWaterMark 1 /* 获取任务堆栈历史剩余最小值 */
- #define INCLUDE_xTaskGetIdleTaskHandle 1 /* 获取空闲任务的任务句柄 */
- #define INCLUDE_eTaskGetState 1 /* 获取任务状态 */
- #define INCLUDE_xEventGroupSetBitFromISR 1 /* 在中断中设置事件标志位 */
- #define INCLUDE_xTimerPendFunctionCall 1 /* 将函数的执行挂到定时器服务任务 */
- #define INCLUDE_xTaskAbortDelay 1 /* 中断任务延时 */
- #define INCLUDE_xTaskGetHandle 1 /* 通过任务名获取任务句柄 */
- #define INCLUDE_xTaskResumeFromISR 1 /* 恢复在中断中挂起的任务 */
-
- /* 中断嵌套行为配置 */
- #ifdef __NVIC_PRIO_BITS
- #define configPRIO_BITS __NVIC_PRIO_BITS
- #else
- #define configPRIO_BITS 4
- #endif
-
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
- #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
- #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
- #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
- #define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
-
- /* FreeRTOS中断服务函数相关定义 */
- #define xPortPendSVHandler PendSV_Handler
- #define vPortSVCHandler SVC_Handler
-
- /* 断言 */
- #define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
- #define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
-
- /* FreeRTOS MPU 特殊定义 */
- //#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
- //#define configTOTAL_MPU_REGIONS 8
- //#define configTEX_S_C_B_FLASH 0x07UL
- //#define configTEX_S_C_B_SRAM 0x07UL
- //#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
- //#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
-
- /* ARMv8-M 安全侧端口相关定义。 */
- //#define secureconfigMAX_SECURE_CONTEXTS 5
-
- #endif /* FREERTOS_CONFIG_H */

FreeRTOS从管理的堆中,自动分配任务的堆栈空间和任务块(TCB)所需内存
- BaseType_t xTaskCreate(
- TaskFunction_t pxTaskCode,
- const char * const pcName,
- const configSTACK_DEPTH_TYPE usStackDepth,
- void * const pvParameters,
- UBaseType_t uxPriority,
- TaskHandle_t * const pxCreatedTask);
- BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
- const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
- const configSTACK_DEPTH_TYPE usStackDepth,
- void * const pvParameters,
- UBaseType_t uxPriority,
- TaskHandle_t * const pxCreatedTask )
- {
- TCB_t *pxNewTCB;
- BaseType_t xReturn;
-
- /* If the stack grows down then allocate the stack then the TCB so the stack
- does not grow into the TCB. Likewise if the stack grows up then allocate
- the TCB then the stack. */
- #if( portSTACK_GROWTH > 0 )
- {
- /* Allocate space for the TCB. Where the memory comes from depends on
- the implementation of the port malloc function and whether or not static
- allocation is being used. */
- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
-
- if( pxNewTCB != NULL )
- {
- /* Allocate space for the stack used by the task being created.
- The base of the stack memory stored in the TCB so the task can
- be deleted later if required. */
- pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
-
- if( pxNewTCB->pxStack == NULL )
- {
- /* Could not allocate the stack. Delete the allocated TCB. */
- vPortFree( pxNewTCB );
- pxNewTCB = NULL;
- }
- }
- }
- #else /* portSTACK_GROWTH */
- {
- StackType_t *pxStack;
-
- /* Allocate space for the stack used by the task being created. */
- pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */
-
- if( pxStack != NULL )
- {
- /* Allocate space for the TCB. */
- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */
-
- if( pxNewTCB != NULL )
- {
- /* Store the stack location in the TCB. */
- pxNewTCB->pxStack = pxStack;
- }
- else
- {
- /* The stack cannot be used as the TCB was not created. Free
- it again. */
- vPortFree( pxStack );
- }
- }
- else
- {
- pxNewTCB = NULL;
- }
- }
- #endif /* portSTACK_GROWTH */
-
- if( pxNewTCB != NULL )
- {
- #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
- {
- /* Tasks can be created statically or dynamically, so note this
- task was created dynamically in case it is later deleted. */
- pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
- }
- #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */
-
- prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
- prvAddNewTaskToReadyList( pxNewTCB );
- xReturn = pdPASS;
- }
- else
- {
- xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
- }
-
- return xReturn;
- }

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

用户自己分配任务栈内存
- TaskHandle_t xTaskCreateStatic(
- TaskFunction_t pxTaskCode,
- const char * const pcName,
- const uint32_t ulStackDepth,
- void * const pvParameters,
- UBaseType_t uxPriority,
- StackType_t * const puxStackBuffer,
- StaticTask_t * const pxTaskBuffer);
- /* Dimensions of the buffer that the task being created will use as its stack.
- NOTE: This is the number of words the stack will hold, not the number of
- bytes. For example, if each stack item is 32-bits, and this is set to 100,
- then 400 bytes (100 * 32-bits) will be allocated. */
- #define STACK_SIZE 200
-
- /* Structure that will hold the TCB of the task being created. */
- StaticTask_t xTaskBuffer;
-
- /* Buffer that the task being created will use as its stack. Note this is
- an array of StackType_t variables. The size of StackType_t is dependent on
- the RTOS port. */
- StackType_t xStack[ STACK_SIZE ];
-
- /* Function that implements the task being created. */
- void vTaskCode( void * pvParameters )
- {
- /* The parameter value is expected to be 1 as 1 is passed in the
- pvParameters value in the call to xTaskCreateStatic(). */
- configASSERT( ( uint32_t ) pvParameters == 1UL );
-
- for( ;; )
- {
- /* Task code goes here. */
- }
- }
-
- /* Function that creates a task. */
- void vOtherFunction( void )
- {
- TaskHandle_t xHandle = NULL;
-
- /* Create the task without using any dynamic memory allocation. */
- xHandle = xTaskCreateStatic(
- vTaskCode, /* Function that implements the task. */
- "NAME", /* Text name for the task. */
- STACK_SIZE, /* Number of indexes in the xStack array. */
- ( void * ) 1, /* Parameter passed into the task. */
- tskIDLE_PRIORITY,/* Priority at which the task is created. */
- xStack, /* Array to use as the task's stack. */
- &xTaskBuffer ); /* Variable to hold the task's data structure. */
-
- /* puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
- been created, and xHandle will be the task's handle. Use the handle
- to suspend the task. */
- vTaskSuspend( xHandle );
- }

被删除的任务将从就绪任务,阻塞任务,挂起任务和事件列表中移除
传入参数为NULL时,代表删除自身任务,此时该任务的堆栈内存由空闲任务释放
如果传入参数是其他任务,则在该函数进行释放
在临界区不会进行任务的切换,可以在临界区内创建任务。
进入临界区taskENTER_CRITICAL()
退出临界区taskEXIT_CRITICAL()
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数
暂停任意任务。无论任务优先级如何,任务被暂停后将永远无法获取任何微控制器处理时间。
对 vTaskSuspend 的调用不会累积次数,例如:若在同一任务上调用 vTaskSuspend () 两次,将仍然仅需调用一次 vTaskResume (),即可准备完毕暂停的任务。
参数:
xTaskToSuspend | 被挂起的任务句柄。传递空句柄将导致调用任务被暂停。 |
当参数为NULL,代表挂起当前任务
void vTaskResume( TaskHandle_t xTaskToResume );
必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数。
恢复已挂起的任务。
由一次或多次调用 vTaskSuspend () 而挂起的任务可通过单次调用 vTaskResume () 重新运行。
参数:
xTaskToResume | 要恢复的任务句柄 |
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
必须将 include_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 定义为 1 才能使用此函数。
可从 ISR 内调用的恢复挂起任务的函数。
由多次调用 vTaskSuspend() 中的一次调用挂起的任务可通过单次调用 xTaskResumeFromISR() 重新运行。
xTaskResumeFromISR() 通常被视为危险函数,因为其操作未被锁定。 因此,如果中断可能在任务被挂起之前到达, 从而中断丢失, 则绝对不应使用该函数 来同步任务与中断。 可使用信号量, 或者最好是直达任务通知,来避免这种可能性。
中断优先级数值越小,优先级越高
任务优先级数值越小,优先级越低
中断服务程序中调用API函数时中断优先级不能高于FreeRTOS管理的最高优先级(5-15)
- TaskHandle_t xHandle;
-
- void vAFunction( void )
- {
- // Create a task, storing the handle.
- xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
- // ... Rest of code.
- }
-
- void vTaskCode( void *pvParameters )
- {
- // The task being suspended and resumed.
- for( ;; )
- {
- // ... Perform some function here.
-
- // The task suspends itself.
- vTaskSuspend( NULL );
-
- // The task is now suspended, so will not reach here until the ISR resumes it.
- }
- }
-
-
- void vAnExampleISR( void )
- {
- BaseType_t xYieldRequired;
-
- // Resume the suspended task.
- xYieldRequired = xTaskResumeFromISR( xHandle );
-
- // We should switch context so the ISR returns to a different task.
- // NOTE: How this is done depends on the port you are using. Check
- // the documentation and examples for your port.
- portYIELD_FROM_ISR( xYieldRequired );
- }

调度器没被挂起,就将挂起任务添加到就绪列表
调度器被挂起,就将挂起任务添加到等待就绪列表,等调度器被挂起后,会自动添加到就绪列表
在使用在中断中恢复被挂起的任务 xTaskResumeFromISR()函数时,需要将中断分组设置为4,即全部为抢占式优先级
通过SysTickc触发PenSV中断用来实现任务切换
中断服务函数的优先级需要在FreeRTOS管理的范围内,只能调用带有“FromISR”的API函数
FreeRTOS的中断管理通过BASEPRI寄存器实现的
关中断后不能使用vTaskDelay()函数进行延时,因为该函数会打开中断
临界区:指不能被打断的代码段
进入临界段代码前需要关中断,执行完后需要开中断
保护函数可以进行成对使用(开多少关多少)和嵌套使用
任务调度器挂起后就不能进行任务切换,挂起时不需要关闭中断
只是防止任务之间的资源抢夺,中断可以照常响应
挂起时会把就绪列表任务放到等待就绪列表,恢复时会将等待就绪列表的任务放回就绪列表,并切换优先级最高的任务执行
挂起调度器时,uxSchedulerSuspend+1
当uxSchedulerSuspend不为0时SysTick无法触发PendSV中断,从而无法切换任务
恢复调度器时,uxSchedulerSuspend-1 直到等于0时 允许被调度
列表类似链表,是一个双项环形链表,列表项相当于节点,用来存放列表中的项目
xItem Value 用于进行升序排列
pvOwner指向任务控制块
仅用于标记列表的末尾和挂载其他插入列表中(方便第一个列表项的插入)的列表项,所以没有pxOwner和pxContainer两个成员变量
xItemvalue默认数值时最大为0xFFFFFFFF
按照升序方式进行排序插入
void vListInsert(List* const pxList,ListItem * const pxNewListItem)
- void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
- {
- ListItem_t *pxIterator;
- const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
- if( xValueOfInsertion == portMAX_DELAY )
- {
- pxIterator = pxList->xListEnd.pxPrevious;
- }
- else
- {
-
- for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
- {
- /* There is nothing to do here, just iterating to the wanted
- insertion position. */
- }
- }
-
- pxNewListItem->pxNext = pxIterator->pxNext;
- pxNewListItem->pxNext->pxPrevious = pxNewListItem;
- pxNewListItem->pxPrevious = pxIterator;
- pxIterator->pxNext = pxNewListItem;
-
- /* Remember which list the item is in. This allows fast removal of the
- item later. */
- pxNewListItem->pxContainer = pxList;
-
- ( pxList->uxNumberOfItems )++;
- }

用于将列表项插入到列表pxIndex指针的前面,是无序插入
- void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
- {
- ListItem_t * const pxIndex = pxList->pxIndex;
-
- /* Only effective when configASSERT() is also defined, these tests may catch
- the list data structures being overwritten in memory. They will not catch
- data errors caused by incorrect configuration or use of FreeRTOS. */
- listTEST_LIST_INTEGRITY( pxList );
- listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
-
- /* Insert a new list item into pxList, but rather than sort the list,
- makes the new list item the last item to be removed by a call to
- listGET_OWNER_OF_NEXT_ENTRY(). */
- pxNewListItem->pxNext = pxIndex;
- pxNewListItem->pxPrevious = pxIndex->pxPrevious;
-
- /* Only used during decision coverage testing. */
- mtCOVERAGE_TEST_DELAY();
-
- pxIndex->pxPrevious->pxNext = pxNewListItem;
- pxIndex->pxPrevious = pxNewListItem;
-
- /* Remember which list the item is in. */
- pxNewListItem->pxContainer = pxList;
-
- ( pxList->uxNumberOfItems )++;
- }

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
因为列表项里面的pxContainer变量指明了所属列表,因此传入参数只需要列表项一个
- UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
- {
- /* The list item knows which list it is in. Obtain the list from the list
- item. */
- List_t * const pxList = pxItemToRemove->pxContainer;
-
- pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
- pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
-
- /* Only used during decision coverage testing. */
- mtCOVERAGE_TEST_DELAY();
-
- /* Make sure the index is left pointing to a valid item. */
- if( pxList->pxIndex == pxItemToRemove )
- {
- pxList->pxIndex = pxItemToRemove->pxPrevious;
- }
- else
- {
- mtCOVERAGE_TEST_MARKER();
- }
-
- pxItemToRemove->pxContainer = NULL;
- ( pxList->uxNumberOfItems )--;
-
- return pxList->uxNumberOfItems;
- }

prvStartFirstTask() 开启第一个任务
vPortSVCHandler() SVC中断服务函数
SVC中断只在第一次启动任务时调用,以后不会调用,后面任务切换通过PenSV实现
Free使用双堆栈指针,裸机只使用MSP
启动第一个任务时回(动态创建)先初始化一个空闲任务和软件定时器(可选的,如果使能了软件定时器)(有自己的堆栈),然后软件定时器的堆栈内容会被赋值到CPU寄存器中,接着PSP指针指向任务函数,开始执行定时器任务(portTASK_FUNCTION())
软件定时器的任务优先级最高为31 空闲任务(IDLE)优先级最低为0
软件定时器相当于裸机的定时器,可以定时中断,在FreeRTOS中可以扩展出很多个定时器出来
软件定时器的使用相当于扩展了定时器的数量,允许创建更多的定时业务
调用xPortStartScheduler完成任务调度器
本质就是CPU寄存器的切换
例如任务A切换到任务B 只需要将任务A的寄存器保存到自身堆栈 将任务B堆栈中寄存器的值恢复到CPU寄存器中
自动出栈压栈过程用的是PSP
任务切换过程在PendSV中断函数中完成
1.滴答定时器中断调用(正常任务切换使用方法)
2.使用portYIELD()函数调用
本质上都是通过向中断控制及状态寄存器ICSR的bit28写入1来启动PenSV中断
针对同等优先级的任务
同等优先级任务轮流地享有相同的 CPU 时间(可设置),叫时间片,在FreRTOS中,一个时间片就等于sysTick 中断周期
一个时间片的大小取决于滴答定时器的中断时间
void vTaskGetRunTimeStats( char *pcWriteBuffer );
除了调用外还需要实现其他函数功能
configGENERATE_RUN_TIME_STATS、configUSE_STATS_FORMATTING_FUNCTIONS 和 configSUPPORT_DYNAMIC_ALLOCATION 必须全部定义为 1,该函数才可用。该应用程序 还必须提供 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 和 portGET_RUN_TIME_COUNTER_VALUE 的定义, 以便分别配置外设定时器/计数器和返回定时器的当前计数值。计数器的频率应该至少是滴答计数的 10 倍
vTaskDelay()和vTaskDelayUntil()会将任务变成阻塞态,但是delay_ms()不会,任务依旧处于就绪态
会先挂载任务调度器,然后将当前任务挂载到阻塞列表,然后恢复任务调度器,再判断是否需要进行任务切换。
在滴答定时器中断里面会进行计时,获取阻塞列表的最小延时时间,到达延时时间后将相应的任务移除阻塞列表,并且移动到就绪列表中(滴答定时器中断后面还会实现时间片 抢占式调度)
绝对延时是指整个任务周期的延时,是将整个任务周期作为一个整体。
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
pxPreviousWakeTime | 指向一个变量的指针,该变量 用于保存任务最后一次解除阻塞的时间。 该变量在第一次使用前 必须用当前时间进行初始化(见下方示例)。 在这之后,该变量 会在 vTaskDelayUntil() 中自动更新。 |
xTimeIncrement | 周期时间段。 该任务将在 (*pxPreviousWakeTime + xTimeIncrement)时间解除阻塞。 配合相同的 xTimeIncrement 参数值 调用 vTaskDelayUntil 将导致任务 以固定的间隔期执行。 |
- //示例用法:
- // Perform an action every 10 ticks.
- void vTaskFunction( void * pvParameters )
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 10;
-
- // Initialise the xLastWakeTime variable with the current time.
- xLastWakeTime = xTaskGetTickCount();
-
- for( ;; )
- {
- // Wait for the next cycle.
- vTaskDelayUntil( &xLastWakeTime, xFrequency );
-
- // Perform action here.
- }
- }

用于任务之间 中断之间 任务与中断之间的消息(数值)传递 相较于全局变量不会出现数据出错,因为写队列读队列是在临界区进行操作的
基于队列可以实现很多功能 如队列集 互斥信号量 技术型信号量 二值信号量 递归互斥信号量
队列支持先进先出和后进后出,可以设置阻塞时间,在设定时间内等待写或读完成,或者死等。
多个任务出现入队阻塞时
区别在于动态创建无需手动创建内存
QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxQueueBuffer );
参数:
uxQueueLength | 队列可同时容纳的最大项目数 。 |
uxItemSize | 存储队列中的每个数据项所需的大小(以字节为单位)。 数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 的大小必须相同。 |
pucQueueStorageBuffer | 如果 uxItemSize 非零,则 pucQueueStorageBuffer 必须 指向一个至少足够大的 uint8_t 数组, 以容纳队列中可以同时存在的最大项数, 即 (uxQueueLength * uxItemSize) 个字节。 如果 uxItemSize 为零,则 pucQueueStorageBuffer 可以为 Null。 |
pxQueueBuffer | 必须指向 StaticQueue_t 类型的变量, 该变量将用于保存队列的数据结构体。 |
返回:
如果队列创建成功,则返回所创建队列的句柄 则返回已创建队列的句柄。 如果 pxQueueBuffer 为 NULL,则返回 NULL。
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数:
uxQueueLength | 队列可同时容纳的最大项目数 。 |
uxItemSize | 存储队列中的每个数据项所需的大小(以字节为单位)。 数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 必须大小相同。 |
返回:
如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。
判断队列是否已满
xQueueReceive()读完会移除已读信息
xQueuePeek()读完不会移除已读信息
进入临界区后需要判断队列是否为空 以及 调度器是否被挂起
解决同步问题,实现对共享资源的访问
本质就是队列长度为1的队列 队列只有空和满两种情况
信号量的计数值最大值为1时 就是二值信号量 不是1时 是计数型信号量
函数的详细说明可查看官方文档https://www.freertos.org/zh-cn-cmn-s/a00113.html
用于事件计数和资源管理
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
参数:
uxMaxCount | 可以达到的最大计数值。 当信号量达到此值时,它不能再被“给定”。 |
uxInitialCount | 创建信号量时分配给信号量的计数值。 |
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
参数:
xSemaphore | 正在查询的信号量的句柄。 |
返回:
如果信号量是计数信号量,则返回信号量的当前计数值 。 如果信号量是二进制信号量, 则当信号量可用时,返回 1,当信号量不可用时, 返回 0。
xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
参数:
xSemaphore | 正在获取的信号量的句柄——在创建信号量时获得。 |
xTicksToWait | 等待信号量变为可用的时间(以滴答为单位)。宏 portTICK_PERIOD_MS 可用于将其转换为实时。可以用一个为零的阻塞时间来轮询信号量。 如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。 |
返回:
如果获得信号量,则返回 pdTRUE;如果 xTicksToWait 过期,信号量不可用,则返回 pdFALSE。
高优先级慢执行 低优先级先执行
由于信号量被低优先级获取并且不释放就会出现优先级翻转(高优先级获取不到信号量进入阻塞)
可以使用互斥信号量解决
就是一个具有优先级继承的信号量
信号量的释放和获取与二值信号量一样 xSemaphoreGive xSemaphoreTake
如果低优先级获取到信号量 高优先级会阻塞 然后将低优先级任务提高到与自己同等优先级水平
这样高优先级任务的阻塞时间等于低优先级任务获得信号量的时间
不支持中断调用,因为互斥信号量有任务优先级继承 中断不是任务 没有任务优先级 而且中断服务函数不能因为等待互斥信号量而阻塞(因为互斥信号量要设置阻塞时间,而二值信号量不需要)
SemaphoreHandle_t xSemaphoreCreateMutex( void )
如果已成功创建互斥锁类型信号量,则返回创建的 互斥锁的句柄。 否则返回 NULL。
其他函数与二值信号量一致
xSemaphoreCreateMutex()和 xSemaphoreCreateMutexStatic()用于创建非递归互斥锁。xSemaphoreCreateRecursiveMutex()用于创建递归互斥锁。
非递归互斥锁只能被一个任务 获取一次, 如果同一个任务想再次获取则会失败, 因为当任务第一次释放互斥锁时,互斥锁就一直 处于释放状态。
与非递归互斥锁相反,递归互斥锁可以被同一个任务获取很多次, 获取多少次就需要释放多少次, 此时才会返回递归互斥锁。
递归互斥锁采用优先级继承算法
一个队列之间传递的消息为一种类型
一个队列之间传递的消息为多种类型
往对列集添加和移除队列时,队列里面不能有消息
事件标志位:用一个位标志事件是否发生
事件标志组:标志位的集合 就是一个整数 变量类型为EventBits_t(要么16位要么32位数据)
任何任务和中断都可以读写这些位
队列信号量只会唤醒一个任务
事件标志组会唤醒所有符合的任务 相当于“广播”
可以等待一位 也 可以等待多位
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
队列 信号量 事件标志组 也可以用来任务通知 但是占用内存大 需要另外创建结构体
任务通知就是TCB(任务控制块)的结构体成员变量ulNotifiedValue
占用内存小 效率高 但是无法给中断并且进行广播 发送受阻不会阻塞 接收可以发生阻塞
可以用于任务和中断中
发送通知都是基于这个函数实现的
只能用于任务中
接受通知都是基于这个函数实现的
硬件定时器:芯片自带的定时器模块 精度很高 达到定时时间后会自动触发中断 数量有限
软件定时器:具有定时功能的软件 只要内存够多 可以有无限个
软件定时器的超时回调函数用软件定时器任务调用 在回调函数中不能调用会导致任务阻塞的API
软件定时器优先级最大 保证定时时间一到立马执行
新创建的软件定时器处于休眠状态 未运行
软件定时器有两个定时器列表 一个是当前定时器列表 一个是溢出定时器列表
还有一个队列用来传递消息 控制软件定时器的开启 停止 复位等等
软件定时器ID用于多个软件定时器使用同一个回调函数
当发送命令队列或者定时器超时 会唤醒软件定时器任务(刚创建时处于休眠状态)
低功耗模式本质就是通过调用WFI指令进入睡眠模式
低功耗模式的实现就是让空闲任务执行期间进入低功耗模式,其他任务下退出低功耗模式
需要将滴答定时器的中断周期修改为低功耗运行时间 退出低功耗后 补上系统时钟节拍数
定义进入低功耗函数操作
通过调用configPRE_SLEEP_RPOCESSING()
通过调用configPost_Sleep_RPOCESSING()
使用最适应算法 支持释放内存 不能将相邻空闲内存合成一个大的空闲内存 会产生内存碎片
适用于创建的任务堆栈都相同 才不会出现碎片化
直接调用C库的malloc()和free()
使用首次适应算法 能够实现内存申请和释放 将空闲和相邻内存合并 减少碎片化现象
适用于频繁申请 删除内存
在 heap_4算法的基础上实现的 增加了管理多个非连续内存区域的能力
需要用户手动定义内存堆 即内存的起始地址和大小
适用于嵌入式系统中 内存地址不连续的场景
申请内存的单位是字节
由于8字节对齐 以及 内存块需要占用内存 因此实际占用的内存比申请的值大
申请内存之后要记得释放 否则可能出现内存泄漏
内存块结构跟链表结构相似 一个成员变量表示当前内存大小 另一个成员变量指向下一个内存块
内存申请会生成对应的内存块 并且将内存块移除内存空闲列表
内存释放将内存块放到内存空闲列表
在一段内存没有被释放之前不要再调用pvPortMalloc()为其再分配内存 否则会出现内存泄漏
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。