首页 > 应用与设计 > 电器 > Service Robots > 服务型机器人 > TI机器人系统学习套件(TI-RSLK) >

服务型机器人

最新课程

热门课程

TI-RSLK 模块 3 - 讲座视频 - ARM Cortex M 组件

大家好, 我是 Jon Valvano, 在本视频中, 我们将讨论 汇编语言编程。 正如我上次说过的, 您对 Cortex M 处理器 了解的越多, 您就越了解 计算机实际上 是如何执行 代码的,也就越有可能 成为更加优秀的嵌入式 系统工程师。 因此,即使您使用 C 编写您的所有程序, 我也认为您 应该在机器级别 或汇编级别了解 您的程序的实际 执行方式。 因此,我们要 在这里讨论 Cortex M 上的几条 指令,以便在您 调试 C 代码时,您 可以了解它在做什么。 让我们开始吧。 让我们从逻辑 运算开始。 正如您知道的,我们将 使用“与”运算来执行掩码。 因此,如果我们以“1”结束, 那么它将用于选择位。 如果我们以零结束, 那么我们将清除位。 这里的或运算 用于设置位。 因此,如果我们与一执行 或运算,那么它将设置位, 如果我们与零执行或运算, 则不会发生任何变化。 我们将 使用或来设置位 或将两个不同 数字的不同部分 合并到一个值中。 我要讨论的 第三个逻辑 运算是异或。 因此,如果我与一执行异或 运算,那么它将对该位取反。 换句话说,如果我进行 切换,如果我与一执行 异或运算,它将从一 变为零,或者从零变为一。 如果我与零执行异或 预算,则不会发生变化。 那么,再说一次,我们 也会在该课程使用异或。 这里是汇编 语言指令。 这里是操作码。 这些是操作数。 这恰好是寄存器 或立即寻址模式。 具体而言,在这里, Cortex M 上的大多数指令 都沿着这个方向执行。 换句话说,这里的 指令获取 R2 中的值、 R1 中的值,然后一起 对它们执行或运算, 并将结果放回到 R0 中。 您需要从这 里面获取的 -- 我们也会在 C 中看到它 -- 这称为按位 逻辑运算。 那么,在本例中, 我们要将 R1 的值 与 R2 的值 执行或运算。 在这里,我有一些 随机数,它们可能 已位于 R1 和 R2 中,这样,您就 可以看到会 发生什么情况。 它是按位发生的。 因此,如果您选择 这里的一个位 -- 让我们看看位八 我们会看到它将 获取 R1 中位八的值 以及 R2 中位八的 值,将它们在一起 对齐,然后将该值 存储到 R0 的位八中。 它会对全部 32 个 位执行该过程。 再说一次,逻辑运算 用于设置和清除位。 移位运算本质上 是除法或乘法运算。 换句话说,向右 移位即除以二, 向左移位 即乘以二。 但您需要了解的 重要部分是这样 一个事实,即您需要 知道您具有的是 无符号数还是 有符号数,这一点 对您而言非常重要。 因为如果它是 无符号数,那么 您需要进行 逻辑移位,以便 零移位到这里,您 实际上将这个数除以 2。 相反,如果它 是算术移位, 那么您将在将其 向右移位时保留 最高有效位。 因此,了解该运算的 重点在于这样的事实, 即您看到您具有 一个 32 位寄存器。 因此,我们将在对该 32 位数字进行运算, 您应该知道您具有的 数字是有符号数还是 无符号数,这一点很重要。 当您向左移位时, 无论它是有符号数 还是无符号数 都没有关系。 我们将放入一个零。 再说一次,运算 沿着这个方向执行。 因此,它会获取 RM 中的值, 将其右移多次,然后 将结果存储到结果 寄存器中。 因此,我们将使用 移位来移动位,或者 执行乘和除。 这里的重点是, 它是 32 位运算, 您必须知道它 是有符号数还是 无符号数。 算术运算 -- 它 看起来很容易执行。 我将获取两个 数字,然后我要 将它们相加。 P 等于 m 加 n。 但这里的问题是, 它们是 32 位寄存器, 如果我获取该 32 位 数字,然后将其与 另一个 32 位数字相加, 我实际上将得到 33 位。 这就导致了一种 可能,即我的结果 可能无法重新 存储到我的 32 位 寄存器中,我可能会出错。 该错误 称为溢出。 该情况会在加法 和减法中发生。 它实际上在 乘法中更糟糕。 如果我获取一个 32 位数字, 然后将其与另一个 32 位 数字相乘,那么我 实际上得到 -- 理论上而言 我得到 64 位。 那么,这是一条 溢出路线。 那么,当您编写您的 软件来防止或消除 对该溢出情况的担忧时, 我们将要做两件事。 第一件事是,我们将 通过某种方法在理论上 预测中间结果的 最大值将是多少, 并确保该值 不超过 32 位。 因此,这通常意味着 我们知道该值、 我们的输入的范围,并且 我们知道输入是受限的。 那么,例如,如果您知道 我有一个 12 位的数字 -- 换句话说,我知道我的数字 介于 0 和 4,095 之间 -- 我获取该数字,然后 我将其乘以 1,000, 我知道它可能达到的 最大值是 400 万。 这是它可能达到的最大值。 因此,理论上而言, 这是一个 10 位的数字。 因此,我知道该数字 可以存储在 22 个位中。 因此,如果我可以 限制输入并且我 知道该限制是 什么,那么我可以 在理论上证明, 在整个计算中, 任何中间结果都 不会超过 32 位限制。 我们将用于解决 它的第二种方法是, 将其升级至 更高的精度。 因此,我可能必须在 64 位模式下执行运算, 或者我可能必须获取 我的16 位运算并在 32 位 模式下执行它们。 因此,升级将 使数字更大, 在该更高的 精度下执行运算, 然后再将其降级回到原来的精度。 因此,您需要 控制您的 数字的大小, 这一点很重要, 因为我们在该处理器结构中 具有该 32 位限制。 当我们执行除法时,很显然, 我们不希望除以零, 另一个可能 发生的问题是 称为掉出的问题, 它会在除法上发生。 此外,它还会在向右移位上 发生,正如我们先前看到的, 向右移位是除法。 基本而言,掉出 即信息丢失。 正如您看到的, 这相当容易。 如果我获取数字 n 并将其除以 1,024, 这在本质上 将丢弃 10 个位。 因此,我将丢失我的 数字的最低 10 个位。 因此,再说一次,我要 小心提防因为掉出 而导致灾难性的信息 丢失 -- 因为溢出而导致 灾难性的信息 丢失,以及因为 除法或向右移位 而导致信息丢失。 就像移位、加法、 减法以及乘法 和除法一样,您 必须确保您知道 它是有符号数 还是无符号数。 使用一个有符号数 与一个无符号数 进行加、 减、乘、除 是没有意义的。 因此,我们无法对两个 类型不同的数字进行 算术运算。 这没有任何意义。 基本而言,我们必须 将无符号数之一升级为 更大的有符号数, 以便我们可以对它们 一起进行运算。 这是加法指令。 再说一次,它的 工作方式是, 它获取第二个操作数, 这可能是一个寄存器, 将其与中间操作数 相加,然后这两个 操作数进入这里。 请注意,在汇编级别, 它实际上会设置一些标志。 进位标志会在发生 无符号溢出时设置, 溢出 V 位会在发生 有符号溢出时设置。 不幸的是,C 中 不提供这些位, 当我们执行加、 减、乘和除运算时, 我们必须 限制输入, 或者我们必须 升级到更高的精度, 以防发生溢出。 当我们获取 F 时, 我们将看到比较 指令。比较指令 其实就是减法, 但它不在 任何位置 存储结果, 它用于 if/then 语句。 乘法和除法 -- 再说一次,我们 先前已提到, 必须确保您 知道您的数字是 有符号数还是无符号数。 实际上,我们 有一条不同的 用于无符号数/除法与 有符号数/除法的指令。 就像所有 其他指令一样, 该运算沿着 这个方向执行。 您可以看到它 是寄存器寻址。 因此,它获取 该数字,将其 乘以该数字,然后 将结果存储在该位置, 存储在该寄存器中。 这是前一个讲座中 介绍的有关我如何 访问变量的原理。 那么这位于 RAM 中。 那么我在这里有一个 RAM 变量。 我在那里有 另一个 RAM 变量。 我们上次看到了 访问 RAM 变量需要 执行两个步骤。 第一步是 获取指针。 这是该指令。 这里是指针。 那么,这是指向 该点的 32 位指针, 那么,这里的第一条 指令将设置 R3,以指向 该 RAM 位置。 然后,我们可以使用 第二条指令来访问它。 在本例中, 我将读取该值。 现在,我们 要乘以三 并除以五, 再说一次, 这里是乘法指令。 它获取 R0, 将其乘以 R1, 然后将结果存储回到 R1 中。 除法指令获取 这里的 R1,将其 除以 R0,然后将结果 存储回到该位置。 再说一次,我们看到用于 访问变量的一、二步骤。 第一步是使一个 寄存器指向该位置。 现在,该访问 恰好是存储, 因此它要将结果 存储回到原来的位置。 总之,需要 考虑溢出。 总之,需要考虑它是 有符号数还是无符号数。 堆栈非常重要。 我们将在整个 课程中使用它。 我们可以将其 用于临时存储, 比如我们有 一些数据, 我们希望临时存储它, 稍后再将其取回。 这是后进 先出存储。 换句话说,我可以保存某些 数据,然后再将其取回。 它还是编译器 生成局部变量的 方法之一, 当然,局部 变量也是 另一种临时存储。 不过,让我们 看看该入栈指令。 堆栈位于 RAM 中。 当我绘制一个 堆栈图像时, 我要将零地址 放在那里, 将 FFFFFFFC 地址放在那里。 在这里的某个 位置,我要为堆栈 分配一些空间。 现在,很显然,它必须 位于 RAM 中,因为它可重复写入。 然后,我将设置 我的堆栈指针, 以指向该位置。 我将定义堆栈 指针,以指向 堆栈的顶部 -- 我最近,上次 入栈的数据。 因此,如果我查看 这里的堆栈图像, 该堆栈指针将最初 指向堆栈的顶部 元素。 我将在这里 暂且假设 R0 最初等于 0, R1 最初等于 1, R2 最初等于 2。 我将执行 这六条指令。 那么,这是第一条 指令,就在这里。 它的工作方式是, 当我想入栈时, 它将递减四。 这是因为,您应该 记得,内存本身 是 8 位可寻址的, 但寄存器是 32 位 或四个字节。 因此,为了腾出空间, 我必须减四,然后 我将在该位置 进行存储。 因此,请记住, 我说过 R0 是零, 我要将零 存储在该位置。 然后,我执行 第二条指令, 它存储一,递减堆栈 指针,在该位置 进行存储。 第三条指令,使 R2 入栈。 然后在该位置使 R2 入栈。 因此,在任何给定的 时间,堆栈指针 都会指向数据,并且 它指向最新的数据, 最后入栈的数据。 因此,堆栈的顶部 是最新的数据,最近 入栈的数据。 但是,出栈按照 相反的方向发生, 这是出栈。 现在,我要 弹出到 R3 中, 因此,这现在也 将存储到 R3 中。 第五条指令,弹出 R4。 R4 将获得一, R5 将获得零。 堆栈现在与 它的开始状态 实现了平衡。 那么,三个入栈,三个 出栈,我在寄存器中 移动了一些数据。 再说一次,入栈指令 将递减,然后存储, 而出栈会首先 读取,然后递减四。 那么,再说一次, 堆栈会非常有用, 当您在计算机上进行 调试时,它是您可以 实际看到的东西。 因此,在整个 课程中,我们 都将讨论 抽象的概念, 它是一种用于 处理复杂问题的方法。 那么,具体而言, 我们将看到 如何定义和 调用函数。 这是一个函数。 它是一种用于 简化系统的方法。 因此,如果我有大量的代码, 我可以将其置于多个级别中。 这是低级别的代码 而这是高级别代码。 那么,函数 -- 这些点是伪操作, 实际的指令是 这三个。 因此,当这执行时, 我们将具有两个 函数调用。 因此,该函数的 分支连接指令 是函数调用, 我们可以 在这里看到 BXLR 指令是子例程返回。 我们将看到这是怎么工作的。 让我们来执行它。 第一条指令将 一放置到 R0 中。 那么,在我的寄存器中, 在这里的某个位置, R0 等于一。 第二条指令是 分支连接构造, 分支连接上会 发生两件事情。 然后,它要将连接 寄存器 -- R14 设置为返回地址, 它恰好位于此处。 返回地址是 调用函数之后的指令。 那么,这里的 指令是调用。 调用函数之后的指令 是返回地址, 它插入到 连接寄存器中。 然后,它将更改或 设置程序计数器, 使其等于函数地址。 程序计数器在这里。 就是这样。 因此,要执行的 下一条指令是 该指令,第三条指令。 我们以前看到过它。 它要将 M 的地址 提取到 R1 中。 因此,R1 现在将指向此处, 现在这是一个变量。 那么,第三条 和第四条指令 要获取该存储到 M 中的值。 因此,我初始化 该全局变量。 但这里有趣的是 指令五和分支连接 构造,以及一条 非常简单的指令, BXLR,它基本上会 获取连接寄存器 并将其放置到 程序计数器中。 但连接寄存器 在这里,因此 这是程序计数器 到达的位置。 因此,第六条指令将是 这一条,它恰好是 另一个子例程调用。 那么,让我们来执行它。 现在连接寄存器 已经更改为该指令, 程序计数器已 更改为该指令。 然后它将执行 该 -- 6、7、8、9、 10, 11, 12, 13, 14, 15. 现在,这恰好是 随机数生成器。 这不是一个非常好的 随机数生成器, 但是它会返回一个介于 0 和 255 之间的数字。 请注意,它使用了 R0 作为参数。 它在 R0 中作为 输入参数进行传递。 然后,它还将使用 R0 作为返回参数。 因此,R0 将包含该介于 0 和 255 之间的数字。 但 BXLR 指令,我们 在这里关注的指令, 实际上是子例程 返回指令。 它获取连接寄存器 并将其存储到程序 计数器中。 因此,现在程序计数器 也将指向此处。 那么,第 16 条 指令将位于此处, 它将存储值, 将地址存储到 那一个中。 那么,现在我们使 R1 指向此处。 现在,在第 17 条 指令中,第一个 随机数生成器, 第一个随机数 将存储在这里。 现在,第 18 条指令 是一个非条件分支, 因此,我们在 19 处 结束,返回到这里。 那么,这里的重点是, 我们将使用函数 来降低复杂性, 以重用代码, 并且我们将 使用 BL 指令 来调用函数。 我们将使用 BXLR 从该子例程返回。 这是有关条件 语句的简短介绍。 这里的重点是,需要 知道它是无符号数 还是有符号数。 由于我们有一组 不同的分支,因此, 基本而言,我们会 将一个值存储到 一个寄存器中。 我们将使用比较指令, 将其与第二个值 进行比较。 然后,我们将 执行条件分支, 如果该条件为真, 那么它将进行分支。 那么,这就是我们 将实现 if/then 的方式。 再说一次,我们 将看到将某个数据 存储到一个寄存器中,将某个 数据存储到第二个寄存器中, 然后进行比较。 然后,我们可以 比较这两个值。 只要两侧都是 无符号数或者 两个值都是有符号数, 一切就会正常运行。 我们使用循环 来比较指令。 那么,在本例中, 我们要“当 G2 大于 G1 时”进行循环, 因此我将反复 不断地运行 该指令。 有时,我们会将某个 操作执行有限次数, 那么,我们可以通过 C 语言或者 通过汇编语言来执行该操作。 在本例中,R4 将是计数器, 我们将使 R4 递减, 直到 R4 等于零。 然后,我们将退出。 这就是我们将 执行循环的方式。 总之,我们 在这里简要 概述了 Cortex M 上的部分指令。 我建议您打开 指令集手册,查看 您的 C 代码会生成 什么,并看看您 是否还有什么不明白的地方。 在本次讲座中, 您需要了解的 可以使您成为 更优秀程序员的 重点是:需要考虑 溢出,它意味着数字 太大而无法存储; 需要考虑掉出, 这会在除法或向右移位上 发生,它意味着信息丢失; 确保您了解您的 数字是 8 位、16 位 还是 32 位,以及它们 是有符号数还是无符号数。 随着我们继续学习, 这会非常重要, 因为这些指令专门 针对一种类型的 数字运行。 非常感谢,祝您 本次实验愉快。 再说一次,在那里稍微深入 研究一下,看看您是否不明白 该汇编语言的某些部分。 420

