| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> FreeRTOS操作系统理论知识笔记(全网最详细的亲笔手写的理论知识!我敢保证,你一定会懂!)(持续更新) -> 正文阅读 |
|
[嵌入式]FreeRTOS操作系统理论知识笔记(全网最详细的亲笔手写的理论知识!我敢保证,你一定会懂!)(持续更新) |
目录 -------->分支问题1:运行位置很好理解,就是被打断的时候函数执行的位置(当前指令PC的值),那什么是运行环境呢? -------->分支问题2:栈在FreeRTOS里怎么表示呢? -------->分支问题3:任务在RTOS怎么去创建一个任务? -------->分支问题4:在FreeRTOS怎么去表示一个任务? -------->分支问题2:如果所有任务优先级都是0会发生什么? -------->分支问题3:什么情况下一个任务还没完成一个Tick时间就放弃运行了? 1)为什么引入队列?--->互斥机制、休眠与唤醒、提高CPU利用率 -------->分支问题2:为什么队列的引入可以提高CPU的利用率? -------->分支问题3:怎么实现互斥?环形缓冲区和链表有什么作用?怎么休眠一个任务,又怎么知道唤醒哪个任务? -------->分支问题5:如果一直没有任务写/读队列,那岂不是就会有任务一直等待? 零、ARM架构与C语言汇编指令1)ARM架构????????STM32的CPU负责计算和执行指令,RAM负责数据的存储,Flash负责代码的存储。CPU有很多的寄存器,其中有典型的R0到R15寄存器,R13为栈SP,R14为返回地址LR即下一条指令的位置,R15为当前指令地址PC 2)汇编指令? ? ? ? 1.汇编读(Load,即LDR):CPU读到Flash当中的“读a变量的指令操作”,在RAM中找到a变量的地址,读取a变量的值保存在CPU的一个寄存器当中比如R0(具体读几个字节跟汇编读指令有关,比如LDR就是读4个字节,LDRH就是读2个字节) ? ? ? ? 2.汇编写(Store,即STR):CPU读到Flash当中的“写a变量的指令操作”,在RAM中找到a变量的地址,先读a变量的值保存在CPU的一个寄存器当中,CPU通过计算得到a变量新的值,然后再把这个值写到RAM中的a变量 ? ? ? ? 3.汇编计算(常见的加法减法,高级的还有乘除法) ? ? ? ? 4.汇编Push(本质是写内存):比如CPU读到Flash当中的Push{R3,LR},指的是CPU把自己的R3和LR寄存器的值写到CPU的R13寄存器(栈)当前所记录的RAM的位置(注意到这里要写两个寄存器的值到RAM中,意味着写完一个值,R13寄存器所记录的RAM的位置会发生偏移确保写下一个值) ? ? ? ? 5.汇编Pop(本质是读内存):比如CPU读到Flash当中的Pop{R3,PC},指的是把CPU的R13寄存器(栈)当前所记录的RAM的那个位置的值”依次“(说明有偏移)赋值给R3和LR 一、什么是FreeRTOS,为什么要学它?1)第一个问题的回答FreeRTOS是基于单片机的一种多线程多任务管理的操作系统,即(Real Time Operating System)实时操作系统 ????????比如:一个人不仅要给孩子喂饭还要回同事信息 ????????裸机做法是:喂完一口饭然后再去回同事一条信息 ????????RTOS做法是:记喂一口饭为A任务并拆分成(假设是4步)4个步骤,记回一个信息为B任务并拆分成(假设是3步)3个步骤,即做A任务的步骤1然后做B任务的步骤1,再做A任务的步骤2然后做B任务的步骤2如此循环 2)第二个问题的回答????????一是项目需要,随着产品 要实现的功能越来越多,单纯的裸机系统已经不能够完美地解决问题,反而会使编程变得 更加复杂,如果想降低编程的难度,我们可以考虑引入 RTOS 实现多任务管理,这是使用 RTOS 的最大优势。 ? ? ? 二是学习的需要,必须学习更高级的东西,实现更好的职业规划,为 将来走向人生巅峰迎娶白富美做准备,而不是一味的在裸机编程上面死磕。作为一个合格 的嵌入式软件工程师,学习是永远不能停歇的事,时刻都得为将来准备。书到用时方恨少, 我希望机会来临时你不要有这种感觉。 二、RTOS的任务是什么?RTOS怎么实现多任务的管理?1)第一个问题的回答????????任务是一个个能够运行的函数和它们的栈(栈保存运行位置和运行环境),这个栈是CPU的R13寄存器 -------->分支问题1:运行位置很好理解,就是被打断的时候函数执行的位置(当前指令PC的值),那什么是运行环境呢?------->在“零部分,ARM架构和C语言汇编指令”中,已经知道了C语言底层实现的过程了。任务其实不仅仅是一个函数,因为随时会发生任务的切换,所以在这里要保存各个任务他们的局部变量,否则当执行完别的函数回到刚才被打断的函数时,局部变量就会丢失,而局部变量的保存又是通过汇编指令保存到CPU的典型寄存器(R0到R15)当中,所以运行环境相当于这16个寄存器的值(当然还有别的寄存器),并且要”依次“保存在CPU的R13寄存器(栈)所指向的RAM的位置 故:任务的切换需要保存现场,保存现场又是保存在栈当中的,所以任务=运行的函数+各自的栈 -------->分支问题2:栈在FreeRTOS里怎么表示呢?--------->FreeRTOS会事先定义一个巨大的数组,大概有17K的大小,然后以后需要栈就在这个数组里进行划分即可,或者是用malloc进行分配。所以有静态创建(xTaskCreateStatic函数)和动态创建(xTaskCreate函数) (下面是这个巨大数组的定义)
-------->分支问题3:任务在RTOS怎么去创建一个任务?1)--------->●首先明确创建一个任务这件事是一个函数,需要传参 ? ? ? ? ? ? ????●第一个参数:传入一个函数(可能顺带还会传入这个函数的参数),所以要有一个指向函数的指针,即函数指针 ? ? ? ? ? ? ? ? ●第二个参数:其次是任务的名称 ? ? ? ? ? ? ? ? ●第三个参数:栈的深度(字节为单位),用于malloc动态分配一段内存或者是在巨大的数组中进行划分,用来保存这个任务的运行环境,取决于局部变量所占的空间,取决于函数调用的深度(有没有LR的值,如果一个函数它里面不会再去调用别的函数,那么LR的值不会改变,否则就要再额外的把LR的值保存在栈中) ? ? ? ? ? ? ? ? ●第四个参数:第一个参数传入的函数它自己需要的参数,会保存在CPU的R0寄存器 ? ? ? ? ? ? ? ? ●第五个参数:这个任务的优先级 ? ? ? ? ? ? ? ? ●第六个参数:传出一个句柄,即指向TCB结构体(任务控制块)的指针 (下面是创建函数传入的参数说明)
-------->分支问题4:在FreeRTOS怎么去表示一个任务?2)---------->●首先明确创建一个任务这件事需要传参,传入的参数一个一个赋值给TCB这个结构体 ? ? ? ? ? ? ? ? ?●创建任务的第一个参数:函数指针也就是函数的地址,在完成了别的任务后,要想回到刚才被打断的任务,只要知道被打断的任务的地址就可以回来了,所以把这个函数的地址保存在PC寄存器,也保存在栈里面了,这样要返回到这个被打断的任务,就可以去它自己的栈里找PC寄存器的值找到它被打断的位置了 ? ? ? ? ? ? ? ? ?●创建任务的第二个参数:任务的名称保存在TCB的第六个成员 ? ? ? ? ? ? ? ? ?●创建任务的第三个参数:由于需要保存现场要有一个栈,要么是动态分配内存要么是在巨大的数组中划分一块内存,最终每个任务有自己的栈,这个栈的地址保存在TCB的第五个成员,这样如果要恢复一个任务的现场,就可以找到任务它自己的栈在哪里了 ?????????????????●创建任务的第四个参数:传入的函数它自己的参数已经事先保存在R0寄存器,保存在栈里面了,这样返回被打断的任务就可以找到R0的值传递给创建任务的第一个参数--函数指针所指向的函数它自己的参数 ?????????????????●创建任务的第五个参数:任务优先级保存在TCB的第四个成员 ? ? ? ? ? ? ? ? ?●TCB的第一个成员是栈的另一个端头的位置 ? ? ? ? ? ? ? ? ?●TCB的第二个成员????? ? ? ? ? ? ? ? ? ?●TCB的第三个成员????? 故:在TCB结构体里,没有体现到创建任务传入的第一个参数和第四个参数 (下面是这个TCB结构体的说明)
2)第二个问题的回答? ? ? ? 1.出现任务的切换一定会保存现场(已在上面详细说明) ? ? ? ? 2.任务的调度机制----即暂停当前的任务,找到下一个要完成的任务并且去执行它 -------->分支问题1:具体是怎么实现任务调度?--------->●任务有各自优先级(数字越大优先级越高),对于高优先级的任务可以抢占低优先级的任务,并且如果高优先级的任务不停止,低优先级的任务就无法进行;对于同等的优先级的多个任务,轮流执行,也就是时间片轮转 --------->●任务有各自的运行状态,有正在运行状态(running),有准备好了的状态(ready),有在等待别人被阻塞的状态(blocked),有暂停状态(stop) --------->●处理方法:找到最高优先级的running态或者ready态的任务,如果大家都是同等优先级,就“依次排队”,轮流执行,排在前面的先运行一个Tick,然后自动的排到队伍的尾部,一直循环 --------->●链表的引入(超级重要!) 1)RTOS中有如下3种链表 2)分析一下任务调度的过程
(下面是休眠函数操作)
--------●Tick中断来解决“谁去调度任务”这个问题,在中断函数中进行相应的任务调度工作,它做的事情:取出下一个要完成的任务,保存当前任务,恢复上一次任务 -------->分支问题2:如果所有任务优先级都是0会发生什么?--------●空闲任务(IdleTack)指优先级为0并且是就绪态的任务,保存在pxReadyTasksLists[0]中,这个任务是在启动调度器后创建的(可以看下面的代码),作用是:如果有一些任务“自杀了”(差不多那个意思哈哈)空闲任务就会去释放这个“自杀”任务的栈
--------●如果依次创建A,B,C三个任务,优先级为0并且他们都是就绪态任务,那此时他们在pxReadyTasksLists[0]中,且顺序是A--B--C(右边的先运行),由于调用了“启动调度器”这个函数,它还会创建一个空闲任务Idle,这个时候在pxReadyTasksLists[0]中,且顺序是A--B--C--Idle,但是右边的先运行,所以先运行完Idle后(并且在这种特例中空闲函数一直最先被运行),Idle移到A后面,轮到C运行,依次类推 ---------●值得注意的是:在上述特例中,且在一定的条件下,空闲任务会有礼让操作!也就是主动放弃一次CPU执行的机会,让别的任务运行,然后终于轮到自己执行的时候,再从被打断的位置进行 ---------●根据代码框架有下面这种情况(在上面的特例中): 1)设置了不抢占(有没有设置可礼让结果都会礼让),那就不会有轮流执行(下面代码有),那就是空闲任务礼让后轮到A任务并且一直都是A任务因为不会开启时间片轮转 ,不会因为Tick中断而切换任务,但如果要让别的任务运行,就需要A任务主动放弃,然后轮到B任务并且一直是B任务一直运行? ? ?? 2)设置了可抢占可礼让,就是上面说的过程 (下面是空闲函数的代码,已经把核心部分找出来了,注释和一些其他的先删除了)
(下面是如果设置不抢占,会导致无法开启时间片轮转)
-------->分支问题3:什么情况下一个任务还没完成一个Tick时间就放弃运行了?----------●主动放弃:一个是休眠函数;一个是读队列但是目前没数据(后面会介绍) ----------●被动放弃:外设(比如GPIO)唤醒了处于延时链表中的一个高优先级的任务,此时高优先级的任务会立刻抢占 三、FreeRTOS中的调度链表在”二、RTOS的任务是什么?RTOS怎么实现多任务的管理?“中的”2)第二个问题的回答”中的“分支问题1:具体是怎么实现任务调度?”中的“2)分析一下任务调度的过程”已经非常详细的介绍了调度链表,如果想看视频的话,可以看韦东山老师的讲解:09_回顾调度链表_哔哩哔哩_bilibili 四、FreeRTOS中的队列(关中断,环形缓冲区,链表)1)为什么引入队列?--->互斥机制、休眠与唤醒、提高CPU利用率---------●如果有一种情景:A任务和B任务都要对同一个全局变量(假设为a)进行操作,A任务是a++,B任务是a--。对于裸机STM32来说没有任何问题,因为STM32在主函数中假设先完成了A任务,那么a++,然后轮到B任务,a--,一直循环a的值也会按照我们想要的方式变化;对于RTOS来说,由于a++和a--是被拆分成3条汇编指令(1--从内存中读a的值保存在CPU的R0(假设是R0),2--计算a的新值,3--把a的新值写在内存中变量a的位置),假设A任务执行了第1条后,发生任务调度需要保存A任务现场,然后被切换到B任务,然后B任务完成了a--的操作,被切换到A任务,这个时候A任务去执行第2条和第3条。但是我们想要实现的效果是A任务执行完了a++,才轮到B任务去执行a--的操作,所以这就出现了多系统任务中的互斥机制引入! -------->分支问题1:什么是互斥机制?----------●出现上面情景的问题是因为有多个任务会同时访问同一个变量(假设是a),所以需要有这种机制:各个任务访问a变量时,如果此时是A任务去访问,那就等A任务完成有关对a变量的访问操作执行完之后再进行任务的切换,如果此时是B任务去访问,就等B任务完成对a变量的访问操作再进行任务的切换,以此类推---------->这就是互斥机制(互相排斥互不干扰的访问同个变量) -------->分支问题2:为什么队列的引入可以提高CPU的利用率?首先引入一个情景:假设只有两个任务A,B,有一个全局变量Flag一开始为0,A任务在一定条件下会让Flag=1,B任务有一个while循环时刻判断Flag是否为1,如果是逻辑程序跑了完整的100w次,相当于B任务中这个判断会进行100w次,但是对于RTOS程序,可以这么设计:A任务在一定的条件下去写队列,B任务去读队列。 读队列的过程(写过程也类似): 1)如果B任务第一次读队列就有数据(相当于一开始A任务满足条件了让Flag=1),那B任务就不会休眠,继续执行 2)如果B任务第一次读队列但是里面没有数据(当于A任务没有执行Flag=1的操作),要么B任务不选择等待然后返回错误,要么就让B任务休眠,进入休眠有两个步骤:第1个是从就绪态链表移到延时链表,第2个是放在队列当中的一个等待读链表(后面会介绍的)。如果一直没数据,B任务就一直休眠,CPU就会一直去处理A任务。如果某个时刻A写了数据了,也就是队列有数据(相当于Flag=1了),A任务会去唤醒B任务,唤醒有两个步骤:第1个是从延时链表移到它对应的就绪态链表中,第2个是从等待读链表当中进行移除,然后B任务就可以读数据继续运行这样就可以提高CPU利用率,让其他任务去执行! 3)还有一种情况是:如果有别的任务(假设为C任务)等待A任务写完队列,也就是C任务会被放到等待写链表中,并且进行休眠,如果A任务写完数据了,就会去唤醒C任务 -------->分支问题3:怎么实现互斥?环形缓冲区和链表有什么作用?怎么休眠一个任务,又怎么知道唤醒哪个任务?----------●队列的引入:对于同个变量有写变量和读变量两种操作,队列的一头就是一些任务对变量a的(沿用上面的情景)写操作,队列的另一头就是一些任务对变量a的读操作 ----------●写/读队列函数: (1)关中断-----比如此刻是A任务写数据,关了中断后(包括定时器中断),A任务就可以不用担心在写完数据之前会被打断-------->实现了互斥 (2)写数据到队列中:环形缓冲区 ? ? ? ? 这个环形缓冲区其实是一个数组,操作这个数组是让它有点像是环形的。在这个缓冲区中会有W(这次要写的位置的数组下标)和R(这次要读的位置的数组下标),每次写数据,就会把那个要写的值写在数组中,并且W偏移,到了末尾时,在末尾写完了数据,W就回到起点(也就是有取余操作,读数据也是一样的)--------->实现了对数据的保存 (3)链表的操作:在队列里的链表会单独的保存互斥机制中需要休眠的任务,也就是休眠一个任务不仅仅是把它从就绪链表转移到延时链表,还要把它放在队列里面的一个链表当中!这样就可以知道怎么休眠一个任务;并且当A任务实现了Flag=1,那就可以去链表里面找到B任务并且唤醒它,也就可以实现要去唤醒谁了!--------->实现了“唤醒谁,怎么休眠“ ????????队列里面的链表有两个:一个是专门保存写队列失败的任务的链表;一个是专门保存读队列失败的任务的链表 (4)开中断-----因为A任务已经写完数据了,所以完成了互斥机制,不用担心数据紊乱,以后别的任务也要参与写数据,那就总是按照这样的操作依次进行 -------->分支问题4:在RTOS中队列怎么表示?----------●应该明确队列里面有一个队列结构体,后面紧跟着就是环形缓冲区 ----------●对于环形缓冲区的读和写操作类似于队列的操作 ----------●以下代码会涉及到环形缓冲区的内存大小,读写指针;两个链表 (下面是队列结构体和环形缓冲区读指针)
(下面是创建队列的函数)
-------->分支问题5:如果一直没有任务写/读队列,那岂不是就会有任务一直等待?---------●可以在等待的任务中限定它等待的时间,也就是超时唤醒 ---------●也就是:一个因为没有读到或者写队列成功过的任务被休眠了,它被唤醒的原因有两个,一个是被别的任务唤醒了,一个是过了等待的时间了被唤醒 |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/28 18:16:52- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |