【RTOS学习】精简RTOS源码 | 认识RTOS | 任务的创建和删除

news2024/11/26 18:29:17

🐱作者:一只大喵咪1201
🐱专栏:《RTOS学习》
🔥格言:你只管努力,剩下的交给时间!
图

在前面对ARM架构以及C语言在单片机中的表现有一个简单的认识后,现在开始正式进入RTOS的学习。

精简RTOS源码 | 认识RTOS | 任务的创建和删除

  • 😼精简RTOS源码
    • 🐱增加串口
  • 😼认识RTOS
    • 🐱第一个RTOS程序
    • 🐱RTOS中的数据类型和编程规范
  • 😼任务创建和删除
    • 🐱动态创建
    • 🐱静态创建
    • 🐱任务删除
    • 🐱了解就绪队列
  • 😼总结

😼精简RTOS源码

图
如上图所示,在百度上搜索freertos进入官网,然后点击红色框中的Download下载最新的完整FreeRTOS源码,该源码中包含使用示例,下面黑色的下载选项下载的源码不包含实例,我们一般都不下载这个。

图
如上图所示,可以看到整个源码中包含许多文件,因为它是支持多种芯片以及多种工具的(Keil5等等),所以我们只保留需要的,其他的统统删掉。

图
以本喵使用的STM32F103xxx系列芯片为例,最终只留下上图所示的目录,此时源码文件的精简工作就完成了。

图
如上图,此时Keil5中工程文件只保留这些,main函数中也只保留红色框中的内容,之后就在这个基础上增加我们所需要的内容。

然后就是编译工程,此时必然会有很多错误和警告,接下来就是考验大家的时候了,需要挨个将报错和警告全部解决。

图
如上图所示编译结果,本喵这里就不讲解了,接下来就可以写我们自己的代码了。

🐱增加串口

为了后面调试程序方便,本喵这里增加一个串口,用来打印一些信息:

图
如上图,在serial.c文件中,在官网源文件的基础上进行修改,删除一些内容,按照我们平常初始化串口的方式初始化串口,然后再定义fputc函数,实现串口的重定向,串口及重定向在本喵的文章(点我)中有过详细讲解,有兴趣的小伙伴移步查看。

图
如上图,在main.c中的prvSetupHardware函数中最后位置增加串口初始化,此时我们就可以使用printf函数来向串口打印信息了。

图
如上图所示,在main函数中使用printf打印Hello, world!,然后点击Debug按钮进行软件仿真,再从工具栏的View选项中的Serial Windows中选择UART #1,此时会出现串口1的显示框,然后点击全速运行按钮,就会在显示框中显示打印的语句。

  • 按照图中步骤1~5进行操作,就可以像在VSCode上写代码一用printf打印信息一样,只是这里是在UART #1的显示框中显示。

😼认识RTOS

图
在上图中,这位母亲有两件事要做,一边要给小孩喂饭,一边要加班跟同事微信交流。

  • 无法一心多用

对于单线条的人,不能分心、不能同时做事,她只能给小孩喂一口饭,瞄一眼电脑,有信息就去回复,再回来给小孩喂一口饭,如果小孩吃这口饭太慢,她回复同事的信息也就慢了,被同事催:你半天都不回我?如果回复同事的信息要写一大堆,小孩就着急得大哭起来。

这种做法,在软件开发上就是一般的单片机开发,没有用操作系统。

  • 可以一心多用

对于眼明手快的人,她可以一心多用,她左手拿勺子,给小孩喂饭,右手敲键盘,回复同事两不耽误,小孩“以为”妈妈在专心喂饭,同事“以为”她在专心聊天",但是脑子只有一个啊,虽然说“一心多用”,但是谁能同时思考两件事?只是她反应快,上一秒钟在考虑夹哪个菜给小孩,下一秒钟考虑给同事回复什么信息。

这种做法,在软件开发上就是使用操作系统,在单片机里叫做使用RTOS。

  • RTOS的意思是:Real-time operating system,实时操作系统。

