这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32F103ZET6】12:RTOS-详细介绍使用cubeMX软件初建R

共3条 1/1 1 跳转至

【STM32F103ZET6】12:RTOS-详细介绍使用cubeMX软件初建RTOS配置过程

高工
2025-11-05 08:49:46     打赏

一:RTOS知识分享:

 RTOS 全称是 RealTime OperatingSystem,中文名就是实时操作系统。可以看出 FreeROTS 就是一个免费的 RTOS 类系统。这里要注意,RTOS 不是指某一个确定的系统,而是指一类系统。、操作系统允许多个任务同时运行,这个叫做多任务,实际上,一个处理器核心在某一时刻只能运行一个任务。操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务,任务调度在各个任务之间的切换非常快!这就给人们造成了同一时刻有多个任务同时运行的错觉。

 RTOS 的任务调度器被设计为可预测的,而这正是嵌入式实时操作系统所需要的,实时环境中要求操作系统必须对某一个事件做出实时的响应,因此系统任务调度器的行为必须是可预测的。像 FreeRTOS 这种传统的 RTOS 类操作系统是由用户给每个任务分配一个任务优先级,任务调度器就可以根据此优先级来决定下一刻应该运行哪个任务。

二:硬件平台:

    STM32F103ZE开发板, 编译器:keil MDK 5.35版本。

    对于使用cube MX 软件配置项目工程这里就不做过多的介绍,可以参考之前的帖子。这里仅仅介绍配置RTOS过程。

三:STM32cube MX 配置过程

3.1 时基的选择

在使用cube MX 配置工程项目时候,系统的延时函数 hal_delay()是基于systick 时钟,而RTOS时钟可以使用系统滴答定时器或者是定时器的时钟。

这里我是用系统的滴答定时器创建功能时候,会弹出一个错误的对话框,会提示我们在使用操作系统时候,建议我们使用滴答定时器之外的定时器来作为系统的时间基础。

为了把系统滴答定时器作为系统的其他时候的延时时候,这里我所用STM32F1的基本定时器6作为系统的基本时基。

12-1.png

3.2 使能RTOS的功能:

12-2.png

各个参数意义如下所示:

3.2:Kernel settings:

3.2.1.1 USE_PREEMPTION:   Enabled: 使用抢占式实时操作系统调度程序;  Disabled:使用协作式实时操作系统调度程序(意思就是时间片的意思)

3.2.1.2 TICK_RATE_HZ:  设置为 1000,即周期就是1ms。RTOS系统节拍中断的频率,单位为HZ. 设置的范围1-1000。可见此处的最小的时间延时是1ms。

MAX_PRIORITIES: MAX_PRIORITIES 的值必须在 4 到 32 之间。

        诊断信息:当 configUSE_PORT_OPTIMISED_TASK_SELECTION 被启用时,其上限为 32。

        参数描述:设置可分配给任务的最大优先级。任务的优先级可以从 0(最低优先级)到 (MAX_PRIORITIES - 1)(最高优先级)进行分配。

        警告:当 configUSE_PORT_OPTIMISED_TASK_SELECTION 等于 1(启用)时,其最大值被限制为 32(详情请参阅 portmacro.h)。

3.2.1.3 MINIMAL_STACK_SIZE: 设置空闲任务的最小任务堆栈大小,实际是以字为单位,而不是字节。

    最小栈大小 最小栈大小必须在 64 字节 和 768 字节 之间。

    参数说明:设置分配给空闲任务的栈的大小。该值以字节为单位指定(此处为 32 位),而非字节。128 * 4 = 512 字节

    警告:用户选择的值应考虑到线程数量、总堆大小以及系统栈大小。如果已使用的总栈大小高于总堆大小,FreeRTOS 将无法创建和管理任务。

    有关信息:最大值 = 配置的总堆大小 / 4(当分配是动态的) 最大值 = MCU 内存大小 / 4(当分配是静态的)

3.2.1.4 MAX_TASK NAME_LEN:  这里说明的是 任务名称中可以使用的最大字节数。

