TI教室 >
应用与设计 >
工业应用 >
工厂自动化与控制系统 >
Automated Machinery >
TI-RSLK 模块 10 - 调试实时系统 > 讲座视频 - 调试实时系统 - SysTick 中断
- 本课程为精品课,您可以登录eeworld继续观看:
- 讲座视频 - 调试实时系统 - SysTick 中断
- 登录
- 课程目录
- 相关资源
- 课程笔记
大家好,我是 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,它是一种简单的方法,用来 --
它作为我们的第一个中断,因为它非常简单,只有
几行初始化代码,它是非常简单的
中断处理例程。
然后,我们再次看到了三次切换技术,
它是一种用于查看运行中断所花费的
时间和中断之间的时间的方法。
我为您带来了有关关键部分的坏消息,
关键部分是当您具有非原子操作时发生的错误,
非原子意味着它可以拆分。
中断已启用。
该事件序列 --
非原子序列,如对共享全局
变量的读取、修改、写入 --
这种序列 --
还有其他序列,如写入、写入和写入、读取 --
但这种序列可能会创建关键部分。
然后,我们讨论了三种修复它的方法,这些
方法的思想是通过使它们成为原子序列来删除共享。
我们可以通过位带来删除共享。
我们可以通过更改不同的端口来删除共享。
我们可以通过禁用中断或使中断具有相同的
优先级来实现原子性。
好吧,祝您本次实验愉快。
本次实验有点复杂。
但是,我认为一旦您完成它,它就可以极大地提高您的技能。
好的,祝您愉快。
课程介绍
共计4课时,1小时4分14秒
猜你喜欢
换一换
推荐帖子