当前位置:首页 >> 信息与通信 >>

freertos资料整理

用了半天时间对 FreeRTOS 有了一个初步的认识,大概总结一下,其中混杂了 系统实现和实际应用方面的问题。 现只是以应用为目的,实现方面待以后进一步研究。 1.FreeRTOS 提供的功能包括:任务管理、时间管理、信号量、消息队列、内存 管理。与平台有关的文件包含在 portable 文件夹中,主要是 port.c, portmacro.h 两个文件。平台无关的文件主要是:list.c(基本链表结构), queue.c(包括消息队 列,信号量的实现), croutine.c,tasks.c(任务管理,时间管理)。 命名协定 RTOS 内核与范例程序源代码使用下面的协定: 变量 char 类型的变量以 c 为前缀 short 类型的变量以 s 为前缀 long 类型的变量以 l 为前缀 float 类型的变量以 f 为前缀 double 类型的变量以 d 为前缀 枚举变量以 e 为前缀 其他类型(如结构体)以 x 为前缀 指针有一个额外的前缀 p , 例如 short 类型的指针前缀为 ps 无符号类型的变量有一个额外的前缀 u , 例如无符号 short 类型的变量前缀为 us 函数 文件内部函数以 prv 为前缀 API 函数以其返回值类型为前缀,按照前面对变量的定义 函数的名字以其所在的文件名开头。如 vTaskDelete 函数在 Task.c 文件中定义 数据类型 数据类型并不直接在 RTOS 内核内部引用。相反,每个平台都有其自身的定义 方式。例如,char 类型定义为 portCHAR,short 类型定义为 portSHORT 等。 范例程序源代码使用的就是这种符号,但这并不是必须的,你可以在你的程序中 使用任何你喜欢的符号。 此外,有两种额外的类型要为每种平台定义。分别是: portTickType 可配置为 16 位的无符号类型或 32 位的无符号类型。参考 API 文档中的定制部

分获取详细信息。 portBASE_TYPE 为特定体系定义的最有效率的数据类型。 如果 portBASE_TYPE 定义为 char 则必须要特别小心的保证用来作为函数返回 值的 signed char 可以为负数,用于指示错误。

2. FreeRTOS 内核支持优先级调度算法,每个任务可根据重要程度的不同被赋 予一定的优先级,CPU 总是让处于就绪态的、优先级最高的任务先运行。 FreeRT0S 内核同时支持轮换调度算法, 系统允许不同的任务使用相同的优先级, 在没有更高优先级任务就绪的情况下, 同一优先级的任务共享 CPU 的使用时间。

3.freertos 既可以配置为可抢占内核也可以配置为不可抢占内核。当 FreeRTOS 被设置为可剥夺型内核时,处于就绪态的高优先级任务能剥夺低优先级任务的 CPU 使用权,这样可保证系统满足实时性的要求;当 FreeRTOS 被设置为不可 剥夺型内核时,处于就绪态的高优先级任务只有等当前运行任务主动释放 CPU 的使用权后才能获得运行,这样可提高 CPU 的运行效率。

4.任务管理 系统为每个任务分配一个 TCB 结构 typedef struct tskTaskControlBlock { volatile portSTACK_TYPE xListItem xGenericListItem; *pxTopOfStack;//指向堆栈顶 //通过它将任务连入就绪链表或者延时链表或

者挂起链表中, xListItem 包含其 TCB 指针 xListItem xEventListItem;//通过它把任务连入事件等待链表 uxPriority;//优先级 //指向堆栈起始位置

unsigned portBASE_TYPE portSTACK_TYPE

*pxStack;

signed portCHAR

pcTaskName[ configMAX_TASK_NAME_LEN ];

。。。。。。。。。。。。。。。。省略一些次要结构 } tskTCB;

系统的全局变量: static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; 就绪队列 static xList xDelayedTaskList1; static xList xDelayedTaskList2; 两个延时任务队列 static xList * volatile pxDelayedTaskList; static xList * volatile pxOverflowDelayedTaskList; 两个延时队列的指针,应该 是可互换的。 static xList xPendingReadyList; static volatile xList xTasksWaitingTermination; 等待结束队列

static volatile unsigned portBASE_TYPE uxTasksDeleted = ( unsigned portBASE_TYPE ) 0; 结束队列中的个数????? static xList xSuspendedTaskList; 挂起队列

static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks;记录了当 前系统任务的数目 static volatile portTickType xTickCount;是自启动以来系统运行的 ticks 数 static unsigned portBASE_TYPE uxTopUsedPriority; 记录当前系统中被使用的 最高优先级, static volatile unsigned portBASE_TYPE uxTopReadyPriority; 记录当前系统中 处于就绪状态的最高优先级。 static volatile signed portBASE_TYPE xSchedulerRunning ;表示当前调度器 是否在运行,也即内核是否启动了

任务建立和删除,挂起和唤醒

5.时间管理 操作系统总是需要个时钟节拍的, 这个需要硬件支持。 freertos 同样需要一个 time tick 产生器,通常是用处理器的硬件定时器来实现这个功能。(时间片轮转调度 中和延时时间控制??) 它周期性的产生定时中断, 所谓的时钟节拍管理的核心就是这个定时中断的服务 程序。freertos 的时钟节拍 isr 中除去保存现场,灰度现场这些事情外,核心的 工作就是调用 vTaskIncrementTick()函数。vTaskIncrementTick()函数主要做两 件事情:维护系统时间(以 tick 为单位,多少个节拍);处理那些延时的任务, 如果延时到期,则唤醒任务。

任务可用的延时函数:vTaskDelay();vTaskDelayUntil(); 特别之处在于 vTaskDelayUntil()是一个周期性任务可以利用它可以保证一个固 定的(确定的)常数执行频率,而 vTaskDelay()无法保证。

6.任务间的通信(详见“FreeRTOS 任务间通讯”) 1)当然可以用全局变量的形式通信,但是不安全。 2)队列(xQueueHandle)是 FreeRTOS 中通信所需的主要数据结构。 3)信号量(xSemaphoreHandle),有二进制信号量,计数信号量和互斥信号量,其 都是以队列为基础结构建立。 二进制信号量可以用于中断和任务间的同步。 也就是说希望任务随外部中断而执 行。即外设给出“数据已就绪”信号,系统中断,任务收到此中断信号接收数据。 互斥一般用于都共享资源或数据结构的保护。 因为任务调度不能保证数据不被破 坏。当一个任务需要访问资源,它必须先获得 ('take') 令牌;当访问结束后,它 必须释放令牌 - 允许其他任务能够访问这个资源。 (对此还有待进一步实验研究)。

7.系统配置 freeRTOS 配置在:FREERTOS_CONFIG.H 里面,条目如下: /* 是否配置成抢先先多任务内核,是 1 的时候,优先级高的任务优先执行。为 0 任务就没有优先级之说,用时间片轮流执行 */ #define configUSE_PREEMPTION 1

/* IDLE 任务的 HOOK 函数,用于 OS 功能扩展,需要你自己编相应函数,名字 是 void vApplicationIdleHook( void ) */ #define configUSE_IDLE_HOOK 0

/* SYSTEM TICK 的 HOOK 函数, 用于 OS 功能扩展, 需要你自己编相应函数, 名字是 void vApplicationTickHook(void ) */ #define configUSE_TICK_HOOK 0

/* 系统 CPU 频率,单位是 Hz */ #define configCPU_CLOCK_HZ 58982400

/* 系统 SYSTEM TICK 每秒钟的发生次数,数值越大系统反应越快,但是 CPU 用在任务切换的开销就越多 */ #define configTICK_RATE_HZ 250

/* 系统任务优先级数。5 说明任务有 5 级优先度。这个数目越大耗费 RAM 越多 */ #define configMAX_PRIORITIES 5

/* 系统最小堆栈尺寸, 注意 128 不是 128 字节, 而是 128 个入栈。 比如 ARM32 位,128 个入栈就是 512 字节 */

#define configMINIMAL_STACK_SIZE

128

/* 系统可用内存。 一般设成除了操作系统和你的程序所用 RAM 外的最大 RAM。 比如 20KRAM 你用了 2K,系统用了 3K,剩下 15 就是最大 HEAP 尺寸。你可 以先设小然后看编译结果往大里加*/ #define configTOTAL_HEAP_SIZE 10240

/* 任务的 PC 名字最大长度, 因为函数名编译完了就不见了, 所以追踪时不知道 哪个名字。16 表示 16 个 char */ #define configMAX_TASK_NAME_LEN 16

/* 是否设定成追踪, 由 PC 端 TraceCon.exe 记录,也可以转到系统显示屏上 */ #define configUSE_TRACE_FACILITY 0

/* 就是 SYSTEM TICK 的长度, 16 是 16 位, 如果是 16 位以下 CPU, 一般选 1; 如果是 32 位系统,一般选 0 */ #define configUSE_16_BIT_TICKS 0

/* 简单理解以下就是和 IDLE TASK 同样优先级的任务执行情况。建议设成 1, 对系统影响不大 */ #define configIDLE_SHOULD_YIELD 1

/* 是否用 MUTEXES。 MUTEXES 是任务间通讯的一种方式,特别是用于任务 共享资源的应用,比如打印机,任务 A 用的时候就排斥别的任务应用,用完了 别的任务才可以应用 */ #define configUSE_MUTEXES 0

/* 确定是否用递归式的 MUTEXES */ #define configUSE_RECURSIVE_MUTEXES 0

/* 是否用计数式的 SEMAPHORES,SEMAPHORES 也是任务间通讯的一种方 式 */ #define configUSE_COUNTING_SEMAPHORES 0

/* 是否应用可切换式的 API。freeRTOS 同一功能 API 有多个,有全功能但是 需求资源和时间较多的,此项使能后就可以用较简单的 API,节省资源和时间, 但是应用限制较多 */ #define configUSE_ALTERNATIVE_API 0

/* 此项用于 DEBUG,来看是否有栈溢出,需要你自己编相应检查函数 void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName ) */ #define configCHECK_FOR_STACK_OVERFLOW 0

/* 用于 DEBUG,登记 SEMAPHORESQ 和 QUEUE 的最大个数,需要在任务 用应用函数 vQueueAddToRegistry()和 vQueueUnregisterQueue() */ #define configQUEUE_REGISTRY_SIZE 10

/* 设定可以改变任务优先度 */ #define INCLUDE_vTaskPrioritySet 1

/* 设定可以查询任务优先度 */ #define INCLUDE_uxTaskPriorityGet 1

/* 设定可以删除任务 */ #define INCLUDE_vTaskDelete 1

/* 据说是可以回收删除任务后的资源(RAM 等)*/ #define INCLUDE_vTaskCleanUpResources 0

/* 设置可以把任务挂起 */ #define INCLUDE_vTaskSuspend /* 设置可以从中断恢复(比如系统睡眠,由中断唤醒 */ #define INCLUDE_vResumeFromISR 1 1

/* 设置任务延迟的绝对时间,比如现在 4:30,延迟到 5:00。时间都是绝对 时间 */ #define INCLUDE_vTaskDelayUntil 1

/* 设置任务延时,比如延迟 30 分钟,相对的时间,现在什么时间,不需要知道 */ #define INCLUDE_vTaskDelay 1

/* 设置取得当前任务分配器的状态 */ #define INCLUDE_xTaskGetSchedulerState 1

/* 设置当前任务是由哪个任务开启的 */ #define INCLUDE_xTaskGetCurrentTaskHandle 1

/* 是否使能这一函数,还数的目的是返回任务执行后任务堆栈的最小未用数量, 同样是为防止堆栈溢出 */ #define INCLUDE_uxTaskGetStackHighWaterMark 0

/* 是用用协程。协程公用堆栈,节省 RAM,但是没有任务优先级高,也无法和 任务通讯 */ #define configUSE_CO_ROUTINES 0

/* 所有协程的最大优先级数, 协程优先级永远低于任务。 就是系统先执行任务, 所有任务执行完了才执行协程。*/ #define configMAX_CO_ROUTINE_PRIORITIES 1