大家好,

我是 Jon Valvano, 在本视频中,

我们将讨论 汇编语言编程。

正如我上次说过的, 您对 Cortex M 处理器

了解的越多, 您就越了解

计算机实际上 是如何执行

代码的,也就越有可能 成为更加优秀的嵌入式

系统工程师。

因此,即使您使用 C 编写您的所有程序,

我也认为您 应该在机器级别

或汇编级别了解 您的程序的实际

执行方式。

因此,我们要 在这里讨论

Cortex M 上的几条 指令,以便在您

调试 C 代码时,您 可以了解它在做什么。

让我们开始吧。

让我们从逻辑 运算开始。

正如您知道的,我们将 使用“与”运算来执行掩码。

因此,如果我们以“1”结束, 那么它将用于选择位。

如果我们以零结束, 那么我们将清除位。

这里的或运算 用于设置位。

因此,如果我们与一执行 或运算,那么它将设置位,

如果我们与零执行或运算, 则不会发生任何变化。

我们将 使用或来设置位

或将两个不同 数字的不同部分

合并到一个值中。

我要讨论的 第三个逻辑

运算是异或。

因此,如果我与一执行异或 运算,那么它将对该位取反。

换句话说,如果我进行 切换,如果我与一执行

异或运算,它将从一 变为零,或者从零变为一。

如果我与零执行异或 预算,则不会发生变化。

那么,再说一次,我们 也会在该课程使用异或。

这里是汇编 语言指令。

这里是操作码。

这些是操作数。

这恰好是寄存器 或立即寻址模式。

具体而言,在这里, Cortex M 上的大多数指令

都沿着这个方向执行。

换句话说,这里的 指令获取 R2 中的值、

R1 中的值,然后一起 对它们执行或运算,

并将结果放回到 R0 中。

您需要从这 里面获取的 --

我们也会在 C 中看到它 --

这称为按位 逻辑运算。

那么,在本例中, 我们要将 R1 的值

与 R2 的值 执行或运算。

在这里,我有一些 随机数,它们可能

已位于 R1 和 R2 中,这样,您就

可以看到会 发生什么情况。

它是按位发生的。

因此,如果您选择 这里的一个位 --

让我们看看位八

我们会看到它将 获取 R1 中位八的值

以及 R2 中位八的 值,将它们在一起

对齐,然后将该值 存储到 R0 的位八中。

它会对全部 32 个 位执行该过程。

再说一次,逻辑运算 用于设置和清除位。

移位运算本质上 是除法或乘法运算。

换句话说,向右 移位即除以二,

向左移位 即乘以二。

但您需要了解的 重要部分是这样

一个事实,即您需要 知道您具有的是

无符号数还是 有符号数,这一点

对您而言非常重要。

因为如果它是 无符号数,那么

您需要进行 逻辑移位,以便

零移位到这里,您 实际上将这个数除以 2。

相反,如果它 是算术移位,

那么您将在将其 向右移位时保留

最高有效位。

因此,了解该运算的 重点在于这样的事实,

即您看到您具有 一个 32 位寄存器。

因此,我们将在对该 32 位数字进行运算,

您应该知道您具有的 数字是有符号数还是

无符号数,这一点很重要。

当您向左移位时, 无论它是有符号数

还是无符号数 都没有关系。

我们将放入一个零。

再说一次,运算 沿着这个方向执行。

因此,它会获取 RM 中的值,

将其右移多次,然后 将结果存储到结果

寄存器中。

因此,我们将使用 移位来移动位,或者

执行乘和除。

这里的重点是, 它是 32 位运算,

您必须知道它 是有符号数还是

无符号数。

算术运算 -- 它 看起来很容易执行。

我将获取两个 数字,然后我要

将它们相加。

P 等于 m 加 n。

但这里的问题是, 它们是 32 位寄存器,

如果我获取该 32 位 数字,然后将其与

另一个 32 位数字相加, 我实际上将得到 33 位。

