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

服务型机器人

最新课程

热门课程

TI-RSLK 模块 10 - 讲座视频 - 调试实时系统 - 理论

大家好,我是 John Valvano。 在本章中, 我们一直 在讨论调试和中断。 在前两个视频中, 我们讨论了 调试的 基本原理 和中断的 基本原则。 在本视频中,让我们讨论 一个使用 SysTick 的 中断的具体示例。 SysTick 将再次创建 周期性中断,它是 硬件触发的 软件操作, 可导致每隔固定 的一段时间就执行 这里的中断服务 例程,具体取决于 我们的需要。 因此,事实证明这是 一项非常强大的技术, 我们可以在许多 应用中使用它, 在这些应用中,我们 希望周期性地执行 某些软件事件。 我们将在实验中 实际看到的是, 我们将能够 与线传感器交互 ,并弥补 我们花一毫秒 等待传感器激活 而浪费的所有时间。 然后,当我们 转到实验 17 时, 我们要把它用于 我们的数字控制器。 在实验 15 中,我们将 使用它测量 ADD 转换器。 因此,我们将 在整堂课中使用 该周期性中断。 那么,让我们开始吧。 那么一般而言, 我们具有的是 硬件触发的 软件操作。 我们要在这里 解决的特定案例 是定期从传感器 读取输入。 好的,那么我们 将定期收集数据。 在实验 10 中, 我们将讨论线传感器。 正如我说过的,当我们 学习到实验 15 时, 我们将使用 ADD 转换器。 然后,当我们 学习到实验 17 时, 我们将以周期性的 速率运行输入输出, 以便周期性地 运行我们的控制器。 我们上次提到的 问题之一是 我获得的传感器数据,我必须 把它放在某个地方。 我不能把它留在 局部变量中。 我必须把它 放入规定的空间。 因此我要创建 某种永久性 RAM 变量, 某个永久性 RAM 位置, 我可以存储该数据。 它可以是公共、全局性的, 每个人都可以访问, 也可以是静态的 永久变量,只能 在该文件的函数内共享。 无论是哪种方法, 它都必须进入永久性 RAM。 现在,我们可以进入 单个变量,进入数组, 进入标志中的变量。 正如我上次提到的, 当我们学习到实验 18 时, 我们实际上可以 将数据放到一个 先入先出环队列中。 当我们到实验 18 时,会介绍更多内容。 那么让我们回顾一下 前面有关 SysTick 时间的章节。 SysTick 的本质是, 我们具有一个 24 位计数器, 它会倒数, 因此它会递减 24 位宽度。 我们具有一个 24 位常数, 我们的软件将对它 进行写入。 那么,尤其是,如果 我写入数字 n,其中 n 是介于 2 和 2 乘以 24 减 1 之间的某个数, 那么它将对 n 进行 倒数,n 减 1,n 减 2, 一直到 2、1、0。 正是这种 1 到 0 的转换 将触发中断。 一旦 1 转换为 0, 将立即设置计数标志。 那么,如果 我在这里放置数字 n, 这是一个模块 n 加 1 计数器。 它连接到总线时钟。 因此,如果我们 以 48 兆赫运行, 那么该时钟将 大约每 20 纳秒, 或确切地说每 20.8333 纳秒, 即 4800 万分之一秒递减一次。 让我们更深入地 探究 SysTick,看看 它的寄存器。 再说一次,我们上次看到过这个。 它仅有三个寄存器, 一个控制寄存器,一个加载 寄存器,以及一个值寄存器。 这是计数器本身。 为了使它发生, 我们将选择 时钟源作为 总线时钟。 我们将启用它,以便 SysTick 可以实际进行计数。 在先前的使用中,我们 使中断使能引脚为 0, 但现在我们将使用中断。 因此我要使它为 1。 正如我先前 提到的,计数 标志是该 SysTick 模块的另一部分。 该计数标志 会进行设置 -- 因此随着它 从 4 到 3、2、1、0, 它将重新 加载到 n 值中, 我将 n 值存储在 该重新加载值中。 但这里的转换, 该 1 到 0 的转换 将设置计数标志。 由于我们已经 启用它,触发它, 因此如果我们 将 I 位设置为 0, 那么这些是导致 中断的三个条件。 首先我们必须 对它进行设置。 我们必须 启用它。 我们必须 触发它。 对于生成 中断而言, 这些是三个必要条件, 并且是充分条件。 初始化 相当简单。 也就是说,如果我 希望在每个周期 中断一次,周期的 单位为 20 纳秒,那么 我要将周期减 1 发送到重新加载值中。 例如,如果我希望 每一毫秒中断一次, 那么总共是 48,000 个总线周期。 这是转换。 48,000 个总线 周期是一毫秒。 因此,这里的值 将是 47,999。 那么,该中断 会恰好每毫秒 触发一次。 7 与这 相关联。 我们曾在先前的 视频中看到,如果 我们有多个中断, 那么我们可以 在该 8 位寄存器顶部的 3 个位中设置优先级。 这是一个 8 位寄存器。 顶部的 3 个位 包含该 SysTick 中断的优先级。 那么这是我们的例程。 这是我们的初始化。 它会执行一次。 在我们的主程序中, 在我们确实对一切 都进行 初始化之后, 我们将启用中断。 然后,第一个 中断将在计数器 从 1 到 0 计数时发生。 它将不停地 自动发生, 因为计数器 会翻转并继续 计数。 那么,这是初始化。 中断服务例程 本身相当简单。 再说一次,我们曾在上一张 幻灯片中看到,如果我编写 一个函数,并且我使用 这些特殊名称之一, 例如 SysTick handler, 这将成为 SysTick 中断的中断 服务例程。 现在,我要 执行一项任务。 请注意,这不是 一项很有趣的任务。 但这是一项一般任务。 这是该项任务 将遇到的点。 您应该记得计数 标志是我的触发器。 它使用某个值进行了设置, 当该值减小至零时,计数 标志进行了设置。 运行该中断 服务例程将自动 清除上一个 中断的标志。 好的,现在我们 在先前的视频中 看到了作为一种性能评测 方法的三次切换技术。 再说一次,它的功能和它的 名称所暗示的完全一样。 在该例程中,我要 对输出引脚进行 三次切换。 在前一个视频中, 我们讨论了位带。 那么我要获取 端口一的地址。 我要删掉 顶端部分。 我将把它乘以 32。 这样我会得到这个数字。 我要在其中添加的 是端口 1 位 0, 因此,我要 添加 4 次 0, 然后我会得到一个特定的寄存器, 一个特定的地址,这只是针对 端口 1 位 0 而言。 那么,如果我向端口 1 位 0 写入 1,它会进行切换。 但该三次切换技术的 美妙之处在于, 如果我放大逻辑 分析仪或示波器, 那么我可以看到三次 切换,一、二、三。 一、二、三,等等, 现在我能够测量 运行我的 例程 所花费的时间。 再说一次,这具有 极低的侵入性, 因为如果我要 每毫秒执行一次 该中断,那么执行这些代码行 所花费的时间将远少于 一微秒。 因此,调试检测 所花费的时间 与调试检测之间的 时间之比非常小。 再说一次,一微秒 与一毫秒相比非常小。 因此,它具有极低的侵入性。 这意味着调试 本身的存在 不会修改 我测试的内容。 但是,如果我缩小, 三次切换的美妙之处 -- 如果我缩小,我就不再 能够在我的逻辑分析仪 示波器规模上分别 看到这三次切换。 现在,我只能 看到一次切换。 那么,该三次 切换将放大, 看起来像是一次切换。 因此,我将能够 非常轻松地测量 中断之间的时间。 这样,利用单个 调试设置,我能够 通过缩小测量 中断之间的时间, 并且能够通过 放大测量中断内的 时间。 这就是该调试 技术的美妙之处。 但再说一次, SysTick 非常简单。 我需要做的 只是设置周期。 然后它就会运行。 到目前为止,我在这次课中 已经两次提到这一点了。 现在,是时候 开始讨论称为 关键部分的 中断问题了。 那么,您需要 记住的一点是, 如果您曾共享过 任何东西, 这个永久性 -- 因为您应该记得 我们有多个线程 -- 如果它们曾共享 某个永久性结构, 这可能是全局性的, 也可能是 I/O 端口, 例如在本例中 -- 您可以看到这是端口 2 的输出。 这是端口 2 的输出。 我有两个不同的线程。 我要共享它。 如果它们中的任何一个是写入 -- 我允许您开始 为它感到担心。 因为在微观级别, 在纳秒总线级别 发生了某种情况, 它可能会非常 糟糕,是灾难性的。 那么,让我们来说明一下 可能会发生什么情况。 那么,我将再次获取我的 -- 现在,看起来这个 会设置为 0, 那个会设置为 1。 那么这个会影响 您可能会说,嘿,John, 那个仅会更改端口 1 位 0。 这个仅会更改 端口 1 位 2。 但不是这样的, 因为中断以及 它们生成的线程 具有并发特性。 那么,让我们放大 机器代码、汇编代码, 它们由任何 适当的编译器 生成以尝试实现 该 C 代码行。因此, 正如我们在先前的 讲座之一中看到的, 要访问全局变量, 我首先要获取 一个指向 该端口的指针。 因此,R2 将 指向端口 2 -- 指向端口 2 的输出。 接下来,它将 8 位 值存储到一个 寄存器中。 那么,该端口的值 将存储到寄存器中。 现在,某个 -- 让我们 在这里停一会儿, 思考一下刚才发生了什么。 在这里,我有一个 该端口中的值。 该 8 位端口的 所有位值 都位于该端口中。 但现在我有一个寄存器, 它包含的值和该值相同。 它具有该端口的值。 我在两个不同的位置 具有该同一数字。 那么,我要问您, 哪个是正确的? 情况变得更糟了。 让我们看看该点。 现在,我在这里具有 端口 2 的值,在端口 2 中, 就在这里。 我在该寄存器中具有 端口 2 的另一个值。 这两个值是不同的。 您看到了吗? 这个值现在设置了位 0。 那么,这时,我有 -- 这两个值 -- 我有存在于 两个不同位置的 信息的副本。 它们不一定 是相同的,这样 -- 让我们在这里 让我们执行 该指令,一、二。 那么我得到了值。 让我们挑选一个 -- 让我挑选一个数字。 那么我要挑选 -- 只是为了说明 这是怎样出错的, 我要挑选一个 特定的数字。 那么,让我们假设 端口 2 中碰巧具有 0、0、0、0。 好的,这很好。 这只是初始的状态。 它包含一些零, 一共有 8 个。 1,2,3,4,5,6,7,8。 端口 2 包含 0。 让我们重点看看 底部的两个位。 这是我们在时间 1、时间 2 和时间 3 在这里 具有的内容。 那么,在这里, 我们的位 0 中是 0。 端口中 是 0。 但幸运的是 -- 在这两条指令之间, 或在那两条指令之间 -- 接下来让我们 执行那一条。 现在,这个 -- 我刚才设置了位 1 -- 它具有值 0。 我是说,它具有值 1,因为 我刚才把它与 1 进行了 或运算。 但我们获得了中断。 该线程 1 现在将运行。 那么,要执行的 下一条指令是那个。 那么,现在我 -- 再说一次,您还记得吗, 上下文是它将所有 寄存器入栈, 并且为我创建了 一些新的 寄存器。 那么,该寄存器 r0 与那个寄存器是不同的。 那么,当我读取 这个寄存器时, 我将得到 0 的 另一个副本。 这一次,我将设置这个位。 那么,它将变为 2。 然后,我将 执行那个。 现在,在这里写入了该 2。 好的,那么这是时间 7。 然后,我 从中断返回。 请记住,连接 寄存器是 FFFFF9。 那么,它会 从中断返回。 那么,最终,它会 重新显示在这里, 这是第 9 步。 但您应该记得,它所做的是, 它将所有寄存器从堆栈中 弹出。 因此 r0 的值是 1。 因为我把它留在了这里。 那么,当我这么做时, 值会从 2 更改回到 1, 这是第 10 步。 那么,现在,如果 您考虑一下, 我已经执行了 这个和那个。 我最终 -- 我应该最终 使一个端口等于 3。 但我最终使一个 端口等于 1。 这是因为该共享 全局变量本质上 是一个关键部分,它具有 对全局共享变量的非原子性 -- 换句话说, 它可能被中断 -- 读取、修改、写入。 这是一件糟糕的事情。 因此,我们要 做几件事情。 我们可以使这个成为端口 2,使这个成为端口 3。 这样就可以 摆脱该问题。 我们已经看到过 位带好几次了。 我们可通过它 摆脱该问题。 我们可以 为这两个 中断赋予相同的 优先级,这样它们 就无法相互中断。 这样就可以解决该问题。 或者,我们可以针对 关键部分禁用中断 再重新启用中断。 这会导致 -- 一旦我开始 执行该序列, 它就会不可分割地 以原子方式运行。 它会以原子方式运行。 换句话说, 它无法拆分。 那么,这些就是我可以 用来解决该关键部分的 三种方法。 但请记住,如果您有 -- 如果您有多线程, 并且它们不知为何 共享某个永久性的 东西,比如一个端口, 而某人采取了正确的 做法,请思考一下, 当一个东西使另一个 东西中断时,可能会 发生什么怪异的事情。 然后,使用这 三种方法之一 来解决 该关键部分。 好,那么我们 详细地讨论了中断。 我们讨论了 SysTick, 它是一种简单的方法,用来 -- 它作为我们的第一个中断, 因为它非常简单,只有 几行初始化代码, 它是非常简单的 中断处理例程。 然后,我们再次 看到了三次切换技术, 它是一种用于查看 运行中断所花费的 时间和中断之间的 时间的方法。 我为您带来了有关 关键部分的坏消息, 关键部分是当您具有 非原子操作时发生的错误, 非原子意味着它可以拆分。 中断已启用。 该事件序列 -- 非原子序列, 如对共享全局 变量的读取、修改、写入 -- 这种序列 -- 还有其他序列,如写入、 写入和写入、读取 -- 但这种序列可能 会创建关键部分。 然后,我们讨论了三种 修复它的方法,这些 方法的思想是通过使它们 成为原子序列来删除共享。 我们可以通过 位带来删除共享。 我们可以通过更改 不同的端口来删除共享。 我们可以通过禁用中断 或使中断具有相同的 优先级来实现原子性。 好吧,祝您本次实验愉快。 本次实验 有点复杂。 但是,我认为一旦您完成它, 它就可以极大地提高您的技能。 好的,祝您愉快。 382

