FreeRTOS学习笔记2

1、FreeRTOS的同步和互斥的解决方法

        同步和互斥的方法都是为了提高cpu的执行效率,也就是当两个任务需要同时占用同一个外设资源的时候,这时候只能有一个任务执行,另一个任务处于休眠的状态是最节省资源的(如果不是休眠状态,就需要一直查询另一个任务是否执行完成)。

        如下图所示代码,连个任务函数的优先级不同,没有添加休眠的延时函数,那么低优先级的任务函数是不能运行的。这时候可以添加同步的判断函数,就可以实现两个任务函数都能运行。也就是定义一个全局变量,如下面第二块代码所示。

TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
 
 
static void vCheckTask1( void *pvParameters )
{
  extern unsigned short usMaxJitter;

  for( ;; )
	{
		printf("1\r\n");
	}
}

static void vCheckTask2( void *pvParameters )
{
  extern unsigned short usMaxJitter;

   for( ;; )
	{
		printf("2\r\n");
	}
}

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

        下面代码虽然低优先级的任务函数可以运行,但是低优先级的函数一直在查询变量的状态,这样是一直在占用cpu资源的。

static void vCheckTask1( void *pvParameters )
{
  for( ;; )
	{
		if(vTask1_point==0)
		{
			printf("1\r\n");
			vTask1_point=1;
			vTaskDelay(1);
		}
	}
}
static void vCheckTask2( void *pvParameters )
{
  for( ;; )
	{
		if(vTask1_point==1)
		{
			printf("2\r\n");
			vTask1_point=0;
		}
	}
}
int main( void )
{
	prvSetupHardware();
  xTaskCreate( vCheckTask1, "Check", 100, NULL,2, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check", 100, NULL,1, &vCheckTask2_hander);
	vTaskStartScheduler();
	return 0;
}

        上面这种情况有几种方式可以解决,在第三部分会给出解决这个问题的办法。

2、printf打印函数的编写

int fputc( int ch, FILE *f )
{
    while(((USART1->SR)&0x0080)==FALSE);
    USART1->DR=ch;
    return ch;
}

代码中高亮的部分,放在发送数据前面的效率更加高一点,首先判断发送寄存器是否为空(也就是发送寄存器的数据是否移入到移位寄存器中),如果发送寄存器为空,就向发送寄存器写入数据,写入之后就可以去干去其他的事情了;如果高亮部分放在写入发送寄存器的后面,很可能写入数据失败,并且等待的是本次数据是否发送成功,浪费cpu资源。

3、同步和互斥的解决与使用

        一句话理解同步与互斥:我等你用完厕所,我再用厕所。

什么叫同步?

        就是:哎哎哎,我正在用厕所,你等会。

什么叫互斥?

        就是:哎哎哎,我正在用厕所,你不能进来。

        同步与互斥经常放在一起讲,是因为它们之的关系很大,“互斥”操作可以使用“同步”来实现。我“等”你用 完厕所,我再用厕所。这不就是用“同步”来实现“互斥”吗?

        能实现同步、互斥的内核方法有:任务通知(task notification)、队列(queue)、事件组(event group)、 信号量(semaphoe)、互斥量(mutex)。

        它们都有类似的操作方法:获取/释放、阻塞/唤醒、超时。

        比如: A获取资源,用完后A释放资源 A获取不到资源则阻塞,B释放资源并把A唤醒 A获取不到资源则阻塞,并定个闹钟;A要么超时返回,要么在这段时间内因为B释放资源而被唤 醒。

1、队列的方法

        使用队列实现同步的方法,主要利用了队列中的持续的死等待的效果,代码如下图所示:

xQueueSend(QueueUsart1,&Qdata,portMAX_DELAY);中的portMAX_DELAY是死等的效果,这时候的死等就相当于实现了等待的任务函数的休眠,当消息队列中有数据传输的时候,就会唤醒处于等待中休眠的任务函数。一个数据只能唤醒一个任务函数。

        队列中不能传输volatile修饰的变量。

static void vCheckTask1( void *pvParameters )
{
	uint16_t i=0;
  for( ;; )
	{
		Qdata=0;
		for(i=0;i<1000;i++)
		{
			Qdata++;
		}
		printf("task1%d\r\n",Qdata);
		xQueueSend(QueueUsart1,&Qdata,portMAX_DELAY);
	}
}
static void vCheckTask2( void *pvParameters )
{
	uint16_t data;
  for( ;; )
	{
		xQueueReceive(QueueUsart1,&data,portMAX_DELAY);
		printf("task2%d\r\n",data);
	}
}
int main( void )
{
	prvSetupHardware();
	QueueUsart1=xQueueCreate(2,sizeof(uint16_t));
  xTaskCreate( vCheckTask1, "Check", 100, NULL,2, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check", 100, NULL,1, &vCheckTask2_hander);
	vTaskStartScheduler();
	return 0;
}

        使用队列之后不管任务函数的优先级高低,两个任务函数都会被运行,debug调试如下图所示:

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

        使用队列实现互斥的方法,  这时候需要创建三个函数:创建串口锁的函数,上锁函数,解锁函数。通过上锁解锁操作,实现了任务函数1运行的时候,任务函数2通过解锁函数,或者队列获取函数在休眠等待唤醒,这样也就实现了任务函数直接的互斥。互斥一般使用在创建不同的任务,但是任务的函数是相同的,只是栈上分配的内存不同,下面是实现的代码。

static void vCheckTask3( void *pvParameters )
{
	uint16_t data;
  for( ;; )
	{
		UsartGetLock();
		printf("%s\r\n",pvParameters);
		UsartPutLock();
		vTaskDelay(1);//这里添加休眠延时函数让出cpu的资源
	}
}
int main( void )
{
	prvSetupHardware();
	UsartCreateLock();
	QueueUsart1=xQueueCreate(2,sizeof(uint16_t));
//  xTaskCreate( vCheckTask1, "Check", 100, NULL,2, &vCheckTask1_hander);
//	xTaskCreate( vCheckTask2, "Check", 100, NULL,1, &vCheckTask2_hander);
	
	xTaskCreate( vCheckTask3, "Check1", 100, "task1 is run",2, &vCheckTask3_hander);
	xTaskCreate( vCheckTask3, "Check2", 100, "task2 is run",1, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

 调试的结果如下图所示:

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

 2、队列集的使用

        队列集是当很多任务的时候,就会设置很多的队列,这时就会有一个队列叫队列集,来存放多个队列的head,主要使用到下面的几个函数:

        xQueueUsartHandle=xQueueCreateSet(3);队列集创建函数,参数为队列集的长度
       xQueueAddToSet(QueueUsart1,xQueueUsartHandle);队列添加进队列集函数

       hander=xQueueSelectFromSet(xQueueUsartHandle,portMAX_DELAY);数据队列获取函数
       xQueueReceive(hander,&data,portMAX_DELAY);队列接受函数

下面是函数使用的示例代码:

TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;

QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueSetHandle_t  xQueueUsartHandle;


static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
  for( ;; )
	{
		xQueueSend(QueueUsart1,&data,portMAX_DELAY);
		printf("task1:%d\r\n",data);
		vTaskDelay(10);
	}
}
static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
  for( ;; )
	{
		xQueueSend(QueueUsart2,&data,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));
	xQueueUsartHandle=xQueueCreateSet(3);
	xQueueAddToSet(QueueUsart1,xQueueUsartHandle);
	xQueueAddToSet(QueueUsart2,xQueueUsartHandle);
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
	xTaskCreate( vCheckTask3, "Check3", 100, NULL,1, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

任务函数的最后,最好添加上休眠的延时函数,从上面代码中,可以观察出,任务函数3是处于阻塞状态的,在持续查询时候有队列发送数据。

你可能感兴趣的:(FreeRTOS的学习,学习,java,服务器)