细说STM32单片机FreeRTOS任务管理API函数及多任务编程的实现方法

目录

一、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会发生栈溢出,导致程序无法正常运行。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/77479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

星露谷物语 7000+ 大型MOD整合包

衣服美化、家具美化、地图美化、人物肖像美化 全地图装修存档、人物美化、扩展包、环境美化、家具、动植物、通用前置包、新增NPC、功能、服装发饰妆 帽子发型农场小镇美化大型玩法拓展实用功能mod 动漫人物形象MOD 地点/动物/地图/功能/机械/家具/建筑/界面美化/扩展/农场/食谱…

C++ `unique_ptr` 多线程使用

C unique_ptr 多线程使用 一、核心结论 操作同一个 unique_ptr&#xff1a;必须加锁&#xff08;所有权转移是非原子操作&#xff09;访问被管理对象&#xff1a;若对象非线程安全&#xff0c;仍需额外同步独立 unique_ptr 实例&#xff1a;不同线程操作不同实例时无需加锁 二…

Android audio系统六 AudioEffect音效加载

对于Android系统智能硬件设备&#xff0c;音效处理的实现方式有以下几种&#xff1a; AudioEffect – android系统音效处理 优点&#xff1a;纯软件实现&#xff0c;移植调试简单方便 缺点&#xff1a;cpu上运行&#xff0c;容易因为资源竞争而出现卡顿 DSP/ADSP – 数字信号处…

深度学习总结(21)

超越基于常识的基准 除了不同的评估方法&#xff0c;你还应该了解的是利用基于常识的基准。训练深度学习模型&#xff0c;你听不到也看不到。你无法观察流形学习过程&#xff0c;它发生在数千维空间中&#xff0c;即使投影到三维空间中&#xff0c;你也无法解释它。唯一的反馈…

接口自动化测试(二)

一、接口测试流程&#xff1a;接口文档、用例编写 拿到接口文档——编写接口用例以及评审——进行接口测试——工具/自动化框架进行自动化用例覆盖(70%)——输出测试报告 自动化的目的一般是为了回归 第一件事情&#xff1a;理解需求&#xff0c;学会看接口文档 只需要找到我…

Linux上位机开发实践(以MCU小系统入门嵌入式电路)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 一直都主张嵌入式软件工程师&#xff0c;也要会做一点电路设计的工作。哪怕自己做的是嵌入式linux上层开发&#xff0c;一个会硬件设计&#xff0c…

浏览器的存储机制 - Storage

浏览器的存储机制 - Storage 前言一、核心概念与区别二、常用 API1、存储数据&#xff08;setItem(key, value)&#xff09;2、 获取数据&#xff08;getItem(key)&#xff09;3、删除单个数据&#xff08;removeItem(key)&#xff09;4、清空所有数据&#xff08;clear()&…

考研单词笔记 2025.04.18

chance n机会&#xff0c;风险&#xff0c;冒险&#xff0c;可能性&#xff0c;巧合&#xff0c;意外a偶然的&#xff0c;意外的 opportunity n机会&#xff0c;时机 crisis n危机&#xff0c;危急关头 the economic crisis 经济危机 danger n危险&#xff0c;可能性&#…

第三方API——Spring Boot 集成阿里云短信发送功能

目录 一. 创建阿里云OSS服务并获取密钥&#xff0c;开通短信服务 1.1 注册阿里云服务器 1.2 开通短信服务 1.3 创建对象存储OSS服务 1.4 RAM用户授权短信权限 1.5 新增用户并授权用户短信权限 1.6 获取 AccessKey ID 和 AccessKey Secret 二. 创建项目集成短信发送 2.1…

b站PC网页版视频播放页油猴小插件制作

文章目录 前言需求分析实施观察页面起始渲染编码效果展示 总结 前言 新手上路,欢迎指导 需求分析 想要一个简约干净的界面,需要去除推荐栏和广告部分. 想要自由调节视频播放速率,需要在视频控制栏加一个输入框控制视频倍速 实施 观察页面起始渲染 因为要使用MutationObse…

畅游Diffusion数字人(27):解读字节跳动提出主题定制视频生成技术Phantom