/* 系统内核的中断优先级,中断优先级越低,越不会影响其他中断。一般设成 最低 */ #define configKERNEL_INTERRUPT_PRIORITY processor] [dependent of

/* 系统 SVC 中断优先级,这两项都在在 M3 和 PIC32 上应用 */ #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]

#endif /* FREERTOS_CONFIG_H */

配置 FreeRTOS
FreeRTOS 是高度可配置的。所有的可配置项都在 FreeRTOSConfig.h 文件中。每一个 Demo 程序中都包含了一个配置好的 FreeRTOSConfig.h 文件,可以以 Demo 程序中的 FreeRTOSConfig.h 文件作为模板,在其基础上加以修改。 下面先给出一个典型的 FreeRTOSConfig.h 文件,然后再逐项加以说明。
[cpp] view plaincopy

1. #ifndef FREERTOS_CONFIG_H 2. #define FREERTOS_CONFIG_H 3. 4. /* Here is a good place to include header files that are required across 5. your application. */ 6. #include "something.h" 7. 8. #define configUSE_PREEMPTION 9. #define configUSE_IDLE_HOOK 10. #define configUSE_TICK_HOOK 11. #define configCPU_CLOCK_HZ 12. #define configTICK_RATE_HZ 13. #define configMAX_PRIORITIES 14. #define configMINIMAL_STACK_SIZE 15. #define configTOTAL_HEAP_SIZE 16. #define configMAX_TASK_NAME_LEN 17. #define configUSE_TRACE_FACILITY 18. #define configUSE_16_BIT_TICKS 19. #define configIDLE_SHOULD_YIELD 20. #define configUSE_MUTEXES 21. #define configUSE_RECURSIVE_MUTEXES 22. #define configUSE_COUNTING_SEMAPHORES 23. #define configUSE_ALTERNATIVE_API 24. #define configCHECK_FOR_STACK_OVERFLOW 25. #define configQUEUE_REGISTRY_SIZE 26. #define configGENERATE_RUN_TIME_STATS 27. 28. #define configUSE_CO_ROUTINES 29. #define configMAX_CO_ROUTINE_PRIORITIES 30. 31. #define configUSE_TIMERS 32. #define configTIMER_TASK_PRIORITY 33. #define configTIMER_QUEUE_LENGTH 34. #define configTIMER_TASK_STACK_DEPTH 35. 36. #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] 1 3 10 configMINIMAL_STACK_SIZE 0 1 1 0 0 58982400 250 5 128 10240 16 0 0 1 0 0 0 0 0 10 0

37. #define configMAX_SYSCALL_INTERRUPT_PRIORITY application] 38. 39. #define configASSERT( ( x ) ) FILE__, __LINE__ ) 40. 41. #define INCLUDE_vTaskPrioritySet 42. #define INCLUDE_uxTaskPriorityGet 43. #define INCLUDE_vTaskDelete 44. #define INCLUDE_vTaskSuspend 45. #define INCLUDE_xResumeFromISR 46. #define INCLUDE_vTaskDelayUntil 47. #define INCLUDE_vTaskDelay 48. #define INCLUDE_xTaskGetSchedulerState 49. #define INCLUDE_xTaskGetCurrentTaskHandle 50. #define INCLUDE_uxTaskGetStackHighWaterMark 51. #define INCLUDE_xTaskGetIdleTaskHandle 52. #define INCLUDE_xTimerGetTimerDaemonTaskHandle 53. #define INCLUDE_pcTaskGetTaskName 54. 55. #endif /* FREERTOS_CONFIG_H */

[dependent on processor and

if( ( x ) == 0 ) vCallAssert( __

1 1 1 1 1 1 1 1 1 0 0 0 0

可配置的参数
configUSE_PREEMPTION
设为 1 则采用抢占式调度器, 设为 0 则采用协作式调度器。

configUSE_IDLE_HOOK
设为 1 则使能 idle hook,设为 0 则禁止 idle hook。

configUSE_TICK_HOOK
设为 1 则使能 tick hook,设为 0 则禁止 tick hook。

configCPU_CLOCK_HZ
设置为 MCU 内核的工作频率,以 Hz 为单位。配置 FreeRTOS 的时钟 Tick 时会用到。对 不同的移植代码也可能不使用这个参数。如果确定移植代码中不用它就可以注释掉这行。

configTICK_RATE_HZ
FreeRTOS 的时钟 Tick 的频率,也就是 FreeRTOS 用到的定时中断的产生频率。这个频率 越高则定时的精度越高,但是由此带来的开销也越大。FreeRTOS 自带的 Demo 程序中将 TickRate 设为了 1000Hz 只是用来测试内核的性能的。实际的应用程序应该根据需要改为 较小的数值。

当多个任务共用一个优先级时,内核调度器回来每次时钟中断到来后轮转切换任务(round robin),因此,更高的 Tick Rate 会导致任务的时间片“time slice”变短。

configMAX_PRIORITIES
程序中可以使用的最大优先级。FreeRTOS 会为每个优先级建立一个链表,因此没多一个 优先级都会增加些 RAM 的开销。 所以, 要根据程序中需要多少种不同的优先级来设置这个 参数。

configMINIMAL_STACK_SIZE
任务堆栈的最小大小,FreeRTOS 根据这个参数来给 idle task 分配堆栈空间。这个值如果 设置的比实际需要的空间小,会导致程序挂掉。因此,最好不要减小 Demo 程序中给出的 大小。

configTOTAL_HEAP_SIZE
设置堆空间(Heap)的大小。只有当程序中采用 FreeRTOS 提供的内存分配算法时才会用 到。

configMAX_TASK_NAME_LEN
任务名称最大的长度,这个长度是以字节为单位的,并且包括最后的 NULL 结束字节。

configUSE_TRACE_FACILITY
如果程序中需要用到 TRACE 功能,则需将这个宏设为 1。否则设为 0。开启 TRACE 功能 后,RAM 占用量会增大许多,因此在设为 1 之前请三思。

configUSE_16_BIT_TICKS
将 configUSE_16_BIT_TICKS 设为 1 后 portTickType 将被定义为无符号的 16 位整形类 型,configUSE_16_BIT_TICKS 设为 0 后 portTickType 则被定义为无符号的 32 位整型。

configIDLE_SHOULD_YIELD
这个参数控制那些优先级与 idle 任务相同的任务的行为,并且只有当内核被配置为抢占式 任务调度时才有实际作用。 内核对具有同样优先级的任务会采用时间片轮转调度算法。当任务的优先级高于 idle 任务 时,各个任务分到的时间片是同样大小的。 但当任务的优先级与 idle 任务相同时情况就有些不同了。当 configIDLE_SHOULD_YIELD 被配置为 1 时,当任何优先级与 idle 任务相同的任务处于就绪态时,idle 任务会立刻要求 调度器进行任务切换。 这会使 idle 任务占用最少的 CPU 时间, 但同时会使得优先级与 idle 任 务相同的任务获得的时间片不是同样大小的。 因为 idle 任务会占用某个任务的部分时间片。

configUSE_MUTEXES
设为 1 则程序中会包含 mutex 相关的代码,设为 0 则忽略相关的代码。

configUSE_RECURSIVE_MUTEXES
设为 1 则程序中会包含 recursive mutex 相关的代码,设为 0 则忽略相关的代码。

configUSE_COUNTING_SEMAPHORES

设为 1 则程序中会包含 semaphore 相关的代码,设为 0 则忽略相关的代码。

configUSE_ALTERNATIVE_API
设为 1 则程序中会包含一些关于队列操作的额外 API 函数,设为 0 则忽略相关的代码。 这些额外提供的 API 运行速度更快,但是临界区(关中断)的长度也更长。有利也有弊, 是否要采用需要用户自己考虑了。

configCHECK_FOR_STACK_OVERFLOW
控制是否检测堆栈溢出。

configQUEUE_REGISTRY_SIZE
队列注册表有两个作用,但是这两个作用都依赖于调试器的支持: 1. 2. 给队列一个名字,方便调试时辨认是哪个队列。 包含调试器需要的特定信息用来定位队列和信号量。

如果你的调试器没有上述功能,哪个这个注册表就毫无用处,还占用的宝贵的 RAM 空间。

configGENERATE_RUN_TIME_STATS
设置是否产生运行时的统计信息,这些信息只对调试有用,会保存在 RAM 中,占用 RAM 空间。因此,最终程序建议配置成不产生运行时统计信息。

configUSE_CO_ROUTINES
设置为 1 则包含 co-routines 功能, 如果包含了 co-routines 功能, 则编译时需包含 croutine.c 文件

configMAX_CO_ROUTINE_PRIORITIES
co-routines 可以使用的优先级的数量。

configUSE_TIMERS
设置为 1 则包含软件定时器功能。

configTIMER_TASK_PRIORITY
设置软件定时器任务的优先级。

configTIMER_QUEUE_LENGTH
设置软件定时器任务中用到的命令队列的长度。

configTIMER_TASK_STACK_DEPTH
设置软件定时器任务需要的任务堆栈大小。

configKERNEL_INTERRUPT_PRIORITY 和 configMAX_SYSCALL_INTERRUPT_PRIORITY
Cortex-M3, PIC24, dsPIC, PIC32, SuperH 和 RX600 的移植代码中会使用到 configKERNEL_INTERRUPT_PRIORITY. PIC32, RX600 和 Cortex-M 系列会使用到 configMAX_SYSCALL_INTERRUPT_PRIORITY configKERNEL_INTERRUPT_PRIORITY 应该被设为最低优先级。

对那些只定义了 configKERNEL_INTERRUPT_PRIORITY 的系统: configKERNEL_INTERRUPT_PRIORITY 决定了 FreeRTOS 内核使用的优先级。 所有调用 API 函数的中断的优先级都应设为这个值,不调用 API 函数的中断可以设为更高 的优先级。

对那些定义了 configKERNEL_INTERRUPT_PRIORITY 和 configMAX_SYSCALL_INTERRUPT_PRIORITY 的系统: configKERNEL_INTERRUPT_PRIORITY 决定了 FreeRTOS 内核使用的优先级。 configMAX_SYSCALL_INTERRUPT_PRIORITY 决定了可以调用 API 函数的中断的最高优 先级。高于这个值的中断处理函数不能调用任何 API 函数。

configASSERT
宏 configASSERT()的作用类似 C 语言标准库中的宏 assert(),configASSERT() 可以帮助 调试,但是定义了 configASSERT()后会增加程序代码,也会使程序变慢。

以 INCLUDE 开头参数
以 'INCLUDE' 开头的宏允许我们将部分不需要的 API 函数排除在编译生成的代码之外。 这可以使内核代码占用更少的 ROM 和 RAM。 比如,如果代码中需要用到 vTaskDelete 函数则这样写: #defineINCLUDE_vTaskDelete 如果不需要,则这样写: #defineINCLUDE_vTaskDelete 0 1

一、任务创建
1. xTaskCreate
task. h portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters,

unsigned portBASE_TYPE uxPriority, xTaskHandle *pvCreatedTask ); 创建新的任务并添加到任务队列中,准备运行 Parameters: pvTaskCode pcName 指向任务的入口函数. 任务必须执行并且永不返回 (即:无限循环). 描述任务的名字。主要便于调试。最大长度由 configMAX_TASK_NAME_LEN.定义 usStackDepth 指定任务堆栈的大小 ,堆栈能保护变量的数目- 不是字节数. 例如,如果堆 栈为 16 位宽度, usStackDepth 定义为 100, 200 字节, 这些将分配给堆栈。 堆栈嵌套深度(堆栈宽度)不能超多最大值——包含了 size_t 类型的变量 pvParameters uxPriority 指针用于作为一个参数传向创建的任务 任务运行时的优先级

pvCreatedTask 用于传递一个处理——引用创建的任务 返回: pdPASS 是如果任务成功创建并且添加到就绪列中, 另外错误代码在 projdefs. H 文件 定义

2. vTaskDelete
task. h void vTaskDelete( xTaskHandle pxTask ); INCLUDE_vTaskDelete 必须定义为 1,这个函数才能可用。查看配置部分获得更多信息。 从 RTOS 实时内核管理中移除任务。要删除的任务将从就绪,封锁,挂起,事件列表中移 除。 注意:空闲任务负责释放内核分配给已删除任务的内存。因此,如果应用程序调用了 vTaskDelete (),微控制器执行时间,空闲任务不假死是很重要的。内存分配给任务的代码 不会自动释放,应该在任务删除之前。

参数: pxTask 处理要删除的任务。传递 NULL 将引起调用任务删除

二、任务控制
3. vTaskDelay
task. h void vTaskDelay( portTickType xTicksToDelay ); INCLUDE_vTaskDelay 必须设置为 1,这个函数才为可用。参考配置获得更多信息。 延时任务为已知时间片。 任务被锁住剩余的实际时间由时间片率决定。 portTICK_RATE_MS 常量用来用来从时间片速率(一片周期代表着分辨率)来计算实际时间。 vTaskDelay()指定一个任务希望的时间段, 这个时间之后 (调用 vTaskDelay() ) 任务解锁。 例如,指定周期段为 100 时间片,将使任务在调用 vTaskDelay()100 个时间片之后解锁。 vTaskDelay()不提供一个控制周期性任务频率的好方法,像通过代码采取的路径,和其他任 务和中断一样,在调用 vTaskDelay()后 影响频率,因此任务所需的时间下一次执行。 参考 vTaskDelayUntil() ,这个交替的 API 函数设计了执行固定的频率。它是指定的一个绝 对时间(而不是一个相对时间)后,调用任务解锁。 参数: xTicksToDelay 时间数量,调用任务应该锁住的时间片周期

4. vTaskDelayUntil
task. h void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncreme nt ); INCLUDE_vTaskDelayUntil 必须定义为 1,此函数才能用。参考配置获得更多信息。 延时一个任务到指定时间。 这个函数用在循环任务以确保一定频率执行。 这个函数在一个重 要方面上不同于 vTaskDelay(),vTaskDelay() 指定的这个时间(任务希望开启)是与

vTaskDelay() 有关,而 vTaskDelayUntil() 指定是一个绝对时间(任务希望开 启) vTaskDelay()中断任务从调用这个函数开始到指定时间。很难使用 vTaskDelay()自身 来产生一个固定的执行频率,因为:随着调用 vTaskDelay()一个任务开启的时间和任务下 一次调用 vTaskDelay()的时间,这两者之间可能不是固定的【任务可能采取不同方式,可 能通过调用,可能来自中断,或者优先取得每次任务执行的时间】。 而 vTaskDelay()指定 的时间与调用这个函数相关,vTaskDelayUntil() 指定的是绝对时间(任务希望开启的)。 应该注意:vTaskDelayUntil() 如果指定的苏醒时间使用完,将立即返回。因此,一个使用 vTaskDelayUntil() 来周期性的执行的任务,如果执行周期因为任何原因(例如任务是临时 为悬挂状态)暂停而导致任务错过一个或多个执行周期,那么需要重新计算苏醒时间。通过 检查像 pxPreviousWakeTime 可变的参数来组织当前时间片计数。然而在大多数使用中并 不是必须的。 产量 portTICK_RATE_MS 用来计算时间片频率的实时时间- 按照一个时间片 周期。 参数: pxPreviousWakeTime 指定一个变量来掌握任务最后开启的时间。这个变量在第一次使用 中(参考下面的例子)必须使用当前时间来初始化。在 vTaskDelayUntil()中,这个变量是自动修改的 xTimeIncrement 循环周期时间。任务将在一定时间开启 (*pxPreviousWakeTime + xTimeIncrement)。使用相同的 xTimeIncrement 参数值, 来调用 vTaskDelayUntil()将使任务按固定 的周期执行。

5. uxTaskPriorityGet
task. h unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ); 设置 INCLUDE_vTaskPriorityGet 为 1,此函数才能用。参考配置以获得更多信息。 获得任 务的优先级。 参数: pxTask 需要处理的任务. 当传递 NULL 时,将返回所调用任务的优先级 Returns:

pxTask 的优先级

6. vTaskPrioritySet
task. h void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ); 设置 INCLUDE_vTaskPrioritySet 为 1,才能使用此函数。参考配置获得更多信息。 设置任 务的优先级。 如果设置的优先级高于当前执行任务的优先级,则上下文切换将在此函数返 回之前发生。 参数: 需要设置优先级的任务。当传递 NULL,将设置调用任务的优先级

pxTask

uxNewPriority 任务需要设置的优先级

7. vTaskSuspend
task. h void vTaskSuspend( xTaskHandle pxTaskToSuspend ); 设置 INCLUDE_vTaskSuspend 为 1,此函数才能使用。参考配置获得更多信息。 挂起任 务,当挂起一个任务时,不管优先级是多少,不需要占用任何微控制器处理器时间。调用 vTaskSuspend 不会累积——即: 在统一任务中调用 vTaskSuspend 两次, 但只需调用一次 vTaskResume () 来是挂起的任务就绪。 参数: pxTaskToSuspend 处理需要挂起的任务。传递 NULL 将挂起调用此函数的任务。

8. vTaskResume
task. h void vTaskResume( xTaskHandle pxTaskToResume ); 设置 INCLUDE_vTaskSuspend 为 1,此函数才能使用。参考配置获得更多信息。

唤醒挂起的任务。 必须是调用 vTaskSuspend () 后挂起的任务,才有可能通过调 用 vTaskResume ()重新运行。 Parameters: pxTaskToResume 就绪的任务的句柄

9. vTaskResumeFromISR
task. h portBASE_TYPE vTaskResumeFromISR( xTaskHandle pxTaskToResume ); 设置 INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 都为 1,才能使用此 函数。参考配置获得更过信息。从 ISR 中唤醒挂起的任务。可能通过多个 调用 vTaskSuspend()挂起的任务,可以通过调用 xTaskResumeFromISR()再次运行。 vTaskResumeFromISR()不应该用于任务和中断同步,因为可能会在中断发生期间,任务 已经挂起——这样导致错过中断。使用信号量最为同步机制将避免这种偶然性。 参数: pxTaskToResume 就绪任务的句柄 Returns: pdTRUE: 如果唤醒了任务将,引起上下文切换。 pdFALSE.:用于 ISR 确定是否上下文切换

10. vTaskSetApplicationTaskTag
task. h void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxTagV alue ); onfigUSE_APPLICATION_TASK_TAG 必须定义为 1,这个函数才能可用。参考配置获得 更多信息。能为每个任务分配标签值。这个值仅在应用程序中使用,内核本身不使用 它。 RTOS trace macros 文档提供了一个很好的例子——应用程序如何利用这个特点。

参数: 将分配给标签值的任务。传递 NULL 将分配标签给调用的任务。

xTask

pxTagValue 分配给任务的标签值 类型为 pdTASK_HOOK_CODE , 允许一个函数指针赋 值给标签。因此实际上任何值都可以分配。参考下面的例子。

xTaskCallApplicationTaskHook task. H #define traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook( pxCurrentTCB, 0) portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParamete r ); configUSE_APPLICATION_TASK_TAG 必须设置为 1,这个函数才能使用。参考配置获得 更多信息。 每个任务可以分配一个标签值。 正常情况下, 这个值仅用于应用程序, 内核并不存储。 然而, 可以使用标签赋值给钩子(或回调)函数给任务——钩子函数将通过调用 xTaskCallApplicationTaskHook()执行。每个任务定义自己的返回值,或者仅仅定义一个返 回值。 尽管可以使用第一个函数参数来调用任务中的钩子函数, 最常见的使用是任务钩子函 数——描述钩子宏,像每个如下的例子。任务钩子函数必须使用 pdTASK_HOOK_CODE 类型,传递 void *参数,和返回 portBASE_TYPE 类型的值。Void *参数用来传递给钩子函 数的任何信息。 参数: 处理将被调用钩子函数的任务。xTask 当作 NULL 传递,将调用钩子函数赋 值给当前执行的任务 pvParameter 传递给钩子函数的值。可以是指向结构或数值的指针

xTask

三、内核控制
11. vTaskStartScheduler

task. h void vTaskStartScheduler( void ); 启动实时内核处理。在调用之后,内核已经控制执行的任务。当 vTaskStartScheduler() 被 调用时,空闲任务自动创建。如果 vTaskStartScheduler() 成功调用,这个函数不返回,直 到执行任务调用 vTaskEndScheduler()。如果可供给空闲任务的 RAM 不足,那么函数调用 失败,并立即返回。

12. vTaskEndSchedule
task. h void vTaskEndScheduler( void ); 停止实时内核运行。所有创建的任务将自动删除,并且多任务(优先级或合作式)将停止。 当 vTaskStartScheduler()调用时,执行将再次开始,像 vTaskStartScheduler()仅仅返回。 在演示或 PC 路径的例子中,参考演示应用程序文件 main.c,例子中使用 vTaskEndScheduler ()。vTaskEndScheduler ()需要在便携层定义出口函数(参考在 port.c 提供 PC 接口的 vPortEndScheduler () )。执行硬件指定操作如停止内核执行。 vPortEndScheduler () 导致所有由内核分配的资源释放——但是不会释放由应用程序的任 务分配的资源。

13. vTaskSuspendAll
task. h void vTaskSuspendAll( void ); 挂起所有活动的实时内核, 同时允许中断 (包括内核滴答) 。 任务在调用 vTaskSuspendAll () 后,这个任务将继续执行,不会有任何被切换的危险,直到调用 xTaskResumeAll ()函数。 API 中有可能影响影响上下文切换的函数 (例如, vTaskDelayUntil(), xQueueSend()等等) , 一定不能再调度器挂起时被调用。

四、队列管理
14. xQueueCreate[队列管理]

queue. h xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); 创建一个新的队列。为新的队列分配所需的存储内存,并返回一个队列处理。 Parameters: uxQueueLength 队列中包含最大项目数量。 uxItemSize 队列中每个项目所需的字节数。项目通过复制而不是引用排队,因为,所 需的字节数,将复制给每个项目。队列中每个项目必须分配同样大小。

Returns: 如果队列成功创建,并返回一个新建队列的处理。 如果不能创建队列,将返回 0。

15. xQueueSend[队列管理]
queue.h portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );

是一个调用 xQueueGenericSend()的宏。能向后兼容 FreeRTOS.org(没有包括 xQueueSendToFront()和 xQueueSendToBack() 宏的) 版本。 与 xQueueSendToBack()等效。传递一个项目到队列。 这个项目通过复制而不是通过引用排队。这个函数不能从中断服务程序调用。参考 xQueueSendFromISR(),在 ISR 中交错使用。xQueueSend() 是全特点任务间通信 API 接 口。xQueueSend() 等效于交叉 API。版本不要同样的参数和同样的返回值。 Parameters: xQueue 处理将项目传递给队列

pvItemToQueue 指向队列中放置的项目的指针。项目的大小,由队列创建时定义,因为许 多字节可以从 pvItemToQueue 复制到队列的储存区域 xTicksToWait 最大时间量(任务应该锁住,等待队列中的可用空间)应该已经满了。如 果设置为 0,调用将立即返回。时间使用滴答周期来定义,因此如果需要, 常量 portTICK_RATE_MS 应该用来转换实时时间

Returns: pdTRUE:项目成功传递。 否则为:errQUEUE_FULL.

16. xQueueSendToBack[队列管理]
Only available from FreeRTOS.org V4.5.0 onwards. queue.h portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );

是一个调用 xQueueGenericSend()的宏。 相当于 xQueueSend()。 传递项目到一个队列中的 后面。这个项是复制队列,不是引用。这个函数不能被中断服务函数调用。参考 xQueueSendToBackFromISR (),可以在 ISR 中交替使用。 xQueueSendToBack()是全功 能任务间通信 API 接口。xQueueAltSendToBack()相当于 API 其中之一。版本需要相同的 参数和相同的返回值 。 参数: 将要传进的队列句柄