大家好,我是 John Valvano。

在本章中, 我们一直

在讨论调试和中断。

在前两个视频中, 我们讨论了

调试的 基本原理

和中断的 基本原则。

在本视频中,让我们讨论 一个使用 SysTick 的

中断的具体示例。

SysTick 将再次创建 周期性中断,它是

硬件触发的 软件操作,

可导致每隔固定 的一段时间就执行

这里的中断服务 例程,具体取决于

我们的需要。

因此,事实证明这是 一项非常强大的技术,

我们可以在许多 应用中使用它,

在这些应用中,我们 希望周期性地执行

某些软件事件。

我们将在实验中 实际看到的是,

我们将能够 与线传感器交互

,并弥补 我们花一毫秒

等待传感器激活 而浪费的所有时间。

然后,当我们 转到实验 17 时,

我们要把它用于 我们的数字控制器。

在实验 15 中,我们将 使用它测量 ADD 转换器。

因此,我们将 在整堂课中使用

该周期性中断。

那么,让我们开始吧。

那么一般而言, 我们具有的是

硬件触发的 软件操作。

我们要在这里 解决的特定案例

是定期从传感器 读取输入。

好的,那么我们 将定期收集数据。

在实验 10 中, 我们将讨论线传感器。

正如我说过的,当我们 学习到实验 15 时,

