FreeRTOS(基础)

基本概念

ROTS

RTOS全称:Real time OS,就是实时操作系统,强调的是:实时性。实时操作系统又分为软实时和硬实时。硬实时要求在规定时间内必须完成操作,硬实时不允许超时,软实时里面处理过程超时的后果就没有那么严格。
在实时操作系统中,我们可以把要实现的功能划分为多个任务,每个任务负责实现其中的一部分,每个任务都是一个很简单的程序,通常是一个死循环。
RTOS操作系统:FreeRTOS,UCOS,RTX,RT-THread,DJYOS等
RTOS操作系统的核心内容在于:实时内核。

可剥夺内核

RTOS的内核负责管理所有的任务,内核决定了运行那个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。多任务管理给人的感觉就是芯片有多个CPU,多任务管理实现了CPU资源的最大化利用,多任务管理有助于实现程序的模块化开发,能够实现复杂的实时利用。
可剥夺性内核顾名思义就是可以剥夺其他任务的CPU使用权,他总是运行就绪任务中优先级最高的任务。

为什么学习FreeRtos

1 因为Freertos开源
2 Freertos免费
3 Freertos是很多第三方组件钦定的系统

FreeRTOS 特点

FreeRTOS 是一个可裁剪的小型 RTOS 系统,其特点包括:
1 任务简单
2 任务没有数量使用限制
3 任务支持抢占
4 任务支持优先级
5 每个任务都拥有堆栈导致了RAM使用量大
6 如果使用抢占的话必须仔细考虑重入问题

FreeRTOS 任务状态

对于单内核的芯片而言,任一任务要么处于运行态,要么处于非运行态。但同一时刻只能有一个任务处于运行态。这也是为什么这个图中①画的任务框是多个叠起来的,而②所示的任务只有一个框的原因。

任务从非运行态转移到运行态被称为”切换入或切入(switched in)”或”交换入(swapped in)”。相反,任务从运行态转移到非运行态被称为”切换出或切出(switched
out)”或”交换出(swapped out)”。FreeRTOS 的调度器是能让任务切入切出的唯一实体。

在这里插入图片描述

那么事实上,对于非运行态其内部又被划分出了几个子状态:
挂起态
就绪态
阻塞态
僵尸态
在这里插入图片描述

FreeRTOS资料与源码下载

要找资料,官网是最好的地方,FreeRTOS的官网是www.freertos.org

进入 FreeRTOS 首页,就会看到download下载链接,进入后下载“FreeRTOS 202112.00”文件

FreeRTOS移植

1、添加 FreeRTOS 源码

在基础工程中新建一个名为 FreeRTOS 的文件夹。创建FreeRTOS 文件夹以后就可以将 FreeRTOS 的源码添加到这个文件夹中,我们只需要留下 keil、MemMang 和 RVDS 这三个文件夹,其他的都可以删除掉。在这里插入图片描述

2、向工程分组中添加文件

打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE,然后向这两个分组中添加文件。
在这里插入图片描述

3、添加相应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路径

头文件路径添加完成以后编译一下,看看有没有什么错误,结果会发现提示打不开“FreeRTOSConfig.h”这个文件
打开FreeRTOS 针对 STM32F103 的移植工程文件,从例程中找到这个文件复制过来。
在这里插入图片描述

4、修改 SYSTEM 文件

1、修改 sys.h 文件

sys.h 文件修改很简单,在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS,
我们使用了 FreeRTOS,所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。

//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS		1		//定义系统文件夹是否支持OS

2、修改 usart.c 文件

一个是添加 FreeRTOS.h 头文件

#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"					//FreeRTOS使用	 	  
#endif

另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉。

3、修改 delay.c 文件

delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟

1 在滴答定时器中断服务函数中调用FreeRTOS 的 API 函数 xPortSysTickHandler()。

extern void xPortSysTickHandler(void);

2 在基础例程中滴答定时器的时钟频率设置的是 AHB 的 1/8,这里为了兼容 FreeRTOS 将滴答定时器的时钟频率改为了 AHB,也就是 72MHz!

void delay_init()
{
	u32 reload;
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟  HCLK
	fac_us=SystemCoreClock/1000000;				//不论是否使用OS,fac_us都需要使用
	reload=SystemCoreClock/1000000;				//每秒钟的计数次数 单位为M  
	reload*=1000000/configTICK_RATE_HZ;			//根据configTICK_RATE_HZ设定溢出时间
												//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右	
	fac_ms=1000/configTICK_RATE_HZ;				//代表OS可以延时的最少单位	   

	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 						//每1/configTICK_RATE_HZ秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;   	//开启SYSTICK    
}		

