三、freeRTOS_任务管理

目录

1. 任务状态理论

2. 任务状态实验

3. vTaskDelay和vTaskDelayUntil

4. 空闲任务及其钩子函数

5. 任务调度算法

5.1 状态与事件

5.2 调度策略


1. 任务状态理论

  • 对于整个单片机程序,我们称之为application,应用程序。
  • 使用FreeRTOS时,我们可以在application中创建多个任务(task),有些文档把任务也称为线程

(thread)。

FreeRTOSConfig.h,每个任务之间是以1ms的tick中断来切换的。

#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )

任务切换的4种状态。

 任务状态转换图,每个任务都是以链表的形式加入到里面,如:

xTaskCreate() -> prvAddNewTaskToReadyList( pxNewTCB );

vTaskSuspend -> vListInsertEnd; 

2. 任务状态实验

对应程序:08_freertos_example_task_status

  • 任务切换的基础:tick中断

  • 有哪些任务状态?状态切换图

  • 怎么管理不同状态的任务:放在不同链表里

  • 阻塞状态(Blocked)举例:vTaskDelay函数

  • 暂停状态(Suspended)举例:vTaskSuspend/vTaskResume

任务1调用vTaskSuspend(),让任务3进入暂停状态,过了一会,在调用vTaskResume(),让任务3进入到就绪状态;

任务2调用vTaskDelay(),直接进入到阻塞状态;

 main.c

TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask3;

static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;

void Task1Function(void * param)
{
	TickType_t tStart = xTaskGetTickCount();
	TickType_t t;
	int flag = 0;
	
	while (1)
	{
		t = xTaskGetTickCount();
		
		task1flagrun = 1;
		task2flagrun = 0;
		task3flagrun = 0;
		printf("1");

		if (!flag && (t > tStart + 10))
		{
			vTaskSuspend(xHandleTask3);
			flag = 1;
		}

		if (t > tStart + 20)
		{
			vTaskResume(xHandleTask3);
		}
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		task3flagrun = 0;
		printf("2");

		vTaskDelay(10);
	}
}

void Task3Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 0; 
		task3flagrun = 1;
		printf("3");
	}
}


/*-----------------------------------------------------------*/

StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;

StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;


/*
 * The buffers used here have been successfully allocated before (global variables)
 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
    *ppxIdleTaskStackBuffer = xIdleTaskStack;
    *pulIdleTaskStackSize = 100;
}


int main( void )
{
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
	xHandleTask3 = xTaskCreateStatic(Task3Function, "Task3", 100, NULL, 1, xTask3Stack, &xTask3TCB);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

 运行结果和代码逻辑一致:

任务1调用vTaskSuspend(),让任务3进入暂停状态,过了一会,在调用vTaskResume(),让任务3进入到就绪状态;

任务2调用vTaskDelay(),直接进入到阻塞状态;

URAT1打印出的信息,基本和上图一致,但串口信息直观性不好,没有上图清晰。

3. vTaskDelay和vTaskDelayUntil

对应程序:09_freertos_example_delay

有两个Delay函数:

  • vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态

  • vTaskDelayUntil

    • 老版本,没有返回值

    • 等待到指定的绝对时刻,才能变为就绪态。

下面这个do_something()函数,执行时间不定,但是vTaskDelay(N)延迟函数的tick是固定的。

如下图,每个N Tick是固定的时间。

 如果,想让\Delta t1 == \Delta t2 == ... = \Delta tn

使用vTaskDelayUntil(t2, ∆t)函数, 传入参数(t2, ∆t),假如在t2 - t3的时刻,不管t2从A B C哪个位置开始运行,必须保证t1 - t2和t2 - t3的∆t是相等的。

t2为 指针结构体 之前唤起的时间,∆t为增量。

①运行间隔时间:*pxPre + ∆t

②更新每次传入的起始时间:*pxPre = *pxPre + ∆t

代码:

让任务1的优先级为2,任务2 3的优先级为1,先让任务1运行,任务1使用xTaskGetTickCount()函数获得当前开始的Tick Count,

TickType_t tStart = xTaskGetTickCount(); 

给vTaskDelayUntil(&tStart, 20);函数使用。

任务2、任务3 不做复杂的逻辑。

TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask3;

static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;

static int rands[] = {3, 56, 23, 5, 99};

void Task1Function(void * param)
{
	TickType_t tStart = xTaskGetTickCount();
	int i = 0;
	int j = 0;
	
	while (1)
	{
		
		task1flagrun = 1;
		task2flagrun = 0;
		task3flagrun = 0;

		for (i = 0; i < rands[j]; i++)
			printf("1");

		j++;
		if (j == 5)
			j = 0;
#if 0
		vTaskDelay(20);
#else
		vTaskDelayUntil(&tStart, 20);
#endif
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		task3flagrun = 0;
		printf("2");
	}
}

void Task3Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 0; 
		task3flagrun = 1;
		printf("3");
	}
}


/*-----------------------------------------------------------*/

StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;

StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;


/*
 * The buffers used here have been successfully allocated before (global variables)
 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
    *ppxIdleTaskStackBuffer = xIdleTaskStack;
    *pulIdleTaskStackSize = 100;
}


int main( void )
{
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xTaskCreate(Task1Function, "Task1", 100, NULL, 2, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
	xHandleTask3 = xTaskCreateStatic(Task3Function, "Task3", 100, NULL, 1, xTask3Stack, &xTask3TCB);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果:

图1:是 vTaskDelay(20)函数运行的结果,任务1的空闲时间都是相等的,为20ms。

图2:是vTaskDelayUntil(&tStart, 20)函数的运行结果,任务1的每个∆t都是相等的,为20ms。

4. 空闲任务及其钩子函数

对应程序:10_freertos_example_idletask,在05_freertos_example_createtask基础上修改

  • 任务后的清理工作在哪执行?分两类:

    • 自杀的任务:在空闲任务中完成清理工作,比如释放内存(都自杀了,怎么清理自己的尸体? 由别人来做)

    • 非自杀的任务:在vTaskDelete内部完成清理工作(凶手执行清理工作)

  • 空闲任务何时才能执行?

  • 空闲任务只能处于这2个状态之一:Running、Ready

  • 空闲任务钩子函数

    • 执行一些低优先级的、后台的、需要连续执行的函数

    • 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任 务占据的时间,就可以算出处理器占用率。

    • 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式 了。

    • 绝对不能导致任务进入Blocked、Suspended状态

    • 如果你会使用 vTaskDelete() 来删除任务,那么钩子函数要非常高效地执行。如果空闲任务移植 卡在钩子函数里的话,它就无法释放内存。

任务1的优先级是1,运行一段时间后,创建任务2,然后任务2马上运行,之后任务2进入2ms的休眠中,任务1删除任务2,在之后的循环中,任务1不断的创建2,然后在不断的删除任务2

void Task2Function(void * param);

/*-----------------------------------------------------------*/
static int task1flagrun = 0;
static int task2flagrun = 0;
static int taskidleflagrun = 0;

void Task1Function(void * param)
{
	TaskHandle_t xHandleTask2;
	BaseType_t xReturn;
	
	while (1)
	{
		task1flagrun = 1;
		task2flagrun = 0;
		taskidleflagrun = 0;
		printf("1");
		xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);
		if (xReturn != pdPASS)
			printf("xTaskCreate err\r\n");
		vTaskDelete(xHandleTask2);
			
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		taskidleflagrun = 0;
		printf("2");
		vTaskDelay(2);
	}
}

void vApplicationIdleHook( void )
{
	task1flagrun = 0;
	task2flagrun = 0;
	taskidleflagrun = 1;	
	printf("0");
}


/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

空闲任务的优先级是 0,任务1的优先级是 1,任务2的优先级是 2

在任务1中不断的去创建,不断的去消耗内存,但是删除任务2的清理工作没办法进行,于是任务1的堆会逐渐消耗光,最终导致创建任务失败。

		xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);
		if (xReturn != pdPASS)
			printf("xTaskCreate err\r\n");