我们将使用 ADD 转换器。

然后,当我们 学习到实验 17 时,

我们将以周期性的 速率运行输入输出,

以便周期性地 运行我们的控制器。

我们上次提到的 问题之一是

我获得的传感器数据,我必须 把它放在某个地方。

我不能把它留在 局部变量中。

我必须把它 放入规定的空间。

因此我要创建 某种永久性 RAM 变量,

某个永久性 RAM 位置, 我可以存储该数据。

它可以是公共、全局性的, 每个人都可以访问,

也可以是静态的 永久变量,只能

在该文件的函数内共享。

无论是哪种方法, 它都必须进入永久性 RAM。

现在,我们可以进入 单个变量,进入数组,

进入标志中的变量。

正如我上次提到的, 当我们学习到实验 18 时,

我们实际上可以 将数据放到一个

先入先出环队列中。

当我们到实验 18 时,会介绍更多内容。

那么让我们回顾一下 前面有关 SysTick

时间的章节。

SysTick 的本质是, 我们具有一个

24 位计数器, 它会倒数,

因此它会递减 24 位宽度。

我们具有一个 24 位常数, 我们的软件将对它

进行写入。

那么,尤其是,如果 我写入数字 n,其中

n 是介于 2 和 2 乘以 24 减 1 之间的某个数,

