stm32外设笔记-freertos配置(二)


本文用到的实验平台:

  • 野火MINI-stm32开发板
  • STM32CUBE-IDE开发工具

本文使用是延续上一篇文章介绍的内容进行的补充介绍,上一篇文章见:

1、任务状态概述

FreeRTOS 系统中的每一个任务都有多种运行状态,一般来说在我们创建任务后可以有就绪态,挂起态,运行态,以及阻塞态四种模式,他们之间的关系图如下所示:
在这里插入图片描述
他们之间的关系如下:

  • (1): 创建任务→就绪态(Ready):任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度。
  • (2): 就绪态→运行态(Running):发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态。
  • (3): 运行态→就绪态:有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪列表中,等待最高优先级的任务运行完毕继续运行原来的任务(此处可以看做是 CPU 使用权被更高优先级的任务抢占了)。
  • (4): 运行态→阻塞态(Blocked):正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。
  • (5): 阻塞态→就绪态:阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。
  • (6) (7) (8): 就绪态、阻塞态、运行态→挂起态(Suspended):任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU 的使用权,也不会参与调度,除非它从挂起态中解除。
  • (9): 挂起态→就绪态: 把一个 挂起状态的任务恢复的唯一途径就调用vTaskResume() 或vTaskResumeFromISR() API函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

2、静态任务的创建

之前我们创建的任务都是动态的,一般也默认使用动态任务,下面来创建下静态任务试试:
在这里插入图片描述
生成代码后就可以看到静态任务任务比动态任务多的东西了
在这里插入图片描述
可以看到他是要事先申请好任务栈
在这里插入图片描述
不过我们用的时候还是直接用的这个任务主体语句,就跟用动态任务一样的
在这里插入图片描述
还是一样,将程序下载到开发板,就可以看到led闪烁了!

3、任务的挂起与恢复

前面提到,任务可以进行挂起,这里我们就来测试下任务的挂起与恢复
就是两个函数:

  • osThreadResume(defaultTaskHandle);
  • osThreadSuspend(defaultTaskHandle);

先创建两个按键
在这里插入图片描述
之后我们用这个两个按键来编写任务函数,如下所示:
在这里插入图片描述
源代码:

  for(;;)
  {
    
    
	  if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1)
	  {
    
    
		  osThreadSuspend(defaultTaskHandle);
	  }
	  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1)
	  {
    
    
		  osThreadResume(defaultTaskHandle);
	  }
	  osDelay(10);
  }

将程序下载到开发板,按下按键,led闪烁停止,按下另一个按键就能看到led继续闪烁了!

4、消息队列

在配置页面新建消息队列
在这里插入图片描述
这里我们新建两个优先级不同的任务,后面可以看到他的区别
在这里插入图片描述
在第一个任务中发布消息
在这里插入图片描述
源码如下:

  uint16_t ProducerValue = 0;
  for(;;)
  {
    
    
	  if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==1)
	  {
    
    
		  HAL_Delay(10);
			if(osMessagePut(myQueue01Handle, ProducerValue, 0) != osOK)
			{
    
    
				osThreadSuspendAll();//这里挂起所有任务,使得我们的串口发送不会打扰任务
				printf("send fail\n");
				osThreadResumeAll();
			}
			else
			{
    
    
				++ProducerValue; // 改变要发送的消息
				osThreadSuspendAll();
				printf("send ok\n");
				osThreadResumeAll();
			}
	  }
	  while(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==1)
	  {
    
    
		  osDelay(10);
	  }
	}
	osDelay(1);

另外两个任务接收消息:
在这里插入图片描述
源码如下:

	osEvent event;
	for(;;)
	{
    
    
		event = osMessageGet(myQueue01Handle,osWaitForever);
		if(event.status == osEventMessage)
		{
    
    
			osThreadSuspendAll();
			printf("任务2接收的数据:%d\n",event.value.v);
			osThreadResumeAll();
		}
	}
	osDelay(1);

将程序下载到开发板,可以看到一直是任务一接收到消息,这是因为消息接收到之后就会被销毁,并且任务一的优先级高于任务2,所以任务二就抢不到消息
在这里插入图片描述
如果想要任务二也接受到消息,那就要在任务一接收到消息后不删除消息了,这里改用另外一个函数,如下所示:
在这里插入图片描述
关于这个函数的描述如下,可以看到他会保存这个队列
在这里插入图片描述
但是这个函数有bug,需要我们手动添加一句话,就是这个保存的是个32位的数据,然后我们新建的消息是16位的,这样高16位都是1,低十六位才是我们的数据,这样拼合的数据就肯定不对,所以我们就需要事先给他清零,添加的代码如下所示:
在这里插入图片描述
将程序下载到开发板,就可以看到现象了:
在这里插入图片描述

5、软件定时器

使能软件定时器
在这里插入图片描述
添加一个软件定时器
在这里插入图片描述
打开文件就可以看到我们创建的软件定时器了
在这里插入图片描述
之后在初始化的下面启动软件定时器,这里第二个单位是ms
在这里插入图片描述
将程序往下翻我们就可以看到软件定时器的回调函数了,上面我设置 的是1000ms一次,将程序下载到开发板就能看到1s进行一次闪烁了,可以看到,即使在系统中也能进行定时器的调用了!!!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_51220742/article/details/123498479