这就导致了一种 可能,即我的结果

可能无法重新 存储到我的 32 位

寄存器中,我可能会出错。

该错误 称为溢出。

该情况会在加法 和减法中发生。

它实际上在 乘法中更糟糕。

如果我获取一个 32 位数字, 然后将其与另一个 32 位

数字相乘,那么我 实际上得到 -- 理论上而言

我得到 64 位。

那么,这是一条 溢出路线。

那么,当您编写您的 软件来防止或消除

对该溢出情况的担忧时, 我们将要做两件事。

第一件事是,我们将 通过某种方法在理论上

预测中间结果的 最大值将是多少,

并确保该值 不超过 32 位。

因此,这通常意味着 我们知道该值、

我们的输入的范围,并且 我们知道输入是受限的。

那么,例如,如果您知道 我有一个 12 位的数字 --

换句话说,我知道我的数字 介于 0 和 4,095 之间 --

我获取该数字,然后 我将其乘以 1,000,

我知道它可能达到的 最大值是 400 万。

这是它可能达到的最大值。

因此,理论上而言, 这是一个 10 位的数字。

因此,我知道该数字 可以存储在 22 个位中。

因此,如果我可以 限制输入并且我

知道该限制是 什么,那么我可以

在理论上证明, 在整个计算中,

