韦东山FreeRTOS学习

如果刚好有大佬看到发现笔记中有什么写错了的,欢迎大佬指点,我十分乐意受到大佬的指点哈哈哈,顺便我还想问一下csdn的编辑器有没有保存的快捷键啊,CTRL+S好像没有用啊。

课程中学习写个RTOS不是为了自己写RTOS,而是为了更好的理解

寄存器

R13:SP(stack point)
R14:LR(link registor)常用于记录断点,比如调用子程序时先保存当前pc减4的值,还有程序异常时也是
PSR:program state rigestor
PC:程序计数器

基本汇编指令

  • STR:STR 源寄存器,目的地址
    STR R1,[SP,#4] R1的值存到SP偏移4的地址中
  • LDR:LDR 目的寄存器,源地址
    LDR R0,[SP,#4] SP偏移4的地址对应的值存到R0中
  • ADD:add 目的寄存器,?,?
    ADD R0,R0,#3,R0 = R0 + 3,‘?’可以是寄存器、地址、立即数。
  • SUB:同ADD
  • MOVS:MOVS 源寄存器,立即数
  • B:(branch)跳转指令
  • BL:(branch and link)BL 函数名;返回值
    BL add_val;0x800318 将0x800318存进LR作为函数返回地址,并执行add_val函数
  • CMP:CMP 寄存器,寄存器
    结果保存在PSR中
  • PUSH:本质是STR(store,写)
  • PUB:本质是LDR(load r,读)

函数调用


一共有17个寄存器。

传参

4个以内用R0-R3传递,否则就要压栈实现。函数返回值返回到R0中

寄存器保护

  • R0-R3:随意使用,无需保护
  • R4-R11:可用,使用前需先保存,使用后再恢复。(即进入函数前寄存器的值要入栈,退出函数前出栈)

函数中断处理

流程:保存现场——>调用处理函数——>恢复现场

保存现场

硬件实现保存R0-R3、LR、R12和返回地址。

调用处理函数

硬件实现调用该处理函数(函数编写时已经具备不会破坏R4-R11原来的内容的特性)。

  • 当调用函数时:
    对应的汇编第一句就是BL指令,保存返回地址到LR再进入调用函数

  • 当发生中断时:
    硬件保存的那几个寄存器和返回地址,这时候的LR只是作为一个普通的寄存器保存,中断结束后的返回地址是那个硬件保存的保存地址。(个人猜想:就是如果发生中断时是在一个子函数中,那么LR里面保存的是那个子函数结束之后的返回地址,如果是在主函数中LR应该是没有存地址,就是说中断的返回地址不存在LR中)

恢复现场

硬件恢复R0-R3、LR、R12和返回地址,软件通过栈恢复其他的那些寄存器。

RTOS核心:保存和恢复现场

现场的本质:就是所有寄存器
假设两个程序交替执行,每次只执行1ms

这就意味着程序A/B可能会在任何任何一处发生中断。
这就需要每次中断时保存现场,把寄存器保存到栈里,返回地址保存到栈里,而每个函数进入时本身就分配有一个自己的栈,栈里有他的局部变量,这样一来只要不破坏这个栈,所有的东西就都保存起来了,当下次切换回这个函数时就恢复现场并从返回地址开始就可以了。

假设任务A中有多个子程序a1、a2、a3,那么A的栈里分配出a1、a2、a3的栈,如果他们套娃了,a1中调用到a2,a2中调用到a3,就会如下图所示,这些函数的现场都可以保存在他们的栈里(子函数退出后回到上一个函数的地址应该时在LR中),同时sp跳转到函数起始处。而当从任务A跳到任务B的时候,A的栈里也会有一块内存保存任务A的现场(因为时中断,重新进入任务A的地址应该是保存现场时存下的返回地址),然后跳到任务B去。

跳转任务B时首先第一件事就是要恢复B的现场,然后从B的存好的返回地址处开始继续运行。

创建任务

创建任务首先要有任务中的函数以及函数所需要的一些参数(param),但是传入一个函数要怎么实现呢,从之前的学习中可以知道函数就是一段代码嘛,而这段代码又保存在一块内存中,所以本质上运行那个函数就是从那个函数的起始地址开始走,所以传个地址就好了,也就是说我们做一个指向这个函数的函数指针传进去就可以了。因为要有函数指针,所以可以先typedef一个定义函数指针的类型,为后续定义函数指针提供便利。
typedef void(*task_function)(void *param)
函数定义:

其次,一个任务还要有自己的栈,其实就是搞一块可以用的内存就行了,char一个数组都行。但是,栈在这块内存如果没有4字节对齐后续的运行就会出问题,可以通过这个指令(不知道是不是叫做指令?)实现字节对齐:

创建任务的本质其实就是在还没有中断发生前模仿中断发生后的现场人为的伪造一个让任务开始的现场。
栈里面的现场保存的排列遵循两个原则:先放硬件保存的,再放软件保存的;高地址对应高标号寄存器,所以,上半部分如果在中断中是硬件实现的,我们的伪造需要和硬件的保存一致,不然之后硬件恢复就会错乱,而下半部分因为汇编中的连续读写多个寄存器的指令(STM/LDM)有个对应关系就是高地址存高标号寄存器,所以为了更方便使用汇编编程软件保存的寄存器就按照高地址存高标号寄存器的原则保存。(大概是因为读写多个寄存器是从低标号往高标号,而SP从下往上走所以应该高标号放在上面吧)

设置好返回地址(设为函数起始地址)和参数(R0-R3)就可以了,这样当第一次中断发生时任务按照切换流程来走就可以从头开始了。

伪造现场

伪造现场本质就是写任务对应的那个栈的那块内存,因为要对栈操作,我们需要知道栈的大小从而得到栈顶,所以创建任务函数需要一个栈大小的参数。所以创建任务的函数应该改为:

typedef void (*task_function)(void *param);//定义了一种task_function的类型,并定义这种类型为指向某种函数的指针,这种函数以(void *param)为参数
void create_task(task_function,void *param,char *stack,stack_len);

如图:


获得栈顶指针后将指针下移16(减16),开始伪造现场,见上面的栈图可知,top此时指的是存R4的地方,以此对应修改各个寄存器的保存值,这里可以将top当数组使用,比如:

int *top = stack_a + stack_len;
top[8] = 参数1;//R0
top[9] = 参数2;//R1
...
top[14] = 函数首地址(函数指针);//返回地址

其实这里有点懵,数组从小到大是从下往上的,但是top不是在最上面吗?还是说这是大小端存储的问题。

除了这些寄存器,还有PSR寄存器需要配置,其中的指令位需要设置选择thumb指令(16位)或者arm指令(32位),M3内核都是用的thumb指令,寄存器结构如图:

记录栈的位置

用全局变量存储下栈的位置(应该是那堆寄存器后一个位置),还没太搞清楚。

栈大小的设置

根据局部变量的大小以及函数调用的深度,函数越多那么函数栈就越多,任务栈就得越大。

启动任务

任务的启动和切换用系统滴答定时器中断实现时间片轮询。
启动的本质就是把任务对应的栈里的寄存器保存值恢复到寄存器。

启动流程:

  • 创建一个变量作为标志位,判断任务是否已经创建。
  • 创建一个变量记录当前当前是在哪一个任务(类比状态机),当等于-1时就启动第一个任务,调用任务中的函数。
  • 确定要启动的任务后,通过汇编指令从任务的栈里读出保存的R4-R11的值写进寄存器里,并触发硬件中断恢复(R0-R3这类硬件保存的寄存器的值恢复),那么硬件中断恢复要怎么实现呢?(用 BX LR)

如何触发硬件中断恢复(恢复中断时硬件保存的寄存器)

这里果然就印证了之前听课的猜想。

硬件保存了那堆寄存器后,这个时候LR会被置为一个特殊值,而不是普通的调用函数那样LR存入一个返回地址(像上图右边就是普通调用),然后就开始调用c函数处理,当c函数处理完了跳回LR所指的地址,这个特殊值就会把pc跳到恢复那堆硬件保存的寄存器的代码段,当这堆寄存器恢复好后才会跳到返回地址那里。

所以要实现汇编函数需要的两个功能就要利用到这个特殊值,把LR置为这个特殊值就可以实现硬件中断恢复。

整合

切换任务

就是让任务标志位变为下一个任务,并获取任务的栈及其大小,然后等待时钟中断时保存当前任务现场,并将任务状态标志位和栈作为参数执行开始任务函数即可。

保存老任务的现场 切换新任务

RTOS本质

多个栈的切换,配合各种状态标志位实现休眠、切换任务、优先级等。

互斥锁

如果两个任务中都要对一个全局变量操作,那么有可能操作这个全局变量的过程中刚好到达定时中断切换任务,那么就会导致对这个全局变量的操作失败,这个时候就要在对全局变量操作的过程中关掉定时中断,等操作完了再打开。

上图的例子,A、B任务都会对a++,理论上A、B任务各执行一遍后a的值应该加了2,但是,如果A任务的a++执行了第一步后就被中断了,然后B任务执行完一次才跳回到A任务继续,那么当A、B任务都执行了一遍后a只会加1。

如何提前切换任务

真正的RTOS系统其实并非通过tick中断实现转换的,而是通过tick触发PSV中断,如果需要提前切换任务只有人为触发PSV中断即可。

多核架构

中断

中断时不会针对每一个核,而是中断先进入中断控制器,然后再根据控制器决定中断进入哪一个核。

自旋锁

当一个核进入一个任务后,就锁上了,直到这个核退出,锁上的时候别的核要进来就得在那个锁里轮询,直到锁开。
如下图:第一条红线就是锁,CPU1进去后CPU2只能在外面,直到CPU1执行完这段代码(这让我联想到了生物的受精过程的那个透明带还是啥来着)。

最后

视频信息量太大了,后半段疯狂汇编,很多东西还不是很通透,无法输出,以后想到再继续修改吧。

【学习笔记】韦东山freertos直播学习笔记相关推荐

  1. 韦东山 数码相框 项目学习(四)简易的TXT文档显示器(电纸书)

    韦东山 数码相框 项目学习(四)简易的TXT文档显示器(电纸书) 有了前面关于LCD.freetype的学习,已经可以开始TXT文档显示器的编写了.整个实现过程并不复杂,必须要弄清楚的是freetyp ...

  2. 韦东山 数码相框 项目学习 (七)awtk的移植

    韦东山 数码相框 项目学习 (七)awtk的移植到百问网STM32MP157 awtk是国内一个优秀的开源GUI引擎,gitee网址在这里 由于LVGL无法直接显示JPG和PNG等格式的图片,折腾起来 ...

  3. 韦东山 数码相框 项目学习(五)libjpeg-turbo的移植

    韦东山 数码相框 项目学习(五)libjpeg-turbo的移植 效果图 能够在100ask STM32MP157上显示一张JPG图片 一.下载源码 首先去libjpeg-turbo官网下载源码,这里 ...

  4. 韦东山第一期学习笔记——重定位

    重定位 说明 必须知道的几个概念 什么是代码重定位? 什么是位置无关码 什么是运行地址 为什么要代码重定位? nand flash启动的情况 nor flash启动的情况 两种方式的重定位 代码重定位 ...

  5. 开发板、Windows、Ubuntu三者互联——韦东山嵌入式Linux学习笔记08

    实验环境: 1. Windows7 2. VMware12.0+Ubuntu 3. JZ2440 按理说,在物理机上安装Ubuntu操作系统是比较推荐的方法.不过,因为windows上有很多好用的工具 ...

  6. win7下不能使用dnw烧写的解决办法——韦东山嵌入式Linux学习笔记05

    本文实验环境: 1. windows 7(64bit) 2. JZ2440 一. 问题的提出--没有驱动 假设板子的 Nor Flash 上已经烧好了 u-boot,那么如何通过 u-boot 的US ...

  7. 韦东山FreeRtos的内部机制,截图+学习笔记【2000字】【原创】

    文章目录

  8. 烧写文件系统——韦东山嵌入式Linux学习笔记11

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 使用 u-boot 烧写文件系统,一般有两种方法. 1. 通过USB下载功能 2. 通过TFTP功能 通过USB下载 ...

  9. 使用 u-boot 烧写内核——韦东山嵌入式Linux学习笔记10

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 使用 u-boot 烧写内核,一般有两种方法. 1. 通过USB下载功能 2. 通过TFTP功能 通过USB下载功能 ...

最新文章

  1. 如何在FreeBSD中安装Nginx,MySQL,PHP(FEMP)
  2. 排序算法笔记:堆排序 HeapSort in java
  3. 【NLP傻瓜式教程】手把手带你CNN文本分类(附代码)
  4. Shell中的一些小技巧
  5. 求【javascript设计模式】【高性能网站建设指南】PDF!哪位有给下啊!!!
  6. 洛谷 - P1361 - 小M的作物 - 最小割 - 最大权闭合子图
  7. HDFS NameNode重启优化
  8. vSphere共享存储全配置流程
  9. OpenCV中的函数waitKey()函数简介
  10. OpenCV4每日一练day8:模型投影函数projectPoints()
  11. 风控人必知必会的征信知识
  12. CentOS6.8安装oracle11gR2
  13. Spring 与 SpringMVC 容器父子关系引出的相应问题
  14. VSTO/Excel: 获取Excel图表中的某个点的数据
  15. 迎建国七十周年,Linux厂商巡礼之优麒麟
  16. 关于解决IAR安装卸载后无法再次破解的问题
  17. tlq_ver.dat文件不存在
  18. iRedMail批量用户添加命令
  19. 介绍一个牛人的机器学习PPT
  20. 华为android9.1.0怎么隐藏应用,华为手机怎么隐藏app

热门文章

  1. Mysql的锁(自用笔记)
  2. 跨链项目总结-比原链
  3. Android Studio 引入AAR的方式
  4. rust自我解脱_自我解脱哲理故事
  5. Hash(哈希/散列)和Bloom Filter(布隆过滤器)
  6. mysql中dual表
  7. 行内元素与块级元素之间的区别及转化
  8. sikuli入门到进阶
  9. 关于getchar的用法及实例解析
  10. 可视化之donut chart