FreeRTOS学习笔记4

1、互斥量的理论知识

        互斥量的主要知识是关于解锁和上锁的问题,通过解锁和上锁的配合实现任务之间的互斥,但是这个互斥量也会延伸出来很多的问题,例如下面的两个问题:

        1、让A(优先级为1),B(优先级为2),C(优先级为3),当A开始执行的时候,B的优先级比较高,打断A的执行,然后C的优先级比B的高,打断B的执行,但是C任务中有获得锁函数,但是A上的锁已经被A给解开了,这时候没有锁可以解,那么就只能进入休眠阻塞状态,就轮到B任务开始执行。

         上面这种情况就会导致优先级比较最高的C任务不能执行,这时候的解决办法就是:在C任务无法解锁不能执行的时候,与A任务函数进行优先级的继承。

        2、就是解锁上锁的循环问题,也就是一个任务函数中上锁解锁的过程中间调用了一个子函数,这个子函数中也有解锁和上锁的过程,从任务函数中进入子函数,有再次进行上锁,这时候就会产生阻塞。

        上面这种情况可以通过多次上锁的方式进行解决。

下面是FreeRTOS的一个缺点:

        正常来说:在任务A 占有互斥量的过程中,任务 B 、任务 C 等等,都无法释放互斥量。
但是 FreeRTOS 未实现这点:任务 A 占有互斥量的情况下,任务 B 也可释放互斥量。
可以说互斥量就是一种特殊的信号量。

2、互斥量的简单实用

        互斥量的创建之后,是不需要赋初始值,写入初始值,创建函数中最后会自己填入初始值,下面互斥量的简单使用代码,task1的任务优先级小于task2的任务优先级,刚开始的时候task2会先执行,但是task2需要获得锁,没有获得就需要进入休眠的阻塞状态,然后进入task1的任务函数进行执行,执行完任务之后,开始上锁,然后激活task2的任务函数,高优先级的任务函数抢占低优先级task1。

TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;

QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueSetHandle_t  xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;


static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
  for( ;; )
	{
//		xQueueSend(QueueUsart1,&data,portMAX_DELAY);
		printf("task1:%d\r\n",data);
//		xSemaphoreGive(SemaphoreHandle);
		xSemaphoreGive(xSemaMutexHandle);
//		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
  for( ;; )
	{
//		xQueueSend(QueueUsart2,&data,portMAX_DELAY);
//		xSemaphoreTake(SemaphoreHandle,portMAX_DELAY);
		xSemaphoreTake(xSemaMutexHandle,portMAX_DELAY);
		printf("task2:%d\r\n",data);
//		vTaskDelay(10);
	}
}

//static void vCheckTask3( void *pvParameters )
//{
//	uint16_t data;
//	QueueSetMemberHandle_t hander;
//  for( ;; )
//	{
		hander=xQueueSelectFromSet(xQueueUsartHandle,portMAX_DELAY);
		xQueueReceive(hander,&data,portMAX_DELAY);
//		printf("data:%d\r\n",data);
//		vTaskDelay(10);
//	}
//}