任何中间结果都 不会超过 32 位限制。

我们将用于解决 它的第二种方法是,

将其升级至 更高的精度。

因此,我可能必须在 64 位模式下执行运算,

或者我可能必须获取 我的16 位运算并在 32 位

模式下执行它们。

因此,升级将 使数字更大,

在该更高的 精度下执行运算,

然后再将其降级回到原来的精度。

因此,您需要 控制您的

数字的大小, 这一点很重要,

因为我们在该处理器结构中 具有该 32 位限制。

当我们执行除法时,很显然, 我们不希望除以零,

另一个可能 发生的问题是

称为掉出的问题, 它会在除法上发生。

此外,它还会在向右移位上 发生,正如我们先前看到的,

向右移位是除法。

基本而言,掉出 即信息丢失。

正如您看到的, 这相当容易。

如果我获取数字 n 并将其除以 1,024,

这在本质上 将丢弃 10 个位。

因此,我将丢失我的 数字的最低 10 个位。

因此,再说一次,我要 小心提防因为掉出

而导致灾难性的信息 丢失 -- 因为溢出而导致

灾难性的信息 丢失,以及因为

除法或向右移位 而导致信息丢失。

就像移位、加法、 减法以及乘法

和除法一样,您 必须确保您知道

它是有符号数 还是无符号数。

使用一个有符号数 与一个无符号数

进行加、 减、乘、除

是没有意义的。

因此,我们无法对两个 类型不同的数字进行

算术运算。

这没有任何意义。

基本而言,我们必须 将无符号数之一升级为

更大的有符号数, 以便我们可以对它们

一起进行运算。

这是加法指令。

再说一次,它的 工作方式是,

它获取第二个操作数, 这可能是一个寄存器,

将其与中间操作数 相加,然后这两个

操作数进入这里。

请注意,在汇编级别, 它实际上会设置一些标志。

进位标志会在发生 无符号溢出时设置,

溢出 V 位会在发生 有符号溢出时设置。

不幸的是,C 中 不提供这些位,

当我们执行加、 减、乘和除运算时,

我们必须 限制输入,

或者我们必须 升级到更高的精度,

以防发生溢出。

当我们获取 F 时, 我们将看到比较

指令。比较指令 其实就是减法,

但它不在 任何位置

存储结果, 它用于 if/then 语句。

乘法和除法 -- 再说一次,我们

先前已提到, 必须确保您

知道您的数字是 有符号数还是无符号数。

实际上,我们 有一条不同的

用于无符号数/除法与 有符号数/除法的指令。

就像所有 其他指令一样,

该运算沿着 这个方向执行。

您可以看到它 是寄存器寻址。

因此,它获取 该数字,将其

乘以该数字,然后 将结果存储在该位置,

存储在该寄存器中。

这是前一个讲座中 介绍的有关我如何

访问变量的原理。

那么这位于 RAM 中。

那么我在这里有一个 RAM 变量。

我在那里有 另一个 RAM 变量。

我们上次看到了 访问 RAM 变量需要

执行两个步骤。

第一步是 获取指针。

这是该指令。

这里是指针。

那么,这是指向 该点的 32 位指针,

那么,这里的第一条 指令将设置 R3,以指向

该 RAM 位置。

然后,我们可以使用 第二条指令来访问它。

在本例中, 我将读取该值。

现在,我们 要乘以三

并除以五, 再说一次,

这里是乘法指令。

它获取 R0, 将其乘以 R1,

