STM32(F103ZET6)第二十课:FreeRtos操作系统的应用
目录
- 调试方式
- 一、任务堆栈溢出检测
- 二、任务管理方式
- 三、二值信号量(任务同步)
- 四、计数信号量
- 五、互斥信号量
- 六、队列
调试方式
问题:传感器数据获取问题,有的DHT11能获取到,有的获取不到
两种方式:调优先级或者进临界区,临界区有关中断的机制,保护程序不被打断。
一、任务堆栈溢出检测
任务空间检测函数:
使用时先开宏定义
然后在主函数下面加一个栈溢出钩子函数。当栈溢出时卡死到钩子函数中。
本质上就是一个钩子函数,在任务上下文切换的时候做检测,具有一定的滞后性,需要在任务发生上下文切换时才会进行,任务堆栈溢出时并不能马上检测到问题。
但大多常见情况下这种检测机制依然是非常实用的功能,可以帮助用户减少代码中的错误并提高应用程序代码的质量。
二、任务管理方式
1、任务创建完成都处于就绪链表,然后启动调度器后,根据优先级去查找就绪链表里优先级最高的任务,然后去执行,优先级一样的按照创建的顺序执行。
2、执行的任务会变成运行态,从就绪链表中删除,然后任务执行的vTaskDelay等函数时,会被加入到阻塞链表里,调度器选择其他任务去执行。
3、当阻塞解除时,任务重新被添加到就绪链表里,等待调度器调度。如果解除阻塞的任务优先级最高,会立即抢占CPU去执行。
启动调度器的流程:
1,创建空闲任务
2,判断是否使用软件定时器,会创建一个软件定时器的任务
3,关闭中断
4,对操作系统的状态付初值
5,启动调度器:通过汇编的的方式,开启中断,开启第一个任务
任务创建的流程:
每个任务都会有一个任务控制块的结构体,里边保存着任务的相关信息
1,申请任务栈空间
2,申请任务控制块空间
3,将任务空间地址,赋值给任务控制块TCB的栈指针参数
4,根据参数去对任务控制块初始化
5,添加任务控制块到就绪链表
三、二值信号量(任务同步)
先创建信号量,然后一个任务给出信号量,一个任务获取信号量
任务B需要等待任务A执行完再去执行,这就叫任务同步。
1、添加头文件
#include "semphr.h"
2、创建信号量
先创建一个信号量句柄:SemaphoreHandle_t Binary;
之后创建一个二值信号量
Binary = xSemaphoreCreateBinary();
3、给出信号量函数
4获取信号量函数
测试代码:当按下按键3之后灯任务获取信号量,灯变亮,如果不按下则灯无变化,一直阻塞。
//任务本体 闪灯任务
TaskHandle_t LED_TaskHandle;
void LED_Task(void *p)
{
uint8_t i=0;
while(1)
{
if(xSemaphoreTake(Binary,1000) == pdTRUE)
{
printf("按键按下,获取到信号量\r\n");
}else
{
printf("获取到信号量等待超时\r\n");
}
i++;
printf("i=%d\r\n",i);
Led_Toggle(1);
//printf("LED任务剩余空间:%d\r\n",(int)uxTaskGetStackHighWaterMark(NULL));
// Delay_nms(200);//也能延时,但是会一直占用CPU
//也是延时200ms,但是FreeRTOS提供的函数,有阻塞机制,能够让任务从运行态变为阻塞态
vTaskDelay(200);
}
}
//任务本体 按键任务
TaskHandle_t KEY_TaskHandle;
void KEY_Task(void *p)
{
while(1)
{
switch(key_getvalue())
{
case 1:vTaskSuspend(LED_TaskHandle);break;
case 2:vTaskResume(LED_TaskHandle);break;
case 3:xSemaphoreGive(Binary);break;
case 4:break;
}
vTaskDelay(10);//MS级别的延时,带有阻塞性质,任务会从运行态变为阻塞态
}
}
四、计数信号量
任务阻塞时,是不会在运行的,什么时候再解除阻塞,是有调度去做的,调度器回去判断某个任务是否具备解除阻塞的条件,如果具备,会将该任务从阻塞链表,放到就绪链表里。一般在资源有限的情况下使用的。
使用之前先创建一个计数信号量句柄:SemaphoreHandle_t Count_Handle;//计数信号量句柄
之后创建一个计数信号量
Count_Handle = xSemaphoreCreateCounting(10,5);//创建计数信号量
其中参数为最大信号量为多少和初始信号量有几个。
由按键给一个信号量xSemaphoreGive(Count_Handle);
灯任务去接受:if(xSemaphoreTake(Count_Handle,portMAX_DELAY) == pdTRUE)
计数信号量测试:按键和灯,按键按下几次,闪烁几次
//任务本体 闪灯任务
TaskHandle_t LED_TaskHandle;
void LED_Task(void *p)
{
uint8_t i=0;
while(1)
{
if(xSemaphoreTake(Count,portMAX_DELAY) == pdTRUE)
{
printf("按键按下,获取到信号量\r\n");
}else
{
printf("获取到信号量等待超时\r\n");
}
i++;
printf("i=%d\r\n",i);
Led_Toggle(1);
//printf("LED任务剩余空间:%d\r\n",(int)uxTaskGetStackHighWaterMark(NULL));
// Delay_nms(200);//也能延时,但是会一直占用CPU
//也是延时200ms,但是FreeRTOS提供的函数,有阻塞机制,能够让任务从运行态变为阻塞态
vTaskDelay(1000);
}
}
//任务本体 按键任务
TaskHandle_t KEY_TaskHandle;
void KEY_Task(void *p)
{
while(1)
{
switch(key_getvalue())
{
case 1:vTaskSuspend(LED_TaskHandle);break;
case 2:vTaskResume(LED_TaskHandle);break;
case 3:xSemaphoreGive(Count);break;
case 4:xSemaphoreGive(Count);break;
}
vTaskDelay(10);//MS级别的延时,带有阻塞性质,任务会从运行态变为阻塞态
}
}
五、互斥信号量
互斥的含义:一个资源在使用时,就不能被其他任务再使用了
先创建,和二值信号量不同的是互斥量创建之初里边就有这个信号量,可以被获取到。互斥信号量有借有还。
互斥锁用于共享资源的保护
资源A是共享资源,此时如果被任务B使用,那么就不能被其他任务使用。
先创建一个互斥信号量的句柄:SemaphoreHandle_t Mutex;
之后创建互斥信号量:Mutex = xSemaphoreCreateMutex();
测试printf的保护
void MyPrintf(char *p)
{
xSemaphoreTake(Mutex,portMAX_DELAY);
printf(“%s”,p);
xSemaphoreGive(Mutex);
}
xSemaphoreTake(Mutex,portMAX_DELAY);获取信号量
xSemaphoreGive(Mutex);还回去
优先级继承机制说明:
优先级反转的情况:M任务先于H任务执行
使用互斥信号量的情况下:当高优先级任务获取信号量时,会临时将L任务的优先级提高到和他同一等级,等到任务L释放信号量时,优先级恢复为初始状态。避免M任务时间过长,导致H任务要等到M任务执行之后才能运行。
六、队列
创建队列:
先创建一个队列句柄:QueueHandle_t Queue;
之后创建一个队列:Queue = xQueueCreate(10,2);创建队列 长度为10 ,队列中每个数据的大小2字节
队列发送函数:
if(xQueueSend(Queue,buff,100) == pdTRUE)
{
printf("发送成功\r\n");
}else
{
printf("队列已满,发送失败\r\n");
}
队列接受函数:
if(xQueueReceive(Queue,buff2,100)==pdTRUE)
{
printf(“接受数据:%x %x\r\n”,buff2[0],buff2[1]);
}else
{
printf(“队列为空,接收失败\r\n”);
}
代码:
//任务本体 按键任务
TaskHandle_t KEY_TaskHandle;
void KEY_Task(void *p)
{
uint8_t buff[2] = {0xAA,0x55};
uint8_t buff2[2] = {0};
MyPrintf("KEY_Task Begin\r\n");
while(1)
{
switch(key_getvalue())
{
case 1:
if(xQueueSend(Queue,buff,100) == pdTRUE)
{
printf("发送成功\r\n");
}else
{
printf("队列已满,发送失败\r\n");
}
break;
case 2:
if(xQueueReceive(Queue,buff2,100)==pdTRUE)
{
printf("接受数据:%x %x\r\n",buff2[0],buff2[1]);
}else
{
printf("队列为空,接收失败\r\n");
}
break;
case 3:xSemaphoreGive(Count);break;
case 4: break;
}
vTaskDelay(10);//MS级别的延时,带有阻塞性质,任务会从运行态变为阻塞态
}
}