1 课时2 OK02
这个课时以上一个课时为基础,让'OK' LED灯能够反复的开关。本课时的前提是你已经有了前一课的代码 作为基础。
1.1 等待
等待是操作系统开发中很有使用价值的一件事情。操作系统经常会发现自己无事可做,这样就只能等待新的任务。在这个例子中,我们希望通过重复开关LED来展示操作系统空闲的时候。因为如果你知识简单的关闭和开启LED,往往没法展示出来目前的状态,因为计算机可以让这个LED每秒开关上千次。后续的课程中,我们会有精确的等待,目前,简单的就这么单纯的耗费时间就足够了。
mov r2,#0x3F0000 wait1$: sub r2,#1 cmp r2,#0 bne wait1$
上面的代码是一般来用于实现一个延时的代码,由于 Raspberry Pi 的结构基本相同,这个代码基本上可以产生一个相同的延时。使用方法就是,使用一个 mov 指令将 3F000016 放入寄存器 r2 中,然后一直减1到0为止。 这里接触的几个新指令是 sub , cmp 和 bne .
sub 就是减法指令,将第二个参数上的数值从第一个参数上减去。
cmp 是一个比较有趣的指令。它把第一个和第二个参数的值进行比较,然后把比较结果放在一个特定的寄存器上,这个寄存器叫做当前处理器状态寄存器(current processor status register).不用考虑太多,简要说就是把两个数值的比较结果记录下来,孰大孰小或者还是相等1。
bne 这个就是一个分支指令。在ARM的汇编语言里面,所有的指令都可以更具条件分支执行。也就是说,这个指令只有当之前的一个比较指令有结果的时候才会执行。之后的内容会经常用到这一指令来实现一些有趣的技巧。在这里,我们使用 ne 后缀的 b 指令,意思是只有最后的比较不是相等的情况下才执行。 ne 前缀可以用在所有的指令上,类似的还有16个条件,比如 eq 代表相等。 lt 代表小于。
1.2 合而为一
上节课中提到过,LED灯可以通过向GPIO控制器的28偏移量写入数据来关闭(代码就是 str r1, [r0,#28] ).所以你得修改一下代码,先打开LED灯,等待,关闭,再等待,然后从头开始新的循环。GPIO 16的操作只需要一次,不用每次开关都操作。其实如果你想试试,可以重用 r1 的值。在下载 页面都可以找到所有课程的代码。要注意代码的标签要唯一。比如,一旦你用过了 wait1$ ,那么其他地方就不能用了。
在我的 Raspberry Pi上差不多每秒闪动两次。时间可以通过修改 r2 的值来灵活调整。不过,具体的运行时间是没法预测的。要是工作不正常的话,请查看下 trouble shooting 页面吧。如果正常工作,那么恭喜你。
在这节课中,我们学习了两个新的汇编指令, sub 和 cmp ,还有ARM上的条件执行。 下节课中,Lesson 3: OK03 会讨论下通过良好的代码和严格的规范来提高代码的复用,可能会用上c和c++代码。
Footnotes:
要是看到这了,估计你确实想了解的更加深入点。CPSR是一个32位的寄存器,有更多个独立的bit位组成。有代表正负数或者是零的bit位。cmp执行的时候,就拿第二个参数去减第一个,记录下来结果的正负。零就代表相等,正数就代表 a > b,负数就代表小于。还有其他很多的比较方法,不过cmp是最直观的一个了。