iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第三十八章 任务管理实验
38.1 任务的特性
简而言之:使用RTOS的实时应用程序可认为是一系列独立任务的集合。每个任务在自己的环境中运行,不依赖于系统中的其它任务或者RTOS调度器。在任何时刻,只有一个任务得到运行,RTOS调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去就像整个应用程序都在执行。作为任务,不需要对调度器的活动有所了解,在任务切入切出时保存上下文环境(寄存器值、堆栈内容)是调度器主要的职责。为了实现这点,每个任务都需要有自己的堆栈。当任务切出时,它的执行环境会被保存在该任务的堆栈中,这样当再次运行时,就能从堆栈中正确的恢复上次的运行环境。
38.2 任务调度器的基本概念
FreeRTOS 中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的。系统理论上可以支持无数个优先级 (0 ~N,优先级数值越小的任务优先级越低,0 为最低优先级,分配给空闲任务使用,一般不建议用户来使用这个优先级。假如使能了 con- fifigUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏(在 FreeRTOSConfifig.h 文件定义,可在STM32CubeIDE 中配置),在系统中,当有比当前任务优先级更高的任务就绪时,当前任务将立刻被换出,高优先级任务抢占处理器运行。CMSIS-RTOS 封装后的 FreeRTOS 优先级数目默认为 56。 一个操作系统如果只是具备了高优先级任务能够“立即”获得处理器并得到执行的特点,那么它仍然不算是实时操作系统。因为这个查找最高优先级任务的过程决定了调度时间是否具有确定性,例如一个包含 n 个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和 n 相关,而下一个就绪任务抉择时间的长短将会极大的影响系统的实时性。FreeRTOS 内核中采用两种方法寻找最高优先级的任务,第一种是通用的方法,在就绪链表中查找从高优先级往低查找,因为在创建任务的时候已经将优先级进行排序,查找到的第一个就是我们需要的任务,然后获取对应的任务控制块。第二种方法则是特殊方法,利用计算前导零指令CLZ,直接在任务就绪表变量中直接得出优先级最高的那位,这样子就知道哪一个优先级任务能 够运行,这种调度算法比普通方法更快捷,但受限于平台(适用在 STM32 中)。
FreeRTOS 内核中也允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方式进行调度(也就是通常说的分时调度器),时间片轮转调度仅在当前系统中无更高优先级就绪任务存在的情况下才有效。为了保证系统的实时性,系统尽最大可能地保证高优先级的任务得以运行。任务调度的原则是一旦任务状态发生了改变,并且当前运行的任务优先级小于优先级队列组中任务最高优先级时,立刻进行任务切换(除非当前系统处于中断处理程序中或禁止任务切换的状态)。
38.3 实验目的
1)STM32CubeIDE工具软件建立freertos工程
2)学习任务的挂起和恢复相关的知识与初步使用
本实验,我们的实验任务为使用三个按键分别来控制LED2、LED3和蜂鸣器任务的挂起和恢复。LED和蜂鸣器的任务为1秒钟进行状态的反转。
首先对实验进行分析,总共需要有四个任务,分别为按键任务、LED2任务、LED3任务、蜂鸣器任务。所实现的功能如下:
按键任务:总共有三个按键,分别为 VOL-UP、VOL-DN、BACK,三个按键分别用来挂起和恢复LED2任务、LED3任务、蜂鸣器任务.
LED2任务:每1秒钟进行状态的反转。
LED3任务:每1秒钟进行状态的反转。
蜂鸣器任务:每1秒钟进行状态的反转。
本章节完成的实验存放位置为“iTOP-STM32MP157开发板\iTOP-STM32MP157开发板网盘资料汇总\08_freertos实验例程\02_任务管理实验.zip”
38.4 实验步骤
38.4.1建立freertos_KEY工程
首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:
然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项
在Project Name框中输入工程名字freertos_KEY,然后点击Finish选项即可,如下图所示:
等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可
至此我们的工程创建完毕,进入工程界面如下图所示界面:
38.4.2输出引脚的配置(LED和蜂鸣器)
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PE1为例进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PE1的复用功能选择,我们在这里选择复用为GPIO_Output功能,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PE1,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
第二个LED的控制管脚PE14按同样的方法进行配置。
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
点击对应的引脚配置之后会弹出右下方的管脚配置界面,如上图所示:
在下方会列出要配置选项的具体说明和我们要进行的配置。
1)选项 GPIO output level 用来设置IO口的输出电平的高低,这这里我们选择LOW
2)选项 GPIO mode 用来设置 IO 口输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开漏)。本实验我们设置为推挽输出 Output Push Pull。
3)选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
4)选项 Mzximum ouput speed 用来设置 IO 口输出速度为低速(Low)/中速(Medium)/高速 (Hign)/快速(Very High)。我们设置为高速 High 。
5)选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为 LED3。按照如上要求设置后的界面如下(由于PE14的配置相同,只是最后的Label值不同,也在下方列了出来):
38.4.3输入引脚的配置(按键)
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PI2为例进行搜索(由于三个按键的配置相同,在这里我们只是列出了BACK按键的配erfed置步骤),输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PI2的复用功能选择,我们在这里选择复用为GPIO_Input功能,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PI2,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
VOL-UP和VOL-DN对应的PI3和PI1引脚按同样的方法进行配置。在此就不一一展示。
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为VOL-DN。按照如上要求设置后的界面如下(由于PI2和PI3的配置相同,只是最后的Label值不同,也在下方列了出来):
38.4.5时钟的配置
我们本次实验所采用的时钟为外部时钟HSE,所以我们要在左侧属性栏中的System Core 属性下找到RCC将High Speed Clock选择为Crystal/Ceramic Resonator(晶体/陶瓷晶振)。如下图所示:
然后在Clock Configuration里我们选择 HSE,作为锁相环 PLL3P 的时钟源,在 MCU 子系统时钟里输入 209 并回车,软件会自动设置相应的倍频和分频,如下图所示:
设置完成之后,如下图所示,然后再手动配置 APB1DIV、APB2DIV 和 APB3DIV的分频值为 2。当 APB1DIV 的分频数大于 1 的时候,基本定时器的倍频器倍频值始终为 2,所以基本定时器的时钟频率为 209MHz。
38.4.6 配置 FreeRTOS
时钟配置完成之后,我们要在左侧属性栏中的Middleware属性下找到FREERTOS将Interface函数接口选择 CMSIS_V2,选择完成如下图所示:
每个功能窗口对应的功能如下:
窗口 | 对应的功能 |
Mutexes | 互斥量 |
Events | 事件 |
FreeRTOS Heap Usage | 堆情况使用 |
User Constants | 常量的定义 |
Tasks and Queues | 任务和消息队列 |
Timers and Semaphores | 软件定时器和信号 |
Config parameters | 配置参数 |
Inclued parameters | 头文件配置 |
Advanced settings | 高级设置 |
然后我么进入到Tasks and Queues任务和消息队列窗口,如下图所示:
随后我们点击default默认创建的任务,将任务名字修改为LED2,然后修改优先级为最低osPriorityLow如下图所示:
我们只需要修改任务名称和设置线程函数名即可,修改完成之后点击OK按钮,随后我们以同样的方式,创建任务名字为LED3、BEEP、KEY的任务,创建完成如下图所示:
配置完成之后我们需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示:
38.4.7工程的生成与完善
在上述的步骤完成之后,按下键盘的“Ctrl+S”组合键保存保存 freertos_KEY.ioc 文件,系统开始生成初始化代码,此处会弹出一个警告,提示我们 Systick 定时器已被 HAL 库占用,在 STM32MP157 Cortex-M4 内核上我们更换不了其他的定时器,选择 Yes 继续生成代码即可。
工程生成之后如下图所示:
然后我们进行工程的完善,以及添加对应的逻辑代码。
38.4.7.1 对应文件与文件夹的添加
由于我们在裸机章节已经完善了对应的LED,BEEP和KEY文件,所以我们将iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\03_KEY\KEY\CM4\Core\BSP文件拷贝到当前工程对应的位置,拷贝完成如下下图所示:
38.4.7.2 app_freertos.c文件的完善
我们要修改的 app_freertos.c文件路径如下图所示:
打开app_freertos.c文件,我们首先在/* USER CODE BEGIN Includes */和/* USER CODE END Includes */中间添加以下内容,将led、beep和key的头文件进行添加。
#include "../BSP/Include/led.h"
#include "../BSP/Include/beep.h"
#include "../BSP/Include/key.h"
添加完成如下图所示:
然后我们来到文件的底部可以看到我们创建的LED2_Task、LED3_Task、BEEP_Task和KEY_Task任务
修改LED2_Task 任务的for循环中的内容,修改内容如下:
LED2_TOGGLE();
osDelay(500);
修改完成如下图所示:
修改LED3_Task 任务的for循环中的内容,修改内容如下:
LED3_TOGGLE();
osDelay(500);
修改完成如下图所示:
修改BEEP_Task 任务的for循环中的内容,修改内容如下:
BEEP_TOGGLE();
osDelay(500);
修改完成如下图所示:
随后我们修改KEY_Task 任务的内容,修改内容如下:
uint8_t key;uint32_t LED2flag,LED3flag,BEEPflag;for(;;){key = key_scan();if (key){switch (key){case VOL_UP_PRES:LED2flag++;if(LED2flag%2==0)osThreadResume(LED2Handle);elseosThreadSuspend(LED2Handle);break;case VOL_DN_PRES:LED3flag++;if(LED3flag%2==0)osThreadResume(LED3Handle);elseosThreadSuspend(LED3Handle);break;case BACK_PRES:BEEPflag++;if(BEEPflag%2==0)osThreadResume(BEEPHandle);elseosThreadSuspend(BEEPHandle);break;}}}
osDelay(20);
添加完成如下图所示:
至此,我们的内容就添加完成了。之后进行编译烧写,当按下VOL_UP按键时会挂起和恢复LED2任务,当按下VOL_UP按键时会挂起和恢复LED3任务、当按下VOL_UP按键时会挂起和恢复蜂鸣器任务。至此,我们的挂起和恢复任务实验就完成了。