首页 > 其他分享 >FreeRTOS入门基础

FreeRTOS入门基础

时间:2024-03-16 10:29:41浏览次数:25  
标签:定时器 入门 FreeRTOS 队列 创建 基础 信号量 任务

RTOS是为了更好地在嵌入式系统上实现多任务处理和时间敏感任务而设计的系统。它能确保任务在指定或预期的时间内得到处理。FreeRTOS是一款免费开源的RTOS,它广泛用于需要小型、预测性强、灵活系统的嵌入式设备。

创建第一个任务

  1. 任务函数:任务是通过函数来定义的。函数通常看起来像这样的无限循环
       void vTaskFunction( void * pvParameters )
       {
           for( ;; )
           {
               // 任务代码
           }
       }

  2. 创建任务:使用xTaskCreate()函数来创建一个任务。
       xTaskCreate(
           vTaskFunction,       // 任务函数
           "TaskName",          // 任务名称
           STACK_SIZE,          // 堆栈大小
           NULL,                // 参数
           TASK_PRIORITY,       // 任务优先级
           NULL );              // 用来传回创建的任务的句柄

  3. 启动调度器:创建任务后,需要启动调度器,这样RTOS就能开始管理这些任务了。
       vTaskStartScheduler();

freertos基本组件

FreeRTOS作为一个实时操作系统(RTOS),提供了多种基础组件来组织代码并有效地管理任务。以下是一些FreeRTOS的基本组件:

  1. 任务 (Tasks):

    • 任务是FreeRTOS中的基本执行单位,相当于一个独立的线程。每个任务都有自己的优先级,调度器根据这些优先级来决定运行哪个任务。
  2. 队列 (Queues):

    • 队列用于在任务之间发送和接收数据。它们可以帮助实现任务同步,并提供一种安全传递消息的方法,如事件、内存块等。
      #include "FreeRTOS.h"
      #include "queue.h"
      
      // 队列句柄,用于后续的队列操作如发送和接收
      QueueHandle_t xQueue;
      
      // main() 或者任何初始化函数中
      void main() {
          // 创建一个可以存储10个元素的队列,每个元素大小为sizeof( BaseType_t )
          xQueue = xQueueCreate(10, sizeof(BaseType_t));
      
          if (xQueue == NULL) {
              // 队列创建失败,可能是由于内存不足
              // 错误处理代码
          } else {
              // 队列成功创建
              // 可以继续使用队列
          }
      
          // ... 其余初始化代码 ...
      
          // 启动任务,开始调度器
          vTaskStartScheduler();
          
          // ... 其余代码 ...
      }

      在这个例子中:

    • xQueueCreate 函数的第一个参数(10)指定了队列能够存储的元素的数量。
    • 第二个参数(sizeof(BaseType_t))指定了队列中每个元素的大小。
    • 注意,在使用 xQueueCreate() 创建队列之前,必须确保已经调用了 vTaskStartScheduler(),因为队列的使用依赖于FreeRTOS的内存管理函数,它们在调度器启动时初始化。此外,创建队列通常在系统的初始化阶段进行,然后各个任务可以通过队列句柄进行数据的发送和接收。

       

      创建队列后,可以使用 xQueueSend()xQueueReceive() 等函数来在任务之间传递数据。如果队列创建成功,xQueueCreate() 将返回一个非NULL的 QueueHandle_t 句柄,用于后续的队列操作。如果内存不足或者有其他原因导致队列创建失败,则返回NULL。

  3. 信号量 (Semaphores):

    • 信号量是一种同步机制,可以用来控制对共享资源的访问,或者在任务之间同步操作。在FreeRTOS中有两种主要类型的信号量:二进制信号量和计数信号量。
      #include "FreeRTOS.h"
      #include "semphr.h"
      
      SemaphoreHandle_t xSemaphore = NULL;
      
      void main_demo( void )
      {
          // 二进制信号量创建
          xSemaphore = xSemaphoreCreateBinary();
          
          if (xSemaphore != NULL)
          {
              // 信号量创建成功,可以被使用
          }
          else
          {
              // 信号量创建失败,处理错误情况
          }
          
          // ...
          // 后续代码,如启动调度器和任务等
          // ...
      }
      #include "FreeRTOS.h"
      #include "semphr.h"
      
      SemaphoreHandle_t xCountingSemaphore;
      
      void main_demo( void )
      {
          // 计数信号量创建,最大计数和初始计数
          xCountingSemaphore = xSemaphoreCreateCounting(maxCount, initialCount);
          
          if (xCountingSemaphore != NULL)
          {
              // 信号量创建成功
          }
          else
          {
              // 信号量创建失败,处理错误情况
          }
          
          // ...
          // 后续代码,如启动调度器和任务等
          // ...
      }

      在计数信号量的创建中, maxCount 表示信号量能够达到的最大计数值,而 initialCount 是信号量的初始计数值。

       

      信号量通常用于同步任务或中断服务程序 (ISR),例如,保护共享资源,协调任务的执行,任务通知等。创建后的信号量句柄可以通过任务和中断服务程序进行给出 (Give) 和取得 (Take) 操作。

  4. 互斥量 (Mutexes):

    • 互斥量是特殊类型的信号量,专门用于管理资源访问。与二元信号量不同,互斥量具有所有权的概念,使其在处理优先级反转问题时更加有效。
  5. 定时器 (Timers):

    • 定时器可以在一个定义好的时间之后运行一个函数。FreeRTOS支持一次性定时器和周期性定时器。
      #include "FreeRTOS.h"
      #include "timers.h"
      
      TimerHandle_t xTimer;
      
      
      void vTimerCallback(TimerHandle_t xTimer)
      {
          // 这里处理定时器到期时的逻辑
      }
      
      void main_demo( void )
      {
          const TickType_t xTimerPeriod = pdMS_TO_TICKS( 1000 ); // 定时周期1000毫秒
          
          // 创建软件定时器
          xTimer = xTimerCreate(
              "Timer",                // 定时器的文本名称,用于调试
              xTimerPeriod,           // 定时器的周期,以tick计数
              pdTRUE,                 // pdFALSE为单次定时器,pdTRUE为周期性定时器
              ( void * ) 0,           // 可用于传递给回调函数的标识符,通常是NULL
              vTimerCallback          // 定时器到期时调用的回调函数
          );
      
          if (xTimer == NULL)
          {
             // 定时器创建失败,可能是由于内存不足
          }
          else
          {
              // 定时器创建成功,可以启动定时器
              if (xTimerStart(xTimer, 0) != pdPASS)
              {
                  // 定时器启动失败
              }
          }
          
          // ...
          // 后续代码,如启动调度器和任务等
          // ...
      }
      
      

      在这个例子中:

    • 第一个参数("Timer")是定时器名称。
    • 第二个参数(xTimerPeriod)设置定时器周期,通过pdMS_TO_TICKS宏转换了毫秒到tick。
    • 第三个参数指定了定时器是单次的 (pdFALSE) 还是自动重载的周期性定时器 (pdTRUE)。
    •  

      定时器创建后,你需要调用xTimerStart()函数来启动定时器,此后定时器将按照设定的周期运行。在资源有限的嵌入式系统里,软件定时器是一种资源节约的实现定时功能的方式,因为你可以在一个定时服务中管理多个定时器,而无需为每个定时任务创建单独的线程。

    • 第四个参数是指针,它会被传递给定时器回调函数,可以用作计数器或存储状态信息。
    • 第五个参数是定时器到期时调用的回调函数。
  6. 事件组 (Event Groups):

    • 事件组是一种可以等待或设置一组事件标志的机制。这允许任务在多种事件中等待任意组合的事件。
      #include "FreeRTOS.h"
      #include "event_groups.h"
      
      EventGroupHandle_t xEventGroup;
      
      void main_demo( void )
      {
          // 创建事件组
          xEventGroup = xEventGroupCreate();
          
          if(xEventGroup == NULL)
          {
              // 事件组创建失败,通常是因为内核没有足够的可用堆空间
              // 错误处理代码
          }
          else
          {
              // 事件组成功创建,可以使用xEventGroup来设置、清除和等待事件标志
          }
          
          // ...
          // 启动任务和调度器等
          // ...
      }

      在这个例子中,xEventGroupCreate 用于创建一个新的事件组,并返回一个 EventGroupHandle_t 类型的句柄,以供后续的事件组操作使用。如果事件组创建失败,则返回 NULL,通常是由于内存不足。

       

      成功创建事件组后,可以使用如下函数操作事件标志:

    • xEventGroupSetBits:设置事件组中的一个或多个事件标志。
    • xEventGroupClearBits:清除事件组中的一个或多个事件标志。
    • xEventGroupWaitBits:等待事件组中的一个或多个特定事件标志变为设置状态。
    • 事件组是处理多个任务和中断共享状态或事件同步时的一个非常有用的工具,能够以线程安全的方式进行复杂的事件标志操作。
  7. 任务通知 (Task Notifications):

    • 任务通知是一种轻量级的信号量替代方式,可以快速地向任务发送信号。
    • 发送任务通知

      // 取得需要通知的任务的句柄,可以在任务创建时获取
      TaskHandle_t xTaskToNotify = ...;
      
      // 发送通知给任务,'ulValue' 是通知值
      uint32_t ulValue = 10;
      BaseType_t xHigherPriorityTaskWoken = pdFALSE; // 仅在中断服务程序中使用
      
      // 任务级代码
      xTaskNotify(xTaskToNotify, ulValue, eSetValueWithOverwrite);
      
      // 或者在 ISR 中
      xTaskNotifyFromISR(xTaskToNotify, ulValue, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
      portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

      在上面的代码中,xTaskToNotify 是要接收通知的任务的句柄。ulValue 是要发送的通知值。eSetValueWithOverwrite 指示即使之前的通知未被任务接收,也将覆盖通知值。

    • 等待任务通知

      // 该变量将接收通知值
      uint32_t ulNotificationValue;
      BaseType_t xResult;
      
      // 等待通知
      xResult = xTaskNotifyWait(0x00,          // 在进入等待时不清除任何位
                                ULONG_MAX,     // 在退出等待时将清除所有位
                                &ulNotificationValue,  // 存储接收到的通知值
                                portMAX_DELAY); // 一直等待直到接收到通知
      
      if(xResult == pdPASS)
      {
          // 任务接收到通知,`ulNotificationValue` 包含收到的值
      }

      在上面的等待通知代码中,xTaskNotifyWait 第一个参数为零表示调用任务进入等待通知状态时,不清除任务的任何通知状态位。第二个参数 ULONG_MAX 表示调用任务在接收到通知时将清除任务的所有通知状态位。第四个参数是阻塞时间,这个例子中使用 portMAX_DELAY,任务会无限期地等待直到收到通知。

       

      通过 xTaskNotifyGive 和 ulTaskNotifyTake 的组合,任务通知机制也可以模仿二进制信号量的行为。任务通知作为一种轻量级的同步机制,在许多场合可以替代信号量和事件组,以节省系统资源。

资源地址

FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

 Free RTOS Book and Reference Manual

标签:定时器,入门,FreeRTOS,队列,创建,基础,信号量,任务
From: https://blog.csdn.net/weixin_40345245/article/details/136716718

相关文章

  • gRPC入门学习之旅(二)
       gRPC入门学习之旅(一)    gRPC是一个高性能、通用的开源远程过程调用(RPC)框架,基于底层HTTP/2协议标准和协议层Protobuf序列化协议开发,支持众多的开发语言,由Google开源。    gRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服......
  • Elasticsearch 基础-1
    Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引孳,基于RESTfulweb接口。功能:分布式的搜索引擎和数据分析引擎全文检索(like),结构化检索(a=1),数据分析(count/groupby)对海量数据进行近实时的处理(秒级)特点:可拓展性:大型分布式集群(......
  • 人工智能入门之旅:从基础知识到实战应用(一)
    一、引言人工智能(ArtificialIntelligence,AI)是指利用计算机科学和技术模拟、延伸和扩展人类智能的理论、方法、技术和应用系统的学科。它的目标是使计算机系统具有类似于人类的智能,能够感知环境、学习、推理、规划、解决问题和交流。在当今社会中,人工智能具有极其重要的地......
  • 深度学习入门:基于Python的理论与实践 笔记
    深度学习入门:基于Python的理论与实践笔记一,Python基础由于本人之前已经系统学习过Python,此处只总结有关深度学习的Python的库NumPy生成NumPy数组要生成NumPy数组,需要使用np.array()方法。np.array()接收Python列表作为参数,生成NumPy数组(numpy.ndarray)>>>x=np.array......
  • 深度学习入门基于python的理论与实现-第四章神经网络的学习(个人向笔记)
    目录从数据中学习损失函数均方误差(MSE)交叉熵误差mini_batch学习mini_batch版交叉熵误差的实现从数据中学习神经网络的"学习"的学习是指从训练数据自动获取最有权重参数的过程。神经网络的特征就是可以从数据中学习即由数据自动决定权重参数的值。机器学习通常是认为确定一些......
  • 前后端分离Ajax入门
    前后端分离之Ajax入门一、概念Ajax(AsynchronousJavascriptAndXML),即是异步的JavaScript和XML,Ajax其实就是浏览器与服务器之间的一种异步通信方式。它可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,在这种情况下,浏览器可以做自己的事情。直到成功获取响应后,浏......
  • 1分钟带你学会Python面向对象基础语法
    1.类和对象python中的面向对象主要学习类和对象类:是多个具有特殊功能的个体的集合,例如:人类/猫类/犬类对象:在一个类中,一个具有特殊功能的个体,能够帮忙解决某件特定的事情,也被称为实例两者之间的关系:类是用于描述某一类对象的共同特征,而对象是类的具体的存在在程序中......
  • C语言新手经典基础题——冒泡排序
    冒泡排序:用户输入一组数,编写程序将该组数据进行从小到大的顺序进行排列。举个例子:用户输入;1413918766这一组数据,现在要将这组数据进行从小到大的程序进行排列。我们编写程序的思路如下:现将第一个数和第二个数进行比较,即14和13,13比14小,那么就将13和14进行位置的调换,13......
  • 深度学习入门基于python的理论与实现-第三章神经网络
    目录激活函数阶跃函数sigmoid函数ReLU函数三层神经网络的实现输出层设计恒等函数和softmax函数输出层的神经元数量手写数字识别MINIST数据集神经网络的推理处理批处理激活函数激活函数是连接感知机和神经网络的桥梁阶跃函数阶跃函数是在感知机中使用的激活函数。\[h(x)=\begi......
  • 大规模C++程序设计 -- 基础知识
    基础知识我们先回顾C++程序语言和面向对象分析的一些重要的方面,这些知识对于大型系统设计来说是基本的。我们仔细分析多文件程序、声明与定义,以及在头文件和实现文件上下文中的内部链接和外部链接,然后研究typedef和assert的使用。多文件C++程序对于所有的(除了最小的)程序来说,将......