[其他ST产品] STM32中的FreeRTOS-#1(入门)

[复制链接]
 楼主| grfqq325 发表于 2022-12-31 22:25 | 显示全部楼层 |阅读模式
写在前面:我一直觉得,如果我能把一点知识说给别人听,并且别人能听懂,大概率我自己真的学会了。记录的过程也是自己梳理的过程,本系列我把它称为“教程”,是想把它写得系统且有条理,本质上更多地是作为自己的学习心得和知识梳理。
本教程默认读者已有一定的STM32编程基础,并且已经熟悉CubeMX的使用,部分操作细节仅做文字提示或略过。

本教程开发环境如下:

软件:MDK Keil,CubeMX(V6.1.2),VSCode(仅作为代码编辑器)
硬件:STM32F4VET6开发板(其他开发板也可以,原理相同)
RTOS(Real Time Operating System,实时操作系统),顾名思义,能够像操作系统(例如Windows)一样处理任务。操作系统的主要目的是“同时”处理多任务,这在传统的“main-while(1){…}”编程中是不能实现的。

本教程是一系列教程中的第一篇,主要包括以下内容:

(1)使用CubeMX配置Free RTOS;

(2)使用Free RTOS的优势;

(3)用“使用CubeMX”或“不使用CubeMX”两种方式,创建任务;

(4)使用任务优先级解决一些常见问题。

现在开始配置CubeMX:

评论

———————————————— 版权声明:本文为CSDN博主「早点儿毕业」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35913527/article/details/120751782  发表于 2022-12-31 22:25
 楼主| grfqq325 发表于 2022-12-31 22:26 | 显示全部楼层
配置CubeMX

选择目标芯片型号后,CubeMX将为您打开默认页面。现在选择FREERTOS并按照下面的截图操作
8256363b0469eb8411.png
 楼主| grfqq325 发表于 2022-12-31 22:27 | 显示全部楼层
这里我们选择CMSIS_V1,因为大多数的STM32芯片型号都支持这个版本。
 楼主| grfqq325 发表于 2022-12-31 22:28 | 显示全部楼层
接下来,转到任务和队列选项卡(tasks and queues),在这里您将看到软件已经自动创建了一个默认任务。双击默认任务,可以看到以下信息:
 楼主| grfqq325 发表于 2022-12-31 22:28 | 显示全部楼层
8906563b04701c6913.png
可以看到,一个任务包含很多设置项,但是不要被吓倒。这节教程,我们只关注TaskName(任务名称)、priority(优先级)和Entry Function(入口函数)。
 楼主| grfqq325 发表于 2022-12-31 22:29 | 显示全部楼层
现在,我们将在这里创建一个任务,下面是该任务的属性
7630563b0472eef36d.png
 楼主| grfqq325 发表于 2022-12-31 22:32 | 显示全部楼层
任务名称默认即可,设置为普通优先级,入口函数也保持默认。一旦我们编写了程序,你就会对这些设置有更深刻的理解。
 楼主| grfqq325 发表于 2022-12-31 22:33 | 显示全部楼层
注意:**一旦使用实时操作系统,我们不能使用systick作为HAL库代码的时间基(因为被操作系统占用了)。因此,转到SYS,并选择如下所示的TIM1作为时间基准
 楼主| grfqq325 发表于 2022-12-31 22:34 | 显示全部楼层
 楼主| grfqq325 发表于 2022-12-31 22:34 | 显示全部楼层
除此之外,我使用UART-1来传输数据,并将PA6和PA7作为推挽输出引脚(这两个引脚是教程演示开发板板载LED-0和LED-1所在引脚,请根据自己的开发板LED所在引脚设置)。代码生成后,打开main.c文件,现在是时候了解使用RTOS的重要性了。
 楼主| grfqq325 发表于 2022-12-31 22:35 | 显示全部楼层
使用RTOS的优势

一般学习单片机编程的第一个例子就是控制LED闪烁。现在假设有这样一个场景,LED-0闪烁周期是100ms,LED-1闪烁周期是300ms,这个编程可以在While循环里面做,并且比较容易,因为两个周期是整数倍关系。但是假如我们的需求更改为:LED-0闪烁周期是100ms,LED-1一般状态熄灭,当有按键按下时,LED1亮,延时5秒后LED1熄灭。在此期间,串口每隔1秒输出当前LED-1的亮/灭状态。这时候,情形就变得复杂起来。想完美完成需求,还是得花费点时间和精力安排代码。为了使CPU资源调度更简单,我们有必要学习使用实时操作系统。
 楼主| grfqq325 发表于 2022-12-31 22:36 | 显示全部楼层
在本节教程中,我们先不涉及上述“复杂”的需求。通过上面的步骤,我们已经创建了两个任务,下面我们以defaultTask为例,看一下CubeMX是如何定义一个任务的。
 楼主| grfqq325 发表于 2022-12-31 22:36 | 显示全部楼层
  1. // 引用头文件
  2. /* Includes ------------------------------------------------------------------*/
  3. #include "FreeRTOS.h"
  4. #include "task.h"
  5. #include "main.h"
  6. #include "cmsis_os.h"

  7. // 定义任务句柄
  8. osThreadId defaultTaskHandle;

  9. // 任务函数的原型
  10. void StartdefaultTask(void const * argument);

  11. // FREERTOS初始化函数
  12. void MX_FREERTOS_Init(void)
  13. {
  14.   /*......其他代码......*/
  15.    
  16.   // 创建任务
  17.   /* Create the thread(s) */
  18.   /* definition and creation of defaultTask */
  19.   osThreadDef(defaultTask, StartdefaultTask, osPriorityNormal, 0, 128);
  20.   defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  21.   /*......其他代码......*/
  22. }

  23. /* USER CODE END Header_StartdefaultTask */
  24. void StartdefaultTask(void const * argument)
  25. {
  26.   /* USER CODE BEGIN StartdefaultTask */
  27.   /* Infinite loop */
  28.   for(;;)
  29.   {
  30.     osDelay(1);
  31.   }
  32.   /* USER CODE END StartdefaultTask */
  33. }
 楼主| grfqq325 发表于 2022-12-31 22:39 | 显示全部楼层
可见,定义一个任务需要完成以下几件事

引入头文件
定义任务句柄
定义任务函数,与其它函数一样,使用前得在文件前面写上函数原型
在FreeRTOS初始化函数中,把任务句柄和任务函数绑定起来,以后利用任务句柄即可操作任务
 楼主| grfqq325 发表于 2022-12-31 22:39 | 显示全部楼层
在任务函数里面添加控制LED闪烁的代码,为了测试任务调度性能,我们把闪烁周期定为1ms
 楼主| grfqq325 发表于 2022-12-31 22:40 | 显示全部楼层
  1. void StartdefaultTask(void const * argument)
  2. {
  3.   /* USER CODE BEGIN 5 */
  4.   /* Infinite loop */
  5.   for(;;)
  6.   {
  7.       HAL_GPIO_TogglePin(BSP_LED0_GPIO_Port,BSP_LED0_Pin);
  8.       osDelay(1);
  9.   }
  10.   /* USER CODE END 5 */
  11. }

  12. /* USER CODE BEGIN Header_Task2_init */
  13. /**
  14. * [url=home.php?mod=space&uid=247401]@brief[/url] Function implementing the Task2 thread.
  15. * @param argument: Not used
  16. * @retval None
  17. */
  18. /* USER CODE END Header_Task2_init */
  19. void StartTask02(void const * argument)
  20. {
  21.   /* USER CODE BEGIN Task2_init */
  22.   /* Infinite loop */
  23.   for(;;)
  24.   {
  25.           HAL_GPIO_TogglePin(BSP_LED1_GPIO_Port,BSP_LED1_Pin);
  26.           osDelay(1);
  27.   }
  28.   /* USER CODE END Task2_init */
  29. }
 楼主| grfqq325 发表于 2022-12-31 22:41 | 显示全部楼层
我们将在defaultTask中翻转引脚LED-0(PA6)的电平,并在Task2中翻转引脚LED-1(PA7)的电平。这样,RTOS调度器将为这两个任务调度时间,以便它们有足够的时间执行。当上述代码被执行时,波器波形如下图所示(黄色-PA6,绿色PA7)
 楼主| grfqq325 发表于 2022-12-31 22:42 | 显示全部楼层
 楼主| grfqq325 发表于 2022-12-31 22:43 | 显示全部楼层
可以看出,两个LED的引脚几乎同时翻转,但是放大以后可以看出,上升沿还是有大约25微秒的时间差,这是因为在宏观上指令的执行是同时的,但在微观上,或者说在指令执行时间尺度上,单片机仍然是在一条一条地执行指令。系统任务调动也会消耗一定的时间,所以绝对的同时是不可能的,所以我在文章开头介绍RTOS的时候,给“同时”加了引号。
 楼主| grfqq325 发表于 2022-12-31 22:44 | 显示全部楼层
手动创建一个任务

**提示:**以下操作都在“freertos.c”文件中进行,代码的书写位置请参照CubeMX给出的代码,然后在相应代码附近的用户自定义代码区域书写,否则自己编写的代码可能会在软件再次生成代码时被清除。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

64

主题

686

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部

64

主题

686

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部