我们的内存也只有几十k,在执行上面代码的时候,并没有打印 printf("xTaskCreate err\r\n");这句话,我们期望的事情并没有发生,我们之前说在空闲任务中执行清理工作,这个结论我们要修正一下,应该是自杀的任务:在空闲任务中完成清理工作,比如释放内存。

 如果把代码修改成这样,运行几次后,代码立马就运行出错了,出现错误的唯一原因就是内存不够了,堆不够了,原因是自杀任务,使用vTaskDelete(NULL)函数后,不能自己释放内存,需要由别人来做。

void Task1Function(void * param)
{
	TaskHandle_t xHandleTask2;
	BaseType_t xReturn;
	
	while (1)
	{
		task1flagrun = 1;
		task2flagrun = 0;
		taskidleflagrun = 0;
		printf("1");
		xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);
		if (xReturn != pdPASS)
			printf("xTaskCreate err\r\n");
		//vTaskDelete(xHandleTask2);
			
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		taskidleflagrun = 0;
		printf("2");
		//vTaskDelay(2);
		vTaskDelete(NULL);
	}
}

 结论:

  • 任务后的清理工作在哪执行?分两类:

    • 自杀的任务:在空闲任务中完成清理工作,比如释放内存(都自杀了,怎么清理自己的尸体? 由别人来做)

    • 非自杀的任务:在vTaskDelete内部完成清理工作(凶手执行清理工作)

  所以自杀任务就要使用空闲任务来完成清理工作了,具体做法如下:

在main函数的任务调度函数,vTaskStartScheduler()中,第一个参数prvIdleTask,

xIdleTaskHandle = xTaskCreateStatic(  prvIdleTask,
                                      configIDLE_TASK_NAME,
                                      ulIdleTaskStackSize,
                                      ( void * ) NULL,
                                      portPRIVILEGE_BIT,
                                      pxIdleTaskStackBuffer,
                                      pxIdleTaskTCBBuffer );

在tasks.c中搜索到static portTASK_FUNCTION( prvIdleTask, pvParameters )函数,一般我们不在tasks.c的核心文件中修改代码,会破坏核心代码,但是该函数末提供了钩子函数,

        #if ( configUSE_IDLE_HOOK == 1 )
            {
                extern void vApplicationIdleHook( void );

                /* Call the user defined function from within the idle task.  This
                 * allows the application designer to add background functionality
                 * without the overhead of a separate task.
                 * NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
                 * CALL A FUNCTION THAT MIGHT BLOCK. */
                vApplicationIdleHook();
            }

 我们要在 FreeRTOSConfig.h 中 添加宏定义configUSE_IDLE_HOOK == 1 ,之后再main.c中实现   void vApplicationIdleHook( void )函数,如下:

void vApplicationIdleHook( void )
{
	task1flagrun = 0;
	task2flagrun = 0;
	taskidleflagrun = 1;	
	printf("0");
}

整体代码如下: 

因为空闲任务(vApplicationIdleHook())的优先级为0,所以我们要修改任务1的优先级也为0,这要在任务2自杀后,空闲任务才可以执行起来,去调用到我们提供的钩子函数,完成任务2的清理工作了。

注:钩子函数不要来死循环,要不然空闲任务就做不了其他事情了。

void Task2Function(void * param);

/*-----------------------------------------------------------*/
static int task1flagrun = 0;
static int task2flagrun = 0;
static int taskidleflagrun = 0;

void Task1Function(void * param)
{
	TaskHandle_t xHandleTask2;
	BaseType_t xReturn;
	
	while (1)
	{
		task1flagrun = 1;
		task2flagrun = 0;
		taskidleflagrun = 0;
		printf("1");
		xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);
		if (xReturn != pdPASS)
			printf("xTaskCreate err\r\n");
		//vTaskDelete(xHandleTask2);
			
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		task1flagrun = 0;
		task2flagrun = 1;
		taskidleflagrun = 0;
		printf("2");
		//vTaskDelay(2);
		vTaskDelete(NULL);
	}
}

void vApplicationIdleHook( void )
{
	task1flagrun = 0;
	task2flagrun = 0;
	taskidleflagrun = 1;	
	printf("0");
}


/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xTaskCreate(Task1Function, "Task1", 100, NULL, 0, &xHandleTask1);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果:

在任务2为低电平时,任务1和空闲任务交替执行,空闲任务会调用到我们提供的钩子函数,完成了清理工作。

 URAT1打印信息

5. 任务调度算法

对应程序:11_freertos_example_scheduler

5.1 状态与事件

正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理器系统中,任何时间里只能有一个任务处于运行状态。

非运行状态的任务,它处于这3种状态之一:

  • 阻塞(Blocked)

  • 暂停(Suspended)

  • 就绪(Ready)

就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。

阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。

事件分为两类:

  • 时间相关的事件

    • 所谓时间相关的事件,就是设置超时时间:在指定时间内阻塞,时间到了就进入就绪状态。

    • 使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。

  • 同步事件

    • 同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。

    • 怎么"发送信息"?方法很多

      • 任务通知(task notification)

      • 队列(queue)

      • 事件组(event group)

      • 信号量(semaphoe)

      • 互斥量(mutex)等

      • 这些方法用来发送同步信息,比如表示某个外设得到了数据。

5.2 调度策略

  • 是否抢占?

  • 允许抢占时,是否允许时间片轮转?

  • 允许抢占、允许时间片轮转时,空闲任务是否让步?

以下调度规则使用的代码如下,修改不同的宏定义,既有不同的效果。

static void prvSetupHardware( void );

static volatile int flagIdleTaskrun = 0;  // 绌洪棽浠诲姟杩愯鏃秄lagIdleTaskrun=1
static volatile int flagTask1run = 0;     // 浠诲姟1杩愯鏃秄lagTask1run=1
static volatile int flagTask2run = 0;     // 浠诲姟2杩愯鏃秄lagTask2run=1
static volatile int flagTask3run = 0;     // 浠诲姟3杩愯鏃秄lagTask3run=1

/*-----------------------------------------------------------*/

void vTask1( void *pvParameters )
{
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 1;
		flagTask2run = 0;
		flagTask3run = 0;
		
		/* 打印任务的信息 */
		printf("T1\r\n");				
	}
}

void vTask2( void *pvParameters )
{	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 1;
		flagTask3run = 0;
		
		/* 打印任务的信息 */
		printf("T2\r\n");				
	}
}

void vTask3( void *pvParameters )
{	
	const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );		
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 0;
		flagTask3run = 1;
		
		/* 打印任务的信息 */
		printf("T3\r\n");				

		// 如果不休眠的话,其他任务无法得到执行
		vTaskDelay( xDelay5ms );
	}
}

void vApplicationIdleHook(void)
{
	flagIdleTaskrun = 1;
	flagTask1run = 0;
	flagTask2run = 0;
	flagTask3run = 0;	
	
	/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */
	//printf("Id\r\n");				
}

int main( void )
{
	prvSetupHardware();
	
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
	xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
	xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);

	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create theidle task. */
	return 0;
}

 以下配置项都是在 FreeRTOSConfig.h 中。

#define configUSE_PREEMPTION		1

 可抢占调度"(Pre-emptive),宏定义为1的时候,支持该调度规则。

#define configUSE_PREEMPTION		0

可抢占调度"(Pre-emptive),宏定义为0的时候,不支持该调度规则,只要任务3让出cpu资源后,任务1就一直执行下去了,高优先级的就绪态也不能马上运行,只能等待当前任务主动放弃当前cpu的资源,平级任务更应该等待。

破解这种方法,需要使用如下的代码,让其主动休眠一会:

		// 如果不休眠的话,其他任务无法得到执行
		vTaskDelay( xDelay5ms );


#define configUSE_TIME_SLICING      1

时间片轮转"(Time Slicing),如果使用时间片轮转,宏定义应该是1,如果不使用时间片轮转,是0,运行结果为下图 ,高优先级任务3先执行,直到自己让出cpu资源,其他优先级为0的任务才可以执行,这时候谁抢占上,就一直执行,直到任务2再次抢占过去cpu资源。


空闲任务是否让步于用户任务(配置项:configIDLE_SHOULD_YIELD) 

#define configIDLE_SHOULD_YIELD		1

 空闲任务低人一等,每执行一次循环,就看看是否主动让位给用户任务

