前置要求:FreeRTOS   C++环境开发单片机

兼容标准库,HAL库

此文章介绍抽象化FreeRTOS模板的简单使用

git仓库:git clone https://github.com/12379biu/C-FreeRTOS.git

目录

代码使用

对比:使用模板前

对比:使用模板后


freeRTOS自带隔离属性,调用任务调度器osKernelStart()后会自动启动所有已创建的任务函数,利用这个属性,可以做到多人文件隔离,并行开发项目,大大提高项目的简洁性和可维护性。

加上c++对c的兼容性,项目成员在使用时写纯c语言即可运行,无需c++门槛

这里先介绍应用部分,对源码感兴趣的话请看系列的文章2和3部分

代码使用

首先,我们先对比一下没有使用freeRTOS的任务创建,我们以cubemx生成的默认任务为例,首先创建任务定义好一些任务参数,然后调用创建任务的API,最后实现任务。我学习时,老师创建任务更是分散,还有创建启动任务的任务,写一个启动此任务的函数并在主函数调用,啧啧啧。

对比:使用模板前

//1.定义任务
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

//2.创建任务
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

//3.实现任务
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

咳咳有点跑题了,使用freeRTOS你肯定不只创建一个任务,你要创建多个任务,那么到时候代码就不是这样排好的结构了。可能是下面这样的结构,虽然也是大致有序,但找起参数来总有种两眼一黑的感觉......

//1.任务属性
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for maintask */
osThreadId_t maintaskHandle;
const osThreadAttr_t maintask_attributes = {
  .name = "maintask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for uarttask */
osThreadId_t uarttaskHandle;
const osThreadAttr_t uarttask_attributes = {
  .name = "uarttask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for oledtask */
osThreadId_t oledtaskHandle;
const osThreadAttr_t oledtask_attributes = {
  .name = "oledtask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};


//2.任务创建
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of maintask */
  maintaskHandle = osThreadNew(Main, NULL, &maintask_attributes);

  /* creation of uarttask */
  uarttaskHandle = osThreadNew(Uart, NULL, &uarttask_attributes);

  /* creation of oledtask */
  oledtaskHandle = osThreadNew(Oled, NULL, &oledtask_attributes);



//3.任务创建
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

void Main(void *argument)
{
  /* USER CODE BEGIN Main */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END Main */
}

void Uart(void *argument)
{
  /* USER CODE BEGIN Uart */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END Uart */
}

void Oled(void *argument)
{
  /* USER CODE BEGIN Oled */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END Oled */
}

对比:使用模板后

这里你需要知道c++的类class,只需要知道最基础的属性就足够了,两分钟就能学会,可以看我的博客c++ Class的简单入门https://blog.csdn.net/weixin_71306050/article/details/151288644?fromshare=blogdetail&sharetype=blogdetail&sharerId=151288644&sharerefer=PC&sharesource=weixin_71306050&sharefrom=from_link

这里我们创建一个类,继承,虚函数什么的都不用管,只是使用的话也不需要知道是什么。

我们需要创建一个类,就叫它Main_Task(名字随意)吧。

#include "my_task.h"//将我们的文件导入

class Main_Task:public TaskBase{
public:
Main_Task():TaskBase("main_task",32,1){};
virtual void task(void*arg)
{
    while(1)
    {
		LL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		vTaskDelay(pdMS_TO_TICKS(100));
    }
}	
};
Main_Task maintask;

1.就像创建一个结构体那样struct Main_Task---> class Main_Task,然后在TaskBase()括号里填写参数

任务的函数名称,任务的字大小,任务的优先级。

2.在函数task()中填写我们的业务逻辑,比如用LL库点个灯。

3.最后,创建出这个类就像结构体struct Main_Task maintask-->Main_Task maintask

好了,任务创建完成。

回顾一下我们需要修改的地方

然后在主函数,如果是CubeMX,那么什么都不用改,直接启动

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM7_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();  /* Call init function for freertos objects (in cmsis_os2.c) */
  MX_FREERTOS_Init();

  /* Start scheduler */
  osKernelStart();

.......

如果你是自己手写,直接掉官方API就好了,完全隔离,任务直接就启动了

int main(void)
{

	// 优先级分组4,因为 FreeRTOS 只支持抢占优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

	// 启动任务调度器
	vTaskStartScheduler();
    while (1);
}

如此,一个任务就创建好了,好了,用同样的方法创建4个任务做一下对比

创建一个任务一路复制粘贴下去就好了,这可比CubeMX快多了,而且信息高度集中,参数就在里面。

class Default_Task:public TaskBase{
public:
Default_Task():TaskBase("default_Task",32,1){};
virtual void task(void*arg)
{
    while(1)
    {
		vTaskDelay(pdMS_TO_TICKS(100));
    }
}	
};
Default_Task defaultTask;

/*Task------------------------------------------------------*/

class Main_Task:public TaskBase{
public:
Main_Task():TaskBase("main_task",128,1){};
virtual void task(void*arg)
{
    while(1)
    {
		vTaskDelay(pdMS_TO_TICKS(100));
    }
}	
};
Main_Task maintask;

/*Task------------------------------------------------------*/

class Uart_Task:public TaskBase{
public:
Uart_Task():TaskBase("uart_Task",128,1){};
virtual void task(void*arg)
{
    while(1)
    {
		vTaskDelay(pdMS_TO_TICKS(100));
    }
}	
};
Uart_Task uartTask;

/*Task------------------------------------------------------*/

class Oled_Task:public TaskBase{
public:
Oled_Task():TaskBase("oled_Task",128,1){};
virtual void task(void*arg)
{
    while(1)
    {
		vTaskDelay(pdMS_TO_TICKS(100));
    }
}	
};
Oled_Task oledTask;
/*--------------------------------------------------------*/

先介绍到这里,下一期文章2介绍一些此模板API的使用

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