xQueue

pvItemToQueue 指向将要放进队列的项目。项目的大小由队列定义(队列创建时),许多 字节从 pvItemToQueue 复制到队列存储区域 xTicksToWait 任务中断并等待队列中可用空间的最大时间, 应该是满的。 如果设置为 0, 调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS 常量用来转换为实际时间。 如果 INCLUDE_vTaskSuspend 定义为 1 , 指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间 溢出)。

Returns: pdTRUE 如果任务成功传递。 否则为 errQUEUE_FULL.

17. xQueueSendToFront[队列管理]
仅适用于 FreeRTOS.org V4.5.0 以前版本 queue.h portBASE_TYPE xQueueSendToToFront( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );

是一个调用 xQueueGenericSend()的宏。传递项目到一个队列中的前面。这个项是复制队 列, 不是引用。 这个函数不能被中断服务函数调用。 参考 xQueueSendToBackFromISR (), 可以在 ISR 中交替使用。xQueueSendToFront() 是全功能任务间通信 API 接口。 xQueueAltSendToFront() 相当于 API 其中之一。版本需要相同的参数和相同的返回值。 Parameters: xQueue 将要传进的队列句柄

pvItemToQueue 指向将要放进队列的项目。项目的大小由队列定义(队列创建时),许多 字节从 pvItemToQueue 复制到队列存储区域 xTicksToWait 任务中断并等待队列中可用空间的最大时间, 应该是满的。 如果设置为 0, 调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS 常量用来转换为实际时间。 如果 INCLUDE_vTaskSuspend 定义为 1 , 指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间 溢出)。

Returns: pdTRUE 如果任务成功传递。 否则为 errQUEUE_FULL.

18. xQueueReceive[队列管理]
queue. h portBASE_TYPE xQueueReceive( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait ); 一个调用 xQueueGenericReceive()函数的宏。从队列接收一个项目。这个项目通过复制接 收,因此缓冲器必须提供足够大的空间。复制进缓冲器的字节数,在队列创建时已经定义。 这个函数一定不能在中断服务程序中使用。 参考 xQueueReceiveFromISR 获得能够的选择。

xQueueReceive()是全功能任务间通信 API 接口。xQueueAltReceive() 相当于 API 其中之 一。版本需要相同的参数和相同的返回值。 参数: 将要接收项目的队列句柄 指向将要复制接收项目的缓冲器的指针。

pxQueue pvBuffer

xTicksToWait 任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为 0, 调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS 常量用来转换为实际时间。如果 INCLUDE_vTaskSuspend 定义为 1 , 指定 的中断时间 ( portMAX_DELAY) 将导致任务无限期中断 (没有时间溢出)。

Returns: 如果项目成功被队列接收为 pdTRUE 。 否则为 pdFALSE.

19. xQueuePeek[队列管理]
Only available from FreeRTOS.org V4.5.0 onwards. queue.h portBASE_TYPE xQueuePeek( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait ); 一个调用 xQueueGenericReceive()函数的宏。从队列接收一个项目并从队列中移除该项目。 这个项目通过复制接收,因此缓冲器必须提供足够大的空间。复制进缓冲器的字节数,在队 列创建时已经定义。成功接收的项目在队列中保留,再下次调用时将再次返回,或者调用 xQueueReceive() 这个函数一定不能在中断服务程序中使用。 xQueuePeek() 是全功能任 务间通信 API 接口。xQueueAltPeek() 相当于 API 其中之一。版本需要相同的参数和相同 的返回值。

参数: 将要接收项目的队列的句柄 指向将要复制接收项目的缓冲器的指针。项目的大小由队列定义(队列创建 时),许多字节从 pvItemToQueue 复制到队列存储区域 xTicksToWait 任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为 0, 调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS 常量用来转换为实际时间。如果 INCLUDE_vTaskSuspend 定义为 1 , 指定 的中断时间 ( portMAX_DELAY) 将导致任务无限期中断 (没有时间溢出)。 返回值: pdTRUE 如果项目成功被队列接收。 否则为 pdFALSE.

xQueue pvBuffer

20. xQueueSendFromISR[队列管理]
queue.h portBASE_TYPE xQueueSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, portBASE_TYPE *pxHigherPriorityTaskWoken ); 这是一个调用 xQueueGenericSendFromISR()的宏。是为了兼容 FreeRTOS.org 以后的版 本 (没有包含 xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏) 传递 一个项到队列的后面。在终端服务程序中可以安全使用。项在队列中是复制而不是引用,排 列小项目更加灵活,特别是当从 ISR 调用时。在大多数情况下,使用一个指向项目的指针 传进队列更加灵活 参数:

xQueue pvItemToQueue

将项目传进的队列 一个指向将在队列中放置的项目的指针。 项目的大小, 队列在 创建时已经定义了,将从 pvItemToQueue 复制许多字节到队 列的存储区域

并且解锁的任务的优先级高于 pxHigherPriorityTaskWoken 如果传进队列而导致任务解锁, 当前运行任务的优先级 xQueueSendFromISR 将设 置 *pxHigherPriorityTaskWoken 到 pdTRUE 。如果 xQueueSendFromISR()设置这个值到 pdTRUE,在中断推出 之前将请求任务切换。

Returns: pdTRUE:数据成功传递进队列。 否则为:errQUEUE_FULL。使用范例是缓冲 IO(每次调用 ISR 能够更多的值)

21. xQueueSendToBackFromISR[队列管理]
queue.h portBASE_TYPE xQueueSendToBackFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, portBASE_TYPE *pxHigherPriorityTaskWoken );

这是一个调用 xQueueGenericSendFromISR()的宏。是为了兼容 FreeRTOS.org 以后的版 本(没有包含 xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏) 传 递一个项到队列的后面。在终端服务程序中可以安全使用。项在队列中是复制而不是引用, 排列小项目更加灵活,特别是当从 ISR 调用时。在大多数情况下,使用一个指向项目的指 针传进队列更加灵活。.

参数: 将项目传进的队列 一个指向将在队列中放置的项目的指针。 项目的大小, 队列在创建 时已经定义了,将从 pvItemToQueue 复制许多字节到队列的存储 区域 并且解锁的任务的优先级高于当前 xTaskPreviouslyWoken 如果传进队列而导致任务解锁, 运行任务的优先级 xQueueSendFromISR 将设 置 *pxHigherPriorityTaskWoken 到 pdTRUE 。如果 xQueueSendFromISR()设置这个值到 pdTRUE,在中断推出之前 将请求任务切换。

xQueue pvItemToQueue

Returns: pdTRUE:数据成功传递进队列。 否则为:errQUEUE_FULL。使用范例是缓冲 IO(每次调用 ISR 能够更多的值)

22. xQueueSendToFrontFromISR[队列管理]
queue.h portBASE_TYPE xQueueSendToFrontFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, portBASE_TYPE *pxHigherPriorityTaskWoken ); 是一个调用 xQueueGenericSendFromISR().的宏。传递一个项到队列的后面。在终端服务 程序中可以安全使用。 项在队列中是复制而不是引用,排列小项目更加灵活,特别是当从 ISR 调用时。 参数:

xQueue pvItemToQueue

将项目传进的队列 一个指向将在队列中放置的项目的指针。 项目的大小, 队列在创建 时已经定义了,将从 pvItemToQueue 复制许多字节到队列的存储 区域

并且解锁的任务的优先级高于当前 xTaskPreviouslyWoken 如果传进队列而导致任务解锁, 运行任务的优先级 xQueueSendTobackFromISR() 将设 置 *pxHigherPriorityTaskWoken 到 pdTRUE 。如果 xQueueSendToBackFromISR()设置这个值到 pdTRUE, 在中断推 出之前将请求任务切换。

Returns: pdTRUE:数据成功传递进队列。否则为:errQUEUE_FULL。使用范例是缓冲 IO(每次调 用 ISR 能够更多的值)

23. xQueueReceiveFromISR[队列管理]
queue. h portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, portBASE_TYPE *pxTaskWoken ); 从队列接收一个项目。在中断程序中使用此函数是安全的。 Parameters: pxQueue pvBuffer 发送项目的队列句柄 指向缓冲区的指针,将接收的项目被复制进去。

pxTaskWoken 任务将锁住,等待队列中的可用空间。如果 xQueueReceiveFromISR 引起 一个任务解锁,*pxTaskWoken 将设置为 pdTRUE,否则*pxTaskWoken

保留不变

Returns: pdTRUE :如果项目成功从队列接收。 否则为: pdFALSE

24. vQueueAddToRegistry[队列管理]
queue.h void vQueueAddToRegistry( xQueueHandle xQueue, signed portCHAR *pcQueueName, ); 为队列命名,并加入队列到登记管理中 Parameters: xQueue 将要添加登记的队列句柄

pcQueueName 为指定的队列命名。 仅仅是文本串,方便调试。 队列登记有两个特点, 均与内核的相关调试有关: 允许文本式名字,使队列在调试 GUI 中方便定义。 包含调试器需要的信息,如:定位每个已经登记的队列和信号量。 队列的登记没有目的,除非使用内核相关的调试。configQUEUE_REGISTRY_SIZE 定义了 队列和信号量的最大数目.仅当使用内核相关的调试时需要显示已经登记的信号量和队列。

25. vQueueUnregisterQueue[队列管理]
queue.h

void vQueueUnregisterQueue( xQueueHandle xQueue, ); 从登记管理中移除队列。 Parameters: xQueue 从登记管理处中移出的队列句柄 队列登记有两个特点, 均与内核的相关调试有关: 允许文本式名字,使队列在调试 GUI 中方便定义。 包含调试器需要的信息,如:定位每个已经登记的队列和信号量。 队列的登记没有目的,除非使用内核相关的调试。configQUEUE_REGISTRY_SIZE 定义了 队列和信号量的最大数目.仅当使用内核相关的调试时需要显示已经登记的信号量和队列。

五、信号量
26. xSemaphoreCreateCounting[信号量]
semphr. h xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCou nt, unsigned portBASE_TYPE uxInitialCount ) 使用已存在的队列结构来创建计数型信号量。计数型信号量有下面两种典型应用: 1 事件计数 在这种应用的情形下, 事件处理程序会在每次事件发生时发送信号量 (增加信号量计数值) , 而任务处理程序会在每次处理事件时请求信号量(减少信号量计数值)。因此计数值为事件 发生与事件处理两者间的差值,在这种情况下计数值初始化为 0 是合适的。 2 资源管理

在这种应用情形下,计数值指示出可用的资源数量。任务必须首先“请求”信号量来获得资源 的控制权--减少信号量计数值。 当计数值降为 0 时表示没有空闲资源。 任务使用完资源后“返 还”信号量--增加信号量计数值。 在这种情况下计数值初始化为与最大的计数值相一致是合适 的,这指示出所有的空闲资源。 参数: uxMaxCount 可以达到的最大计数值。当信号量达到此值后将不能被“返还”。 uxInitialCount 信号量创建时分配的初始值。 返回: 已创建的信号量句柄,为 xSemaphoreHandle 类型, 如果信号量无法创建则为 NULL。

27. xSemaphoreCreateMutex[信号量]
只在 FreeRTOS.org V4.5.0 以后的版本中可用。 semphr. h xSemaphoreHandle xSemaphoreCreateMutex( void ) 使用已存在的队列结构来创建互斥锁信号量的宏。通过此宏创建的互斥锁可以使用 xSemaphoreTake()与 xSemaphoreGive()宏来访问。 不能使用 xSemaphoreTakeRecursive() 与 xSemaphoreGiveRecursive()宏。二元信号量与互斥锁十分相像,不过两者间有细微的 差别:互斥锁包含一个优先级继承机制,而信号量没有。这种差别使得二元信号量更适合于 实现同步(任务之间或任务与中断之间),互斥锁更适合于实现简单的互斥。当有另外一个 具有更高优先级的任务试图获取同一个互斥锁时, 已经获得互斥锁的任务的优先级会被提升。 已经获得互斥锁的任务将继承试图获取同一互斥锁的任务的优先级。 这意味着互斥锁必须总 是要返还的, 否则高优先级的任务将永远也不能获取互斥锁, 而低优先级的任务将不会放弃 优先级的继承。 xSemaphoreTake()页中提供了互斥锁用于互斥的范例。 二元信号量并不需要在得到后立即释放,因此任务同步可以通过一个任务/中断持续释放信 号量而另外一个持续获得信号量来实现。xSemaphoreGiveFromISR()页中有此种方式的实