int main( void )
{
	prvSetupHardware();
	QueueUsart1=xQueueCreate(2,sizeof(uint16_t));
	QueueUsart2=xQueueCreate(2,sizeof(uint16_t));
	
//	SemaphoreHandle=xSemaphoreCreateCounting(10,0);
//	
//	xQueueUsartHandle=xQueueCreateSet(3);
//	xQueueAddToSet(QueueUsart1,xQueueUsartHandle);
//	xQueueAddToSet(QueueUsart2,xQueueUsartHandle);
	xSemaMutexHandle=xSemaphoreCreateMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
//	xTaskCreate( vCheckTask3, "Check3", 100, NULL,1, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

        下图是debug的的调试结果:

FreeRTOS学习笔记4_第1张图片

        FreeRTOS并没有实现“谁上锁就由谁开锁”的情况,只能额有程序员自己写代码的时候实现,上面的代码就验证了这个结论。

3、递归上锁

        递归上锁实现了:谁上锁,就有谁解锁的功能。下面代码将证明任务函数是否能够打开不是自己的任务函数上的锁。

        下面代码验证了这个问题,逻辑是:首先创建了一个递归锁,创建的时候会自动give一次(也就是传入一次),创建的任务函数task1的优先级大于task2的优先级,进入task1之后首先执行一次take(读出通道中的数据),执行了之后进入延时休眠函数,让出cpu资源,此时task2开始执行,首先执行一次take(读取通道中的数据,不添加等待时间),这个函数因为没有等待时间,会很快的退出, 如果退出了,就说明task2中不能解锁task1任务中上的锁,然后进入上锁的if判断语句中,之后开始解锁(向通道中发送数据),但是此时task1的锁还没有解开,不能进行解锁操作,所以就会卡死在后面的上锁操作,下面是测试代码:

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
	BaseType_t Taks_Flag;
  for( ;; )
	{
		Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);//刚开始创建的时候产生的锁数据
		printf("take take1 one\r\n");
//		xSemaphoreGiveRecursive(xSemaRecursiveHandle);
		vTaskDelay(500);
		for(int i=0;i<5;i++)
		{
			xSemaphoreGiveRecursive(xSemaRecursiveHandle);
			printf("take take1 two\r\n");
			xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
		}
		xSemaphoreGiveRecursive(xSemaRecursiveHandle);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
	BaseType_t Taks_Flag;
	if(xSemaphoreTakeRecursive(xSemaRecursiveHandle,0)!=pdTRUE)  //刚开始创建的时候产生的锁数据
  {
	   printf("take take2 error\r\n");
		Taks_Flag=pdFALSE;
	}
	if(Taks_Flag!=pdTRUE)
	{
		if(xSemaphoreGiveRecursive(xSemaRecursiveHandle)!=pdTRUE);
		 printf("give take2 flas\r\n");
	}
	Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
	printf("take take2 wate\r\n");
  for( ;; )
	{
		printf(",,,,\r\n");
		vTaskDelay(100);
	}
}


int main( void )
{
	prvSetupHardware();
	
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,2, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
	vTaskStartScheduler();
	return 0;
}

        下面的debug仿真:

FreeRTOS学习笔记4_第2张图片

        如果task1中解锁上锁操作完了之后,task2就可以进行上锁解锁操作了,代码如下所示:

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
	BaseType_t Taks_Flag;
  for( ;; )
	{
		Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);//刚开始创建的时候产生的锁数据
		printf("take take1 one\r\n");
		xSemaphoreGiveRecursive(xSemaRecursiveHandle);
		vTaskDelay(500);
		for(int i=0;i<5;i++)
		{
			xSemaphoreGiveRecursive(xSemaRecursiveHandle);
			printf("take take1 two\r\n");
			xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
		}
//		xSemaphoreGiveRecursive(xSemaRecursiveHandle);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
	BaseType_t Taks_Flag;
	if(xSemaphoreTakeRecursive(xSemaRecursiveHandle,0)!=pdTRUE)  //刚开始创建的时候产生的锁数据
  {
	   printf("take take2 error\r\n");
		Taks_Flag=pdFALSE;
	}
	if(Taks_Flag!=pdTRUE)
	{
		if(xSemaphoreGiveRecursive(xSemaRecursiveHandle)!=pdTRUE);
		 printf("give take2 flas\r\n");
	}
	Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
	printf("take take2 wate\r\n");
  for( ;; )
	{
		printf(",,,,\r\n");
		vTaskDelay(100);
	}
}


int main( void )
{
	prvSetupHardware();
	
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,2, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
	vTaskStartScheduler();
	return 0;
}

        下面是debug的仿真测试:

FreeRTOS学习笔记4_第3张图片

 

你可能感兴趣的:(FreeRTOS的学习,学习,stm32)