畅游Diffusion数字人(0):专栏文章导航 前言:主题定制视频生成,特别是zero-shot主题定制视频生成,一直是当前领域的一个难点,之前的方法效果很差。字节跳动提出了一个技术主题定制视频生成技术Phantom,效果相比于之前的技术进步非常显著。这篇博客详细解读一下这一工作。 …

ESP8266简单介绍

ESP8266模块图如下 ESP8266的工作模式有三种 ESP8266支持STA、AP、STAAP三种工作模式 ①STA模式 &#xff08;ESP充当设备与路由器相连&#xff09; ②AP模式 &#xff08;ESP充当路由器&#xff09; ③APSTA&#xff08;上述两种模式兼具&#xff09; AT指令介绍 使用安…

DeepSeek-R3、GPT-4o 与 Claude-3.5-Sonnet 全面对比:性能、应用场景与技术解析

随着大模型技术的迅猛发展&#xff0c;国产模型正逐渐崭露头角&#xff0c;尤其是DeepSeek-R3的发布&#xff0c;更是在AI技术社区中引起广泛关注。而与此同时&#xff0c;国际领先的GPT-4o和Claude-3.5-Sonnet也在不断迭代升级&#xff0c;持续刷新业界对AI能力的认知。下文将…

城市街拍暗色电影胶片风格Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 城市街拍暗色电影胶片风格 Lr 调色&#xff0c;是借助 Adobe Lightroom 软件&#xff0c;为城市街拍的人像或场景照片赋予独特视觉风格的后期处理方式。旨在模拟电影胶片质感&#xff0c;营造出充满故事感与艺术感的暗色氛围&#xff0c;让照片仿佛截取于某部充满张力…

数字后端设计 (一):数字电路设计的「前后端」到底是什么?

—— 想象你在做一道菜——前端设计是写菜谱&#xff0c;后端设计是进厨房真正炒菜。这篇文章帮你搞懂「芯片设计」里这两个阶段到底在干嘛。 1. 前端设计——写一份「理想化」的菜谱 任务&#xff1a;用代码描述芯片的功能。例子&#xff1a;你要做一个自动计算“112”的芯片…

网站301搬家后谷歌一直不收录新页面怎么办?

当网站因更换域名或架构调整启用301重定向后&#xff0c;许多站长发现谷歌迟迟不收录新页面&#xff0c;甚至流量大幅下滑。 例如&#xff0c;301跳转设置错误可能导致权重传递失效&#xff0c;而新站内容与原站高度重复则可能被谷歌判定为“低价值页面”。 即使技术层面无误&a…

WiFi“管家”------hostapd的工作流程

目录 1. 启动与初始化 1.1 解析命令行参数 1.2 读取配置文件 1.3 创建接口和 BSS 数据结构 1.4 初始化驱动程序 2. 认证和关联处理 2.1 监听认证请求 2.2 处理认证请求 2.3 处理关联请求 3. 数据转发 3.1 接收客户端数据 3.2 转发数据 4. 断开连接处理 4.1 处理客…

YOLOv2 快速入门与核心概念:更快、更准的目标检测利器

今天&#xff0c;我们就来聊聊 YOLO 系列的第二代—— YOLOv2&#xff0c;看看它是如何在速度的基础上&#xff0c;进一步提升检测精度的。 目标检测的重要性&#xff1a;让机器“看懂”世界 想象一下&#xff0c;自动驾驶汽车需要实时识别道路上的车辆、行人、交通标志&…

[苍穹外卖 | 项目日记] 第三天

前言 实现了新增菜品接口实现了菜品分页查询接口实现了删除菜品接口实现了根据id查询菜品接口实现了修改菜品接口 今日收获&#xff1a; 今日的这几个接口其实和之前写的对员工的操作是一样的&#xff0c;都是一整套Curd操作&#xff0c;所以今天在技术层面上并没有…

Go语言入门到入土——三、处理并返回异常

Go语言入门到入土——三、处理并返回异常 文章目录 Go语言入门到入土——三、处理并返回异常1. 在greetings.go中添加异常处理代码2. 在hello.go中添加日志记录代码3. 运行 1. 在greetings.go中添加异常处理代码 处理空输入的异常&#xff0c;代码如下&#xff1a; package g…