当前位置: 首页 > article >正文

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级别的延时,带有阻塞性质,任务会从运行态变为阻塞态
	}
}	

http://www.kler.cn/a/282785.html

相关文章:

  • 【C++】—— map 与 set 深入浅出:设计原理与应用对比
  • 云计算研究实训室建设方案
  • 3588 yolov8 onnx 量化转 rknn 并运行
  • sqlsever 分布式存储查询
  • ECharts 实现大屏地图功能
  • 67页PDF |埃森哲_XX集团信息发展规划IT治理优化方案(限免下载)
  • 低代码归根结底差不多,但又差很多
  • WPF框架(Prism 和 Community Toolkit)区别点
  • Docker原理及实例
  • WPF UserControl 进行界面绑定,怎么进行内存释放
  • linux中下载nginx
  • 闲鱼ip属地怎么查看?闲鱼怎么修改ip属地
  • 高级前端工程师React面试题
  • Esxi学习记录
  • 天工股份业绩暴增之谜,与第一大客户常州索罗曼的神秘关联疑点
  • 【YOLOv8改进[Conv]】 感受野注意力卷积RFAConv(2024.3)| 使用RFAConv改进目标检测效果 + 含全部代码和详细修改方式
  • 双系统安装:一键解锁电脑新境界,Windows与Linux并肩作战!
  • 0基础学习Python路径(27)sys模块
  • 深入探究Nginx中的URL哈希负载均衡策略
  • 2008-2024年荣威汽车维修手册和电路图线路图接线图资料更新
  • DARKTIMES集成到Sui,带来中世纪格斗大逃杀游戏体验
  • 力扣高频50题,带个人代码,最简单规范的解法
  • 机器学习-朴素贝叶斯
  • LeetCode题练习与总结:添加与搜索单词 - 数据结构设计--211
  • 模糊C-means算法原理及Python实践
  • 电容应用原理