本文用到的实验平台:
- 野火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进行一次闪烁了,可以看到,即使在系统中也能进行定时器的调用了!!!