现范例。互斥锁与二元信号量均赋值为 xSemaphoreHandle 类型,并且可以在任何此类型 参数的 API 函数中使用。 Return: 已创建的信号量句柄,需要为 xSemaphoreHandle 类型.

28. xSemaphoreCreateRecursiveMutex[信号量]
semphr. h xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void ) 使用已存在的队列结构来创建递归互斥锁的宏。通过此宏创建的互斥锁可以使用 xSemaphoreTakeRecursive()与 xSemaphoreGiveRecursive()宏 来访问。不能使用 xSemaphoreTake()与 xSemaphoreGive()宏。一个递归的互斥锁可以重复地被其所有者“获 取”。在其所有者为每次的成功“获取”请求调用 xSemaphoreGiveRecursive()前,此互斥锁 不会再次可用。例如,如果一个任务成功“获取”同一个互斥锁 5 次,则在其“释放”互斥锁恰 好为 5 次后, 其他任务才可以使用此互斥锁。 这种类型的信号量使用一个优先级继承机制, 因此已取得信号量的任务“必须总是”在不再需要信号量时立刻“释放”。互斥类型的信号量不 能在中断服务程序中使用。可以参考 vSemaphoreCreateBinary()来获得一个二选一运行的 实现方式,可以在中断服务程序中实现纯粹的同步(一个任务或中断总是“释放”信号量,而 另一个总是“获取”信号量)。 返回: 已创建的互斥锁信号量句柄,需要为 xSemaphoreHandle 类型.

29. xSemaphoreTake[信号量]
semphr. h xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xBlockTime )

用于获取信号量的宏。信号量必须已经通过调用 vSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或 xSemaphoreCreateCounting() 来创建。这个宏不能在服务中断程序中调用。如果有必要,可以调用 xQueueReceiveFromISR() 来在中断服务程序中获取信号量,虽然这并不是一种正规的操 作。 xSemaphoreTake() 是一个全功能任务间通讯 API, xSemaphoreAltTake() 是其等价的 替代 API 。这两个版本均需要同样的参数并返回同样的值。 Parameters: xSemaphore 将被获得的信号量句柄,此信号量必须已经被创建 xBlockTime 等待信号量可用的时钟滴答次数, 可以使用 portTICK_RATE_MS 宏来转换为 实际的时间 。当为 0 时可以用于 对信号量进行轮询(poll the semaphore) 如果 INCLUDE_vTaskSuspend 置位“1”,则指定 xBlockTime 为 portMAX_DELAY 会导致任务阻塞时间不确定(不会超时) 返回: 如果成功获取信号量则返回 pdTRUE, 如果 xBlockTime 超时而信号量还未可用则返回 pdFALSE。

30. xSemaphoreTakeRecursive[信号量]
semphr. h xSemaphoreTakeRecursive( xSemaphoreHandle xMutex, portTickType xBlockTime ) 用于递归获得互斥锁信号量的宏。此互斥锁必须已经通过调用 xSemaphoreCreateRecursiveMutex()来创建。 FreeRTOSConfig.h 中的 configUSE_RECURSIVE_MUTEXES 必须设置为 1 才可以使用此宏。此宏不能用于由 xSemaphoreCreateMutex()函数创建的信号量。一个递归型的互斥锁可以被其所有者重复 地“获取”, 一个递归的互斥锁可以重复地被其所有者“获取”。 在其所有者为每次成功的“获取” 请求调用 xSemaphoreGiveRecursive()前,此互斥锁不会再次可用。例如,如果一个任务 成功“获取”同一个互斥锁 5 次,则在其“释放”互斥锁 恰好为 5 次后,其他任务才可以使用此 互斥锁。

参数: 将被获得的互斥锁句柄, 此句柄由 xSemaphoreCreateRecursiveMutex()返回。

xMutex

xBlockTime 等待信号量可用的时钟滴答次数,可以使用 portTICK_RATE_MS 宏来转换为 实际的时间 。当为 0 时可以用于 对信号量进行轮询(poll the semaphore)。 如果任务已经拥有此信号量则函数将立刻返回,而不管 xBlockTime 为何值。 返回值: 如果成功获取信号量则返回 pdTRUE, 如果 xBlockTime 超时而信号量还未可用则返回 pdFALSE。

31. xSemaphoreGive[信号量]
semphr. h xSemaphoreGive( xSemaphoreHandle xSemaphore ) 用于释放信号量的宏。这个信号量必须已经通过调用 vSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或 xSemaphoreCreateCountin g()创建,并且使用 sSemaphoreTake()获取。这个宏不能在中断服务程序中使用。参考 xSemaphoreGiveFromISR()获取一个在中断中使用的替代。这个宏也不能应用于由 xSemaphoreCreateRecursiveMutex()创建的信号量。 xSemaphoreGive()是一个全功能任务 间通讯 API。xSemaphoreAltGive() 是其等价的替代 API。这两个版本均需要相同的参数, 并返回相同的值。 参数: xSemaphore 即将释放的信号量的句柄,在信号量创建是返回。 返回值: 如果信号量成功释放返回 pdTRUE, 如果发生错误则返回 pdFALSE。 信号量使用的是队列, 因此如果队列没有位置用于发送消息就会发生一个错误——说明开始时没有正确获取信号 量。

32. xSemaphoreGiveRecursive[信号量]
semphr. h xSemaphoreGiveRecursive( xSemaphoreHandle xMutex ) 用于递归释放,或?返还?,互斥锁信号量的宏。这个互斥锁必须已经通过调用 xSemaphoreCreateRecursiveMutex()来创建;FreeRTOSConfig.h 文件中的 configUSE_RECURSIVE_MUTEXES 必须设置为 1 来是此宏可用。 这个宏不能用与由 xSemaphoreCreateMutex().创建的互斥锁。递归的互斥锁可以被其所有者重复地?获取?,在 其所有者为每次的成功“获取”请求调用 xSemaphoreGiveRecursive()前, 此互斥锁不会再次 可用。例如,如果一个任务成功“获取”同一个互斥锁 5 次,则在其“释放”互斥锁恰好为 5 次 后,其他任务才可以使用此互斥锁。 Parameters: xMutex 将被释放或?返还?的互斥锁的句柄,由 xSemaphoreCreateRecursiveMutex()返回。 Returns: 如果信号量成功释放则为 pdTRUE

33. xSemaphoreGiveFromISR[信号量]
semphr. h xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken ) 用于释放一个信号量的宏。释放的信号量必须已经通过 vSemaphoreCreateBinary() 或 xSemaphoreCreateCounting()函数创建。互斥信号量(通 过 xSemaphoreCreateMutex()函数创建) 不能使用此宏。 此宏可以在中断服务程序中使用。

参数: 将被释放的信号量的句柄,此句柄在信号量创建时返回。

xSemaphore

pxHigherPriorityTaskWoken 如果释放此信号量会导致一个比当前任务具有更高优先级的 任务解除阻塞,xSemaphoreGiveFromISR() 函数将设置 *pxHigherPriorityTaskWoken 为 pdTRUE。如果 xSemaphoreGiveFromISR() 函数将其置为 pdTRUE,则必须 在离开中断前进行上下文切换。 返回: 如果信号量成功释放则返回 pdTRUE, 否则返回 errQUEUE_FULL。

六、联合程序
34. xCoRoutineCreate[联合程序]
croutine.h portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ); 创建一个新的联合程序并且将其增加到联合程序的就绪列表中 参数: pxCoRoutineCode 联合程序函数的指针。联合程序函数需要使用特定的语法——参考联合 程序部分获取更多信息。 uxPriority 优先级,用于与其他联合程序确定哪个将运行

uxIndex

当不同的联合程序使用同一个函数来运行时用于相互识别。参考下面的 例子以及联合程序部分获取更多信息。

Returns: 如果联合程序成功创建并增加到就绪列表中则返回 pdPASS,否则返回 ProjDefs.h 中定义 的错误代码。 xCoRoutineHandle 用于引用联合程序的类型,联合程序函数句柄可以自动的传入每一个使 用联合程序的函数。

35. xCoRoutineCreate[联合程序]
croutine.h portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ); 创建一个新的联合程序并且将其增加到联合程序的就绪列表中 参数: pxCoRoutineCode 联合程序函数的指针。联合程序函数需要使用特定的语法——参考联合 程序部分获取更多信息。 uxPriority uxIndex 优先级,用于与其他联合程序确定哪个将运行 当不同的联合程序使用同一个函数来运行时用于相互识别。参考下面的 例子以及联合程序部分获取更多信息。

Returns:

如果联合程序成功创建并增加到就绪列表中则返回 pdPASS,否则返回 ProjDefs.h 中定义 的错误代码。 xCoRoutineHandle 用于引用联合程序的类型,联合程序函数句柄可以自动的传入每一个使 用联合程序的函数。

36. crDELAY[联合程序]
croutine.h void crDELAY( xCoRoutineHandle xHandle, portTickType xTicksToDelay ) crDELAY 是一个宏,上面原型的数据类型只作为参考。把一个联合程序延时一个特定的时 间。crDELAY 只能在联合程序函数自身内部使用——不能在联合程序函数调用的一个函数 内部使用。这是因为联合函数没有维护自身的堆栈。 Parameters: xHandle 要延时的联合程序的句柄,这是联合程序函数的 xHandle 参数。

xTickToDelay 联合程序要延时的时间片数。实际的时间相当于 configTICK_RATE_HZ (位 于 FreeRTOSConfig.h)。可以使用 portTICK_RATE_MS 来转换为毫秒。

37. crQUEUE_SEND[联合程序]
croutine.h crQUEUE_SEND( xCoRoutineHandle xHandle, xQueueHandle pxQueue, void *pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE *pxResult )

crQUEUE_SEND 是一个宏,上面原型中的数据类型只用作参考。联合程序中使用的 crQUEUE_SEND() 与 crQUEUE_RECEIVE()宏等价于任务中的 xQueueSend() 与 xQueueReceive()函数。crQUEUE_SEND 与 crQUEUE_RECEIVE 只能 被联合程序使用,而 xQueueSend() 与 xQueueReceive()只能被任务使用。注意:联合程 序只能向其他联合程序发送数据。 联合程序不能使用队列来发送向任务发送数据, 反之亦然。 crQUEUE_SEND 只能在联合程序函数内部访问——不能再联合程序函数调用的函数中使 用,这是因为联合函数没有维护自身的堆栈。 参考联合程序部分获取关于任务与联合程序或中断服务程序与联合程序间传递数据的信 息。 参数: 调用的联合程序的句柄,这是联合程序函数的 xHandle 参数。 数据将被发送到的队列的句柄,这是队列使用 xQueueCreate() API 函数 创建时的返回值。 pvItemToQueue 将被发送到队列的数据的指针。队列中每个条目的数据量在队列创建是已 指定,这个数量的字节数据将从 pvItemToQueue 中复制到队列。 xTickToDelay 如果此刻队列没有可用空间,此值为联合程序用于阻塞等待队列空间可用 的时间片数。实际的时间相当于 configTICK_RATE_HZ (位 于 FreeRTOSConfig.h)。可以使用 portTICK_RATE_MS 来转换为毫秒。 pxResult 一个指向 pxResult 变量的指针,如果数据成功发送到队列就会被设置为 pdPASS,否则设置为 ProjDefs.h 中定义的错误码。

xHandle pxQueue