那么它将对 n 进行 倒数,n 减 1,n 减 2,

一直到 2、1、0。

正是这种 1 到 0 的转换

将触发中断。

一旦 1 转换为 0, 将立即设置计数标志。

那么,如果 我在这里放置数字 n,

这是一个模块 n 加 1 计数器。

它连接到总线时钟。

因此,如果我们 以 48 兆赫运行,

那么该时钟将 大约每 20 纳秒,

或确切地说每 20.8333 纳秒, 即 4800 万分之一秒递减一次。

让我们更深入地 探究 SysTick,看看

它的寄存器。

再说一次,我们上次看到过这个。

它仅有三个寄存器, 一个控制寄存器,一个加载

寄存器,以及一个值寄存器。

这是计数器本身。

为了使它发生, 我们将选择

时钟源作为 总线时钟。

我们将启用它,以便 SysTick 可以实际进行计数。

在先前的使用中,我们 使中断使能引脚为 0,

但现在我们将使用中断。

因此我要使它为 1。

正如我先前 提到的,计数

标志是该 SysTick 模块的另一部分。

该计数标志 会进行设置 --

因此随着它 从 4 到 3、2、1、0,

它将重新 加载到 n 值中,

我将 n 值存储在 该重新加载值中。

但这里的转换, 该 1 到 0 的转换

将设置计数标志。

由于我们已经 启用它,触发它,

因此如果我们 将 I 位设置为 0,

那么这些是导致 中断的三个条件。

首先我们必须 对它进行设置。

我们必须 启用它。

我们必须 触发它。

对于生成 中断而言,

这些是三个必要条件, 并且是充分条件。

初始化 相当简单。

也就是说,如果我 希望在每个周期

中断一次,周期的 单位为 20 纳秒,那么

我要将周期减 1 发送到重新加载值中。

例如,如果我希望 每一毫秒中断一次,

那么总共是 48,000 个总线周期。

这是转换。

48,000 个总线 周期是一毫秒。

因此,这里的值 将是 47,999。

那么,该中断 会恰好每毫秒

触发一次。

7 与这 相关联。

我们曾在先前的 视频中看到,如果

我们有多个中断, 那么我们可以

在该 8 位寄存器顶部的 3 个位中设置优先级。

这是一个 8 位寄存器。

顶部的 3 个位 包含该 SysTick

中断的优先级。

那么这是我们的例程。

这是我们的初始化。

它会执行一次。

在我们的主程序中, 在我们确实对一切

都进行 初始化之后,

我们将启用中断。

然后,第一个 中断将在计数器

从 1 到 0 计数时发生。

它将不停地 自动发生,

因为计数器 会翻转并继续

计数。

那么,这是初始化。

中断服务例程 本身相当简单。

再说一次,我们曾在上一张 幻灯片中看到,如果我编写

一个函数,并且我使用 这些特殊名称之一,

例如 SysTick handler, 这将成为 SysTick

中断的中断 服务例程。

现在,我要 执行一项任务。

请注意,这不是 一项很有趣的任务。

但这是一项一般任务。

这是该项任务 将遇到的点。

您应该记得计数 标志是我的触发器。

它使用某个值进行了设置, 当该值减小至零时,计数

标志进行了设置。

运行该中断 服务例程将自动

清除上一个 中断的标志。

好的,现在我们 在先前的视频中

看到了作为一种性能评测 方法的三次切换技术。

再说一次,它的功能和它的 名称所暗示的完全一样。

在该例程中,我要 对输出引脚进行

三次切换。

在前一个视频中, 我们讨论了位带。

那么我要获取 端口一的地址。

我要删掉 顶端部分。

我将把它乘以 32。

这样我会得到这个数字。

我要在其中添加的 是端口 1 位 0,

因此,我要 添加 4 次 0,

然后我会得到一个特定的寄存器, 一个特定的地址,这只是针对

端口 1 位 0 而言。