3.delay_us()是 us 级延时函数,delay_ms 和 delay_xms()都是 ms 级的延时函数,delay_us()和delay_xms()不会导致任务切换。delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的简单封装,所以在使用 delay_ms()的时候就会导致任务切换。

4 要将 stm32f10x_it.c 中的三个函数:滴答定时器中断服务函数、SVC 中断服务函数和 PendSV 中断服务函数屏蔽掉。

//void SVC_Handler(void)
//{
//}
 
void DebugMon_Handler(void)
{
}
 
//void PendSV_Handler(void)
//{
//}
// 
//void SysTick_Handler(void)
//{
//}

任务动态创建

动态创建任务, 调用函数内部向系统申请创建新任务所需的内存,包括任务控制块和栈。 所以调用这个函数,在内存堆空间不足或者碎片话的情况下,可能创建新任务失败,需要判断函数执行后是否成功返回。

xTaskCreat();
vTaskDelete();

将 configSUPPORT_STATIC_ALLOCATION 宏设为0

#ifndef configSUPPORT_STATIC_ALLOCATION
	/* Defaults to 0 for backward compatibility. */
	#define configSUPPORT_STATIC_ALLOCATION 0
#endif

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

//开始任务
#define START_STK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t  StartTask_Hander ;
void start_task( void * pvParameters );

//Task1任务
#define TASK1_STK_SIZE 128
#define TASK1_TASK_PRIO 1
TaskHandle_t  Task1Task_Handle ;
void task1_task( void * pvParameters );

//Task2任务
#define TASK2_STK_SIZE 128
#define TASK2_TASK_PRIO 1
TaskHandle_t  Task2Task_Handle ;
void task2_task( void * pvParameters );


int main(void)
{
	
	   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	   delay_init();	    				//延时函数初始化	  
	   uart_init(115200);					//初始化串口
	   LED_Init();		  					//初始化LED

	  //创建开始任务
        xTaskCreate(	(TaskFunction_t) start_task,
								                         	(char * )   "start_task",
									                        (uint32_t)       START_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    START_TASK_PRIO,
									                        (TaskHandle_t *) &StartTask_Hander );

							
    vTaskStartScheduler();          //开启任务调度
}
void start_task( void * pvParameters )
{
	
  //创建开始任务1
      xTaskCreate(	(TaskFunction_t)       task1_task,
								                         	(char * )   "task1_task",
									                        (uint32_t)       TASK1_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    TASK1_TASK_PRIO,
									                        (TaskHandle_t *) &StartTask_Hander );
																					
	 //创建开始任务2
      xTaskCreate(	(TaskFunction_t)       task2_task,
								                         	(char * )   "task2_task",
									                        (uint32_t)       TASK2_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    TASK2_TASK_PRIO,
									                        (TaskHandle_t *) &StartTask_Hander );
	    vTaskDelete(StartTask_Hander); //删除任务
}
void task1_task( void * pvParameters )
{
	/* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的task1_num变
		量。但如果task1_num被定义为static,这一点则不成立 – 这种情况下只存在一个变量,所有的任务实
		例将会共享这个变量。 */ 

		char task1_num = 0;
    /* 任务通常实现在一个死循环中。 */
		while(1)
		{
			/* 完成任务功能的代码将放在这里。 */
			if(task1_num == 6 )
			{
					printf("task1 run %d\r\n",task1_num);
					vTaskDelete(Task1Task_Handle);

			}
			task1_num++;
		  LED1= ~LED1;
			vTaskDelay(1000);
			printf("task1 run %d\r\n",task1_num);
		  
		}
		/* 如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除
		的是当前任务 */ 
		//vTaskDelete( NULL );
}
	
void task2_task( void * pvParameters )
{
		char task2_num = 0;
	  while(1)
		{
			 task2_num++;
		   LED0 = ~LED0;
			 vTaskDelay(1000);
			
			 printf("task1 run %d\r\n",task2_num);
		}
}


任务静态创建

将 configSUPPORT_STATIC_ALLOCATION 宏设为1

#ifndef configSUPPORT_STATIC_ALLOCATION
	/* Defaults to 0 for backward compatibility. */
	#define configSUPPORT_STATIC_ALLOCATION 1
