FreeRTOS入门——STM32多任务程序实现

文章目录

    • 一、FreeRTOS简介
    • 二、多任务系统
    • 三、实验
    • 四、小结与参考链接

一、FreeRTOS简介

Free 即免费的,RTOS 全称是 Real Time Operating System,中文就是实时操作系统。注意,RTOS
不是指某一个确定的系统,而是指一类系统。比如 uC/OS,FreeRTOS,RTX,RT-Thread 等这些都是 RTOS 类操作系统。
操作系统允许多个任务同时运行,这个叫做多任务。实际上,一个处理器核心在某一时刻只能运行一个任务。操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务。任务调度在各个任务之间的切换非常快,就给人们造成了同一时刻有多个任务同时运行的错觉。
某些操作系统给每个任务分配同样的运行时间,时间到了就轮到下一个任务,比如Unix 操作系统。 FreeRTOS
操作系统则是由用户给每个任务分配一个任务优先级,任务调度器就可以根据此优先级来决定下一刻应该运行哪个任务。 FreeRTOS 是 RTOS
系统的一种,FreeRTOS 十分的小巧,可以在资源有限的微控制器中运行,当然,FreeRTOS
不仅局限于在微控制器中使用。但从文件数量上来看 FreeRTOS 要比uC/OSII 和 uC/OSIII 小的多。

二、多任务系统

  • 轮询系统:即在裸机编程时,先初始化相关硬件,让主程序在一个死循环里面不断循环,顺序地处理各种事件。不能说轮询是低端的,轮询系统是一种非常简单的软件结构,但适用于仅需要顺序执行代码且不需要外部事件来驱动就能完成的事情,这会变得简单可靠。
  • 缺点:如果加入按键操作等需要检测外部信号的事件,整个系统的实时响应能力就会体现不好。试想一下,但按键按下时,程序正在运行顺序1程序,而且顺序1程序占用的程序时间片比较长,系统就有可能错过对按键的检测(直到按键松开),实时性极差,用户无法接受
  • 前后台系统:相比轮询系统,前后台系统在轮询系统的基础上加入了中断的概念,外部事件的响应在中断里面完成,事件的处理还是回到轮询系统中完成,中断我们称之为前台,main()函数中的无限循环称为后台。如果事件要处理的事情很简短,则可在中断服务程序里面处理,如果事件要处理的事情比较多,则返回后台程序处理。通过中断可以大大提供程序的实时响应能力,避免造成外部事件的丢失。
  • 多任务系统:相比前后台系统,多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。在多任务系统中,任务与中断一样,也具有优先级,优先级高的任务会被优先执行。但一个紧急事件在中断中被标志之后,如果事件对应的任务优先级足够高,就会立刻得到响应,相比前后台系统,多任务系统的实时性又被提高了。

FreeRTOS入门——STM32多任务程序实现_第1张图片
在多任务系统中,程序的主体会分割成一个个独立的、无限循环且不能返回的任务,每个任务都是独立的、互不干扰的,而且具备自身的优先级,由操作系统调度管理。整个系统的额外开销就是操作系统占据的少量FLASH和RAM,但是对于现在的片上资源,已经是微不足道。

三、实验

1、

实验要求:在STM32下完成一个基于FreeRTOS的多任务程序,执行3个周期性task;

task1,每间隔500ms闪烁(变化)一次LED;
task2,每间隔2000ms,向串口发送一次指令数据“helloworld!";
task3,每间隔5000ms,从AHT20采集一次温湿度数据(不考虑硬件情况,仅写出整个多任务框架模拟代码)

2、
实验模板下载
[野火]《FreeRTOS内核实现与应用开发实战指南》系列

3、
打开工程文件Project —> RVMDK(uv5)—> Fire_FreeRTOS.uvprojx
4、
修改mian.c如下

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"

/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED1任务句柄 */
static TaskHandle_t LED1_Task_Handle = NULL;
/* LED2任务句柄 */
static TaskHandle_t LED2_Task_Handle = NULL;
/* USART1任务句柄 */
static TaskHandle_t USART1_Task_Handle = NULL;
/* AHT20任务句柄 */
static TaskHandle_t AHT20_Task_Handle = NULL;
/********************************** 内核对象句柄 *********************************/
/*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 * 
 */


/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */


/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void LED1_Task(void* pvParameters);/* LED1_Task任务实现 */
static void LED2_Task(void* pvParameters);/* LED2_Task任务实现 */
static void USART1_Task(void* pvParameters);/* USART1_Task任务实现 */
static void AHT20_Task(void* pvParameters);/* AHT20_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */

int main(void)
{	
  /* 定义一个创建信息返回值,默认为pdPASS */
  BaseType_t xReturn = pdPASS;

  /* 开发板硬件初始化 */
  BSP_Init();
  printf("这是一个STM32基于FreeRTOS实现多任务程序的实验!\r\n");
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}



static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )LED1_Task, /* 任务入口函数 */
                        (const char*    )"LED1_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&LED1_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建LED1_Task任务成功!\r\n");
  
	/* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )LED2_Task, /* 任务入口函数 */
                        (const char*    )"LED2_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )3,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&LED2_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建LED2_Task任务成功!\r\n");
  /* 创建USART1_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t)USART1_Task,
						(const char*   )"USART1_Task",
						(uint16_t	   )512,
						(void*		   )NULL,
						(UBaseType_t   )3,
						(TaskHandle_t* )&USART1_Task_Handle);
  if(pdPASS == xReturn)
	  printf("创建USART1_Task任务成功!\r\n");
  
  /* 创建AHT20_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t)AHT20_Task,
						(const char*   )"AHT20_Task",
						(uint16_t	   )512,
						(void*		   )NULL,
						(UBaseType_t   )4,
						(TaskHandle_t* )&AHT20_Task_Handle);
  if(pdPASS == xReturn)
	  printf("创建AHT20_Task任务成功!\r\n");
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}


static void LED1_Task(void* parameter)
{	
    while (1)
    {
        LED1_ON;
        vTaskDelay(500);   /* 延时500个tick */
        printf("LED1_Task Running,LED1_ON\r\n");
        
        LED1_OFF;     
        vTaskDelay(500);   /* 延时500个tick */		 		
        printf("LED1_Task Running,LED1_OFF\r\n");
    }
}



static void LED2_Task(void* parameter)
{	
    while (1)
    {
        LED2_ON;
        vTaskDelay(500);   /* 延时500个tick */
        printf("LED2_Task Running,LED2_ON\r\n");
        
        LED2_OFF;     
        vTaskDelay(500);   /* 延时500个tick */		 		
        printf("LED2_Task Running,LED2_OFF\r\n");
    }
}



static void USART1_Task(void* parameter)
{
	while(1)
	{
		vTaskDelay(2000);
		printf("helloworld!\r\n");
	}
}



static void AHT20_Task(void* parameter)
{
	while(1)
	{
		vTaskDelay(5000);
		printf("温度湿度采集!\r\n");
		
	}
}


/*外设初始化*/
static void BSP_Init(void)
{
	
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
}

5、
编译程序,再进行烧录。我这里使用的串口下载程序,所以需要用到野火提供的串口下载助手。并且要提前CH340驱动。
FreeRTOS入门——STM32多任务程序实现_第2张图片
FreeRTOS入门——STM32多任务程序实现_第3张图片
6、
运行效果如下

四、小结与参考链接

主要就是试了一下多任务,学习了多任务程序的基本实现方法。做的是最基本的实验,感觉还有很大的自己动手实验的空间,比较期待。
参考:
轮询系统、前后台系统、多任务系统的区别

FreeRTOS简介

《FreeRTOS 内核实现与应用开发实战指南》(来自野火)

你可能感兴趣的:(嵌入式系统,嵌入式)