通过不一样的方式带你快速入门FreeRTOS

哈喽,大家好,这里是自律鸽。写这篇文章的起因是由于我经常水群,在聊天的时候发现很多小伙伴在学习FreeRTOS时不知道从何下手,对为什么学习FreeRTOS也没有很清晰的认知。因此,我今天想用一篇文章带大家快速入门FreeRTOS。


一、什么是FreeRTOS?

正如其名,FreeRTOS是一款“开源免费”的实时操作系统,由美国的Richard Barry于2003年发布,经历了数几十年的更新迭代,它已成为目前市场上占有率最高的RTOS。

二、为什么要学习FreeRTOS?

如果你一直在从事或者开发单片机,你会发现越来越多的单片机会用到譬如FreeRTOS这样的操作系统。在这里,有的小伙伴可能会问了。为什么我们会需要用到操作系统呢?为了解答大家的这个疑惑,在这里我给大家举个小栗子加以说明。

在刚入门学习单片机的时候,我想大家一定做过这种类似的跑马灯或者呼吸灯实验,就是让两个LED灯同时闪烁。(Tips:LED1间隔0.4s闪烁,LED2间隔0.6s闪烁。)

裸机代码实现:

 while (1)  
    {  
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);  
        HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_SET);  
        HAL_Delay(200); 
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
        HAL_Delay(100);          
        HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_RESET);        
        HAL_Delay(100); 
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET); 
        HAL_Delay(200); 
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);        
        HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_SET); 
        HAL_Delay(200);  
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
        HAL_Delay(100); 
        HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_RESET);
        HAL_Delay(100);     
        HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET); 
        HAL_Delay(200);               
    }  
}

从上述代码可以看出,由于单片机是单核系统,所以想要实现两个LED灯同时闪烁的功能就需要将两个LED闪烁的代码融合在一起。这样带来的后果就是代码的可读性非常差,不利于开发。众所周知,在实际的开发过程中,单片机所接的外设远不止两个LED灯这么简单。 

FreeRTOS代码实现: 

//创建LED1_Task任务
 xTaskCreate(LED1_Task,"LED1",1000,NULL,1,&LED1_Task_Handle);
 //创建LED2_Task任务 
 xTaskCreate(LED2_Task,"LED2",1000,NULL,2,&LED2_Task_Handle);
              /*LED2_Task 任务入口函数*/ 
             /*"LED2" 任务名字*/ 
             /*1000 任务栈大小*/
             /*NULL 任务入口函数参数*/
             /*2 任务优先级*/
             /*&LED2_Task_Handle 任务控制块指针*/ 
// LED1闪烁任务  
void LED1_Task(void *pvParameters)  
{  
    while(1)  
    {  
        digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN);  
        vTaskDelay(200); // 延时200毫秒 
        digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN); 
        vTaskDelay(200); // 延时200毫秒
    }  
}  
// LED2闪烁任务  
void LED2_Task(void *pvParameters)  
{  
    while(1)  
    {  
        digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN);
        vTaskDelay(300); // 延时300毫秒 
        digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN);
        vTaskDelay(300); // 延时300毫秒 
    }  
}

从上述代码可以看出,FreeRTOS引入了多线程的概念,两个LED灯闪烁分别对应着两个任务,且两个任务是同时进行的。相比于裸机系统,FreeRTOS的代码是否更简洁且易读呢?答案是肯定的。那么FreeRTOS是如何让单核的单片机系统同时运行两个任务的呢? 

三、FreeRTOS的核心知识

(1)任务调度

由上文可知,在利用FreeRTOS实现两个LED闪烁的时候引入了多线程概念,那么这一小节将具体讲解FreeRTOS是如何同时运行不同任务的。

实际上FreeRTOS有自己的任务调度算法。它可以让cpu在不同的任务之间来回切换。即当cpu执行完一段代码后,它会在任务队列中检索是否还有其他任务可以做,如果有,则会去执行其他的任务,执行完后再回到原代码处,以此反复运行。因此,每个任务都可以轮流地享有相同的cpu时间。我们通常把该时间称为时间片。在RTOS中,最小的时间单位为一个tick,即SysTick中断周期。因为研读过RT-Thread源码,所以我知道RT-Thread中是可以指定时间片的大小为多少个tick的。而FreeRTOS的时间片只能为一个tick。

(2)线程交互