#endif

vApplicationGetIdleTaskMemory() 空闲任务所需内存
vApplicationGetTimerTaskMemory() 定时器所需内存

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

//开始任务
#define START_STK_SIZE 128
#define START_TASK_PRIO 1
StackType_t StartTaskStack[START_STK_SIZE];
TaskHandle_t  StartTask_Handle ;
StaticTask_t  StartTaskTCB;
void start_task( void * pvParameters );

//Task1任务
#define TASK1_STK_SIZE 128
#define TASK1_TASK_PRIO 1
StackType_t Task1TaskStack[TASK1_STK_SIZE];
TaskHandle_t  Task1Task_Handle ;
StaticTask_t  Task1TaskTCB;
void task1_task( void * pvParameters );

//Task2任务
#define TASK2_STK_SIZE 128
#define TASK2_TASK_PRIO 1
StackType_t Task2TaskStack[TASK2_STK_SIZE];
TaskHandle_t  Task2Task_Handle ;
StaticTask_t  Task2TaskTCB;
void task2_task( void * pvParameters );

//空闲任务
static  StackType_t  IdleTaskStack[configMINIMAL_STACK_SIZE];
static  StaticTask_t IdleTaskTCB;
//定时器任务
static  StackType_t  TimerTaskStack[configTIMER_TASK_STACK_DEPTH];
static  StaticTask_t TimerTaskTCB;

//空闲任务所需内存
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
	                                  StackType_t **ppxIdleTaskStackBuffer,
                                    uint32_t *pulIdleTaskStackSize )
{

      *ppxIdleTaskTCBBuffer    = &IdleTaskTCB;
      *ppxIdleTaskStackBuffer  = IdleTaskStack;
      *pulIdleTaskStackSize    = configMINIMAL_STACK_SIZE;

}

//定时器所需内存
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, 
                                     StackType_t **ppxTimerTaskStackBuffer, 
                                     uint32_t *pulTimerTaskStackSize )
{

      *ppxTimerTaskTCBBuffer    = &TimerTaskTCB;
      *ppxTimerTaskStackBuffer  = TimerTaskStack;
      *pulTimerTaskStackSize    = configTIMER_TASK_STACK_DEPTH;
	
}

int main(void)
{
	
	   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	   delay_init();	    				//延时函数初始化	  
	   uart_init(115200);					//初始化串口
	   LED_Init();		  					//初始化LED

	  //创建开始任务
    StartTask_Handle =   xTaskCreateStatic(	(TaskFunction_t) start_task,
								                         	(char * )   "start_task",
									                        (uint32_t)       START_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    START_TASK_PRIO,
									                        (StackType_t *)  StartTaskStack,
									                        (StaticTask_t *) &StartTaskTCB );

							
    vTaskStartScheduler();          //开启任务调度
}
void start_task( void * pvParameters )
{
	
  //创建开始任务1
 Task1Task_Handle =    xTaskCreateStatic(	(TaskFunction_t)       task1_task,
								                         	(char * )   "task1_task",
									                        (uint32_t)       TASK1_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    TASK1_TASK_PRIO,
									                        (StackType_t *)  Task1TaskStack,
									                        (StaticTask_t *) &Task1TaskTCB );
																					
	 //创建开始任务2
 Task2Task_Handle =    xTaskCreateStatic(	(TaskFunction_t)       task2_task,
								                         	(char * )   "task2_task",
									                        (uint32_t)       TASK2_STK_SIZE,
									                        (void * )        NULL,
								                         	(UBaseType_t)    TASK2_TASK_PRIO,
									                        (StackType_t *)  Task2TaskStack,
									                        (StaticTask_t *) &Task2TaskTCB );
	vTaskDelete(StartTask_Handle); //删除任务
}
void task1_task( void * pvParameters )
{
		char task1_num = 0;
    while(1)
		{
			if(task1_num == 6 )
			{
					printf("task1 run %d\r\n",task1_num);
					vTaskDelete(Task1Task_Handle);

			}
			task1_num++;
		  LED1= ~LED1;
			vTaskDelay(1000);
			printf("task1 run %d\r\n",task1_num);
		  
		}
}
	
void task2_task( void * pvParameters )
{
		char task2_num = 0;
	  while(1)
		{
			 task2_num++;
		   LED0 = ~LED0;
			 vTaskDelay(1000);
			
			 printf("task1 run %d\r\n",task2_num);
		}
}


  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值