使用到的软件
- STM32CubeMX
- Keil5
- xcom串口助手
- (使用的HAL库)
FreeRTOS介绍
RTOS(Real Time OS)实时操作系统逻辑图:
- 注:图片来源为正点原子教学视频
FreeRTOS由美国的Richard Barry于2003年发布,是目前市场占有率最高的RTOS系统。
FreeRTOS是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。
FreeRTOS是用 C 和汇编来写的,其中绝大部分都是用 C 语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS 结构简洁,可读性很强。
FreeRTOS源码获取:
最新版:www.freertos.org
以往版本:https://sourceforge.net/projects/freertos/files/FreRTOS/
- PS:笔者使用了STM32CubeMX来生成FreeRTOS的源文件,如果不想使用Cube来生成,可以从上面的FreeRTOS官网地址下载。
关于vTaskList()函数
vTaskList()函数是FreeRTOS中自带的一个用来获取任务信息的函数,该函数会创建一个列表,列表中包含任务名称,任务状态信息,任务优先级,剩余堆栈以及任务编号的信息。
函数原型为:
void vTaskList( char * pcWriteBuffer )
关于vTaskGetRunTimeStats()函数
vTaskGetRunTimeStats()函数是FreeRTOS中自带的一个用来统计各个Task占用CPU时间的函数,使用这个功能我们可以清晰地看到每个任务所占用时间、百分比以及CPU整体的占用率,任务的运行时间信息提供了每个任务获取到的CPU使用权总的时间。
函数vTaskGetRunTimeStats()会将统计到的信息填充到另一个列表里,这样我们就可以随时知道所有任务抢占CPU的结果,便于我们合理规划安排这些任务。
函数原型为:
void vTaskGetRunTimeStats( char *pcWriteBuffer )
如何使用
虽然说vTaskList()函数和vTaskGetRunTimeStats()函数都是FreeRTOS中已经给出的,但是我们无法直接使用。使用这两个函数之前我们需要使能以下这四个宏:
- configUSE_TRACE_FACILITY
- configGENERATE_RUN_TIME_STATS
- configUSE_STATS_FORMATTING_FUNCTIONS
- configSUPPORT_DYNAMIC_ALLOCATION
在FreeRTOSConfig.h中加入下列代码以启用这四个宏:
#define configUSE_TRACE_FACILITY 1
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
启用这四个宏之后我们还必须要定义下面两个宏:
-
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
-
portGET_RUN_TIME_COUNTER_VALUE()
第一个宏用来初始化一个外设来提供统计功能所需的时基。这个时基的分辨率一定要比FreeRTOS的系统时钟高,一般高出10~20倍。
在FreeRTOSConfig.h中加入如下代码定义这两个宏:
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimerForRunTimeStates()
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
下一步我们需要编写ConfigureTimerForRunTimeStates()函数以及定义变量FreeRTOSRunTimeTicks。FreeRTOSRunTimeTicks用于节拍计数。
在tim.c中加入如下代码:
- 注:这里因为笔者的定时器初始化在tim.c中,只是为了方便所以把下列代码也放到了tim.c中
volatile unsigned long long FreeRTOSRunTimeTicks=0;
void ConfigureTimerForRunTimeStates(void)
{
FreeRTOSRunTimeTicks= 0;
MX_TIM3_Init();
}
因为之后还需要在其他C文件中调用,所以别忘了在tim.h中再次声明。
extern volatile unsigned long long FreeRTOSRunTimeTicks;
void ConfigureTimerForRunTimeStates(void);
笔者这里使用的是stm32f427的板子以及定时器3(TIM3),定时器3是挂在APB1上的,f427的APB1频率为90MHz。
所以此处分频系数设置为90,重装载值设为50。此时中断频率=90M/90/50=20kHz,满足是FreeRTOS系统节拍频率1000Hz的20倍的条件。具体定时器初始化配置可参考如下代码:
void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 90-1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 50-1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
之后在main.c中的定时器中断中加入下列代码,每进一次中断,FreeRTOSRunTimeTicks变量+1:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM3) {
HAL_IncTick();
FreeRTOSRunTimeTicks++;
}
/* USER CODE END Callback 1 */
}
完成以上步骤后,笔者这里会出现一个 error: A1586E: Bad operand types (UnDefOT, Constant) for operator (。
这里只需要在port.c中按照这个路径:configMAX_SYSCALL_INTERRUPT_PRIORITY -> configPRIO_BITS -> __NVIC_PRIO_BITS 一路F12到__NVIC_PRIO_BITS将4U改为4即可。
到了这里,基本的配置已经完毕。接下来需要新建一个统计任务时间的任务:
osThreadDef(CPU_RunTime, CPU_RunTime, osPriorityHigh, 0, 128);
CPU_RunTimeHandle = osThreadCreate(osThread(CPU_RunTime), NULL);
uint8_t InfoBuffer[1000];
void CPU_RunTime(void)
{
portTickType xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
while(1)
{
osDelayUntil(&xLastWakeTime,1);//1000HZ
vTaskList((char *)&InfoBuffer);
vTaskGetRunTimeStats((char *)&InfoBuffer);
if(RC_Ctl.rc.s1 == 2)
{
printf("=================================================\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务号 \r\n");
printf("=================================================\r\n");
printf("%s\r\n", InfoBuffer);
printf(" B:阻塞 R:就绪 D:删除 S:暂停 X:运行 \r\n");
printf("=================================================\r\n");
printf("=================================================\r\n");
printf("任务名 运行计数 CPU使用率 \r\n");
printf("=================================================\r\n");
printf("%s\r\n",InfoBuffer);
printf("=================================================\r\n");
printf("=================================================\r\n\n\n");
vTaskDelay(1300);
}//获取CPU使用权总
}
}
最后,将数据通过串口输出到电脑即可,这里还需要对printf函数进行重定向操作,具体可以参考笔者之前的一篇博客,这里就不再赘述。
【STM32】如何将数据通过printf打印到串口_大忽悠的博客-CSDN博客
效果演示
第一个表是vTaskList()函数列出的任务状态信息表,第二个表是vTaskGetRunTimeStats()函数列出的CPU使用率信息。