然后将结果存储回到 R1 中。

除法指令获取 这里的 R1,将其

除以 R0,然后将结果 存储回到该位置。

再说一次,我们看到用于 访问变量的一、二步骤。

第一步是使一个 寄存器指向该位置。

现在,该访问 恰好是存储,

因此它要将结果 存储回到原来的位置。

总之,需要 考虑溢出。

总之,需要考虑它是 有符号数还是无符号数。

堆栈非常重要。

我们将在整个 课程中使用它。

我们可以将其 用于临时存储,

比如我们有 一些数据,

我们希望临时存储它, 稍后再将其取回。

这是后进 先出存储。

换句话说,我可以保存某些 数据,然后再将其取回。

它还是编译器 生成局部变量的

方法之一, 当然,局部

变量也是 另一种临时存储。

不过,让我们 看看该入栈指令。

堆栈位于 RAM 中。

当我绘制一个 堆栈图像时,

我要将零地址 放在那里,

将 FFFFFFFC 地址放在那里。

在这里的某个 位置,我要为堆栈

分配一些空间。

现在,很显然,它必须 位于 RAM 中,因为它可重复写入。

然后,我将设置 我的堆栈指针,

以指向该位置。

我将定义堆栈 指针,以指向

堆栈的顶部 --

我最近,上次 入栈的数据。

因此,如果我查看 这里的堆栈图像,

该堆栈指针将最初 指向堆栈的顶部

元素。

我将在这里 暂且假设 R0

最初等于 0, R1 最初等于 1,

R2 最初等于 2。

我将执行 这六条指令。

那么,这是第一条 指令,就在这里。

它的工作方式是, 当我想入栈时,

它将递减四。

这是因为,您应该 记得,内存本身

是 8 位可寻址的, 但寄存器是 32 位

或四个字节。

因此,为了腾出空间, 我必须减四,然后

我将在该位置 进行存储。

因此,请记住, 我说过 R0 是零,

我要将零 存储在该位置。

然后,我执行 第二条指令,

它存储一,递减堆栈 指针,在该位置

进行存储。

第三条指令,使 R2 入栈。

然后在该位置使 R2 入栈。

因此,在任何给定的 时间,堆栈指针

都会指向数据,并且 它指向最新的数据,

最后入栈的数据。

因此,堆栈的顶部 是最新的数据,最近

入栈的数据。

但是,出栈按照 相反的方向发生,

这是出栈。

现在,我要 弹出到 R3 中,

因此,这现在也 将存储到 R3 中。

第五条指令,弹出 R4。

R4 将获得一, R5 将获得零。

堆栈现在与 它的开始状态

实现了平衡。

那么,三个入栈,三个 出栈,我在寄存器中

移动了一些数据。

再说一次,入栈指令 将递减,然后存储,

而出栈会首先 读取,然后递减四。

那么,再说一次, 堆栈会非常有用,

当您在计算机上进行 调试时,它是您可以

实际看到的东西。

因此,在整个 课程中,我们

都将讨论 抽象的概念,

它是一种用于 处理复杂问题的方法。

那么,具体而言, 我们将看到

如何定义和 调用函数。

这是一个函数。

它是一种用于 简化系统的方法。

因此,如果我有大量的代码, 我可以将其置于多个级别中。

这是低级别的代码

而这是高级别代码。

那么,函数 --

这些点是伪操作, 实际的指令是

这三个。

因此,当这执行时, 我们将具有两个

函数调用。

因此,该函数的 分支连接指令

是函数调用, 我们可以

在这里看到 BXLR 指令是子例程返回。

我们将看到这是怎么工作的。

让我们来执行它。

第一条指令将 一放置到 R0 中。

那么,在我的寄存器中, 在这里的某个位置,

R0 等于一。

第二条指令是 分支连接构造,

分支连接上会 发生两件事情。

然后,它要将连接 寄存器 -- R14

设置为返回地址, 它恰好位于此处。

返回地址是 调用函数之后的指令。

那么,这里的 指令是调用。

调用函数之后的指令 是返回地址,

它插入到 连接寄存器中。

然后,它将更改或 设置程序计数器,

使其等于函数地址。

程序计数器在这里。

就是这样。

因此,要执行的 下一条指令是

该指令,第三条指令。

我们以前看到过它。

它要将 M 的地址 提取到 R1 中。

