目录
一、FreeRTOS任务管理API函数
1、任务管理API函数
2、获取任务的句柄
(1)函数xTaskGetCurrentTaskHandle()
(2)函数xTaskGetIdleTaskHandle()
(3)函数xTaskGetHandle()
3、单个任务的操作
(1)获取和设置任务的优先级
(2)函数vTaskGetInfo()
(3)函数pcTaskGetName()
(4)函数uxTaskGetStackHighWaterMark()
(5)函数eTaskGetState()
4、内核信息统计
(1)函数uxTaskGetNumberOfTasks()
(2)函数vTaskList()
(3)函数uxTaskGetSystemState()
(4)函数vTaskGetRunTimeStats()
(5)函数xTaskGetSchedulerState()
二、示例演示任务管理API函数的使用方法
1、示例功能
2、项目设置
(1)RCC、SYS、Code Generator
(2) ADC3_IN6
(3)FreeRTOS
(4)GPIO
(5)USART3
(6)NVIC
3、软件设计
(1)main.c
(2)freertos.c
(3)FreeRTOSConfig.h
4、运行与调试
FreeRTOS中除了用于任务管理的一些RTOS函数外,还有一些API函数(也叫任务管理工具函数),用于操作任务或取任务信息。一些内容需要参考本文作者写的其他文章,
参考文章:细说STM32单片机FreeRTOS任务管理相关函数及多任务编程的实现方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/147139017?spm=1011.2415.3001.5331
一、FreeRTOS任务管理API函数
1、任务管理API函数
分组 | FreeRTOS函数 | 函数功能 |
获取 任务 句柄 | xTaskGetCurrentTaskHandle() | 获取当前任务的句柄 |
xTaskGetIdleTaskHandle() | 获取空闲任务的句柄 | |
xTaskGetHandle() | 根据任务名称返回任务句柄,运行比较慢 | |
单个 任务 的信 息 | uxTaskPriorityGet() | 获取一个任务的优先级 |
uxTaskPriorityGetFromISR() | 函数uxTaskPriorityGet()的ISR版本 | |
vTaskPrioritySet() | 设置一个任务的优先级,可以在运行过程中改变一个任务的优先级 | |
vTaskGetInfo() | 返回一个任务的信息,包括状态信息、栈空间信息等 | |
pcTaskGetName() | 根据任务句柄返回任务的名称,参数为NULL时,返回任务自己的名称 | |
uxTaskGetStackHighWaterMark() | 返回一个任务的栈空间的高水位值,即最少可用空间。返回值越小,表明任务的栈空间越容易溢出。高水位值的单位是字 | |
eTaskGetState() | 返回一个任务的当前运行状态,返回值是枚举类型eTaskState | |
内核 信息 | uxTaskGetNumberOfTasks() | 返回内核管理的所有任务的个数,包括就绪的、阻塞的、挂起的任务,也包括虽然删除了,但还没有在空闲任务里释放的任务 |
vTaskList() | 创建一个列表,显示所有任务的信息。此函数会禁止所有中断,需要使用sprintf()函数,所以一般只用于调试阶段 | |
uxTaskGetSystemState() | 获取系统中所有任务的任务状态,包括每个任务的句柄、任务名称、优先级等信息 | |
vTaskGetRunTimeStats() | 获取每个任务的运行时间统计 | |
xTaskGetTickCount() | 返回嘀嗒信号当前计数值 | |
xTaskGetTickCountFromISR() | 函数xTaskGetTickCount()的ISR版本 | |
xTaskGetSchedulerState() | 返回任务调度器的运行状态 | |
其他 | vTaskSetApplicationTaskTag() | 设置一个任务的标签值,每个任务可以设置一个标签值,保存于任务控制块中 |
xTaskGetApplicationTaskTag() | 获取一个任务的标签值 | |
xTaskCallApplicationTaskHook() | 调用任务关联的钩子函数 | |
vTaskSetThreadLocalStoragePointer() | 每个任务有一个指针数组,此函数为任务设置一个本地存储指针,指针的用途由用户自己决定,内核不使用此指针数组 | |
pvTaskGetThreadLocalStoragePointer() | 获取任务的指针数组中的一个指定序号的指针 | |
vTaskSetTimeOutState() | 获取当前的时钟状态,以便作为xTaskCheckForTimeOut()函数的初始条件参数 | |
xTaskCheckForTimeOut() | 检查是否超过等待的节拍数,与vTaskSetTimeOutState()函数结合使用,仅用于高级用途 |
要在程序中使用这些函数,某些“config”参数或“INCLUDE_”参数需要设置为1。
2、获取任务的句柄
对单个任务进行操作的函数,一般需要一个TaskHandle_t类型的表示任务句柄的变量作为参数
typedef void *TaskHandle_t;
FreeRTOS中还有3个用于获取任务句柄的函数。
(1)函数xTaskGetCurrentTaskHandle()
用于获得当前任务的句柄,其原型定义如下:
TaskHandle_t xTaskGetCurrentTaskHandle(void);
要使用这个函数,需要将参数INCLUDE_xTaskGetCurrentTaskHandle设置为1(默认值为0),可以在CubeMX里设置。
(2)函数xTaskGetIdleTaskHandle()
用于获得空闲任务的句柄,其原型定义如下:
TaskHandle_t xTaskGetIdleTaskHandle(void);
要使用这个函数,需要将参数INCLUDE_xTaskGetldleTaskHandle设置为1(默认值为0)。
这个参数在CubeMX里不能设置,需要用户在FreeRTOSConfig.h文件中自己添加宏定义,添加到文件FreeRTOSConfig.h的用户定义代码沙箱段,示例如下:
/*USER CODE BEGIN Defines*/
/*在这里添加定义,例如,用于覆盖FreeRTOS.h中的默认定义*/
#define INCLUDE_xTaskGetIdleTaskHandle 1
/*USER CODE END Defines */
上述宏定义是要手工添加的,如果漏掉了此操作,编译的时候会出现2处警告,直到手工添加了此处的宏定义,警告才消失。“/freertos.c:160:(.text.AppTask_info+0x8): undefined reference to `xTaskGetIdleTaskHandle'”。
(3)函数xTaskGetHandle()
用于通过任务名称获得任务句柄,其原型定义如下:
TaskHandle_t xTaskGetHandle(const char *pcNameToQuery);
参数pcNameToQuery是任务名称字符串。这个函数运行时间相对较长,不宜大量使用。女果两个任务具有相同的任务名称,则函数返回的结果是不确定的。函数使用示例代码如下:
const char *taskName ="Task_LED1";
TaskHandle_t taskHandle = xTaskGetHandle(taskName);
要使用这个函数,需要将参数INCLUDE_xTaskGetHandle设置为1(默认值为0)。这个参数可以在CubeMX里设置。
3、单个任务的操作
(1)获取和设置任务的优先级
执行3个API函数,可以获取或改变一个任务的优先级,相关3个函数的原型定义如下。要使用这3个API函数,需要将参数INCLUDE_uxTaskPriorityGet或INCLUDE_vTaskPrioritySet设置为1(默认值都是1),可以在CubeMX里设置。
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask); //返回一个任务的优先级
UBaseType_t uxTaskPriorityGetFromISR(TaskHandle_t xTask); //函数的ISR版本
void vTaskPrioritySet(TaskHandle_t xTask,UBaseType_t uxNewPriority);//设置优先级
其中,优先级用UBaseType_t类型的数表示,而在文件cmsis_os2.h中,定义了优先级的枚举类型osPriority_t,其部分定义如下:
/// Priority values.
typedef enum {osPriorityNone = 0, ///< No priority (not initialized).osPriorityIdle = 1, ///< Reserved for Idle thread.osPriorityLow = 8, ///< Priority: lowosPriorityLow1 = 8+1, ///< Priority: low + 1osPriorityLow2 = 8+2, ///< Priority: low + 2osPriorityLow3 = 8+3, ///< Priority: low + 3osPriorityLow4 = 8+4, ///< Priority: low + 4osPriorityLow5 = 8+5, ///< Priority: low + 5osPriorityLow6 = 8+6, ///< Priority: low + 6osPriorityLow7 = 8+7, ///< Priority: low + 7osPriorityBelowNormal = 16, ///< Priority: below normalosPriorityBelowNormal1 = 16+1, ///< Priority: below normal + 1osPriorityBelowNormal2 = 16+2, ///< Priority: below normal + 2osPriorityBelowNormal3 = 16+3, ///< Priority: below normal + 3osPriorityBelowNormal4 = 16+4, ///< Priority: below normal + 4osPriorityBelowNormal5 = 16+5, ///< Priority: below normal + 5osPriorityBelowNormal6 = 16+6, ///< Priority: below normal + 6osPriorityBelowNormal7 = 16+7, ///< Priority: below normal + 7osPriorityNormal = 24, ///< Priority: normalosPriorityNormal1 = 24+1, ///< Priority: normal + 1osPriorityNormal2 = 24+2, ///< Priority: normal + 2osPriorityNormal3 = 24+3, ///< Priority: normal + 3osPriorityNormal4 = 24+4, ///< Priority: normal + 4osPriorityNormal5 = 24+5, ///< Priority: normal + 5osPriorityNormal6 = 24+6, ///< Priority: normal + 6osPriorityNormal7 = 24+7, ///< Priority: normal + 7osPriorityAboveNormal = 32, ///< Priority: above normalosPriorityAboveNormal1 = 32+1, ///< Priority: above normal + 1osPriorityAboveNormal2 = 32+2, ///< Priority: above normal + 2osPriorityAboveNormal3 = 32+3, ///< Priority: above normal + 3osPriorityAboveNormal4 = 32+4, ///< Priority: above normal + 4osPriorityAboveNormal5 = 32+5, ///< Priority: above normal + 5osPriorityAboveNormal6 = 32+6, ///< Priority: above normal + 6osPriorityAboveNormal7 = 32+7, ///< Priority: above normal + 7osPriorityHigh = 40, ///< Priority: highosPriorityHigh1 = 40+1, ///< Priority: high + 1osPriorityHigh2 = 40+2, ///< Priority: high + 2osPriorityHigh3 = 40+3, ///< Priority: high + 3osPriorityHigh4 = 40+4, ///< Priority: high + 4osPriorityHigh5 = 40+5, ///< Priority: high + 5osPriorityHigh6 = 40+6, ///< Priority: high + 6osPriorityHigh7 = 40+7, ///< Priority: high + 7osPriorityRealtime = 48, ///< Priority: realtimeosPriorityRealtime1 = 48+1, ///< Priority: realtime + 1osPriorityRealtime2 = 48+2, ///< Priority: realtime + 2osPriorityRealtime3 = 48+3, ///< Priority: realtime + 3osPriorityRealtime4 = 48+4, ///< Priority: realtime + 4osPriorityRealtime5 = 48+5, ///< Priority: realtime + 5osPriorityRealtime6 = 48+6, ///< Priority: realtime + 6osPriorityRealtime7 = 48+7, ///< Priority: realtime + 7osPriorityISR = 56, ///< Reserved for ISR deferred thread.osPriorityError = -1, ///< System cannot determine priority or illegal priority.osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
} osPriority_t;
用户可以在函数vTaskPrioritySet()中使用枚举值,例如:
TaskHandle_t taskHandle = xTaskGetCurrentTaskHandle(); //获取当前任务的句柄
vTaskPriorityset(taskHandle,(UBaseType_t)osPriorityAboveNormal);//设置优先级
(2)函数vTaskGetInfo()
vTaskGetInfo()用于获取一个任务的信息,使用前必须将参数configUSE_TRACE_FACILITY设置为1(默认值为1),可在CubeMX里设置。这个函数的原型定义如下,各参数的意义见注释:
void vTaskGetInfo( TaskHandle_t xTask, //任务的句柄TaskStatus_t *pxTaskStatus, //用于存储任务状态信息的结构体指针BaseType_t xGetFreeStackSpace, //是否返回栈空间高水位值eTaskState eState ) ; //指定任务的状态
参数xTask是需要查询的任务的句柄;
参数pxTaskStatus是用于存储返回信息的TaskStatus_t结构体指针;
参数xGetFreeStackSpace,表示是否在结构体TaskStatus_t中返回栈空间的高水位值usStackHighWaterMark,如果xGetFreeStackSpace是pdTRUE,就返回高水位值。因为返回任务的高水位值需要较长的时间,若xGetFreeStackSpace设置为pdFALSE,就可以忽略此过程;
函数vTaskGetInfo()中的参数eState用于指定查询信息时的任务状态,虽然TaskStatus_t中有获取任务状态的成员变量,但是不如直接赋值快。如果需要函数vTaskGetInfo()自动获取任务的状态,将参数eState设置为枚举值eInvalid即可。
其中, 用于存储返回信息的TaskStatus_t结构体指针定义如下:
/* Used with the uxTaskGetSystemState() function to return the state of each task
in the system. */
typedef struct xTASK_STATUS
{TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */UBaseType_t xTaskNumber; /* A number unique to the task. */eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */
} TaskStatus_t;
其中,枚举类型eTaskState表示了任务的运行、就绪、阻塞、挂起等状态,其定义如下:
/* Task states returned by eTaskGetState. */
typedef enum
{eRunning = 0, /* A task is querying the state of itself, so must be running. */eReady, /* The task being queried is in a read or pending ready list. */eBlocked, /* The task being queried is in the Blocked state. */eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
(3)函数pcTaskGetName()
函数pcTaskGetName()用于返回一个任务的任务名称字符串,其原型定义如下:
char *pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
TCB_t *pxTCB;/* If null is passed in here then the name of the calling task is beingqueried. */pxTCB = prvGetTCBFromHandle( xTaskToQuery );configASSERT( pxTCB );return &( pxTCB->pcTaskName[ 0 ] );
}
如果要查询任务自己的任务名称,将参数xTaskToQuery设置为NULL即可。
(4)函数uxTaskGetStackHighWaterMark()
函数uxTaskGetStackHighWaterMark()用于获取一个任务的高水位值,其原型定义如下:
#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ){TCB_t *pxTCB;uint8_t *pucEndOfStack;UBaseType_t uxReturn;pxTCB = prvGetTCBFromHandle( xTask );#if portSTACK_GROWTH < 0{pucEndOfStack = ( uint8_t * ) pxTCB->pxStack;}#else{pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack;}#endifuxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack );return uxReturn;}#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
如果要查询任务自己的高水位值,将参数xTask设置为NULL即可。
若要使用这个函数,必须将参数INCLUDE_uxTaskGetStackHighWaterMark设置为1(默认值为1),可在CubeMX里设置。高水位值实际上就是任务的栈空间最少可用剩余空间的大小,单位是字(word)。这个值越小,表示任务的栈空间越容易溢出。
(5)函数eTaskGetState()
函数eTaskGetState()返回一个任务的当前状态,其原型定义如下:
#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) )eTaskState eTaskGetState( TaskHandle_t xTask ){eTaskState eReturn;List_t const * pxStateList, *pxDelayedList, *pxOverflowedDelayedList;const TCB_t * const pxTCB = xTask;/* 此处省去1万言 */} /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */#endif /* INCLUDE_eTaskGetState */
返回值是枚举类型eTaskState,表示任务的就绪、运行、阻塞、挂起等状态。
若要使用这个函数,需要将参数INCLUDE_eTaskGetState或configUSE_TRACE_FACILITY或INCLUDE_xTaskAbortDelay设置为1,这些参数默认值都是1,且都可以在CubeMX里设置。
4、内核信息统计
(1)函数uxTaskGetNumberOfTasks()
函数uxTaskGetNumberOfTasks()返回内核当前管理的任务的总数,包括就绪的、阻塞的、描起的任务,也包括虽然删除了但还没有在空闲任务里释放的任务。其原型定义如下:
UBaseType_t uxTaskGetNumberOfTasks( void )
{/* A critical section is not required because the variables are of typeBaseType_t. */return uxCurrentNumberOfTasks;
}
(2)函数vTaskList()
函数vTaskList()返回内核中所有任务的字符串列表信息,包括每个任务的名称、状态、优先级、高水位值、任务编号等。其原型定义如下:
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )void vTaskList( char * pcWriteBuffer ){TaskStatus_t *pxTaskStatusArray;UBaseType_t uxArraySize, x;char cStatus;/* 此处省去1万言 */ }#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */
参数pcWriteBuffer是预先创建的一个字符数组的指针,用于存储返回的字符串信息。这个字符数组必须足够大,FreeRTOS不会检查这个数组的大小。这个函数的使用示例代码如下:
char infoBuffer[300];
vTaskList(infoBuffer);
返回的数据存储在字符数组infoBuffer中,使用了“\t”“\n”等转义字符,以便用表格方式显示。
(3)函数uxTaskGetSystemState()
要使用这个函数,需要将参数configUSE_TRACE_FACILITY配置为1(默认值为1),可在CubeMX里配置这个参数。
这个函数用于获得系统内所有任务的状态,为每个任务返回一个TaskStatus_t结构体数据。函数uxTaskGetSystemState()的原型定义如下:
#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ){//此处省略1万言}#endif /* configUSE_TRACE_FACILITY */
- 参数pxTaskStatusArray是一个数组的指针,成员是结构体类型TaskStatus_t。需预先分配数组大小,必须大于或等于FreeRTOS内的任务数。返回的数据就存储在这个数组里,每个任务对应一个数组成员。
- 参数uxArraySize是数组pxTaskStatusArray的大小,表示数组pxTaskStatusArray的成员个数。
- 参数pulTotalRunTime用于返回FreeRTOS启动后总的运行时间,如果设置为NULL,则不返回这个数据。只有参数configGENERATE_RUN_TIME_STATS设置为1,才会返回这个数据,默认值为0,可在CubeMX里设置。
函数的返回值是uxTaskGetSystemState()实际获取的任务信息的条数,也就是FreeRTOS中实际任务的个数,与函数uxTaskGetNumberOfTasks()返回的任务个数相同。
(4)函数vTaskGetRunTimeStats()
要使用这个函数,必须将以下3个参数都设置为1。
- configGENERATE_RUN_TIME_STATS,默认值为0,可在CubeMX里设置。
- configUSE_STATS_FORMATTING_FUNCTIONS,默认值为0,可在CubeMX里设置。
- configSUPPORT_STATIC_ALLOCATION ,默认值为0,可在CubeMX里设置。
函数vTaskGetRunTimeStats()用于统计系统内每个任务的运行时间,包括绝对时间和占用CPU的百分比。其原型定义如下:
#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )void vTaskGetRunTimeStats( char *pcWriteBuffer ){TaskStatus_t *pxTaskStatusArray;UBaseType_t uxArraySize, x;uint32_t ulTotalTime, ulStatsAsPercentage;//此处省去1万言}#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */
参数pcWriteBuffer用于存储返回数据的字符数组,返回的数据以文字表格的形式表示,与函数vTaskList()返回结果的方式类似。
函数vTaskGetRunTimeStats()运行时,会禁止所有中断,所以,不要在程序正常运行时使用这个函数,应该只在程序调试阶段使用。函数vTaskGetRunTimeStats()内部会调用uxTaskGetSystemState(),将其中的任务运行数据转换为更易阅读的绝对运行时间和百分比时间。函数vTaskGetRunTimeStats()依赖于函数sprintf(),会导致编译后的代码量增加,所以在发布的程序中,不要使用此函数。
(5)函数xTaskGetSchedulerState()
这个函数返回调度器的状态,当以下2个参数中的某一个设置为1时,此函数就可用。
- INCLUDE_xTaskGetSchedulerState,默认值为1,可在CubeMX里修改。
- configUSE_TIMERS,默认值为1,CubeMX里有这个参数,但不允许修改。
函数xTaskGetSchedulerState()返回任务调度器当前的状态,其原型定义如下:
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )BaseType_t xTaskGetSchedulerState( void ){BaseType_t xReturn;if( xSchedulerRunning == pdFALSE ){xReturn = taskSCHEDULER_NOT_STARTED;}else{if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ){xReturn = taskSCHEDULER_RUNNING;}else{xReturn = taskSCHEDULER_SUSPENDED;}}return xReturn;}#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */
返回值用如下的3个宏定义常数表示任务调度器的状态:
#define taskSCHEDULER_SUSPENDED ((BaseType_t)0) //被挂起
#define taskSCHEDULER_NOT_STARTED ((BaseType_t)1) //未启动
#define taskSCHEDULER_RUNNING ((BaseType_t)2) //正在运行
二、示例演示任务管理API函数的使用方法
1、示例功能
创建一个示例,演示FreeRTOS中一些任务管理函数的使用。在示例中,在FreeRTOS中创建2个任务:任务Task_ADC通过ADC3的IN6通道周期采集电位器的电压值,并在串口助手上显示;任务Task_Info用于测试任务信息统计的一些工具,统计信息在串口助手上显示。
继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。电位器部分的原理图如下:
2、项目设置
(1)RCC、SYS、Code Generator
外部晶振25MHz,HCLK=168MHz;
选择Serial Wire,选择TIM6为Timebase Source;
勾选Code Generator页面的自动生成.c/.h文件对选择项;
(2) ADC3_IN6
设置ADC3的IN6输入通道,对应管脚PF8。开发板上的电位器对应管脚PF8,调压作为IN6的输入电压。默认其它所有参数。
(3)FreeRTOS
启用FreeRTOS,使用CMSIS_V2接口。Config parameters和Include parameters两个设置页面的参数都保持默认值。
在FreeRTOS中创建2个任务,设置任务的参数,两个任务的优先级不同,将栈空间大小都修改为256,并设置它们都使用动态分配内存方式。
若栈空间大小使用默认值128,本示例程序将无法正常运行。 两个任务的栈空间大小并不是随意给出的,是在程序运行过程中,通过多次统计栈空间的高水位值,给出的一个比较安全合理的值。栈空间太小,会导致栈空间溢出,程序无法正常运行,栈空间太大,会浪费内存。要设置合理的栈空间大小,最好在调试阶段统计一下任务的高水位值。
(4)GPIO
配置项目中需要的LED1。
(5)USART3
选择使用开发板上的USART3串口,管脚PB10、PB11。选择默认值。
(6)NVIC
选择默认值。
3、软件设计
(1)main.c
main.c的代码基本是CubeMX自动生成的。
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */uint8_t startstr[] = "Demo2-2_RTOS_MutiTasks: Test RTOS with Multiple tasks.\r\n\r\n";HAL_UART_Transmit(&huart3,startstr,sizeof(startstr),0xFFFF);/* USER CODE END 2 */
在外设初始化之后,仍然是依次调用3个函数进行FreeRTOS内核初始化、对象初始化和内核启动,与具体示例相关的就是函数MX_FREERTOS_Init(),它创建项目里定义的任务。
(2)freertos.c
FreeRTOS对象初始化函数MX_FREERTOS_Init(),以及两个任务函数的框架。重写这两个任务函数:
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "adc.h"
#include "usart.h"
#include <stdio.h>
/* USER CODE END Includes */
下面的任务定义代码是自动生成的:
/* Definitions for Task_ADC */
osThreadId_t Task_ADCHandle;
const osThreadAttr_t Task_ADC_attributes = {.name = "Task_ADC",.stack_size = 256 * 4,.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for Task_info */
osThreadId_t Task_infoHandle;
const osThreadAttr_t Task_info_attributes = {.name = "Task_info",.stack_size = 256 * 4,.priority = (osPriority_t) osPriorityLow,
};
下面创建任务代码是自动生成的:
/* Create the thread(s) *//* creation of Task_ADC */Task_ADCHandle = osThreadNew(AppTask_ADC, NULL, &Task_ADC_attributes);/* creation of Task_info */Task_infoHandle = osThreadNew(AppTask_info, NULL, &Task_info_attributes);
下面的任务函数代码,CubeMX自动生成函数框架,手动重写函数体:
/* USER CODE BEGIN Header_AppTask_info */
/**
* @brief Function implementing the Task_info thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_AppTask_info */
void AppTask_info(void *argument)
{/* USER CODE BEGIN AppTask_info *////*----------获取单个任务信息----------*///TaskHandle_t taskHandle=xTaskGetCurrentTaskHandle(); ///<获取当前任务句柄TaskHandle_t taskHandle=xTaskGetIdleTaskHandle(); ///<获取空闲任务句柄//TaskHandle_t taskHandle=xTaskGetHandle("Task_ADC"); ///<通过任务名称获取句柄//TaskHandle_t taskHandle=Task_ADCHandle; ///<直接使用句柄变量TaskStatus_t taskInfo; ///<任务信息BaseType_t getFreeStackSpace=pdTRUE; ///<是否获取高水位值eTaskState taskState=eInvalid; ///<当前任务状态vTaskGetInfo(taskHandle, &taskInfo, getFreeStackSpace, taskState); ///<获取任务信息taskENTER_CRITICAL(); ///<开始临界代码段,禁止任务调度printf("Task_Info: Show task info and get it by vTaskGetInfo(). \r\n");printf("Task Name = %s\r\n",taskInfo.pcTaskName);printf("Task Number = %ld\r\n",taskInfo.xTaskNumber);printf("Task State = %d\r\n",taskInfo.eCurrentState);printf("Task Priority = %ld\r\n",taskInfo.uxCurrentPriority);printf("High Water Mark = %d\r\n\r\n",taskInfo.usStackHighWaterMark);///*----------获取每个任务的高水位值----------*/printf("Task_Info: Get High Water Mark of tasks. \r\n");taskHandle=xTaskGetIdleTaskHandle();UBaseType_t hwm=uxTaskGetStackHighWaterMark(taskHandle);printf("Idle Task = %ld\r\n",hwm);taskHandle=Task_ADCHandle;hwm=uxTaskGetStackHighWaterMark(taskHandle);printf("Task_ADC = %ld\r\n",hwm);taskHandle=Task_infoHandle;hwm=uxTaskGetStackHighWaterMark(taskHandle);printf("Task_Info = %ld\r\n\r\n",hwm);///*----------获取内核信息----------*/printf("Task_Info: Get Kernel Info. \r\n");UBaseType_t taskNum=uxTaskGetNumberOfTasks(); ///<获取任务数量printf("uxTaskGetNumberOfTasks() = %ld\r\n\r\n",taskNum);//char infoBuffer[300];//vTaskList(infoBuffer); ///<返回字符串表格,用/t/n制表//printf("vTaskList = %d\r\n",infoBuffer);taskEXIT_CRITICAL(); ///<结束临界代码段,允许任务调度UBaseType_t loopCount=0;/* Infinite loop */for(;;){loopCount++;HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6); ///<PA6=LED1 flashingvTaskDelay(pdMS_TO_TICKS(3000));if (loopCount==10) ///<循环10次后退出break;printf("Task_Info is deleted,and then only Task_ADC is running. \r\n\r\n");vTaskDelete(NULL); ///<删除任务自身}/* USER CODE END AppTask_info */
}
(3)FreeRTOSConfig.h
使用函数xTaskGetIdleTaskHandle()时,可能会出现编译错误,显示这个函数未定义。这是因为在源程序tasks.c中,这个函数有个预编译条件,只有当参数INCLUDE_xTaskGetldleTaskHandle值为1时,才编译这个函数。CubeMX中没有这个参数的对应设置项,而文件FreeRTOS.h中,这个参数的默认值为0。我们需要将这个参数的值修改为1,但是要注意,不能在文件FreeRTOS.h中直接修改这个参数的值,而要在文件FreeRTOSConfig.h的用户代码沙箱段内,重新定义这个宏,即下面的代码:
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
#define INCLUDE_xTaskGetIdleTaskHandle 1/* USER CODE END Defines */
这个宏定义必不可少,不然编译器警告。
这个沙箱段在文件FreeRTOSConfig.h中的最下方,就是用于重新定义一些无法在CubeMX中可视化设置的参数,用于替换其在文件FreeRTOS.h中的默认定义。
4、运行与调试
构建项目后,将其下载到开发板上并运行,可以在串口助手上看到各种信息。任务Task_ADC周期性地通过ADC3采集电压值,并在串口助手上显示;任务Task_Info读取的信息在串口助手上显示,LED1闪烁几次后,任务Task_Info被删除,LED1不再闪烁,修改延迟时间会小小改变删除任务的效果。
程序运行时,串口助手上显示了以下各任务的高水位值(单位是字)。
- 空闲任务的高水位值是118。
- 任务Task_ADC的高水位值是132。
- 任务Task_Info的高水位值是129。
空闲任务的栈空间大小是128字,由参数configMINIMAL_STACK_SIZE决定。任务LCD_ADC和Task_Info的栈空间大小被设置为256字,如果使用默认的大小128字,则任务Task_Info会发生栈溢出,导致程序无法正常运行。