freertos 创建互斥量_物联网项目开发快速入门(三):FreeRTOS快速入门1

2023-05-16

起源

先说一下FreeRTOS的起源,FreeRTOS是由Richard Barry在2003年由设计的,由于其设计的小巧简单,整个核心代码只有3到4个C文件。在设计之初就异军突起,累计开发者数百万,是目前市场占有率最高的RTOS,现在FreeRTOS已经支持三十多种芯片,基本包含市场上所有的微控制器。FreeRTOS在2018年被亚马逊收购,继续遵循GPLV2许可协议完全免费。 Richard Barry为了让代码容易阅读、移植和维护,大部分的代码都是以C语言编写,只有一些内核调度函数采用汇编编写。

正题

  1. 获取源码

freeRTOS官网,进入官网点击download FreeRTOS,按照提示下载源码。

最新的版本是10.2.1,下载完成后是一个exe文件,点击解压。

2. 命名规则

FreeRTOS核心源码文件的编写遵循MISRA(The Motor Industry Software Reliability Association 汽车工业软件可靠性联会)代码规则,同时支持各种编译器

变量

  • uint32_t定义的变量都加上前缀ul,u代表unsigned 无符号,l代表long长整型。
  • uint16_t定义的变量都加上前缀us。u代表unsigned无符号,s代表short短整型。
  • uint8_t定义的变量都加上前缀uc。u代表unsigned无符号,c代表char字符型。
  • size_t 定义的变量也要加上前缀ux。枚举变量会加上前缀e。 指针变量会加上前缀p。

函数

  • 加上了static声明的函数,定义时要加上前缀prv(这个是单词private的缩写)。
  • 带有返回值的函数,根据返回值的数据类型,加上相应的前缀,如果没有返回值,即void类型 ,函数的前缀加上字母v。
  • 根据文件名,文件中相应的函数定义时也将文件名加到函数命名中,比如tasks.c文件中函数vTaskDelete,函数中的task就是文件名中的task。

宏定义

  • 据宏定义所在的文件,文件中的宏定义声明时也将文件名加到宏定义中,比如宏定义configUSE_PREEMPTION 是定义在文件 FreeRTOSConfig.h里面。宏定义中的config就是文件名中的config。另外注意,前缀要小写。
  • 除了前缀,其余部分全部大写,同时用下划线分开。

自定义数据类型

主要有4中类型

  • TickType_t:如果使能宏定义configUSE_16_BIT_TICKS,定义为16位无符号类型,如果没有使能这个宏,则表示32位无符号类型。
  • BaseType_t:对于32位的处理器,定位32位有符号数,对于16位处理器,则表示16位有符号数。
  • UBaseType_t:BaseType_t的无符号类型。
  • STackType_t:栈变量的数据类型,16位处理器就对应16位变量,32位处理器对应32位变量.

3.FreeRTOS 初探

在一般的教程中最开始都是系统移植,但是我觉得在对RTOS一无所知的时候就去移植反而会使自己一头雾水,出现一点错误都不知道从那里下手解决问题。

先介绍一下FreeRTOS对一般用户能够用到的特点:

  • 理论上支持无限多个优先级
  • 理论上支持无线多个任务
  • 支持抢占式调度(执行优先级最高的任务),时间片调度(定时切换任务)
  • 支持小时队列,二值信号量,计数信号量,递归信号量,互斥信号量,消息传递和消息同步
  • 静态可裁剪(根据config文件)

RTOS工作流程

  • RTOS工作的一般流程

  • 单片机工作的一般流程

任务TASK我们暂时可以理解为一个while(1)的死循环。不死不休的执行着里面的程序。至于什么时间切换到下一个任务,就需要我们在while(1)中告诉调度器我们在什么时候想切换任务了。

调度器:就是负责根据当前条件切换到相应的任务中。

我们可以认为FreeRTOS跟单片机程序一样,它的调度器就是在中断函数中切换任务。这样一想FreeRTOS是不是一下就简单了。

如何开始写FreeRTOS的应用程序

我的目的是让读者能够快速上手FreeRTOS,其中有所遗漏则学需要读者自己补齐。

  1. 创建任务: 使用函数xTaskCreate ,这个函数是freeRTOS提供的创建任务的函数,FreeRTOS官方已经提供了详细的注释,如果有阅读英文的能力最好是看官方提供的注释。
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,    // 函数指针 
    const char * const pcName,   // 函数名称 
    configSTACK_DEPTH_TYPE usStackDepth,//堆栈大小
    void *pvParameters,//函数参数
    UBaseType_t uxPriority,//优先级
    TaskHandle_t *pvCreatedTask//任务句柄,保存任务的结构体
    );