#define configIDLE_SHOULD_YIELD		0

 空闲任务跟用户任务一样,大家轮流执行,没有谁更特殊

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: FreeRTOS V2 是一个实时操作系统(RTOS),V2 API 是它的应用程序编程接口。 FreeRTOS 是一个广泛应用于嵌入式系统的开源实时操作系统。它提供了可靠的任务调度、时间管理、内存管理和通信机制,使开发者能够轻松地编写出高效、可靠的嵌入式应用程序。 V2 API 是 FreeRTOS 的应用程序编程接口的第二个版本。它提供了一组函数和数据结构,用于控制和管理 FreeRTOS 内核的各个部分。通过这些 API,开发者可以创建和管理任务、队列、信号量、互斥锁等系统资源,实现任务间的通信和同步。 V2 API 在原有的 API 基础上进行了一些增强和改进。它增加了更多的功能和特性,提供了更丰富的资源管理任务调度机制,提高了系统的可靠性和效率。 使用 FreeRTOS V2 API,开发者可以轻松地编写出符合实时要求的嵌入式应用程序。他们可以通过创建任务和使用任务通信机制,实现系统中不同任务之间的并发执行和数据传输。他们还可以使用 V2 API 中提供的事件标志和定时器功能,实现任务的同步和定时控制。 总之,FreeRTOS V2 API 是 FreeRTOS 实时操作系统的应用程序编程接口的第二个版本。它提供了一种方便、高效的方式来使用 FreeRTOS 的功能和特性,使开发者能够轻松地开发出符合实时要求的嵌入式应用程序。 ### 回答2: FreeRTOS是一个开源的实时操作系统内核,是由迈克尔·贝瑞(Michael Barry)创建的。它提供了一个可移植的、可扩展的、可定制的操作系统内核,用于嵌入式系统和实时应用。其中,FreeRTOS V2是FreeRTOS的第二个主要版本。 FreeRTOS V2提供了一些新的API接口和功能,以增强原有版本的功能和性能。它引入了动态任务优先级分配、任务通知和DMA支持,并对进行多核操作和IPC(进程间通信)做出了改进。同时,FreeRTOS V2添加了一些通用的编程接口,以提供更多的灵活性和互操作性。 在FreeRTOS V2中,新增的任务通知机制使得任务之间可以更加方便地进行通信和同步。它允许任务发送信号给其他任务,以通知其有任务需要处理。这个机制在实时系统中非常有用,可以提高系统的响应性和效率。 此外,FreeRTOS V2还引入了DMA支持,可以通过DMA传输数据,从而减少CPU的负担,提高系统的效率和响应速度。 另外的改进包括使用动态任务优先级分配,可以根据不同任务的优先级动态地分配系统资源,使得系统更加灵活。 总之,FreeRTOS V2作为FreeRTOS的新版本,通过新增API接口和功能的方式,进一步提升了它的功能和性能,使得嵌入式系统和实时应用能够更加高效地运行。 ### 回答3: FreeRTOS是一个开源的即时操作系统内核,提供轻量级的任务调度和资源管理功能,针对嵌入式系统设计开发。目前最新版本是FreeRTOS V10.4.1。 FreeRTOS V2 API是FreeRTOS内核的一种编程接口,用于实现任务的创建、删除、挂起和恢复等操作。V2 API是在旧版本API基础上进行了优化和扩展,提供了更强大和灵活的功能。 V2 API引入了任务通知功能,可以在任务之间进行通信和同步操作。通过信号量和消息队列等机制,任务可以共享和传递数据。这大大简化了任务之间的协作和数据交换。 V2 API还增加了事件组功能,可以用于等待和通知多个事件的发生。任务可以根据不同的事件发生状态执行不同的操作。这在多任务并发和同步控制方面非常有用。 另外,V2 API还提供了软件定时器功能,可以用于定期执行一些操作。这对于周期性任务和定时操作非常有帮助。 总之,FreeRTOS V2 API拓展了旧版本API的功能,提供了更多的任务管理和同步机制,使得嵌入式系统开发更加灵活和方便。它广泛应用于各种类型的嵌入式系统,并受到开发者的广泛欢迎。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值