FreeRTOS第一步:创建多任务

2023-05-16

 

 

目录

单片机裸机开发与基于操作系统开发

FreeRTOS介绍

创建多任务—SRAM 动态内存

一、动态内存空间的堆从哪里来:

二、定义任务函数:

三、定义任务栈:

四、定义任务控制块指针:

五、动态创建任务:

六、启动任务:

七、多任务—SRAM 动态内存的main.c:


单片机裸机开发与基于操作系统开发

        裸机就是执行单任务的单片机,对应的访问方式有轮询、中断和DMA(直接存储器访问,看单片机是否支持),这里的操作系统一般指嵌入式开发的操作系统,常见的有uc/os、FreeRTOS、RT-Thread和Linux等。由于操作系统具有并发性,所以可以支持多任务运行,本质上可以认为是裸机开发效率的提升。       

        单片机开发的一个特点就是裸机编程。也就是说内核跑的就是你写的代码(裸机运行的程序代码,一般由一个main函数中的while死循环和各种中断服务程序组成,平时CPU执行while循环中的代码,出现其他事件时,跳转到中断服务程序进行处理,没有多任务、线程的概念)。

        但在操作系统上编程就完全不一样了,因为你的程序大部分都在调用操作系统的接口函数,从而间接地管理硬件。在引入操作系统后,程序执行时可以把一个应用程序分割为多个任务,每个任务完成一部分工作,并且每个任务都可以写成死循环。操作系统根据任务的优先级,通过调度器使CPU分时执行各个任务,保证每个任务都能够得到运行。由操作系统的任务管理衍生出相应的CPU管理、内存管理,它们分别负责分配任务对CPU的占有权和管理任务所占有的内存空间。在linux操作系统中,还具有文件管理、I/O设备管理的功能。

总结:

在(单核)单片机中,它的硬件结构决定了CPU只能在一个时间段做一件事情。

因此就需要移植操作系统。

 

FreeRTOS介绍

(参考:FreeRTOS系列第1篇---为什么选择FreeRTOS从0到1学习FreeRTOS)

RTOS:Real Time OS,实时操作系统,实时性就是规定什么时间做什么任务。假如同一时刻需要执行两个或多个任务,可以人为地把任务划分优先级。

       FreeRTOS是一个可裁剪、可剥夺型的多任务内核,而且没有任务限制。FreeRTOS提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。RTOS的内核负责管理所有任务,内核决定了运行哪个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。“可剥夺内核”意思就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务

创建多任务—SRAM 动态内存

两个任务:

  • 让一个 LED 灯闪烁;
  • 让另外一个 LED 闪烁,两个 LED 闪烁的频率不一样。

一、动态内存空间的堆从哪里来:

        在创建单任务—SRAM 静态内存的例程中,任务控制块和任务栈的内存空间都是从内部的 SRAM 里面分配的,具体分配到哪个地址由编译器决定。

        现在我们开始使用动态内存,即堆,其实堆也是内存,也属于 SRAM。 FreeRTOS 做法是在 SRAM 里面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用,在第一次使用的时候,系统会将定义的堆内存进行初始化,这些代码在 FreeRTOS 提供的内存管理方案中实现(heap_1.c、heap_2.c、 heap_4.c 等)。
 

//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))//堆内存的大小为configTOTAL_HEAP_SIZE,在FreeRTOSConfig.h 中由我们自己定义, configSUPPORT_DYNAMIC_ALLOCATION 这个宏定义在使用 FreeRTOS 操作系统的时候必须开启。
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];//从内部 SRAMM 里面定义一个静态数组 ucHeap,大小由configTOTAL_HEAP_SIZE 这个宏决定, 目前定义为 36KB。定义的堆大小不能超过内部SRAM 的总大小。
/* 如果这是第一次调用 malloc 那么堆将需要
初始化, 以设置空闲块列表,方便以后分配内存,初始化完成之后会取得堆的结束地址,在 MemMang 中的5 个内存分配 heap_x.c 文件中实现。 */
if ( pxEnd == NULL )
{
prvHeapInit();
} else
{
mtCOVERAGE_TEST_MARKER();
}

二、定义任务函数:

        使用动态内存的时候,任务的主体函数与使用静态内存时是一样的。

/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void LED_Task(void* parameter)
{	
    while (1)
    {
        LED1_ON;
        vTaskDelay(500);   /* 延时500个tick */
        printf("LED_Task Running,LED1_ON\r\n");
        
        LED1_OFF;     
        vTaskDelay(500);   /* 延时500个tick */		 		
        printf("LED_Task Running,LED1_OFF\r\n");
    }
}

三、定义任务栈:

        使用动态内存的时候,任务栈在任务创建的时候创建,不用跟使用静态内存那样要预先定义好一个全局的静态的栈空间,动态内存就是按需分配内存,随用随取。

四、定义任务控制块指针:

        使用动态内存时候,不用跟使用静态内存那样要预先定义好一个全局的静态的任务控制块空间。任务控制块是在任务创建的时候分配内存空间创建,任务创建函数会返回一个指针,用于指向任务控制块,所以要预先为任务栈定义一个任务控制块指针,也是我们常说的任务句柄,具体见下面的代码。
 