因此,R1 现在将指向此处, 现在这是一个变量。

那么,第三条 和第四条指令

要获取该存储到 M 中的值。

因此,我初始化 该全局变量。

但这里有趣的是 指令五和分支连接

构造,以及一条 非常简单的指令,

BXLR,它基本上会 获取连接寄存器

并将其放置到 程序计数器中。

但连接寄存器 在这里,因此

这是程序计数器 到达的位置。

因此,第六条指令将是 这一条,它恰好是

另一个子例程调用。

那么,让我们来执行它。

现在连接寄存器 已经更改为该指令,

程序计数器已 更改为该指令。

然后它将执行 该 -- 6、7、8、9、

10, 11, 12, 13, 14, 15.

现在,这恰好是 随机数生成器。

这不是一个非常好的 随机数生成器,

但是它会返回一个介于 0 和 255 之间的数字。

请注意,它使用了 R0 作为参数。

它在 R0 中作为 输入参数进行传递。

然后,它还将使用 R0 作为返回参数。

因此,R0 将包含该介于 0 和 255 之间的数字。

但 BXLR 指令,我们 在这里关注的指令,

实际上是子例程 返回指令。

它获取连接寄存器 并将其存储到程序

计数器中。

因此,现在程序计数器 也将指向此处。

那么,第 16 条 指令将位于此处,

它将存储值, 将地址存储到

那一个中。

那么,现在我们使 R1 指向此处。

现在,在第 17 条 指令中,第一个

随机数生成器, 第一个随机数

将存储在这里。

现在,第 18 条指令 是一个非条件分支,

因此,我们在 19 处 结束,返回到这里。

那么,这里的重点是, 我们将使用函数

来降低复杂性, 以重用代码,

并且我们将 使用 BL 指令

来调用函数。

我们将使用 BXLR 从该子例程返回。

这是有关条件 语句的简短介绍。

这里的重点是,需要 知道它是无符号数

还是有符号数。

由于我们有一组 不同的分支,因此,

基本而言,我们会 将一个值存储到

一个寄存器中。

我们将使用比较指令, 将其与第二个值

进行比较。

然后,我们将 执行条件分支,

如果该条件为真, 那么它将进行分支。

那么,这就是我们 将实现 if/then 的方式。

再说一次,我们 将看到将某个数据

存储到一个寄存器中,将某个 数据存储到第二个寄存器中,

然后进行比较。

然后,我们可以 比较这两个值。

只要两侧都是 无符号数或者

两个值都是有符号数, 一切就会正常运行。

我们使用循环 来比较指令。

那么,在本例中, 我们要“当 G2 大于

G1 时”进行循环, 因此我将反复

不断地运行 该指令。

有时,我们会将某个 操作执行有限次数,

那么,我们可以通过 C 语言或者 通过汇编语言来执行该操作。

在本例中,R4 将是计数器,

我们将使 R4 递减, 直到 R4 等于零。

然后,我们将退出。

这就是我们将 执行循环的方式。

总之,我们 在这里简要

概述了 Cortex M 上的部分指令。

我建议您打开 指令集手册,查看

您的 C 代码会生成 什么,并看看您

是否还有什么不明白的地方。

在本次讲座中, 您需要了解的

可以使您成为 更优秀程序员的

重点是:需要考虑 溢出,它意味着数字

太大而无法存储; 需要考虑掉出,

这会在除法或向右移位上 发生,它意味着信息丢失;

确保您了解您的 数字是 8 位、16 位

还是 32 位,以及它们 是有符号数还是无符号数。

随着我们继续学习, 这会非常重要,

因为这些指令专门 针对一种类型的

数字运行。

非常感谢,祝您 本次实验愉快。

再说一次,在那里稍微深入 研究一下,看看您是否不明白

该汇编语言的某些部分。 420

视频报错
手机看
扫码用手机观看
收藏本课程

相关下载

查看全部

视频简介

TI-RSLK 模块 3 - 讲座视频 - ARM Cortex M 组件

所属课程:TI机器人系统学习套件(TI-RSLK) 发布时间:2018.08.27 视频集数:69 本节视频时长:23:00
在该模块中你将开发并测试迷宫机器人可能会用于进行数值计算的汇编函数。
已有6人参与了讨论去论坛跟帖交流