3.2.1.5 IDLE_SHOULD_YIELD:  Enabled 空闲任务放弃CPU使用权给其他同优先级的用户任务。

    描述:IDLE_SHOULD_YIELD 控制在存在同样运行于空闲优先级的应用任务的情况下,空闲任务的行为。只有在使用抢占式调度器时才有效。 - 如果将 IDLE_SHOULD_YIELD 设置为 0,则空闲任务将从不向其他任务让出执行权,并且只有在被抢占时才会离开“运行”状态。 - 如果将 IDLE_SHOULD_YIELD 设置为 1,则空闲任务在没有其他处于就绪状态的空闲优先级任务的情况下,不会在没有向其他任务让出执行权的情况下执行其定义功能的超过一次迭代。这确保了在有应用任务可运行时,空闲任务至少能有足够的时间处于空闲状态。

3.2.1.6 USE_MUTEXES:为1时使用互斥信号量,相关的API函数会被编译。

    使用互斥锁 使用互斥锁 诊断信息:当未使用 CMSIS_RTOS V2 时,这两个值均可使用。

    参数说明:将该值设为 1 以在构建中包含互斥锁功能,或设为 0 以在构建中不包含互斥锁功能。

3.2.1.7 USE_RECURSIVE_MUTEXES:为1时使用递归互斥信号量,相关的API函数会被编译。

    诊断信息:当 configUSE_MUTEXES 设置为 1 时,这两个值均有效。 

    参数说明:将该值设为 1 可在构建过程中包含递归互斥锁功能,将该值设为 0 则可省略构建过程中的递归互斥锁功能。

3.2.1.8 QUEUE_REGISTRY_SIZE: 设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会在内核调试器中看到,如果不使用内核调试器的话次宏设置为0即可。

队列注册表有两个用途,这两项用途均与具有实时操作系统内核感知功能的调试相关:

1. 它允许为队列指定一个文本名称,以便在调试图形用户界面中方便地识别该队列。2.它包含了调试器定位每个已注册队列和信号量所需的信息。队列注册表只有在您使用具有实时操作系统内核兼容性的调试器时才有意义。

3.2.1.9 USE_APPLICATION_TASK_TAG :

    USE_APPLICATION_TASK_TAG 必须被定义为 1 。此函数仅适用于高级用户。可以为每个任务分配一个“标签”值。该值仅供应用程序使用——实时操作系统内核本身不会以任何方式使用它。FreeRTOS 跟踪宏文档页面提供了一个很好的示例,说明应用程序如何利用此功能。

3.2.1.10 ENABLE BACKWARD COMPATIBILTY:为1时可以使V8.0.0之前的FreeRTOS用户代码直接升级到V8.0.0之后,而不需要做任何修改。

3.2.1.11 USE PORT OPTIMISED TASK SELECTION:FreeRTOS有两种方法来选择下一个要运行的任务,一个是通用的方法,另外一个是特殊的方法,也就是硬件方法,使用MCU自带的硬件指令来实现,STM32有计算前导零指令吗,所以这里强制置1。USE TICKLESS IDLE: 置1:使能低功耗tickess模式;置0:保持系统节拍(tick)中断一直运行。假设开启低功耗的话可能会号致下载出现问题,因为程序在睡眠中,可用ISP下载办法解决。

3.2.1.12 USE_TASK_NOTIFICATIONS:

参数描述:每个实时操作系统(RTOS)任务都有一个 32 位的通知值。

RTOS 任务通知是一种直接发送给任务的事件,它可以解除接收任务的阻塞状态,并可选择更新接收任务的通知值。

RTOS 任务通知功能默认启用,若要从构建中排除(每个任务节省 8 字节),可在 FreeRTOSConfig.h 中将 configUSE_TASK_NOTIFICATIONS 设置为 0(当将 USE_TASK_NOTIFICATIONS 设置为“禁用”时实现)。

3.2.1.13 RECORD STACK HIGH ADDRESS: 

当设置为 1(启用)时,堆栈起始地址将被保存到每个任务的 TCB 中(假设堆栈向下增长)。

3.3:Memory management settings:

Memory Allocation:pynamic/static 支持动态静态内存申请