那么,如果我向端口 1 位 0 写入 1,它会进行切换。

但该三次切换技术的 美妙之处在于,

如果我放大逻辑 分析仪或示波器,

那么我可以看到三次 切换,一、二、三。

一、二、三,等等, 现在我能够测量

运行我的 例程

所花费的时间。

再说一次,这具有 极低的侵入性,

因为如果我要 每毫秒执行一次

该中断,那么执行这些代码行 所花费的时间将远少于

一微秒。

因此,调试检测 所花费的时间

与调试检测之间的 时间之比非常小。

再说一次,一微秒 与一毫秒相比非常小。

因此,它具有极低的侵入性。

这意味着调试 本身的存在

不会修改 我测试的内容。

但是,如果我缩小, 三次切换的美妙之处 --

如果我缩小,我就不再 能够在我的逻辑分析仪

示波器规模上分别 看到这三次切换。

现在,我只能 看到一次切换。

那么,该三次 切换将放大,

看起来像是一次切换。

因此,我将能够 非常轻松地测量

中断之间的时间。

这样,利用单个 调试设置,我能够

通过缩小测量 中断之间的时间,

并且能够通过 放大测量中断内的

时间。

这就是该调试 技术的美妙之处。

但再说一次, SysTick 非常简单。

我需要做的 只是设置周期。

然后它就会运行。

到目前为止,我在这次课中 已经两次提到这一点了。

现在,是时候 开始讨论称为

关键部分的 中断问题了。

那么,您需要 记住的一点是,

如果您曾共享过 任何东西,

这个永久性 --

因为您应该记得 我们有多个线程 --

如果它们曾共享 某个永久性结构,

这可能是全局性的, 也可能是 I/O 端口,

例如在本例中 --

您可以看到这是端口 2 的输出。

这是端口 2 的输出。

我有两个不同的线程。

我要共享它。

如果它们中的任何一个是写入 --

我允许您开始 为它感到担心。

因为在微观级别, 在纳秒总线级别

发生了某种情况, 它可能会非常

糟糕,是灾难性的。

那么,让我们来说明一下 可能会发生什么情况。

那么,我将再次获取我的 --

现在,看起来这个 会设置为 0,

那个会设置为 1。

那么这个会影响

您可能会说,嘿,John, 那个仅会更改端口 1

位 0。

这个仅会更改 端口 1 位 2。

但不是这样的, 因为中断以及

它们生成的线程 具有并发特性。

那么,让我们放大 机器代码、汇编代码,

它们由任何 适当的编译器

生成以尝试实现 该 C 代码行。因此,

正如我们在先前的 讲座之一中看到的,

要访问全局变量, 我首先要获取

一个指向 该端口的指针。

因此,R2 将 指向端口 2 --

指向端口 2 的输出。

接下来,它将 8 位 值存储到一个

寄存器中。

那么,该端口的值 将存储到寄存器中。

现在,某个 -- 让我们 在这里停一会儿,

思考一下刚才发生了什么。

在这里,我有一个 该端口中的值。

该 8 位端口的 所有位值

都位于该端口中。

但现在我有一个寄存器, 它包含的值和该值相同。

它具有该端口的值。

我在两个不同的位置 具有该同一数字。

那么,我要问您, 哪个是正确的?

情况变得更糟了。

让我们看看该点。

现在,我在这里具有 端口 2 的值,在端口 2 中,

就在这里。

我在该寄存器中具有 端口 2 的另一个值。

这两个值是不同的。

您看到了吗?

这个值现在设置了位 0。

那么,这时,我有 --

这两个值 --

我有存在于 两个不同位置的

信息的副本。

它们不一定 是相同的,这样 --

让我们在这里

让我们执行 该指令,一、二。

那么我得到了值。

让我们挑选一个 --

让我挑选一个数字。

那么我要挑选 --

只是为了说明 这是怎样出错的,

我要挑选一个 特定的数字。

那么,让我们假设 端口 2 中碰巧具有

0、0、0、0。

好的,这很好。

这只是初始的状态。

它包含一些零, 一共有 8 个。

1,2,3,4,5,6,7,8。

端口 2 包含 0。

让我们重点看看 底部的两个位。

这是我们在时间 1、时间 2 和时间 3 在这里

具有的内容。

