FreeRTOS深入教程(任务的引入及栈的作用)

简介: FreeRTOS深入教程(任务的引入及栈的作用)

前言

本篇文章开始带大家深入学习FreeRTOS,带大家学习什么是任务,并且深入学习栈的作用。

一、任务的引入

在 FreeRTOS 中,任务(Task)是一个基本的执行单元,它代表了一个并行执行的工作单元。FreeRTOS 是一个实时操作系统,允许你创建多个任务,每个任务都有自己的代码、堆栈和优先级。这些任务可以独立运行,以实现多任务并发。

以下是关于 FreeRTOS 任务的一些关键概念:

任务的特点:

每个任务都有自己的独立堆栈,用于保存任务的局部变量和上下文信息

任务可以具有不同的优先级,操作系统根据任务的优先级来调度它们的执行。

任务可以是无限循环的,也可以在完成其工作后自行删除。

每个任务都有一个唯一的任务句柄,可以用于管理和控制任务。

二、深入理解C语言函数的调用

1.ARM架构

在我们看来执行a+b是一件非常简单的事情,但是在CPU的内部却做了非常多的操作。

CPU运行时,先去Flash上取得指令,再执行指令:

  • 把内存a的值读入CPU寄存器R0
  • 把内存b的值读入CPU寄存器R1
  • 把R0、R1累加,存入R0
  • 把R0的值写入内存a

2.基础汇编指令

LDR(Load Register):

LDR 指令用于将数据从内存加载到寄存器中。

典型的 LDR 指令的语法:LDR Rd, [Rn, #Offset],其中 Rd 是目标寄存器,Rn 是基址寄存器,Offset 是偏移量。

举例:LDR R0, [R1, #4] 表示将存储在地址 (R1 + 4) 处的数据加载到 R0 寄存器中。

STR(Store Register):

STR 指令用于将寄存器中的数据存储到内存中。

典型的 STR 指令的语法:STR Rd, [Rn, #Offset],其中 Rd 是源寄存器,Rn 是基址寄存器,Offset 是偏移量。

举例:STR R0, [R1, #4] 表示将 R0 寄存器中的数据存储到地址 (R1 + 4) 处。

ADD(Add):

ADD 指令用于将两个操作数相加,并将结果存储在目标寄存器中。

典型的 ADD 指令的语法:ADD Rd, Rn, Operand2,其中 Rd 是目标寄存器,Rn 是第一个操作数寄存器,Operand2 是第二个操作数。

举例:ADD R0, R1, #10 表示将 R1 寄存器中的值与 10 相加,并将结果存储在 R0 中。

POP:

POP 指令用于从栈中弹出多个寄存器的值。

POP 指令的操作是根据栈指针(通常是 SP 寄存器)从栈中弹出值,同时更新栈指针。

通常用于函数返回时,以恢复之前保存的寄存器状态。

POP {R3, PC}

PUSH:

PUSH 指令用于将多个寄存器的值推送(压入)到栈上。

PUSH 指令的操作是根据栈指针(通常是 SP 寄存器)将值压入栈中,同时更新栈指针。

通常用于函数调用时,以保存当前寄存器状态。

PUSH {R3, LR}

3.函数运行流程分析

在keil5中编写一个加法函数:

/* a = a + b */
void add_val(int *pa, int *pb)
{
  //*pa = *pa + *pb;
  volatile int tmp;
  tmp = *pa;
  tmp = tmp + *pb;
  *pa = tmp;
}
int a = 1;
int b = 2;
add_val(&a, &b);  

汇编代码:

汇编代码分析:

首先执行PUSH  {r3,lr}命令
将r3寄存器的值和lr返回地址保存到了栈中。
r3就是局部变量tmp的值。

tmp = *pa;

LDR      r2,[r0,#0x00]   读取r0的值到r2中,也就是将*pa读取到r2中。
STR      r2,[sp,#0x00]   将r2的值保存到sp+0x00地址处也就是将*pa保存到tmp中。
      完成tmp = *pa;赋值语句

tmp = tmp + *pb;

LDR      r2,[r1, #0x00] 读取r1的值到r2中,也就是将*pb读取到r2中
LDR    r3,[sp,#0x00] 读取sp+0x00的值到r3中,也就是将tmp读取到r3中
ADD    r2,r2,r3 将r2+r3的和保存到r2中,也就是将*pb + tmp的值保存到r2中
STR    r2,[sp,#0x00] 将r2的值保存到sp中也就是tmp = *pb + tmp

*pa = tmp;

LDR    r2,[sp,#0x00] 读取sp+0x00的值保存到r2中,也就是将tmp保存到r2中
STR    r2,[r0,#0x00] 将r2的值保存到r0中,也就是*pa = tmp

POP {r3,lr}

出栈将lr的值返回给pc寄存器,执行下一条指令。

三.保存现场的几种情况

1.函数调用

当函数调用时R0,R1,R2通常被用来传递参数故不需要保存这几个寄存器的值。

void funA(void)
{
  .........
  funB(a, b);
}

2.中断处理

被中断打断时硬件会帮我们保存R0,R1,R2寄存器,其他寄存器需要通过软件来保存。

void funA(void)
{
  ->被中断打断
}

3.任务切换

当任务切换时需要保存所有的寄存器,因为任务并不知道会使用到哪些寄存器。

总结

本篇文章主要引入了任务及说明了栈在这里的作用。


相关文章
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
42 1
|
3天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
113 75
|
3天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
25 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
3天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
27 9
|
3天前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
23 7
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
79 5
|
2月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
2月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
2月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
57 4
|
2月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
57 0