1.将FreeRtos(嵌入式实时操作系统)移植到STM32中。
2.在该系统中实现任务的创建、挂起、恢复和删除。
3.将之前写的传感器模块的数据获取,移植到FreeRtos(嵌入式实时操作系统)环境下。
STM32裸机运行代码:
加入FreeRtos后:
由此可见,由于单片机只有一个核心,同一时间下只能处理一件事。如果用户想要实现多个任务并行的效果,可以通过加入FreeRtos(嵌入式实时操作系统),操作系统可以让STM32实现任务的快速切换,从而给人一种多个任务并行的错觉。
打开想要移植FreeRtos的工程,把主函数清空,只留一个while(1)。由于FreeRtos是任务块操作,所以之前写的程序就没用了。
移植后使用了三个中断服务函数:
1.SVC中断服务函数:上下切换中断,FreeRtos实现任务切换
2.PendSVC中断服务函数:任务挂起中断。
3.SysTick中断服务函数:FreeRtos运行的时间基准
#define vPortSVCHandler SVC_Handler //上下切换中断,FreeRtos实现任务切换 #define xPortPendSVHandler PendSV_Handler //任务挂起中断 #define xPortSysTickHandler SysTick_Handler //FreeRtos运行的时间基准
先打开移植的文件夹:
将源码直接复制到想要移植的工程文件下,改名为FreeRtos:
在portable文件夹中找到MemMang文件夹,该文件夹放着的就是内存分配文件,我们选择4就行。
port,就是内核相关的接口文件,是必要添加的。
打开样例文件夹,找到相对应的芯片,将FReeRTOSconfig.h文件复制过来即可。
.c
.h
其中,FreeRTOSConfig.h 是直接从 demo 文件夹下面拷贝过来的,该头文件对裁剪整个FreeRTOS 所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始我们只需要配置最简单的功能即可。要想随心所欲的配置 FreeRTOS 的功能,必须对这些宏定义的功能有所掌握。
#define configUSE_TIME_SLICING 1 //使能时间片调度(默认式使能的) #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 //硬件计算前导零指令,如果所使用的, MCU 没有这些硬件指令的话此宏应该设置为 0 #define configUSE_TICKLESS_IDLE 0 //置 1:使能低功耗 tickless 模式;置 0:保持系统节拍(tick)中断一直运行 #define configUSE_QUEUE_SETS 1 //启用队列 #define configUSE_TASK_NOTIFICATIONS 1 //开启任务通知功能,默认开启 #define configUSE_MUTEXES 1 //使用互斥信号量 #define configUSE_RECURSIVE_MUTEXES 1 //使用递归互斥信号量 #define configUSE_COUNTING_SEMAPHORES 1 //为 1 时使用计数信号量 #define configQUEUE_REGISTRY_SIZE 10 //设置可以注册的信号量和消息队列个数 #define configUSE_APPLICATION_TASK_TAG 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请 #define configUSE_MALLOC_FAILED_HOOK 0 //使用内存申请失败钩子函数 #define configCHECK_FOR_STACK_OVERFLOW 1// 大于 0 时启用堆栈溢出检测功能,如果使用此功能用户必须提供一个栈溢出钩子函数如果使用的话此值可以为 1 或者 2,因为有两种栈溢出检测方法 #define configGENERATE_RUN_TIME_STATS 0 //启用运行时间统计功能 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configUSE_TIMERS 1 //启用软件定时器 #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) //软件定时器优先级 #define configTIMER_QUEUE_LENGTH 10 //软件定时器队列长度 #define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) //软件定时器任务堆栈大小 //可选函数配置选项 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xTimerPendFunctionCall 1 //中断服务函数 也可以修改起始文件 #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler
调度
和裸机操作不同,操作系统中执行的是一个一个任务块,通过任务调度器(使用相关的调度算法)来决定当前时刻要执行哪个任务。
调度方式主要有两种:
1.抢占式调度:任务优先级不同时使用。每个任务都有自己的优先级,高优先级的任务会抢占低优先级的任务。
2.时间片调度:任务优先级相同时使用。当多个任务优先级相同时,任务调度器会在每一次系统节拍到的时候切换任务。
其实说白了就是:STM32执行的是线性代码,只有中断能打断。而FreeRtos执行的是不同等级的任务块,等级越高任务块就越先执行,相同等级的任务块执行时系统会来回切换。
优先级不能设置为0,因为FreeRtos的空闲任务优先级为0,一般情况下我们不去抢。
任务状态
状态 | 含义 |
---|---|
运行态 | CPU正在执行的任务。注意:在STM32中,同一时间仅一个任务处于运行态 。 |
就绪态 | 该任务能够被执行,但目前还未被执行,那么该任务就处于就绪态。 |
阻塞态 | 如果一个任务因延时或等待外部事件发生,那么这个任务就处于阻塞态。 |
挂起态 | 类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用恢复函数vTaskResume()才可以进入就绪态 。 |
添加FreeRTOS相关头文件
//使用FreeRtos相关头文件之前,一定要先包含这个#include "FreeRtos.h" #include "FreeRtos.h" #include "task.h"
创建一个句柄,TaskHandle_t 类型
TaskHandle_t LED_TaskHandle;//LED
写与句柄对应的任务函数,通常要加while(1)每个任务都是1个无限循环程序,内容根据需求来定。
void Led_Task(void *p) { uint8_t i=0; while(1) { i++; printf("i=%d\r\n",i); Led_Toggle(1); Delay_nms(300); } }
定义一个BaseType_t变量
BaseType_t Ret = pdPASS;
使用xTaskCreate()创建任务块,返回值由BaseType_t类型变量承接,返回值为pdPASS(1)时即创建成功
Ret = xTaskCreate( Led_Task, "LED1_toggle", 50,//栈深度,32位单片机*4 NULL, 2, &LED_TaskHandle ); if(Ret==pdPASS) { printf("LED_Task创建成功!\r\n"); }
参数1:实现任务的函数名
参数2:自己起一个任务名
参数3:分配空间,栈深度(32位单片机*4)
参数4:任务函数传参时使用
参数5:任务优先级(越高优先级越高)
参数6:该任务的句柄
最后,使用vTaskStartScheduler()函数开始调度就完成了。
vTaskStartScheduler();
挂起
使用vTaskSuspend()函数即可,参数只有一个:想要挂起任务的句柄。
恢复
使用vTaskResume()函数即可,参数只有一个:想要恢复任务的句柄。
删除
使用vTaskDelete()函数即可,参数只有一个:想要删除任务的句柄。
printf("挂起LED\r\n");vTaskSuspend(LED_TaskHandle);//挂起 printf("继续LED\r\n");vTaskResume(LED_TaskHandle);//继续 printf("删除LED\r\n");vTaskDelete(LED_TaskHandle);//删除
在该系统中实现任务的创建、挂起、恢复和删除
#include "stm32f10x.h" #include "led.h" #include "delay.h" //使用FreeRtos相关头文件之前,一定要先包含这个#include "FreeRtos.h" #include "FreeRtos.h" #include "task.h" #include "usart.h" #include "key.h" TaskHandle_t LED_TaskHandle;//LED TaskHandle_t KEY_TaskHandle;//KEY void Led_Task(void *p) { uint8_t i=0; while(1) { i++; printf("i=%d\r\n",i); Led_Toggle(1); Delay_nms(300); } } void KEY_Task(void *p) { while(1) { switch(key_getvalue()) { case 1:{printf("挂起LED\r\n");vTaskSuspend(LED_TaskHandle);}break;//挂起 case 2:{printf("继续LED\r\n");vTaskResume(LED_TaskHandle);}break;//继续 case 3:{printf("删除LED\r\n");vTaskDelete(LED_TaskHandle);}break; case 4:{ BaseType_t Ret = pdPASS; Ret = xTaskCreate( Led_Task, "LED1_toggle",50,NULL,2,&LED_TaskHandle); if(Ret==pdPASS) { printf("LED_Task创建成功!\r\n"); } };break; } vTaskDelay(10); } } int main() { Led_Init(); key_Init(); Usart1_Config(); BaseType_t Ret = pdPASS; Ret = xTaskCreate( Led_Task, "LED1_toggle", 50,//栈深度,32位单片机*4 NULL, 2, &LED_TaskHandle ); if(Ret==pdPASS) { printf("LED_Task创建成功!\r\n"); } Ret = xTaskCreate( KEY_Task, "KEY_Task", 80,//栈深度,32位单片机*4 NULL, 2, &KEY_TaskHandle ); if(Ret==pdPASS) { printf("KEY_Task创建成功\r\n"); } printf("开始调度!\r\n"); vTaskStartScheduler(); while(1) { } }
将之前写的传感器模块的数据获取,移植到FreeRtos(嵌入式实时操作系统)环境下
main.c
#include "stm32f10x.h" #include "led.h" #include "delay.h" #include "delay.h" #include "string.h" #include "pwm.h" #include "adc.h" #include "su03t.h" #include "dht11.h" #include "kqm.h" //使用FreeRtos相关头文件之前,一定要先包含这个#include "FreeRtos.h" #include "FreeRtos.h" #include "task.h" #include "usart.h" #include "key.h" TaskHandle_t Deal_TaskHandle;//数据处理 void Data_Task(void *p) { while(1) { Get_Smoke_Light_MidValue(); DHT11_ReadData(); Delay_nms(300); KQM_DealData(); Su03tDealData(); } } int main() { RGBpwm_Config(); Kqm_U4Config(); Su03t_U5Config(); DHT11_Config(); Adc_Config(); Led_Init(); key_Init(); Usart1_Config(); BaseType_t Ret = pdPASS; Ret = xTaskCreate( Data_Task, "DealData", 100,//栈深度,32位单片机*4 NULL, 2, &Deal_TaskHandle ); if(Ret==pdPASS) { printf("语音创建成功!\r\n"); } printf("开始调度!\r\n"); vTaskStartScheduler(); while(1) { } }