//任务函数实例 
 void vTaskCode( void * pvParameters )
 {
     for( ;; )
     {
         // Task code goes here.
     }
 }

2. 参数解释:

    • 函数指针就是我们执行功能的函数指针,他的格式为 void vTaskCode( void * pvParameters )
    • 函数名称则是在系统中保存的一个字符串,这个一般是给开发者看的,开发者可以直观的看到函数的名字,计算机其实是不需要的。
    • 堆栈大小,堆栈大小的意思就是这个任务所能运行的内存的大小,也就是说整个任务只能运行在这段内存中,所以在定义这个参数时应该根据函数的功能预估出函数所堆栈大小。
    • 函数参数,这个参数一般是用于向给任务传入参数时使用,因为函数指针的格式一定规定好了,所以只能以指针的形式将参数传递到函数内部了,但是一般情况下这个参数是不需要的传入NULL即可。
    • 优先级:定义的任务在整个系统中所执行的时机,FreeTROS的优先级是从0~configMAX_PRIORITIES,0优先级最小,系统已经默认分配了,开发者从优先级1开始分配。
    • 任务句柄,这个参数本质上是一个结构体,这个结构体中保存了当前任务的一些信息,如果想在其他任务中使用这个函数,只需要使用这个句柄就可以找到这个任务了。

3. 任务运行

  • 在调用完创建任务函数之后,还需要执行vTaskStartScheduler() 开始运行调度器,
vTaskStartScheduler();

执行完vTaskStartScheduler后,系统就会按照当前任务中谁的任务优先级高,运行谁的规则执行(抢占式优先级)。

4. 例程展示

#include "stdio.h"
    #include "FreeRTOSconfig.h"

    /*tskIDLE_PRIORITY为系统最低优先级 0*/
    #define TASK1_PRIORITY              ( tskIDLE_PRIORITY + 1 )
    #define TASK2_PRIORITY              ( tskIDLE_PRIORITY + 2 )
    #define TASK3_PRIORITY              ( tskIDLE_PRIORITY + 3 )

    #define TASK_STACK_SIZE            512

    void vTaskFunction1( void * pvParameters )
    {
        for( ;; )
        {
             printf("vTaskFunction1 n");
             vTaskDelay(100);
        }
    }
    void vTaskFunction2( void * pvParameters )
    {
        for( ;; )
        {
             printf("vTaskFunction2 n");
             vTaskDelay(100);
        }
    } 
    void vTaskFunction3( void * pvParameters )
    {
        for( ;; )
        {
             printf("vTaskFunction3 n");
             vTaskDelay(100);
        }
    }

    int main(void)
    {
        ...
        xTaskCreate( vTaskFunction1, "TASK1", TASK_STACK_SIZE, NULL, TASK1_PRIORITY, NULL );
        xTaskCreate( vTaskFunction2, "TASK2", TASK_STACK_SIZE, NULL, TASK2_PRIORITY, NULL );
        xTaskCreate( vTaskFunctio3n, "TASK3", TASK_STACK_SIZE, NULL, TASK3_PRIORITY, NULL );   
        vTaskStartScheduler();
        while(1){
            //永远不会执行到这里!
        }
        return 0;
    }

以上是一个最简单了示例,我在每一个函数中都调用了vTaskDelay这个函数,vTaskDelay这个函数是延时函数(相对延时,延时时间并不准确),调用这个函数的原因在于跳出当前任务,因为RTOS是抢占是调度器,就是移植执行最好优先级的任务,所以如果不让最高优先级停下来的话 ,其他任务是不可能有执行机会的。

vTaskDelay函数就是让当前任务进入到阻塞状态,让任务进行切换,等延时时间到了调度器查看当前任务是不是最好优先级的任务,如果是就执行这个任务。在实际应用当中不一定只能使用vTaskDelay函数进行任务切换,在之后我会介绍使用其他的方式切换任务。

5. 任务状态

刚刚我写到阻塞这个词,挂起状态是表述FreeRTOS任务的当前所处的状态,根据当前的状态调度器判断当前任务是否应该被执行。 FreeRTOS有四个任务状态 Running 运行态 : 当任务正在运行时这个任务就工作在运行态 Ready 就绪态 : 指能够运行,但是当前的CPU被更高优先级的任务所占用 Blcokd 阻塞态 : 任务一直处在等待信号量,消息队列,事件标志的状态称为阻塞态。(可以理解成被阻塞不能往下运行了) Suspended 挂起态 : * 类似于阻塞态,但是这个状态是通过调用vTaskSuspend()函数引起的,挂起后任务不能执行,只有调用xTaskResume()函数才能将任务从挂起状态中恢复出来。

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

freertos 创建互斥量_物联网项目开发快速入门(三):FreeRTOS快速入门1 的相关文章

随机推荐