那么,在这里, 我们的位 0 中是 0。

端口中 是 0。

但幸运的是 --

在这两条指令之间, 或在那两条指令之间 --

接下来让我们 执行那一条。

现在,这个 --

我刚才设置了位 1 --

它具有值 0。

我是说,它具有值 1,因为 我刚才把它与 1 进行了

或运算。

但我们获得了中断。

该线程 1 现在将运行。

那么,要执行的 下一条指令是那个。

那么,现在我 --

再说一次,您还记得吗, 上下文是它将所有

寄存器入栈, 并且为我创建了

一些新的 寄存器。

那么,该寄存器 r0 与那个寄存器是不同的。

那么,当我读取 这个寄存器时,

我将得到 0 的 另一个副本。

这一次,我将设置这个位。

那么,它将变为 2。

然后,我将 执行那个。

现在,在这里写入了该 2。

好的,那么这是时间 7。

然后,我 从中断返回。

请记住,连接 寄存器是 FFFFF9。

那么,它会 从中断返回。

那么,最终,它会 重新显示在这里,

这是第 9 步。

但您应该记得,它所做的是, 它将所有寄存器从堆栈中

弹出。

因此 r0 的值是 1。

因为我把它留在了这里。

那么,当我这么做时, 值会从 2 更改回到 1,

这是第 10 步。

那么,现在,如果 您考虑一下,

我已经执行了 这个和那个。

我最终 --

我应该最终 使一个端口等于 3。

但我最终使一个 端口等于 1。

这是因为该共享 全局变量本质上

是一个关键部分,它具有 对全局共享变量的非原子性 --

换句话说, 它可能被中断 --

读取、修改、写入。

这是一件糟糕的事情。

因此,我们要 做几件事情。

我们可以使这个成为端口 2,使这个成为端口 3。

这样就可以 摆脱该问题。

我们已经看到过 位带好几次了。

我们可通过它 摆脱该问题。

我们可以 为这两个

中断赋予相同的 优先级,这样它们

就无法相互中断。

这样就可以解决该问题。

或者,我们可以针对 关键部分禁用中断

再重新启用中断。

这会导致 --

一旦我开始 执行该序列,

它就会不可分割地 以原子方式运行。

它会以原子方式运行。

换句话说, 它无法拆分。

那么,这些就是我可以 用来解决该关键部分的

三种方法。

但请记住,如果您有 --

如果您有多线程, 并且它们不知为何

共享某个永久性的 东西,比如一个端口,

而某人采取了正确的 做法,请思考一下,

当一个东西使另一个 东西中断时,可能会

发生什么怪异的事情。

然后,使用这 三种方法之一

来解决 该关键部分。

好,那么我们 详细地讨论了中断。

我们讨论了 SysTick, 它是一种简单的方法,用来 --

它作为我们的第一个中断, 因为它非常简单,只有

几行初始化代码, 它是非常简单的

中断处理例程。

然后,我们再次 看到了三次切换技术,

它是一种用于查看 运行中断所花费的

时间和中断之间的 时间的方法。

我为您带来了有关 关键部分的坏消息,

关键部分是当您具有 非原子操作时发生的错误,

非原子意味着它可以拆分。

中断已启用。

该事件序列 --

非原子序列, 如对共享全局

变量的读取、修改、写入 --

这种序列 --

还有其他序列,如写入、 写入和写入、读取 --

但这种序列可能 会创建关键部分。

然后,我们讨论了三种 修复它的方法,这些

方法的思想是通过使它们 成为原子序列来删除共享。

我们可以通过 位带来删除共享。

我们可以通过更改 不同的端口来删除共享。

我们可以通过禁用中断 或使中断具有相同的

优先级来实现原子性。

好吧,祝您本次实验愉快。

本次实验 有点复杂。

但是,我认为一旦您完成它, 它就可以极大地提高您的技能。

好的,祝您愉快。 382

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

相关下载

查看全部

视频简介

TI-RSLK 模块 10 - 讲座视频 - 调试实时系统 - 理论

所属课程:TI机器人系统学习套件(TI-RSLK) 发布时间:2018.08.27 视频集数:69 本节视频时长:20:33

在该实验中,你将学习如何利用SysTick产生周期性中断。

已有7人参与了讨论去论坛跟帖交流
new
关闭广告