TOTAL HEAP SIZE:设置堆大小,如果使用了动态内存管理,FreeRTOS在创建 task,queue,mutex,software timer orsemaphore的时候就会使用heap x.c(x为1~5)中的内存申请函数来申请内存。这些内存就是从堆ucHeap[confgTOTAL HEAP SIZE]中申请的。

Memory Management scheme:内存管理策略 heap 4

3.4 Hook function related defnitions

USE_IDLE_HOOK:

空闲任务钩子函数是一个钩子(或回调)函数,如果已定义并配置,将在空闲任务每次执行时被调用。 - 如果 USE_IDLE_HOOK 被设置为 1(启用),则应用程序必须定义一个空闲任务钩子函数:void vApplicationIdleHook(void)。 

如果 USE_IDLE_HOOK 被设置为 0(禁用),则即使定义了空闲任务钩子函数也不会被调用。注意:当设置为 1 时,freertos.c 文件中会生成一个空函数(由用户完成)。

USE_TICK_HOOK:

参数说明:定时器钩子函数是一个钩子(或回调)函数,如果已定义并配置好,那么在每次定时器中断时将会被调用

如果 USE_TICK_HOOK 设置为 1(启用),则应用程序必须定义一个定时器钩子函数:void vApplicationTickHook(void)。 

如果 USE_TICK_HOOK 设置为 0(禁用),那么即使定义了定时器钩子函数也不会被调用。 注意:当设置为 1 时,在 freertos.c 文件中会生成一个空函数(由用户完成填充)

USE_MALLOC_FAILED_HOOK:

参数说明:`malloc()` 失败钩子函数是一个钩子(或回调)函数,如果已定义并配置好,那么当 `pvPortMalloc()` 函数返回 `NULL` 时,该函数将会被调用。- 

只有在剩余的 FreeRTOS 堆内存不足以使请求的分配成功时,才会返回 `NULL` 。

如果 `USE_MALLOC_FAILED_HOOK` 设置为 1(启用)则应用程序必须定义一个 `malloc()` 失败钩子函数:`void vApplicationMallocFailedHook(void)` 。

如果 `USE_MALLOC_FAILED_HOOK` 设置为 0(禁用)则无论是否定义了该钩子函数都不会被调用。注意:当设置为 1 时,在 `freertos.c` 文件中会生成一个空函数(由用户完成编写)。

USE_DAEMON_TASK_STARTUP_HOOK:

