STM32(F103ZET6)第二十课:FreeRtos操作系统的应用

目录

调试方式

问题:传感器数据获取问题,有的DHT11能获取到,有的获取不到

两种方式:调优先级或者进临界区,临界区有关中断的机制,保护程序不被打断。

一、任务堆栈溢出检测

任务空间检测函数:

使用时先开宏定义

然后在主函数下面加一个栈溢出钩子函数。当栈溢出时卡死到钩子函数中。

本质上就是一个钩子函数,在任务上下文切换的时候做检测,具有一定的滞后性,需要在任务发生上下文切换时才会进行,任务堆栈溢出时并不能马上检测到问题。

但大多常见情况下这种检测机制依然是非常实用的功能,可以帮助用户减少代码中的错误并提高应用程序代码的质量。

二、任务管理方式


1、任务创建完成都处于就绪链表,然后启动调度器后,根据优先级去查找就绪链表里优先级最高的任务,然后去执行,优先级一样的按照创建的顺序执行。
2、执行的任务会变成运行态,从就绪链表中删除,然后任务执行的vTaskDelay等函数时,会被加入到阻塞链表里,调度器选择其他任务去执行。
3、当阻塞解除时,任务重新被添加到就绪链表里,等待调度器调度。如果解除阻塞的任务优先级最高,会立即抢占CPU去执行。

启动调度器的流程:

1,创建空闲任务

2,判断是否使用软件定时器,会创建一个软件定时器的任务

3,关闭中断

4,对操作系统的状态付初值

5,启动调度器:通过汇编的的方式,开启中断,开启第一个任务
任务创建的流程:

每个任务都会有一个任务控制块的结构体,里边保存着任务的相关信息

1,申请任务栈空间

2,申请任务控制块空间

3,将任务空间地址,赋值给任务控制块TCB的栈指针参数

4,根据参数去对任务控制块初始化

5,添加任务控制块到就绪链表

三、二值信号量(任务同步)

先创建信号量,然后一个任务给出信号量,一个任务获取信号量
任务B需要等待任务A执行完再去执行,这就叫任务同步。
1、添加头文件

bash 复制代码
#include "semphr.h"

2、创建信号量

先创建一个信号量句柄:SemaphoreHandle_t Binary;

之后创建一个二值信号量

Binary = xSemaphoreCreateBinary();
3、给出信号量函数

4获取信号量函数

测试代码:当按下按键3之后灯任务获取信号量,灯变亮,如果不按下则灯无变化,一直阻塞。

bash 复制代码
//任务本体 闪灯任务
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)

计数信号量测试:按键和灯,按键按下几次,闪烁几次

bash 复制代码
//任务本体 闪灯任务
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字节
队列发送函数:

bash 复制代码
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");

}

代码:

bash 复制代码
//任务本体 按键任务
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级别的延时,带有阻塞性质,任务会从运行态变为阻塞态
	}
}	
相关推荐
A懿轩A几秒前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导5 分钟前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香5 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3051 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻1 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工1 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
疯狂飙车的蜗牛5 小时前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
yutian06066 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
唐诺7 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨8 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget