蚊章汇 - 计时器
type: channel
link: https://linuxcn.bwsl.wang/tag/%E8%AE%A1%E6%97%B6%E5%99%A8/
items: 计算机实验室之树莓派：课程 4 OK04

计算机实验室之树莓派：课程 4 OK04
type: item
date: 2019-02-11T07:42:00+08:00
description: OK04 课程在 OK03 的基础上进行构建，它教你如何使用定时器让 OK 或 ACT LED 灯按精确的时间间隔来闪烁。假设你已经有了 课程 3：OK03 的操作系统，我们将以它为基础来构建。1、一个新设备定时器是树莓派保持时间的唯一方法。大多数计算机都有一个电池供电的时钟，这样当计算机关机后仍然能保持时间。到目前为止，我们仅看了树莓派硬件的一小部分，即 GPIO 控制器。我只是简单地告诉你做什么，然后它会发生什么事情。现在，我们继续看定时器，并继续带你去了解它的工作原理。和 GPIO 控制器一样，定时器也有地址。在本案例中，定时器的基地址在 20003000 16。阅读手册我们可以找到下面的表：表 1.1 GPIO 控制器寄存器地址大小 / 字节名字描述读或写200030004Control / Status用于控制和清除定时器通道比较器匹配的寄存器RW200030048Counter按 1 MHz 的频率递增的计数器R2000300C4Compare 00 号比较器寄存器RW200030104Compare 11 号比较器寄存器RW200030144Compare 22 号比较器寄存器RW200030184Compare 33 号比较器寄存器RW这个表只告诉我们一部分内容，在手册中描述了更多的字段。手册上解释说，定时器本质上是按每微秒将计数器递增 1 的方式来运行。每次它是这样做的，它将计数器的低 32 位（4 字节）与 4 个比较器寄存器进行比较，如果匹配它们中的任何一个，它更新 Control/Status 以反映出其中有一个是匹配的。关于 位 bit 、 字节 byte 、 位字段 bit field 、以及数据大小的更多内容如下：一个位是一个单个的二进制数的名称。你可能还记得，一个单个的二进制数既可能是一个 1，也可能是一个 0。一个字节是一个 8 位集合的名称。由于每个位可能是 1 或 0 这两个值的其中之一，因此，一个字节有 2 8 = 256 个不同的可能值。我们一般解释一个字节为一个介于 0 到 255（含）之间的二进制数。一个位字段是解释二进制的另一种方式。二进制可以解释为许多不同的东西，而不仅仅是一个数字。一个位字段可以将二进制看做为一系列的 1（开） 或 0（关）的开关。对于每个小开关，我们都有一个意义，我们可以使用它们去控制一些东西。我们已经遇到了 GPIO 控制器使用的位字段，使用它设置一个针脚的开或关。位为 1 时 GPIO 针脚将准确地打开或关闭。有时我们需要更多的选项，而不仅仅是开或关，因此我们将几个开关组合到一起，比如 GPIO 控制器的函数设置（如上图），每 3 位为一组控制一个 GPIO 针脚的函数。我们的目标是实现一个函数，这个函数能够以一个时间数量为输入来调用它，这个输入的时间数量将作为等待的时间，然后返回。想一想如何去做，想想我们都拥有什么。我认为这将有两个选择：从计数器中读取一个值，然后保持分支返回到相同的代码，直到计数器的等待时间数量大于它。从计数器中读取一个值，加上要等待的时间数量，将它保存到比较器寄存器，然后保持分支返回到相同的代码处，直到 Control / Status 寄存器更新。这两种策略都工作的很好，但在本教程中，我们将只实现第一个。原因是比较器寄存器更容易出错，因为在增加等待时间并保存它到比较器的寄存器期间，计数器可能已经增加了，并因此可能会不匹配。如果请求的是 1 微秒（或更糟糕的情况是 0 微秒）的等待，这样可能导致非常长的意外延迟。像这样存在被称为“并发问题”的问题，并且几乎无法解决。2、实现我将把这个创建完美的等待方法的挑战基本留给你。我建议你将所有与定时器相关的代码都放在一个名为 systemTimer.s 的文件中（理由很明显）。关于这个方法的复杂部分是，计数器是一个 8 字节值，而每个寄存器仅能保存 4 字节。所以，计数器值将分到 2 个寄存器中。大型的操作系统通常使用等待函数来抓住机会在后台执行任务。下列的代码块是一个示例。ldrd r0,r1,[r2,#4]ldrd regLow,regHigh,[src,#val] 从 src 中的数加上 val 之和的地址加载 8 字节到寄存器 regLow 和 regHigh 中。上面的代码中你可以发现一个很有用的指令是 ldrd。它加载 8 字节的内存到两个寄存器中。在本案例中，这 8 字节内存从寄存器 r2 中的地址 + 4 开始，将被复制进寄存器 r0 和 r1。这种安排的稍微复杂之处在于 r1 实际上只持有了高位 4 字节。换句话说就是，如果如果计数器的值是 999,999,999,999 10 = 1110100011010100101001010000111111111111 2 ，那么寄…