38. crQUEUE_RECEIVE[联合程序]
croutine.h void crQUEUE_RECEIVE( xCoRoutineHandle xHandle, xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait,

portBASE_TYPE *pxResult ) crQUEUE_RECEIVE 是一个宏。上面原型的数据类型只用作参考。联合程序中使用的 crQUEUE_SEND() 与 crQUEUE_RECEIVE()宏等价于任务中的 xQueueSend() 与 xQueueReceive()函数。crQUEUE_SEND 与 crQUEUE_RECEIVE 只能 被联合程序使用,而 xQueueSend() 与 xQueueReceive()只能被任务使用。注意:联合程 序只能向其他联合程序发送数据。 联合程序不能使用队列来发送向任务发送数据, 反之亦然。 crQUEUE_RECEIVE 只能在联合程序函数内部访问——不能再联合程序函数调用的函数 中使用,这是因为联合函数没有维护自身的堆栈。 参考联合程序部分获取关于任务与联合 程序或中断服务程序与联合程序间传递数据的信息。 参数: 调用的联合程序的句柄,这是联合程序函数的 xHandle 参数。 数据将从其中接收的队列的句柄,这是队列使用 xQueueCreate() API 函数 创建时的返回值。 pvBuffer 用于复制接收到的条目的缓冲区,队列中每个条目的数据量在队列创建是已 指定,这个数量的字节数据将复制到缓冲区。 xTickToDelay 如果此刻队列没有可用空间,此值为联合程序用于阻塞等待队列空间可用的 时间片数。实际的时间相当于 configTICK_RATE_HZ (位 于 FreeRTOSConfig.h)。可以使用 portTICK_RATE_MS 来转换为毫秒。 pxResult 一个指向 pxResult 变量的指针,如果数据成功发送到队列就会被设置为 pdPASS,否则设置为 ProjDefs.h 中定义的错误码。

xHandle pxQueue

39. crQUEUE_SEND_FROM_ISR[联合程序]
croutine.h portBASE_TYPE crQUEUE_SEND_FROM_ISR( xQueueHandle pxQueue, void *pvItemToQueue, portBASE_TYPE xCoRoutinePreviouslyWoken

) crQUEUE_SEND_FROM_ISR() 是一个宏。上面原型的数据类型只用作参考。联合程序中 使用的 crQUEUE_SEND_FROM_ISR() 与 crQUEUE_RECEIVE_FROM_ISR()宏等价于任 务中的 xQueueSendFromISR()与 xQueueReceiveFromISR()函数。 crQUEUE_SEND_FROM_ISR() 与 crQUEUE_RECEIVE_FROM_ISR() 只能用于联合程 序与中断服务程序之间传递数据,同样道理, xQueueSendFromISR() 与 xQueueReceiveFromISR() 只能用于任务与中断服务程序间传 递数据。crQUEUE_SEND_FROM_ISR 只能在一个向队列发送数据的中断服务程序中调用, 并且此队列在一个联合程序中使用。 参考联合程序部分获取关于任务与联合程序或中断服务 程序与联合程序间传递数据的信息。 参数: 将发送到的队列的句柄。 将被发送到队列的条目的指针。队列中每个条目的数据量在 队列创建是已指定,这个数量的字节数据将从 pvItemToQueue 中复制到队列的储存空间。 xCoRoutinePreviouslyWoken 包含此项使得一个中断服务程序可以在一次中断中多次向同 一个队列发送数据。第一次调用时必须总是传入 pdFALSE, 后面的调用时应传入上一次调用返回的值。

xQueue pvItemToQueue

Returns: 如果发送到队列使得一个联合程序唤醒则为 pdTRUE。 这用来使中断服务程序决定是否需要在中断后进行上下文切换。

40. crQUEUE_RECEIVE_FROM_ISR[联合程序]
(注意:官方的文档此处的内容似乎有误,请参考源代码) croutine.h portBASE_TYPE crQUEUE_SEND_FROM_ISR( xQueueHandle pxQueue,

void *pvBuffer, portBASE_TYPE * pxCoRoutineWoken ) 是一个宏。上面原型的数据类型只用作参考。联合程序中使用的 crQUEUE_SEND_FROM_ISR() 与 crQUEUE_RECEIVE_FROM_ISR()宏等价于任务中的 xQueueSendFromISR()与 xQueueReceiveFromISR()函 数。crQUEUE_SEND_FROM_ISR() 与 crQUEUE_RECEIVE_FROM_ISR() 只能用于联合 程序与中断服务程序之间传递数据,同样道理, xQueueSendFromISR() 与 xQueueReceiveFromISR() 只能用于任务与中断服务程序间传 递数据。 crQUEUE_RECEIVE_FROM_ISR 只能在一个从队列接收数据的中断服务程序中调用,并 且此队列在一个联合程序中使用(一个联合程序发送数据到此队列)。参考联合程序部分获 取关于任务与联合程序或中断服务程序与联合程序间传递数据的信息。 Parameters: xQueu e

The handle to the queue on which the item is to be posted.

pvBuff A pointer to a buffer into which the received item will be placed. The size of the ite er ms the queue will hold was defined when the queue was created, so this many by tes will be copied from the queue into pvBuffer. pxCo A co-routine may be blocked waiting for space to become available on the queue. Routin If crQUEUE_RECEIVE_FROM_ISR causes such a co-routine to unblock *pxCoR eWok outineWoken will get set to pdTRUE, otherwise *pxCoRoutineWoken will remain u en nchanged

Returns: pdTRUE an item was successfully received from the queue, otherwise pdFALSE.

41. vCoRoutineSchedule[联合程序]
croutine.h

void vCoRoutineSchedule( void ); 运行一个联合程序 vCoRoutineSchedule() 与性具有最高优先级的就绪联合程序。此联合程 序将运行,直到其阻塞,让出系统或被任务抢占。联合程序是合作式运行的,所以一个联合 程序不会被另一个联合程序抢占, 但是可以被任务抢占。 如果一个应用程序同时包含任务与 联合程序,则 vCoRoutineSchedule 应该在空闲任务中调用(在一个空闲任务钩子中)。

#if ( INCLUDE_vTaskDelay == 1 ) //延时特定时间 xTicksToDelay,这个时间需要转换为唤醒绝对时间 xTimeToWake, //这样才能在与 vTaskIncrementTick 函数中操作的数值是一致的 xTicksToDelay:延时的节拍数 void vTaskDelay( const TickType_t xTicksToDelay ) { TickType_t xTimeToWake; BaseType_t xAlreadyYielded = pdFALSE;

if( xTicksToDelay > ( TickType_t ) 0U )//若延迟的时间 是 0,就是调度器的重新启动。若延时时间大于 0,执行延时操作。 { configASSERT( uxSchedulerSuspended == 0 ); vTaskSuspendAll();//系统维护一个 uxSchedulerSuspended 计数值,当其大于 0 时表示禁止调度;等于 0 时则表示允许调 度。 { traceTASK_DELAY();

//计算唤醒时间--这可能会溢出,但不会有问题, 大家会问为什么 //大家可以自己做个试验,两个 uCHAR 型数据相加,如果超过 255,则 等于多少? xTimeToWake = xTickCount + xTicksToDelay;//任务的唤醒时间更新。

if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )//若删除完后链表中没有任务 //把任务从当前运行链表中移除出去,然后把它添 加到阻塞链表里面 { //当前任务必须在就绪链表中, 所以其是 不必检查的,下面的宏定义可以直接调用

portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); } else { mtCOVERAGE_TEST_MARKER(); }

prvAddCurrentTaskToDelayedList( xTimeToWake ); //prvAddCurrentTaskToDelayedList 是一 个函数,它是把当前的任务控制块,

//放进 DelayedList 链表中,而 DelayedList 有两个链表,一个是溢出的,一个是正在应用的, //所以要根据传递进的参数 xTimeToWake 进行分 别设置,如果是在当前的延时链表里就添加进现在的延时链表, //如果计算出来后是溢出链表,则添加进溢出链表 里面 } xAlreadyYielded = xTaskResumeAll();//得到任務 切換的具體情況,pdTRUE 切换成功否则切换失败。 } else { mtCOVERAGE_TEST_MARKER(); } if( xAlreadyYielded == pdFALSE )//若上面的切换失败,或 不需要延迟。直接进行切换。 { portYIELD_WITHIN_API(); //实际就是终端控制及状态寄存器 ICSR,写位 28 为 1 悬起 PendSV,进入到 xPortPendSVHandler。 } else { mtCOVERAGE_TEST_MARKER(); }

}

#endif

#if ( INCLUDE_vTaskDelayUntil == 1 ) void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) //参数:pxPreviousWakeTime---上一次调用本函数的时间 // 拍数 //由于调用此函数的任务解除阻塞的时间是绝对时刻,比起相对于调用时刻的相对时间更 精确(即比调用 vTaskDelay()可以实现更精确的周期性)。 //pxPreviousWakeTime: 此参数命名时假定 vTaskDelayUntil()用于实现某个任务 以固定频率周期性执行。这种情况下 pxPreviousWakeTime // 保存了任务上一次离开阻塞态(被唤醒) xTimeIncrement---相对于 pxPreviousWakeTime 本次延时的节

的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。 //xTimeIncrement: 此参数命名时同样是假定 vTaskDelayUntil()用于实现某个任 务以固定频率周期性执行 —— 这个频率就是由 xTimeIncrement 指定的。 // *xTimeIncrement 的单位是心跳周期,可以使用常

量 portTICK_RATE_MS 将毫秒转换为心跳周期 { TickType_t xTimeToWake;//和 vTaskDelay 函数中一样定义,最终得到 xTimeToWake 赋值给 xGenericListItem .xItemValue BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

configASSERT( pxPreviousWakeTime );

configASSERT( ( xTimeIncrement > 0U ) ); configASSERT( uxSchedulerSuspended == 0 );

vTaskSuspendAll();//调度器挂起 { const TickType_t xConstTickCount = xTickCount;//xTickCount 在这个函数里面不能改变,所以采用另外一个变量做优化。 xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; //计算下次唤醒的时刻.

if( xConstTickCount < *pxPreviousWakeTime )// 说明 xTickCount 溢出了 { //pxPreviousWakeTime 指向上一次的唤醒时 间,这个地方有点难理解,可以这样说明:时间轴是一个往前的轴, //是不会有溢出之说法,也就是说 xTickCount 永远都是大于唤醒时间的而不管哪一次的唤醒时间都是大于, //但由于溢出的存在,所以有时候 xTickCount 会小于唤醒时间,这里就说明是小于上一次设置的唤醒时间,如果是就说明是溢出了

//::T3::::::T2:::::::::::::::::::::T1::::::::::::::::::::: ::::::::::::::::T2:::::::::::::::::T3:::::::::*/ // *pxPreviousWakeTime xTimeToWake*/ //T1 对应*pxPreviousWakeTime ,T2 对应 xTickCount,T3 对应 xTimeToWake*/ xTickCount xTickCount

//因为在运行这个程序时,任务因为运行了其它程 序,造成了 xTickCount 和唤醒时间不是在同一个点上,要么大于 *pxPreviousWakeTime, //要么小于*pxPreviousWakeTime, 就像 T2 一 样,如果运行到 T 第一个 T0 处,那么,就相当于 xTickCount 已经溢出了,那么需要不 //需要延时, 就要看 xTimeToWake 所处的位置了, 如果没溢出,则说明不需要延时就可以调度任务 //,如果溢出了,还要看是否大于 xTickCount, 如果是才能算的上是真正的需要放入延时链表里面 if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) //这时只有 xTimeToWake 也溢出了,并且 xTimeToWake > xConstTickCount 才需要休眠 { xShouldDelay = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } else { //下面两种情况才需要休眠 if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )

{ xShouldDelay = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } //为下一次 Delay 更新 pxPreviousWakeTime. *pxPreviousWakeTime = xTimeToWake;//把需要唤醒 的绝对时间保存起来,保存到 pxPreviousWakeTime 指针变量里面

if( xShouldDelay != pdFALSE )//这时需要休眠,由 上面的判断任务是否进入延时链表,如果不需要,则仍是当前运行的任务 { traceTASK_DELAY_UNTIL(); //从 Ready 链表中删除,加入 Blocked List

if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) {

portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); }

else { mtCOVERAGE_TEST_MARKER(); }

prvAddCurrentTaskToDelayedList( xTimeToWake ); } else { mtCOVERAGE_TEST_MARKER(); } } xAlreadyYielded = xTaskResumeAll(); //在运行上面临界区的程序时,可能有任务需要调度,但因为调度器的 挂起而没有被调度,只是给出了登记, //而这个 xTaskResumeAll 函数就是要把放进 xPendingReadyList 链表中的任务节点转移到真正的就绪链表 pxReadyTasksLists 里面, //如果任务是因为 tick 缺失或者因为在恢复实际走过的滴答数时有 任务需要抢占 CPU,则 xAlreadyYielded 都为真, //从而导致下面不会运行, 如果没有被抢占也就是说当前还是处于最高 级任务,但是上面的延时已经使其阻塞,从而在下面发生抢占 if( xAlreadyYielded == pdFALSE ) //强制自己交出 CPU,使自身进入等待延时。个人认为:此处并不需要 强制交出,如果上面并不需要加入延时链表,

//表示还是运行的当前任务,如果这个任务仍然是最高级的,则并不需 要切换 { portYIELD_WITHIN_API(); } else { mtCOVERAGE_TEST_MARKER(); } }

#endif

BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, // 指向任务的入口函数. 任务必须执行并且永不返回 (即:无限循环). const char * const pcName, //描述任务的名字。主要便于调试。最大长度由 configMAX_TASK_NAME_LEN.定义 const uint16_t usStackDepth, //指定任务堆栈的深度 void * const pvParameters, //指针用于作为一个参数传向创建的任务 UBaseType_t uxPriority, //任务运行时的优先级 TaskHandle_t * const pxCreatedTask,

//pxCreatedTask 用于传出任务的句柄。 这个句柄将在 API 调用中对该创建出来的任务 进行引用,比如改变任务优先级,或者删除任务。 //如果应用程序中不会用到这个任务的句柄,则 pxCreatedTask 可以被设为 NULL StackType_t * const puxStackBuffer, //堆栈缓冲区 const MemoryRegion_t * const xRegions ) // 缓存区 /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { BaseType_t xReturn; TCB_t * pxNewTCB; configASSERT( pxTaskCode ); configASSERT( ( ( uxPriority & ( ~portPRIVILEGE_BIT ) ) < configMAX_PRIORITIES ) ); //按照新任务的 TCB 和堆栈分配存储空间并检查分配是否成功 pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer );//分配好的空间将填入 0x5a 数值。 if( pxNewTCB != NULL )//若 TCB 不为空,即分配成功。 { StackType_t *pxTopOfStack;//计算堆栈栈顶的位置 #if( portUSING_MPU_WRAPPERS == 1 ) BaseType_t xRunPrivileged; if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) { xRunPrivileged = pdTRUE; }

else { xRunPrivileged = pdFALSE; } uxPriority &= ~portPRIVILEGE_BIT; #endif /* portUSING_MPU_WRAPPERS == 1 */ #if( portSTACK_GROWTH < 0 )// portSTACK_GROWTH = -1 对 应倒生堆栈,比如 x86 { pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( uint16_t ) 1 );//pxStack 指向堆栈的起始地址 pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); //堆栈有地址对齐的要求 configASSERT( ( ( ( uint32_t ) pxTopOfStack & ( uint32_t ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); } #else // 若不是倒生堆栈 { pxTopOfStack = pxNewTCB->pxStack;//新建的堆栈 是空的,所以堆栈的栈顶就在起始地址处(正生堆栈)

configASSERT( ( ( ( uint32_t ) pxNewTCB->pxStack & ( uint32_t ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

//如果要做堆栈检查,就要知道堆栈的另一端界限在哪里 pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); } #endif

//设置新的 TCB,将其置于初始化状态 prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth );

//初始化堆栈,使得任务好像被中断函数中断了一样,中断返回地址就 是任务的首地址,与 uC/OS-II 的方式相同 //依次将 xPSR、PC、LR、R12 以及 R0-R3 初始化 #if( portUSING_MPU_WRAPPERS == 1 ) { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); } #else { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); } #endif

if( ( void * ) pxCreatedTask != NULL )//若需要返回任务 句柄 { //返回任务的句柄 *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; } else { mtCOVERAGE_TEST_MARKER(); } //当正在更新时确保中断不会打断任务链表 taskENTER_CRITICAL(); { uxCurrentNumberOfTasks++;//系统任务数目加一 if( pxCurrentTCB == NULL )//若当前任务 TCB 为空, 即没有任务 { //没有其他任务或者其他任务都被挂起,将刚创建 的任务变成当前任务 pxCurrentTCB = pxNewTCB;

if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )//若系统中任务的数目为 1,即这个任务是第一个任务 {

//如果这是第一个任务,就做初始化工 作,uC/OS-II 有单独的初始化函数,FreeRTOS 没有,初始化工作放在这里 prvInitialiseTaskLists(); } else { mtCOVERAGE_TEST_MARKER(); } } else//若当前任务 TCB 不为空,即存在相应任务 { //若调度器已经没有运行,若新建任务的优先级为 到目前为止的最高优先级即将其设为当前任务 if( xSchedulerRunning == pdFALSE )// 系统调度器没有运行 { if( pxCurrentTCB->uxPriority <= uxPriority )//若当前任务的优先级不大于新建任务的优先级 { pxCurrentTCB = pxNewTCB;//将新建任务 TCB 设为当前任务 TCB } else {

mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } }

uxTaskNumber++;//任务的数量加 1

#if ( configUSE_TRACE_FACILITY == 1 ) { pxNewTCB->uxTCBNumber = uxTaskNumber;//将任务数量保存到 TCB 中,主要用以调试。 } #endif traceTASK_CREATE( pxNewTCB );

prvAddTaskToReadyList( pxNewTCB );//将新建任务 插入到就绪链表中。

xReturn = pdPASS;//返回值为成功 portSETUP_TCB( pxNewTCB );

} taskEXIT_CRITICAL(); } else//若 TCB 为空,即分配不成功 { xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;//返 回值为不能成功分到要求的内存空间 traceTASK_CREATE_FAILED(); }

if( xReturn == pdPASS )//若返回值为成功,即能够创建好任务 { if( xSchedulerRunning != pdFALSE )//若任务调度器正在运 行 { //若当前任务的优先级小于新建任务的优先级 if( pxCurrentTCB->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION();// 新 任务比当前正在运行的任务优先级高,因此要做任务调度 } else { mtCOVERAGE_TEST_MARKER(); }

} else { mtCOVERAGE_TEST_MARKER(); } }

return xReturn;//将任务的返回值送出。 }

用了半天时间对 FreeRTOS 有了一个初步的认识,大概总结一下,其中混杂了系统实现和 实际应用方面的问题。 现只是以应用为目的,实现方面待以后进一步研究。 1.FreeRTOS 提供的功能包括:任务管理、时间管理、信号量、消息队列、内存管理。与平 台有关的文件包含在 portable 文件夹中, 主要是 port.c, portmacro.h 两个文件。 平台无关的 文 件 主 要 是 : list.c( 基 本 链 表 结 构 ), queue.c( 包 括 消 息 队 列 , 信 号 量 的 实 现 ), croutine.c,tasks.c(任务管理,时间管理)。 命名协定 RTOS 内核与范例程序源代码使用下面的协定: 变量 char 类型的变量以 c 为前缀 short 类型的变量以 s 为前缀 long 类型的变量以 l 为前缀 float 类型的变量以 f 为前缀 double 类型的变量以 d 为前缀 枚举变量以 e 为前缀 其他类型(如结构体)以 x 为前缀 指针有一个额外的前缀 p , 例如 short 类型的指针前缀为 ps 无符号类型的变量有一个额外的前缀 u , 例如无符号 short 类型的变量前缀为 us 函数 文件内部函数以 prv 为前缀 API 函数以其返回值类型为前缀,按照前面对变量的定义 函数的名字以其所在的文件名开头。如 vTaskDelete 函数在 Task.c 文件中定义

数据类型 数据类型并不直接在 RTOS 内核内部引用。 相反, 每个平台都有其自身的定义方式。 例如, char 类型定义为 portCHAR,short 类型定义为 portSHORT 等。范例程序源代码使用的就 是这种符号,但这并不是必须的,你可以在你的程序中使用任何你喜欢的符号。 此外,有两种额外的类型要为每种平台定义。分别是: portTickType 可配置为 16 位的无符号类型或 32 位的无符号类型。参考 API 文档中的定制部分获取详细 信息。 portBASE_TYPE 为特定体系定义的最有效率的数据类型。 如果 portBASE_TYPE 定义为 char 则必须要特别小心的保证用来作为函数返回值的 signed char 可以为负数,用于指示错误。 2. FreeRTOS 内核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优 先级,CPU 总是让处于就绪态的、优先级最高的任务先运行。FreeRT0S 内核同时支持轮 换调度算法, 系统允许不同的任务使用相同的优先级, 在没有更高优先级任务就绪的情况下, 同一优先级的任务共享 CPU 的使用时间。 3.freertos 既可以配置为可抢占内核也可以配置为不可抢占内核。 当 FreeRTOS 被设置为可 剥夺型内核时,处于就绪态的高优先级任务能剥夺低优先级任务的 CPU 使用权,这样可保 证系统满足实时性的要求;当 FreeRTOS 被设置为不可剥夺型内核时,处于就绪态的高优 先级任务只有等当前运行任务主动释放 CPU 的使用权后才能获得运行,这样可提高 CPU 的运行效率。 4.任务管理 系统为每个任务分配一个 TCB 结构 typedef struct tskTaskControlBlock { volatile portSTACK_TYPE *pxTopOfStack;//指向堆栈顶 xListItem xGenericListItem; //通过它将任务连入就绪链表或者延时链表或者挂起链表中, xListItem 包含其 TCB 指针 xListItem xEventListItem;//通过它把任务连入事件等待链表 unsigned portBASE_TYPE uxPriority;//优先级 portSTACK_TYPE *pxStack; //指向堆栈起始位置 signed portCHAR pcTaskName[ configMAX_TASK_NAME_LEN ]; 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。省略一些次要结构 } tskTCB; 系统的全局变量: static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; 就绪队列 static xList xDelayedTaskList1; static xList xDelayedTaskList2; 两个延时任务队列 static xList * volatile pxDelayedTaskList; static xList * volatile pxOverflowDelayedTaskList; 两个延时队列的指针, 应该是可互换的。

static xList xPendingReadyList; static volatile xList xTasksWaitingTermination; 等待结束队列 static volatile unsigned portBASE_TYPE uxTasksDeleted = ( unsigned portBASE_TYPE ) 0; 结束队列中的个数????? static xList xSuspendedTaskList; 挂起队列 static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks;记录了当前系统任务 的数目 static volatile portTickType xTickCount;是自启动以来系统运行的 ticks 数 static unsigned portBASE_TYPE uxTopUsedPriority; 记录当前系统中被使用的最高优先级, static volatile unsigned portBASE_TYPE uxTopReadyPriority;记录当前系统中处于就绪状 态的最高优先级。 static volatile signed portBASE_TYPE xSchedulerRunning ;表示当前调度器是否在运行, 也即内核是否启动了 任务建立和删除,挂起和唤醒

5.时间管理 操作系统总是需要个时钟节拍的,这个需要硬件支持。freertos 同样需要一个 time tick 产生 器, 通常是用处理器的硬件定时器来实现这个功能。 (时间片轮转调度中和延时时间控制??) 它周期性的产生定时中断,所谓的时钟节拍管理的核心就是这个定时中断的服务程序。 freertos 的时钟节拍 isr 中除去保存现场,灰度现场这些事情外,核心的工作就是调用 vTaskIncrementTick()函数。vTaskIncrementTick()函数主要做两件事情:维护系统时间(以 tick 为单位,多少个节拍) ;处理那些延时的任务,如果延时到期,则唤醒任务。 任务可用的延时函数:vTaskDelay();vTaskDelayUntil(); 特别之处在于 vTaskDelayUntil()是一个周期性任务可以利用它可以保证一个固定的 (确定的) 常数执行频率,而 vTaskDelay()无法保证。 6.任务间的通信(详见“FreeRTOS 任务间通讯”) 1)当然可以用全局变量的形式通信,但是不安全。 2)队列(xQueueHandle)是 FreeRTOS 中通信所需的主要数据结构。 3)信号量(xSemaphoreHandle),有二进制信号量,计数信号量和互斥信号量,其都是以队列 为基础结构建立。 二进制信号量可以用于中断和任务间的同步。 也就是说希望任务随外部中断而执行。 即外设 给出“数据已就绪”信号,系统中断,任务收到此中断信号接收数据。 互斥一般用于都共享资源或数据结构的保护。 因为任务调度不能保证数据不被破坏。 当一个 任务需要访问资源,它必须先获得 ('take') 令牌;当访问结束后,它必须释放令牌 - 允许 其他任务能够访问这个资源。(对此还有待进一步实验研究)。 7.系统配置 freeRTOS 配置在:FREERTOS_CONFIG.H 里面,条目如下: /* 是否配置成抢先先多任务内核,是 1 的时候,优先级高的任务优先执行。为 0 任务就没 有优先级之说,用时间片轮流执行 */ #define configUSE_PREEMPTION 1