/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;

五、动态创建任务:

       使用静态内存时,使用 xTaskCreateStatic()来创建一个任务,而使用动态内存的时,则使用 xTaskCreate()函数来创建一个任务。

   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数---即任务函数的名称,需要我们自己定义并且实现。*/
                        (const char*    )"AppTaskCreate",/* 任务名字---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (uint16_t       )512,  /* 任务栈大小---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (void*          )NULL,/* 任务入口函数参数---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (UBaseType_t    )1, /* 任务的优先级---优先级范围根据 FreeRTOSConfig.h 中的宏configMAX_PRIORITIES 决定, 如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION,这个宏定义,则最多支持 32 个优先级;如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大可用优先级数目。在 FreeRTOS 中, 数值越大优先级越高, 0 代表最低优先级。*/
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针---在使用内存的时候,需要给任务初始化函数xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务控制块是 xTaskCreate()函数里面动态分配的一块内存。*/ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */

六、启动任务:

        当任务创建好后,是处于任务就绪(Ready) ,在就绪态的任务可以参与操作系统的调度。但是此时任务仅仅是创建了,还未开启任务调度器,也没创建空闲任务与定时器任务(如果使能了 configUSE_TIMERS 这个宏定义),那这两个任务就是在启动任务调度器中实现,每个操作系统,任务调度器只启动一次,之后就不会再次执行了, FreeRTOS 中启动任务调度器的函数是 vTaskStartScheduler(),并且启动任务调度器的时候就不会返回,从此任务管理都由FreeRTOS 管理,此时才是真正进入实时操作系统中的第一步,具体见下面的代码。

/* 启动任务调度 */
if (pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;

 

七、多任务—SRAM 动态内存的main.c:

/**
  ******************************************************************************
  * @file    main.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-09-xx
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
  
/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "sum_common.h"                  
 
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED1任务句柄 */
static TaskHandle_t LED1_Task_Handle = NULL;
/* LED2任务句柄 */
static TaskHandle_t LED2_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 BSP_Init(void);/* 用于初始化板载相关资源 */
 
 
/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
 
  /* 开发板硬件初始化 */
  BSP_Init();
  printf("这是一个FreeRTOS-动态创建任务!\r\n");
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数---即任务函数的名称,需要我们自己定义并且实现。*/
                        (const char*    )"AppTaskCreate",/* 任务名字---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (uint16_t       )512,  /* 任务栈大小---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (void*          )NULL,/* 任务入口函数参数---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (UBaseType_t    )1, /* 任务的优先级---优先级范围根据 FreeRTOSConfig.h 中的宏configMAX_PRIORITIES 决定, 如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION,这个宏定义,则最多支持 32 个优先级;如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大可用优先级数目。在 FreeRTOS 中, 数值越大优先级越高, 0 代表最低优先级。*/
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针---在使用内存的时候,需要给任务初始化函数xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务控制块是 xTaskCreate()函数里面动态分配的一块内存。*/ 
 /* 启动任务调度。*/          
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}
 
 
/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
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");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}
 
 
/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
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");
    }
}
 
/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void LED2_Task(void* parameter)
{	
    while (1)
    {
        LED2_ON;
        vTaskDelay(1000);   /* 延时1000个tick */
        printf("LED2_Task Running,LED2_ON\r\n");
        
        LED2_OFF;     
        vTaskDelay(1000);   /* 延时1000个tick */		 		
        printf("LED2_Task Running,LED2_OFF\r\n");
    }
}
/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();
 
	/* 串口初始化	*/
	Debug_USART_Config();
  
}
 
/********************************END OF FILE****************************/
 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS第一步:创建多任务 的相关文章

  • 图像语义理解

    本文转载 from xff1a http blog csdn net haitun425 article details 8802182 1 目标的检测 分类和识别都为图像语义的理解服务 理解是硬道理 xff1b 2 不在于图像理解模型是否
  • 我的嵌入式5年 VS 我被国嵌的视频坑了的日子

    在嵌入式的行业工作四五年了 xff0c 想想从当初的身无分文和什么都不会 xff0c 到现在的还算衣食无忧 xff0c 住行别想 xff0c 技术也有点提升 xff0c 进入了手机行业 xff0c 其中的酸甜苦辣只有自己知道 xff0c 从
  • 我程序人生的启蒙书

    是这本书 xff0c 大一的我接触了c和c 43 43 xff0c 为数学专业的我打开了通往另一个世界的道路 xff0c 做一名优秀的程序员 是这本书 xff0c 大一的我开始废寝忘食的学习 xff0c 自习室里往往就放着这一本数 xff0
  • C++面试题(三)——STL相关各种问题

    C 43 43 面试题 STL相关各种问题 tanglu2004 http blog csdn net worldwindjp STL相关的各种问题 1 xff0c 用过那些容器 最常用的容器就是 xff1a vector list map
  • 基于 Docker 搭建开发环境

    基于 Node 官方镜像 https hub docker com node 获取镜像 在本地 Terminal 中执行 docker pull node 以获取 node 镜像 xff0c 可在 docker desktop 中查看 创建
  • tomcat 9 与mysql 5 的连接

    1 jdk的安装 配置JAVA HOME变量 xff0c 将该变量设置到path中 2 tomcat 下载 最新版本apache tomcat 9 0 0 M9配置根目录CATALINA HOME 61 D apache tomcat 9
  • 刷完 LeetCode 是什么水平?能拿到什么水平的 offer?

    链接 xff1a https www zhihu com question 32019460 编辑 xff1a 深度学习与计算机视觉 声明 xff1a 仅做学术分享 xff0c 侵删 刷题是我们一贯的学习方式 xff0c 但是学霸和学渣的区
  • 开心网争车位 发布

    本软件第一次使用C 编写 xff0c 是开心001争车位的辅助软件 可以帮助你管理多个账号一起停车挣钱 xff0c 使支持和热爱 开心网 的玩家更方便 xff0c 请勿与用商业 使用方法 xff1a 本软件需依靠 net3 5 xff0c
  • 博士“申请考核制”经验

    作者 xff1a 花花 链接 xff1a https zhuanlan zhihu com p 126168158 本文转载自知乎 xff0c 作者已授权 xff0c 未经许可请勿二次转载 本文是作者真实的经历 xff0c 给打算申请国内院
  • 研究生新生要怎么看论文?

    链接 xff1a https www zhihu com question 304334959 编辑 xff1a 深度学习与计算机视觉 声明 xff1a 仅做学术分享 xff0c 侵删 问题 xff1a 经常各种看不懂论文 而且感觉好多论文
  • 考博热会出现吗?

    链接 xff1a https www zhihu com question 408008199 编辑 xff1a 深度学习与计算机视觉 声明 xff1a 仅做学术分享 xff0c 侵删 考研热已经是不争的事实了 xff0c 每年考研人数都在
  • 基于python的MongoDB入门教程

    总览 MongoDB是数据科学家常用的一种非结构化数据库本文我们讨论如何使用Python xff08 和PyMongo库 xff09 来使用MongoDB数据库 本文我们使用Python实现对MongoDB数据库的所有基本操作 结构化数据库
  • 同组博士师兄的结果复现不出来,我应该怎么办?

    链接 xff1a https www zhihu com question 502804990 编辑 xff1a 深度学习与计算机视觉 声明 xff1a 仅做学术分享 xff0c 侵删 今年研二 xff0c 老师给了一个课题 xff0c 让
  • SOC电源标志 说明 VCC、VSS、VDD、VEE、VPP、Vddf

    VBAT VBAT是电源电压 xff0c VCC xff1a C 61 circuit 表示电路的意思 即接入电路的电压 VDD xff1a D 61 device 表示器件的意思 即器件内部的工作电压 VSS xff1a S 61 ser
  • “error LNK2019: 无法解析的外部符号”原因总结

    C 43 43 工程编译时出现如下链接错误提示 xff1a 原因一 xff1a 缺少实现 只是在 h里面声明了某个方法 xff0c 没有在cpp里面实现 xff1b 我出现过这个问题 xff1b 类方法的实现未加类标识 xff1a 如 xf
  • 不支持S/W HEVC(H265)解码的有效解决方案

    最近从WIN7更换为WIN10后 xff0c PotPlayer播放器加速出现不同步情况 xff0c 网上查找了很多办法 xff0c 最终奏效 失败方法一 xff1a FFmpeg64 dll 下载FFmpeg64 dll xff08 ht
  • Win10打开任务管理器卡死的解决方法

    我的情况是刚开始装了win10家庭版 xff0c 但是安装一些软件后 xff0c 过段时间打开任务管理器就会莫名其妙的卡死 xff0c 我去重新装了原装系统 xff0c 换了固态硬盘 xff0c 清理了电脑 xff0c 还是会出现这个问题
  • 基于双目视觉的非标机械臂的空间定位流程(未完待续)

    文章目录 系统坐标系变换原理双目标定原理准备步骤 图像极线校正 对应点匹配 空间定位图像校正计算视差计算深度目标点空间定位 三维重建手眼标定 xff08 eye in hand xff09 问题故障解决下一步计划参考 系统 接上一次的非标机
  • 如何用VC++60编写查看二进制文件程序

    雷霆工作室 韩燕 在计算机应用中 xff0c 经常需要查看二进制文件的内容 目前 xff0c 在各种VC 43 43 书籍中介绍查看文本文件的文章很多 xff0c 但鲜有介绍查看二进制文件的文章 本文从功能设计 方案设计 编程实现以及技术要
  • Matlab代码导入STM32F103流程

    文章目录 软件准备STM32CubeMX简介配置STM32CUBEMX配置SIMULINKSIMULINK对STM32F103进行点灯试验一般算法导入到STM32问题故障解决参考 软件准备 安装MATLAB2019a xff0c 64位 下

随机推荐