参数说明:malloc() 函数失败的钩子函数是一个钩子(或参数说明:

如果 USE_TIMERS 和 USE_DAEMON_TASK_STARTUP_HOOK 都设置为 1(启用),那么应用程序必须定义一个钩子函数:void vApplicationDaemonTaskStartupHook(void)。

注意:当设置为 1 时,在 freertos.c 文件中会生成一个空函数(由用户完成)。依赖项:强制为 0(禁用),因为 USE_TIMERS 等于 0(禁用)。
CHECK_FOR_STACK_OVERFLOW:

参数说明:FreeRTOS 提供了两种可选机制,可用于辅助检测和调试栈溢出情况。 -

当 CHECK_FOR_STACK_OVERFLOW 设置为 1(选项 1)时,仅使用一种机制 - 当 CHECK_FOR_STACK_OVERFLOW 设置为 2(选项 2)时,将同时使用这两种机制。 

如果 CHECK_FOR_STACK_OVERFLOW 未设置为 0(禁用),则应用程序必须提供一个栈溢出钩子(或回调)函数:void vApplicationStackOverflowHook( xTaskHandle xTask, signed char *pcTaskName );

内核将在检测到栈溢出时调用该栈溢出钩子。 注意:当设置为 1 或 2 时,在 freertos.c 文件中会生成一个空体函数(由用户完成)

3.5 Run time and task stats qathering related definitions

GENERATE_RUN_TIME_STATS:

参数说明:FreeRTOS 可配置为收集任务运行时间统计信息。vTaskGetRunTimeStats() 

API 函数会将收集到的运行时间统计信息格式化为易于理解的表格。要使 vTaskGetRunTimeStats() 函数可用,必须将 GENERATE_RUN_TIME_STATS 设置为 1(启用)。

注意:当设置为 1 时,在 freertos.c 文件中会生成两个空函数(由用户完成编写)

USE_TRACE_FACILITY:

参数说明:若您希望添加额外的结构成员和函数以辅助执行可视化和跟踪操作,请将该参数设置为 1(启用)。

USE_STATS_FORMATTING_FUNCTIONS:

将“USE_TRACE_FACILITY”和“USE_STATS_FORMATTING_FUNCTIONS”设置为 1(启用)以将 vTaskList() 和 vTaskGetRunTimeStats() 函数包含在构建中。将其中任何一个设置为 0(禁用)则会从构建中省略 vTaskList() 和 vTaskGetRunTimeStates() 函数。

3.6 Co-routine related definitions:

USE_CO_ROUTINES:

将该值设为 1 以在构建过程中包含协程功能,将该值设为 0 则不包含协程功能。若要包含协程功能,则必须在项目中包含 croutine.c 文件。

MAX_CO_ROUTINE_PRIORITIES: 

设置可分配给协程的最大优先级。

协程的优先级可以从 0 开始(这是最低优先级)设置到 (MAX_CO_ROUTINE_PRIORITIES - 1)(这是最高优先级)。

3.7 Software_timer_definitions:

USE_TIMERS:

参数说明:将值设为 1(启用)以启用软件计时器功能,将值设为 0(禁用)则不启用软件计时器功能。

注意:当启用时,将为计时器任务分配内存:所需大小(以字节为单位)= 4 * configTIMER_TASK_STACK_DEPTH。

3.8:Interrupt nesting behaviour configuration:

LIBRARY_LOWEST_INTERRUPT_PRIORITY:

库“最低中断优先级”(LIBRARY_LOWEST_INTERRUPT_PRIORITY)必须在 1 到 15 之间。诊断信息:非 M0 型 Cortex 處理核心的系列产品。

参数描述:在调用“设定优先级”函数可用的最低中断优先级。

LIBRARY_LOWEST_INTERRUPT_PRIORITY:

“LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY” 和 “LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY” 必须在 1 到 15 之间。诊断信息:仅适用于具有非 M0 核心的 Cortex 系列。参数说明:任何调用安全的 FreeRTOS API 函数的中断服务程序所能使用的最高中断优先级。切记:不要从优先级高于此值的任何中断中调用安全的 FreeRTOS API 函数!(优先级越高,数值越小)。

四:创建任务:

4.1 简单创建LED 闪烁任务:

12-3.png

然后在初始化了所使用GPIO口,直接生成代码就可以了。

Task Name: 任务名称:LED0

Priority: 优先级,在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级

Stack Size (Words): 堆栈大小,单位为字,在32位处理器(STM32),一个字等于4字节,如果传入128那么任务大小为128*4字节

Entry Function: 入口函数。

Code Generation Option: 代码生成选项。

Parameter: 任务入口函数形参,不用的时候配置为0或NULL即可

Allocation: 分配方式:Dynamic 动态内存创建

Buffer Name: 缓冲区名称

Conrol Block Name: 控制块名称

五:软件代码:

5.0 初始化任务如下所示:

  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of LED0 */
  osThreadDef(LED0, StartTask02, osPriorityIdle, 0, 128);
  LED0Handle = osThreadCreate(osThread(LED0), NULL);

  /* definition and creation of LED1 */
  osThreadDef(LED1, StartTask03, osPriorityIdle, 0, 128);
  LED1Handle = osThreadCreate(osThread(LED1), NULL);

5.1 定时器6回调函数:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM6)
  {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

可见在生成的代码中,在定时器6的回调函数中,完成了系统时间基础的触发函数。

5.2 LED 闪烁函数如下所示:

void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(500);
	HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
  }
  /* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the LED1 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void const * argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1000);		
	HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
  }
  /* USER CODE END StartTask03 */
}

在用户区,添加LED 灯闪烁任务就可以。 

六:实物测试:

12-4.png

稍后上传gitee代码。




关键词: STM32F103     RTOS    

院士
2025-11-07 10:33:37     打赏
2楼

RTOS必须要学习,但不一定要必须要使用。

FreeRTOS的上手难度比较低,非常适合入门学习。


院士
2025-11-07 16:44:44     打赏
3楼

真的假的,至少我在使用RTOS上面很慎重。


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]