【RTOS】初识freeRTOS内部机制


一、学习RTOS

RTOS学习分为三个层次:

  1. 使用API
  2. 知道API内部机制
  3. 彻底掌握代码细节,能移植、改进

我们先提出一个问题:RTOS裸机开发有什么区别?

当有多个事件需要去执行(比如事件A:吃饭和事件B:回信息),裸机通常使用轮询方式、事件中断和定时器中断这种方式,都不可避免当某个事件运行时间长会影响到其他事件
这时我们引出RTOS,把事件拆分成很小的一段轮换运行。当轮换时间片很小就可以认为是事件是同时运行的。
在这里插入图片描述

二、ARM架构

2.1 SOC组成

主控芯片SOC一般包括CPU、内存Flash、GPIO模块等,我们写的程序会烧写进flash。CPU运行第一步读取Flash取指令,然后根据指令去做各种操作,我们下面会介绍这些操作的作用。

在这里插入图片描述

2.2 分析C函数汇编理解程序本质

什么叫做程序?

程序:①有指令 ②有运行过程中的数据。

例如:CPU在读变量A的时候,会把内存上对应地址的数据存储到CPU寄存器;同样在CPU写数据的时候会把CPU某个寄存器的值写入到内存上。这时候涉及到下面这些简单的汇编指令:

汇编指令作用解释
LDR R0,[R1,#4]读内存,(源、目的、长度)源地址R1+4的位置读取长度4字节数据,存放到目的地址为R0的位置
STR R0, [R2,#8]写内存,(源、目的、长度)源地址R0的位置读取长度4字节数据,写到目的地址为R2+8的位置
ADD R0, R1, R2R0=R0+1
SUB R0, R0, #1R0=R0-1
PUSH {R3, LR}入栈把R3,L2的值写入SP所指示内存,同时SP=SP-8
POP {R3, PC}出栈从SP位置读内存赋值给R3,PC
MOVS R0,#1赋值R0=1

我们对以下函数调用程序进行反汇编,有助于我们理解程序本质

int add_val(int *pa, int *pb)
{
    volatile int tmp;
    tmp = *pa;
    tmp += *pb;
    return tmp;
}

int mymain()
{
    volatile int a = 1;
    volatile int b = 2;
    volatile int c;

    c = add_val(&a, &b);

    return 0;
}

c语言反汇编后的指令,可以先保存图片,下面开始详细说指令的作用

在这里插入图片描述
一开始调用main函数之前栈SP会指向内存某一个地方
PUSH{r1-r3,lr}CPU寄存器的lr r3 r2 r1的值压入内存的栈中,同时SP=SP-16(栈顶SP=0)
在这里插入图片描述

MOVS r0,#1 CPU寄存器r0=1
STR r0,[sp,#8] 把r0的值写入sp+8的地方(r3),也就是此时栈区r3即a=1。(volatile int a = 1)
MOVS r0,#2 ,STR r0,[sp,#4]作用同上,栈区r2即b=2(volatile int b = 2)
ADD r1,sp,#4 r1=sp+4,这里其实是r1=&b
ADD r1,sp,#4 同上,r0=sp+8也就是r0=&a
BL add_val 先保存add_val()下一条指令地址到lr,即lr=0x08000384然后执行add_val()

在这里需要注意函数调用栈做了什么

在这里插入图片描述
PUSH {r3,lr} 把lr(之前保存的地址),r3压入栈
在这里插入图片描述

MOV r2,r0 r2=r0=&a
LDR r0,[r2,#0] 从r2+0地址处(&a)读内存,即r0=a=1
STR r0,[sp,#0] r0的值写到sp+0指针处,即栈区r3处 也就是程序tmp = *pa=1;
LDR r0,[r1,#0] 从r1+0地址处(&b)读取数据存放到r0,即r0=b=2
LDR r3,[sp,#0] 从sp+0地址处读取数据存放到r3 r3=tmp=1
ADD r0,r0,r3 r0=r0+r3=2+1=3
STR r0,[sp,#0] tmp=3
LDR r0,[SP,#0] r0=tmp=3 注意此处没有优化程序,写一遍tmp又读一遍tmp,因为加了volatile
POP {r3,pc} 从内存sp位置读取数据写给寄存器r3,pc,lr和r3出栈,此时跳转函数回到lr=0x08000384地址处,即add_val()下一条指令
在这里插入图片描述
STR r0,[sp,#0] 把r0的值写道sp+0的地址处,即内存r1处即c=3

2.3 ARM架构过程调用标准AAPCS

所以函数调用的时候,有以下规则:

参数传递基本原则(浮点数和结构体数组暂不考虑):

前四个参数通过寄存器 R0~R3 传递。
如果有更多的参数,它们通过栈传递,从高地址到低地址,且参数从左到右依次入栈。

返回值传递

通过r0寄存器返回

寄存器保护

R0,R1,R2,R3,R12随便使用,不需要保护
R4~R11 使用前先保存,在函数返回之前需要恢复,从汇编角度看就是通过PUSHPOP来保护

在这里插入图片描述

R13 SP栈指针
R14 LR返回地址(跳转前函数的下一条指令地址)
R15 PC指示当前执行的指令地址

三、栈的作用

3.1 堆和栈

堆(Heap) 就是一块空闲的内存,我们使用的时候可以取出来,不用的时候释放

  • malloc:从堆里划出一块空间给程序使用
  • free:用完后,再把它标记为"空闲"的,可以再次使用
  #include <stdlib.h>
  void *malloc( size_t size );
  void free( void *ptr );

栈(Stack) 后进先出,栈在RTOS很重要,每个任务都有自己的栈

3.2 中断处理

中断处理分为三步:①保存现场处理中断恢复现场
中断处理过程:当产生中断后,硬件保存CPU所有寄存器(r0~r3),硬件调用 中断向量表的处理函数,然后调用函数本身规则保障不会破坏r4~r11这些寄存器,执行完中断后,硬件从栈里面恢复所有寄存器。

在这里插入图片描述
要注意图中栈的返回地址是中断的返回地址,不是之前说的LR(这里LR是作为一个普通寄存器保存)

3.3 任务切换

多任务核心就是保存现场

有两个任务 A和B,通过tick中断切换,发生中断需要保存现场,现场就保存在栈:
在任务的栈会首先保存一些局部变量,然后还有运行过程中的栈。上面已经聊过发生中断硬件会保存寄存器返回地址到栈,然后调用中断函数(此中断函数会去切换任务)通过软件保存R4~R11寄存器
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值