我们使用的Windows也是操作系统,被称为通用操作系统。使用Windows时,我们经常碰到程序卡死、停顿的现象,日常生活中这可以忍受。但是在电梯系统中,你按住开门键时如果没有即刻反应,即使只是慢个1秒,也会夹住人。

  • 在专用的电子设备中,实时性很重要。

将喂饭和回复信息写成两个函数放在代码中:

  • 经典裸机单片机程序:
void main()
{
	while(1)
	{
		喂一口饭();
		回复信息();
	}
}

图
假设喂一口饭需要的时间是t1~t5,回复一个信息需要的时间是ta~te,在经典裸机单片机程序中,会先花费t1~t5一段完整的时间去喂一口饭,再花费ta~te一段完整的时间去回复一个信息,如此往复循环。

站在小孩的角度,他觉得自己被喂了一口饭后有ta~te这么长时间母亲没有搭理他,然后又被喂了一口又没人理他,此时他就感觉这顿饭吃的的断断续续的,吃一口后等一会儿才能吃上下一口。

站在同事的角度,他感觉有t1~t5这么长时间没人理他,然后收到一个信息,如此反复,此时他感觉这次的交流很不流畅,也是断断续续的。

  • RTOS程序
/* 喂饭任务 */
喂饭()
{
	while(1)
	{
		喂一口饭();
	}
}
/* 回信息任务 */
回信息()
{
	while(1)
	{
		回一个信息();	
	}
}

void main()
{
	create_task(喂饭);
	create_task(回信息);
	while(1)
	{
		sleep();
	}
}

图
此时喂饭和回信息就在穿插执行,喂饭任务和回复信息任务中的两个while(1)死循环都在执行。花费t1时间喂一下饭(没有喂完一口),再花费ta时间回复一下信息(没有回复完一条信息)。

就这样将喂饭和回复信息穿插执行,当执行了t1~t5 + ta~tb以后,喂一口饭和回复一个信息两个任务就都完成了,和裸机程序相比,耗费的时间是一样的。

但是不一样的是,此时小孩感觉一直在被喂饭,这顿饭吃的很香,同时感觉一直收到消息,这次交流也非常愉快。

  • 由于只有一个CPU,所以采用时间片轮转的方式执行多个任务,即每个任务被CPU执行的时间是固定的,到了就换另一个任务。
  • 因为CPU执行速度非常快,所以给我们的感觉就是两个任务甚至多个任务在同时执行。

🐱第一个RTOS程序

tu
如上图所示,在main函数中使用xTaskCreate创建两个任务Task1Task2,两个任务中都有一个死循环,在死循环中分别打印数组1和2。

图
如上图,串口中输入的数字有1也有2,如果是裸机程序,两个任务Task1FunctionTask2Function只能按顺序执行,此时只会执行1个死循环,另一个就无法执行。

但是通过现象发现两个死循环都在执行,因为有各自循环中的数字被打印了出来,此时就是前面例子中所说的,两个任务在穿插执行,每个任务执行固定的时间就换另一个,如此往复。

  • 创建任务和Linx系统中创建线程类似,有兴趣的小伙伴可以看一下本喵的文章线程概念 | 线程理解。

🐱RTOS中的数据类型和编程规范

数据类型:

图
如上图,每个移植的版本都含有自己的 portmacro.h 头文件,里面定义了 2 个非常重要的数据类型:

  • TickType_t:

FreeRTOS配置了一个周期性的时钟中断Tick Interrupt,每发生一次中断,中断次数就会累加一次,这个变量是tick_count,它的类型就是TickType_t,它可以是16位的变量类型也可以是32位的。

图
如上图,如果在FreeRTOSConfig.h中定义了configUSE_16_BIT_TICKS时,TickType_t就是uint16_t类型,否则就是uint32_t类型,对于32位的架构,最好要把TickType_t设置成uint32_t

  • BaseType_t:

这个该架构中最高效的数据类型,32位架构中,它就是uint32_t,16位架构中它就是uint16_t,8位架构中,它就是uint8_tBaseType_t通常用作简单的返回值类型,还有逻辑值。

变量名:

FreeRTOS中习惯在变量名前加前缀:

变量名前缀含义
cchar
sint16_t,short
lint32_t,long
xBaseType,其他非标准类型:结构体,task handle,queue handle等
uunsigned
p指针
ucuint8_t,unsigned char
pcchar指针

函数名:

函数名同样习惯加前缀,它前缀有两个含义:

函数名含义
vTaskPriority返回值类型:void,该函数在task.c中定义
xQueueReceive返回值类型:BaseType_t,在queue.c中定义
pvTimerGetTimerID返回值类型:void*, 在timer.c中定义

宏名:

宏的名是大写,前缀是小写,前缀表示在哪个头文件中定义:

宏的前缀含义
portMAX_DELAY在portable.h或portmacro.h中定义
taskENTER_CRITICAL在task.h中定义
pdTURE在projdefs.h中定义
configUSE_PREEMPTION在FreeRTOSConfig.h中定义
errQUEUE_FULL在projdefs.h中定义

通用宏定义:

pdTRUE1
pdFALSE0
pdPASS1
pdFAIL0

😼任务创建和删除

🐱动态创建

动态创建新任务使用的函数是xTaskCreate

图
如上图,该函数在task.c中定义,返回类型是BaseType_t,作用就是创建新任务。

图
如上图,该函数有6个参数,下面本喵来各自讲解一下:

  • TaskFunction_t pxTaskCode

tu
可以看到,TaskFunction_t是一个返回类型是void,参数类型是void*的函数指针,在projdefs.h中使用typedef重命名了。

在调用xTaskCreate创建新任务时,这个参数就是新任务要执行的函数:

图
这两个新任务执行的函数是由我们用户自己定义的,返回类型必须是void,参数类型必须是void*的,函数名无所谓。

  • const char * const pcName

这个参数是用来指定新任务的名字的,以后可以通过名字找到对应的任务,这个参数可以说是最不重要的了。

  • const configSTACK_DEPTH_TYPE usStackDepth

该参数是用来指定栈大小的,我们知道,每一个函数在调用的时候都会开辟一个栈用来存放返回地址以及临时变量。

在RTOS中,每一个任务也会开辟一个栈,用来存放该任务函数执行过程中的返回地址以及临时变量的。

每一个任务都拥有一个独立的栈,不同任务之间的栈不会互相影响,在创建任务之前,用户需要预估该任务需要多大的栈空间,从而在创建任务的时候指定栈的大小。

  • 单位是word,比如传入 100,表示栈大小为 100 word,也就是 400 字节。
  • void * const pvParameters

该参数就是新任务函数的那个形参,在使用xTaskCreate创建任务时传入该参数,新任务函数在执行时就会使用这个形参,这也是为什么我们在定义任务函数的时候,形参类型必须的void*类型的原因。

  • UBaseType_t uxPriority

tu
如上图,该参数的类型本质上就是一个无符号整形,用来表示任务的优先级,优先级范围:0~(configMAX_PRIORITIES – 1),数值越小优先级越低,如果传入的值过大,xTaskCreate会把它调整位 (configMAX_PRIORITIES – 1)

  • 优先级高的任务会优先得到CPU的执行。
  • TaskHandle_t * const pxCreatedTask

tu
该参数的本质是一个二级指针,TaskHandle_t是代表着tskTaskControlBlock*,所以TaskHandle_t*就是一个二级指针,最终指向的是tskTasKcontrolBlock,也就是任务控制块。

图
如上图所示,任务控制块中包含该任务的一些属性,如优先级,起始栈地址,任务名字啊等等信息(结构体中内容有删减)。

所以说,该参数用来保存xTaskCreate的输出结果:task handle,以后如果想操作这个任务,比如修改它的优先级,就需要这个handle

  • handle就是一个任务句柄,在创建任务的时候需要传入一个创建好的句柄地址。
  • 如果不想使用该handle,可以传入 NULL
  • 返回值
  • 成功:pdPASS
  • 失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足),本质是就是返回-1。

xTaskCreate的使用方法现在清楚了,那么动态创建的动态又体现在哪里呢?

图
如上图,通过宏开关configSUPPORT_DYNAMIC_ALLOCATTION来控制使用xTaksCreate来动态创建任务,在函数内部,会使用pvPortMalloc在堆区上动态开辟一块空间来存放任务控制块pxNewTCB,然后再使用pvPortMallocStack在堆区上开辟一块独立空间用来当作该任务的独立栈。

tu
如上图,pvPortMalloc等函数都在task.c中定义,其都是对malloc函数的封装。

  • 动态开辟的动态主要体现在,任务控制块TCB和任务的独立栈结构都存放在堆上,是动态分配的。

🐱静态创建

图
静态创建任务使用的是在task.c文件中定义的xTaskCreateStatic函数,该函数的用法和xTaskCreate的用法有点区别:

图
如上图,首先需要一个宏开关,该函数才会被编译,我们才能使用它静态创建任务。

图
如上图,在FreeRTOSConfig.h中定义宏开关,当然也可以在别的位置定义,但是习惯上在这个头文件中增加一些宏定义。此时该函数就会被编译了。

在调用该函数的时候,会传入七个参数,其中新任务执行的函数,任务名字,任务的栈深度,执行函数的参数,优先级等和动态创建任务的xTaskCreate一模一样。

但是这里需要用户给新任务指定栈,不再是动态分配了:

图
如上图,可以看到StackType_t类型的本质就是一个uint32_t类型,也就是四个字节大小。

所以在调用该函数之前需要提前创建一个数组StackType_t xTask3Stack[100]来作为静态任务的栈。

图
如上图,调用该函数同样需要传入一个任务句柄,但是此时的任务句柄类型变成了StaticTask_t类型,所以需要创建一个任务3的句柄StaticTask_t xTask3TCB


此时该函数所需要的就都齐全了,可以调用了,但是它仍然会报错无法运行,这是因为:

图
如上图,在任务调度函数执行时,如果支持了静态创建任务,就需要调用vApplicationGetIdleTaskMemory函数来获取空闲任务内存。

  • 空闲任务是FreeRTOS默认会执行的一个任务,它的优先级是0,主要用来回收用户任务等。

但是此时这个函数没有被定义,所以我们需要自己定义出该函数,并且需要提供空闲任务的栈和空闲任务的TCB句柄。

图
如上图所示,此时才可以创建静态任务并运行了。

图
如上图所示,此时3个任务就同时开始执行了,任务1和任务2是动态创建的任务,任务3是静态创建的任务。

  • 静态任务的静态就体现在,任务所需要的栈是由用户指定的,而不是自动分配的。

显而易见,创建静态任务要比动态任务繁琐的多,所以我们一般情况下创建的都是动态任务。

🐱任务删除

图
如上图,调用task.c文件中定义的vTaskDelete函数可以删除指定的任务。

图
如上图,该函数只有一个参数TaskHandle_t xTaskToDelete,用来指定要删除的任务,在该函数内部会先根据该任务的句柄去查找,找到该任务后删除。

  • 如果传入的是NULL则删除调用该函数的任务本身,也就是自杀。

图
如上图所示,增加三个任务执行标志位,执行哪个任务的时候哪个执行标志位就被置一。

  • 多个任务只是看起来是同时在执行,实际上CPU某一时刻只能执行一个任务。

任务1在执行一段实际后将任务2删除,然再过一段时间后将自己删除:

图
如上图,将三个任务的执行标志添加到逻辑分析仪中,显示高电平状态表明任务在执行。

可以看到,最开始时,三个任务轮替着在执行,当任务1将任务2删除以后,只剩下任务1和任务3在轮替执行,当任务1自杀以后,只剩下任务3在执行(一直都是高电平)。

🐱了解就绪队列

创建的多个任务都在哪?删除任务时又是在哪里删除的呢?

图

如上图所示,新创建的任务会被添加到就绪链表中,该链表中存在多个任务的TCB,调度器每隔一定的时间就从该链表中取出一个任务让CPU去执行,上一个被切换下来的任务会放在链表的末尾。

  • 每创建一个新任务,该任务就会插入到就绪链表的末尾。
  • 调度器让CPU先执行链表末尾的任务,然后链表头开始执行其他任务。

包括删除一个任务的时候,也是根据任务的句柄从该队列中查找,如果存在则将对应TCB从链表中删除,这样一来CPU就不再执行该任务了。

关于队列的问题,本喵今天不详细讲解,只是让大家知道有这么个东西存在。

😼总结

这篇文章中,最重要的是要掌握如何创建新的任务,动态创建和静态创建的区别,以及如何删除一个任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python技能树练习——统计词频

请编写一段单词统计Python代码,统计下面两个Python三引号字符串里英文单词的词频。要求: 单词请忽略大小写 使用数组splits [\n, , -, :, /, *, _, (, ), ", ”, “,],[]来切割单词 输出词频最高的5个单词和词频信息 * Python 代码风格指南,* [g…

java springboot通过EnableConfigurationProperties全局声明bean并处理装配

Spring Boot中 我们想条件装配一个类 首先 我们要声明他的bean 而 EnableConfigurationProperties 可以直接将 要全局声明的类绑定在 属性类中 例如 我们随便创建一个类 就叫 textData 吧 参考代码如下 package com.example.webdom.domain;import org.springframework.boot.co…

【数据库系统概论】第六章关系数据理论

一句话,研究关系数据库理论就是为了设计出合适的关系模式,也即合适、高效的表 6.1为什么研究关系数据理论 一:概念回顾:关系模式 关系模式:关系模式就是对关系的描述,可以表示为 R ( U , D , D O M , F…

C++指针解读(4)-- 指针和数组(一维数组)

1、数组及数组的访问 1.1 数组的存储方式 在内存中,数组是一块连续的区域。数组的存储结构有如下特点: (1)数组中的元素是同质的数据; (2)索引从0开始; (3&#xff…

数据库安全-H2 databaseElasticsearchCouchDBInfluxdb漏洞复现

目录 数据库安全-H2 database&Elasticsearch&CouchDB&Influxdb 复现influxdb-未授权访问-jwt 验证H2database-未授权访问-配置不当CouchDB-权限绕过配合 RCE-漏洞CouchDB 垂直权限绕过Couchdb 任意命令执行 RCE ElasticSearch-文件写入&RCE-漏洞Elasticsearch写…

Linux Kernel 4.13 RC6发布:正式版9月3日发布

美国当地时间上周末,大神Linus Torvalds发布了Linux Kernel 4.13内核的又一候选版本。上周发布的RC5版本更新幅度也要比上上周的RC4要小,Linus Torvalds表示本周发布的RC6版本属于常规更新,在过去一周的开发过程中并没有出现任何意外。RC6版本…

ESP32网络开发实例-从SD卡加载Web页面文件

从SD卡加载Web页面文件 文章目录 从SD卡加载Web页面文件1、应用介绍2、软件准备3、硬件准备4、Web页面代码实现5、Web服务器代码实现在文中,将展示如何构建一个 Web 服务器,为存储在SD卡中的 HTML 和 CSS 文件提供服务。 我们不必将 HTML 和 CSS 文本硬编码入代码中,而是创建…

如何提升网站排名和用户体验:优化网站速度

网站的排名和用户满意度直接受到站点内容的加载速度影响深远。通过精心的网站优化,您不仅可以提高排名,还可以提供更出色的用户体验,尽管用户可能不会察觉到您的网站加载得更快,但这是一个非常有意义的改进。在这篇文章中&#xf…

计算机毕业设计 基于Web铁路订票管理系统 火车订票管理系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

Git纯操作版 项目添加和提交、SSH keys添加、远程仓库控制、冲突解决、IDEA连接使用

Git 文章目录 Git项目简单克隆通用操作添加和提交回滚分支变基分支优选 远程项目推送认证抓取、拉取和冲突解决 IEDA类软件连接 最近学原理学的快头秃了,特此想出点不讲原理的纯操作版,不过还是放个图吧 项目简单克隆 git在本人日常中最重要的功能还是…

ExposureDiffusion: Learning to Expose for Low-light Image Enhancement论文阅读笔记

南洋理工大学、鹏城实验室、香港理工大学在ICCV2023发表的暗图增强论文。用diffusion模型来进行raw图像暗图增强,同时提出了一个自适应的残差层用来对具有不同信噪比的不同区域采取不同的去噪策略。 方法的框图如下所示: 一张raw图片可以由信号和噪声…

scratch绘制多彩五角星 2023年9月中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析

目录 scratch绘制多彩五角星 一、题目要求 1、准备工作 2、功能实现 二、案例分析

Cadence 设计实践笔记-小哥allegro 2层板笔记

本章节主要跟着B站PCB入门首选视频-小哥Cadence Allegro 2层板视频,结合自己的实践一步步完成一个完整的PCB板的设计。 视频链接地址: PCB入门首选视频-小哥Cadence Allegro 2层板视频_哔哩哔哩_bilibili 规范建立文件夹 建立八个文件夹 DATASHEET 主要存放设计项目…

强化科技创新“辐射力”,中国移动的数智化大棋局

作者 | 曾响铃 文 | 响铃说 丝滑流畅的5G连接、每时每刻的数字生活服务、无处不在的智能终端、拟人交流的AI助手、梦幻般的XR虚拟现实、直接感受的裸眼3D…… 不知不觉,那个科幻片中的世界,越来越近。 数智化新世界的“气氛”,由一个个具…

window系统进行goolge代理配置(falcon proxy+burpsuite)

linux系统自带burpsuite抓包软件,只要火狐下个代理扩展就可以抓包了,想着每次抓包还得去虚拟机抓就有点小烦躁,所以想着给自己本机也弄个burpsuite,有想法就开整! 一、goole代理扩展插件falcon proxy 1、由于goole应…

项目管理工具的功能与帮助一览

项目管理的概念并不新鲜,但是伴随着技术解决方案的出现,项目管理工具帮助企业建立规范科学的管理流程,为企业的管理工作提供助力。 Zoho Projects 是一款适合全行业的标准化项目管理工具,它提供了重要的功能,如任务列…

海康威视、大华、宇视rtsp实时读取网络摄像头

目录 1 RTSP介绍 1.海康 1.2 大华 1.3 宇视 2.实时读取 2.1 cv2.VideoCapture打开视频流 2.2 ffmpeg打开视频流 2.3 c 1 RTSP介绍 RTSP(Real-Time Streaming Protocol)是一种用于实时流媒体传输的网络协议。它被设计用于在客户端和服务器之间传输…

双指针--浅试

在做题中发现一件很奇怪的事情: 我看到了题目描述,心中有了一个解题的思路,然后尝试解题,看题解的时候发现“双指针法”我很熟悉但是又感觉不太懂。 把题解代码看了以后却与我的代码大差不差,才恍然大悟,原…

非类型模板参数+模板的特化

目录 一、非类型模板参数 二、模板的特化 (一)函数模板特化 (二)类模板举例 1. 全特化 2. 偏特化 一、非类型模板参数 模板参数分类:类型形参与非类型形参。类型形参即:出现在模板参数列表中&#x…

String、StringBuilder、StringBuffer区别

String、StringBuilder、StringBuffer区别 面试官:请你谈谈String、StringBuilder、StringBuffer区别 作为经典Java八股,是面试必考的热门点。 下面让我们一起来看一下他们的区别吧! 主要是测试他们的效率和应用场景,具体语法不在…