除了上述所说的多线程以及任务调度之外,FreeRTOS的另一个亮点在于其线程交互机制。在FreeRTOS中,为了支持线程间的灵活通信,它提供了四种用于线程之间交互的方式:分别是消息队列、信号量、事件、任务通知。今天我主要给大家简单介绍一下消息队列的使用。实验很简单,主要是通过按键发送不同指令实现对不同LED灯的控制。代码如下:


#define  QUEUE_LEN    4   /* 队列的长度,最大可包含多少个消息 */
#define  QUEUE_SIZE   4   /* 队列中每个消息大小(字节) */

  /* 创建Test_Queue */
  Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */
                            (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */
  /* 创建Receive_Task任务 */
  xReturn = xTaskCreate(Receive_Task,"Receive_Task",512,NULL,1,&Receive_Task_Handle);  
  /* 创建Send_Task任务 */
  xReturn = xTaskCreate(Send_Task,"Send_Task",512,NULL,2,&Send_Task_Handle);        
/**********************************************************************
  * @ 函数名  :Receive_Task
  * @ 功能说明:Receive_Task任务主体
  * @ 参数    :
  * @ 返回值  :无
  ********************************************************************/
static void Receive_Task(void* parameter)
{  
  BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdTRUE */
  uint32_t r_queue;  /* 定义一个接收消息的变量 */
  while (1)
  {
    xReturn = xQueueReceive( Test_Queue,    /* 消息队列的句柄 */
                             &r_queue,      /* 发送的消息内容 */
                             portMAX_DELAY); /* 等待时间 一直等 */
    if(pdTRUE == xReturn)
      if(r_queue==1)
      {
          LED1_ON;
      }
      else if(r_queue==2)
      {
          LED2_ON;
      }
    else
      printf("数据接收出错,错误代码0x%lx\n",xReturn);
  }
}

/**********************************************************************
  * @ 函数名  :Send_Task
  * @ 功能说明:Send_Task任务主体
  * @ 参数    :
  * @ 返回值  :无
  ********************************************************************/
static void Send_Task(void* parameter)
{   
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  uint32_t send_cmd1 = 1;
  uint32_t send_cmd2 = 2;
  while (1)
  {
    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {/* K1 被按下 */
      printf("发送指令1!\n");
      xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                            &send_cmd1,/* 发送的消息内容 */
                            0 );        /* 等待时间 0 */
      if(pdPASS == xReturn)
        printf("指令1发送成功!\n\n");
    } 
    if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
    {/* K2 被按下 */
      printf("发送指令2!\n");
      xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */
                            &send_cmd2,/* 发送的消息内容 */
                            0 );        /* 等待时间 0 */
      if(pdPASS == xReturn)
        printf("指令2发送成功!\n\n");
    }
    vTaskDelay(20);/* 延时20个tick */
  }
}

 从上述代码可以看出,消息的发送主要是通过函数xQueueSend()实现的。当单片机检测到按键K1按下的时,会通过函数xQueueSend()向消息队列Test_Queue发送消息,而消息的内容会存储在&send_cmd1中,第三个参数0则表示发送消息时阻塞或者等待的时间,即如果队列已满,调用xQueueSend()的线程将不会等待,而是立即返回错误。如果需要等待队列空闲,可以设置一个非零的阻塞时间。而消息的接收主要是通过函数xQueueReceive()实现的。该函数的核心作用是从指定的消息队列中检索消息,其中这些消息的内容主要存储在&r_queue所指向的内存位置。值得注意的是,与消息发送机制类似,消息接收也可能面临阻塞或等待的情况。具体而言,当消息队列为空,即当前没有可供接收的消息时,若你仍希望从该队列中接收到消息,那么调用xQueueReceive()的线程或任务将会进入阻塞状态,等待直至有新的消息被发送到该队列中。一旦有消息到达,原先处于阻塞状态的接收者便能成功接收该消息。


四、总结

完结,撒花。本文主要通过几个常用的例子给大家梳理了一下如何学习FreeRTOS,目的是让大家对FreeRTOS有更加清晰的认知,以便深入了解FreeRTOS的精髓。FreeRTOS的核心在于多线程、任务调度、线程交互。对于初学者来说,只要掌握了这几点,就可以很自信的说自己已经迈入了FreeRTOS的门槛了。

Tips:文章中若有错误的内容,欢迎指出。笔者也一直在研读RTOS的道路中,希望能够共同进步。关注不迷路,谢谢大噶~

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值