/* IDLE 任务的 HOOK 函数,用于 OS 功能扩展,需要你自己编相应函数,名字是 void vApplicationIdleHook( void ) */ #define configUSE_IDLE_HOOK 0 /* SYSTEM TICK 的 HOOK 函数, 用于 OS 功能扩展, 需要你自己编相应函数, 名字是 void vApplicationTickHook(void ) */ #define configUSE_TICK_HOOK 0 /* 系统 CPU 频率,单位是 Hz */ #define configCPU_CLOCK_HZ 58982400 /* 系统 SYSTEM TICK 每秒钟的发生次数,数值越大系统反应越快,但是 CPU 用在任务切 换的开销就越多 */ #define configTICK_RATE_HZ 250 /* 系统任务优先级数。5 说明任务有 5 级优先度。这个数目越大耗费 RAM 越多 */ #define configMAX_PRIORITIES 5 /* 系统最小堆栈尺寸,注意 128 不是 128 字节,而是 128 个入栈。比如 ARM32 位,128 个入栈就是 512 字节 */ #define configMINIMAL_STACK_SIZE 128 /* 系统可用内存。一般设成除了操作系统和你的程序所用 RAM 外的最大 RAM 。比如 20KRAM 你用了 2K,系统用了 3K,剩下 15 就是最大 HEAP 尺寸。你可以先设小然后看 编译结果往大里加*/ #define configTOTAL_HEAP_SIZE 10240 /* 任务的 PC 名字最大长度, 因为函数名编译完了就不见了, 所以追踪时不知道哪个名字。 16 表示 16 个 char */ #define configMAX_TASK_NAME_LEN 16 /* 是否设定成追踪,由 PC 端 TraceCon.exe 记录,也可以转到系统显示屏上 */ #define configUSE_TRACE_FACILITY 0 /* 就是 SYSTEM TICK 的长度,16 是 16 位,如果是 16 位以下 CPU,一般选 1;如果是 32 位系统,一般选 0 */ #define configUSE_16_BIT_TICKS 0 /* 简单理解以下就是和 IDLE TASK 同样优先级的任务执行情况。建议设成 1,对系统影响 不大 */ #define configIDLE_SHOULD_YIELD 1 /* 是否用 MUTEXES。 MUTEXES 是任务间通讯的一种方式,特别是用于任务共享资源的 应用,比如打印机,任务 A 用的时候就排斥别的任务应用,用完了别的任务才可以应用 */

#define configUSE_MUTEXES 0 /* 确定是否用递归式的 MUTEXES */ #define configUSE_RECURSIVE_MUTEXES 0 /* 是否用计数式的 SEMAPHORES,SEMAPHORES 也是任务间通讯的一种方式 */ #define configUSE_COUNTING_SEMAPHORES 0 /* 是否应用可切换式的 API。freeRTOS 同一功能 API 有多个,有全功能但是需求资源和 时间较多的,此项使能后就可以用较简单的 API,节省资源和时间,但是应用限制较多 */ #define configUSE_ALTERNATIVE_API 0

/* 此 项 用 于 DEBUG , 来 看 是 否 有 栈 溢 出 , 需 要 你 自 己 编 相 应 检 查 函 数 void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName ) */ #define configCHECK_FOR_STACK_OVERFLOW 0 /* 用于 DEBUG,登记 SEMAPHORESQ 和 QUEUE 的最大个数,需要在任务用应用函数 vQueueAddToRegistry()和 vQueueUnregisterQueue() */ #define configQUEUE_REGISTRY_SIZE 10 /* 设定可以改变任务优先度 */ #define INCLUDE_vTaskPrioritySet 1 /* 设定可以查询任务优先度 */ #define INCLUDE_uxTaskPriorityGet 1 /* 设定可以删除任务 */ #define INCLUDE_vTaskDelete 1 /* 据说是可以回收删除任务后的资源(RAM 等)*/ #define INCLUDE_vTaskCleanUpResources 0 /* 设置可以把任务挂起 */ #define INCLUDE_vTaskSuspend 1 /* 设置可以从中断恢复(比如系统睡眠,由中断唤醒 */ #define INCLUDE_vResumeFromISR 1 /* 设置任务延迟的绝对时间,比如现在 4:30,延迟到 5:00。时间都是绝对时间 */ #define INCLUDE_vTaskDelayUntil 1 /* 设置任务延时,比如延迟 30 分钟,相对的时间,现在什么时间,不需要知道 */ #define INCLUDE_vTaskDelay 1

/* 设置取得当前任务分配器的状态 */ #define INCLUDE_xTaskGetSchedulerState 1 /* 设置当前任务是由哪个任务开启的 */ #define INCLUDE_xTaskGetCurrentTaskHandle 1 /* 是否使能这一函数,还数的目的是返回任务执行后任务堆栈的最小未用数量,同样是为 防止堆栈溢出 */ #define INCLUDE_uxTaskGetStackHighWaterMark 0 /* 是用用协程。协程公用堆栈,节省 RAM,但是没有任务优先级高,也无法和任务通讯 */ #define configUSE_CO_ROUTINES 0 /* 所有协程的最大优先级数,协程优先级永远低于任务。就是系统先执行任务,所有任务 执行完了才执行协程。*/ #define configMAX_CO_ROUTINE_PRIORITIES 1 /* 系统内核的中断优先级,中断优先级越低,越不会影响其他中断。一般设成最低 */ #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] /* 系统 SVC 中断优先级,这两项都在在 M3 和 PIC32 上应用 */ #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] #endif /* FREERTOS_CONFIG_H */


相关文章:
freertos资料整理.doc
freertos资料整理 - 用了半天时间对 FreeRTOS 有了一个初步的认
FreeRTOS内存管理方案的分析与改进.pdf
FreeRTOS内存管理方案的分析与改进 - 针对FreeRTOS内存管理方案分
os资料整理.doc
os资料整理_IT/计算机_专业资料。主流操作系统整理 据iSuppli 公司,云...C/OS-Ⅱ,FreeRTOS,Linux-2.6 等,但是这些核 多半是用于通用领域或者...
FreeRTOS相关API函数.pdf
FreeRTOS相关API函数 - FreeRTOS--API 函数(翻译整理) FREERTOS API 函数 HAO 32905957@qq.com 目录 一、任务创建 ...
FreeRTOS编程指南_图文.pdf
FreeRTOS编程指南 - 一个开源的实时嵌入式系统。... FreeRTOS编程指南_计算机硬件及网络_IT/计算机_专业资料。一个开源的实时嵌入式系统。 您的评论 发布评论 用户...
在STM32中移植FreeRTOS(纯净版)_图文.pdf
在STM32中移植FreeRTOS(纯净版)_计算机软件及应用_IT/计算机_专业资料。在stm...STM32 固件库整理: 1、 解压固件库压缩包 STM32F10x_StdPeriph_Lib_V3.5.0...
FreeRTOS任务管理分析.pdf
FreeRTOS任务管理分析_计算机软件及应用_IT/计算机_专业资料FreeRTOS任务管理分析 FreeRTOS 任务管理分析 freertos 是一个轻量级的 rtos,它目前实现了一个微内核,...
freertos.doc
freertos - 从基本原理到实现 本文描述 FreeRTOS(飞拓)是如何实现的。 如果你 1. 希望修改 FreeRTOS 源代码 2. 移植实时内核到另一个微控制器或者原型板(pro...
【FreeRTOS操作系统教程】第7章 FreeRTOSConfig配置文....pdf
教程】第7章 FreeRTOSConfig配置文件详解_计算机硬件及网络_IT/计算机_专业资料...本章节内容主要整理自官网:http://www.freertos.org/a00110.html 7.1 配置...
FreeRTOS移植到STM32F103步骤与注意事项.doc
FreeRTOS移植到STM32F103步骤与注意事项_生产/经营管理_经管营销_专业资料。Free...portable 目录下只保留下面几个文件夹的 文件即可: 现在已经把源码整理好了,接...
FreeRTOS进程管理.doc
FreeRTOS进程管理 - freertos 是一个轻量级的 rtos, 它目
【FreeRTOS操作系统教程】第17章 FreeRTOS系统时钟节拍....pdf
FreeRTOS操作系统教程】第17章 FreeRTOS系统时钟节拍和时间管理_计算机硬件及网络_IT/计算机_专业资料。【FreeRTOS操作系统教程】第17章 FreeRTOS系统时钟节拍和...
STM32CubeMX+FreeRTOS学习[2] 二值信号量(Lu).doc
MX+FreeRTOS学习[2] 二值信号量(Lu)_计算机软件及应用_IT/计算机_专业资料。...将 main 函数整理,删除很多注释之后,得到下图所示内容。 其中第①部分,是硬件...
STM32CubeMX FreeRTOS.pdf
STM32CubeMX FreeRTOS_电子/电路_工程科技_专业资料FreeRTOS 学习之二:二值...将 main 函数整理,删除很多注释之后,得到下图所示内容。 其中第①部分,是硬件...
freertos的fatfs文件系统.pdf
freertos的fatfs文件系统_计算机软件及应用_IT/计算机_专业资料freertos的fatfs文件系统相关信息 FreeRTOS 中 FATFS 文件系统当我们遇到存储数据时候就需要对flash ...
STM32CubeMX+FreeRTOS学习[3] 计数信号量(Lu).doc
MX+FreeRTOS学习[3] 计数信号量(Lu)_计算机软件及应用_IT/计算机_专业资料。...将 main 函数整理,删除很多注释之后,得到下图所示内容。 其中第①部分,是硬件...
AUTOSAR资料From官网.doc
AUTOSAR资料From官网_机械/仪表_工程科技_专业资料。Basics Driven by the ...freertos资料整理 66页 1下载券 AUTOSAR OS规范(一部分) 44页 1下载券 ...
STM32CubeMX+FreeRTOS学习[6] 互斥量(Lu).doc
32CubeMX+FreeRTOS学习[6] 互斥量(Lu)_计算机软件及应用_IT/计算机_专业资料...将 main 函数整理,删除很多注释之后,得到下图所示内容。 其中第①部分,是硬件...
freertos_axibram.txt
freertos_axibram_互联网_IT/计算机_专业资料。/*** In
STM32CubeMX FreeRTOS.pdf
STM32CubeMX FreeRTOS_电子/电路_工程科技_专业资料FreeRTOS 学习之六:互斥...将 main 函数整理,删除很多注释之后,得到下图所示内容。 其中第①部分,是硬件...
更多相关标签: