第1课OK01
OK01课程包含如何开始编程,教你如何去使能’OK’这个led灯,这个灯在pi板子的靠近RCA(黄色端口)和USB端口之间的位置,我做了实验后,发现这个灯就是PWR上面的那个ACT灯。
本课内容lesson1
-
1开始准备
-
2开始
-
3第一行真正的代码
-
4使能输出
-
5 A Sign Of Life(点亮你的led,表明开发板有动起来的迹象)
-
6 Happily Ever After(皆大欢喜)
-
7 Pi Time(到了pi的时间了)
1开始前的准备
我假设你已经到下载页面,下载了编译代码的gnu工具链。同时你也下载了一个OS的模板。
请下载这个模板然后解压到一个新的文件目录。接上次课,我们已经建立了一个raspberry的目录,所以下面的工作都是在这个raspberry目录下完成的。还有为了方便,我把gnu的工具链,操作系统镜像,os的模板,还有教程,都放在我的百度云盘了。大家可以到那下载。
当然你需要的硬件:raspberrypi的板子,sd卡,给pi供电的电源。
帐号:397916230@qq.com
密码: 397916230
当然翻译这教程,我也是边学,边翻译的。有些内容也很简单,本来是不想翻译的,但是为了给初学者一个引导作用,我还是翻译了。希望为中国的编程教学,贡献出自己的一份力量。
不多说了,继续。
2开始
现在你解压出了ostempate的模板,解压的命令也很简单:tar xvf template.tar.gz,然后我们在template目录下新建一个source目录,在souce目录下新建一个文件man.s,在这个文件中我们编写操作系统的代码。打开这个mani.s文件,看个人喜好,我用的是nano,
树莓派用的汇编是ARMv6的的版本,因为树莓派上的cpu的指令架构是ARMv6的。所以我们需要写的代码如下:
.section .init
.globl _start
_start:
事实上,上面的代码什么也没有做。上面的指令都是给assembler(汇编器)看的,是我们编程时辅助用的。assembler(汇编器)的作用就是把汇编代码(人类可以理解的)翻译成二进制码(cpu可以理解的)。在汇编代码串,每一行是一个命令。在上面的代码中,第一行告诉assembler(汇编器),我们的代码放在哪里。在我提供的template中,.init段的代码是放在最开始的位置。这对于我们确保在开始时就运行自己的代码,非常重要。如果你不这样做,cpu在开始时就会运行其它的代码。.section命令只是简单的告诉编译器,我们的代码是放在哪一段了,从这一点开始,到下一个.section,之间的代码,都会入在这个section中。在汇编代码中,我们可能会为了增强代码的可读性加入一些空格和空行。
对上面的过程整理下:
1:解压template
tar xvf template.tar.gz
2
:进行telmpatle目录,新建source目录
cd template/
mkdir source
3:
进入source目录,新建main.s文件
cd source
nano main.s
4:
在main.s中输入下面的代码
.section .init
.globl _start
_start:
ldr r0,=0x20200000
mov r1,#1
lsl r1,#18
str r1,[r0,#4]
mov r1,#1
lsl r1,#16
str r1,[r0,#40]
loop$:
b loop$
5 :保存main.s文件后退出,ctr+x ;然后选择y后,回车。
6:回到template目录下,输入make,即可得到kernel.img的文件,这就是我们后面要得到的程序,
译者补充:我把下面几步的代码都贴到这里了,后面再讲解;。
译者补充:对于这些.section部分的起名,还有如何将这些断放到具体的位置,这些知识是与makefile,链接文件有关的。目前可以不用看这些东西,有兴趣的可以先看下template目录下kernel.ld文件。我们会看到.init这段代码放到了最前面。以后我再作为一个部分讲讲这些内容。
译者补充:.S(大写的S)是汇编文件的后缀,大写的汇编文件中,可以用#define这样的宏,而.s(小写的)这样的汇编文件中,不能用宏
3第一行真正的代码
现在我们真正的来编写代码了。在汇编代码中,cpu只是简单的取得这些代码,然后按顺序执行一条一条的代码,当然你的代码中有跳转的话,也会跳到别的地方去。每一条指令占一行。下面这行代码的做了什么?
ldr r0,=0x20200000
ldr reg,=val把val这个值放到寄存器中
这是我们的第一条命令。他告诉cpu把这个数0x20200000保存到r0寄存器中。这里我要回答两个问题:什么是寄存器,0x20200000个什么数。
在树莓派用到的cpu中,一个寄存器,可以保存0到4,294,967,295范围的整数。这个数看起来很大,这是一个32位的寄存器可以保存的数。寄存器是一个很小的存储单元,注意它是在cpu内部。cpu用它来存储数据,并对这些数据进行操作。像r0这样的通过寄存器有很多,如r0到r12这13个都是通过的寄存器。我们可以用它们做我们需要做的计算。在这个例子中,我们用到了r0。但是只要你喜欢,你也可以用它们中的任何一个,这不是重点。0x20200000就是一个真实的数,但是它是以16进制的形式表示的。关于原文中10进制16进制的数转化的内容我就不翻译了,有问题再留言问吧。
现在我们知道这一行代码是令r0=0x20200000,但是为什么要这样做呢?因为在电脑中,会有大量的memory和devices。为了能使用他们,控制他们。我们要给他们每人一个地址。和邮编类似。说白了,我们通过这些地址找到这些memory或是devices。地址在电脑中就是数字。在这里0x20200000这个数字正好是GPIO控制器的地址。这只是设计cpu的厂家在设计时决定的。(当然在设计cpu时也可以用其它的地址,只要与其它的设备不冲突)
我知道这个地址,只是因为我看了官方提供的手册而已(译者:手册我已经上传到百度云soc-peripherals),对于这些系统的地址没有什么特别的,都是一些非常大的16进制的数。
4使能输出
一些汇编的指令:
movreg,#val把数字val放到reg寄存器中。
lslreg,#val把reg寄存器中的数按二进制的方式左移val位
strreg,[dest,#val]把reg寄存器中的数据保存到cpu外面存储器中,存储器的地址为dest中的数+ val数.
通过读上面的手册,我知道我们需要发送两个messages给GPIO控制器。我们需要说GPIO控制器能听的懂的语言,如果我们这样做了,GPIO控制器就会按我们说的那样点亮那个OKled.幸运的是,这是个非常简单的芯片,我们只需要发送一些数字,GPIO控制器就会理解我们想做什么。
mov r1,#1
lsl r1,#18
str r1,[r0,#4]
这些指令使能了GPIO的第16个引脚。首先我们得到需要的数保存在r1中,然后把r1中保存的这个数发送到GPIO控制器中。前面的两条指令就是把我们想要的值保存到r1中,我们也可以像先前那样,用一条ldr指令就完成操作。但是这两条指令对我们后来设置任意GPIO的使能有很大的帮助。所以我们用来两条指令来完成这个操作,而不是一条直接赋值的操作。
OKLED通过导线与GPIO的第16个引脚相连,所以我们需要指令全能第16个引脚。r1中的值,就是用来使能LED引脚的。
第一条指令把1放到r1寄存器中,mov指令要比ldr指令更快,因为mov指令不涉及对外部的memory的操作。但是ldr指令是加载到寄存器中的值通常是保存在外部memory中的。但是,mov指令只能传送小范围的数到寄存器中。在ARM的汇编代码中,几乎所有的指令都是以三个字母开始的。这样做是为了增强记忆,同是告诉你这个指令是做什么的。mov指令是move的简写,而ldr指令是loadregister的简写。mov指令把第二个数送到r1寄存器中。通常在mov指令中,第二个数要以#开头,表明这是一个数。
第二条指令中的lsl指令是logicalshif left。它的意思是以二进制的形式把第一个数,逻辑左移多少位,(多少位通过第二个参数表示)。在这个例子中把r1中的12左移了18位就变成了10000000000000000002,(译者注:官方有对二进制进行解释,我就不翻译了,不懂的留言)
再次强调:我只是通过阅读了官方的cpu手册,才知道用什么值操作哪个地址的。手册上说一个GPIO控制器有24字节,这24字节决定了GPIO引脚的设置。前4个字节决定前10个GPIO引脚的位置。第二个4个字节决定了接下来的10个GPIO引脚的设置,如些下去。cpu上有54个GPIO引脚。所以需要6x4字节的GPIO控制器来控制这54个引脚,4个字节中的每3位与1个特定的GPIO引脚有关。因此我们想要控制第16个GPIO引脚,我们需要控制第二个有4个字节的寄存器,在这4个字节的寄存器中,我们操作pins10到19,q我们需要操作的是pin16,所以在这组寄存器中操作的是第6个三位的数据,在寄存器中的位置就是
6x3=18位。可以看到上面的代码中对我们把1移到了bit18。最后我们用str指令(storeregister)把把r1寄存器中的值保存到了GPIO的第二对控制器中,对于最后一条指令中
是反r0的值加上#4,然后把这个和作为地址。把r1的值保存到这个地址中。这里我们把第1个message送到了GPIO控制器中,告诉控制器把第16引脚配置成输出状态。
5 点亮你的raspberrypi
现在LED已经准备点亮了,我们需要真的点亮他。这意味这我们要给GPIO的控制器发送第2条message,把16引脚关掉。没错,是关掉。cpu设计厂家就是这样设计的,你要点亮你的LED,你就是关闭pin。硬件设计工程师也决定了这样的操作才能点亮led.
没有想像中的复杂,就是把0x20200028GPIO控制器中的第16位置1,就可以点亮led了。下面的操作就是做这样的事
mov r1,#1
lsl r1,#16
str r1,[r0,#40]
希望你能看明白上面的三条代码,因为和之前看到的代码没有多大不同。因为我们想让bit16置1,点亮led。第16引脚是off的,说明输出低电平,led点亮。
6 皆大欢喜
现在工作就完成了,但是cpu停止,他会一直运行下去,只要他有供电源,他就会一直工作。因此,我们需要给他一个任务做,给他一个死循环的任务去做。下面任务没有任何意思,只是没有这个任务,cpu会跑飞。
loop$:
b loop$
我们称loop$:是一个label,,label代表下面代码的地址。
blabel就是跳转到label地址处。
label只是代表下面这行代码的地址,当编译成了机器码后,这个label就没有了,但是我们写程序是时候,这个label可以让我们知道,我们想跳转到哪个地址去。上面这条指令把cpu
stuck住,做一个没用的死循环,直到你安全地关闭电源。
在最后一行最好留一空行,编译器知道你的代码写完了,要不然会产生一个warn,当然不写也没有多大问题。只是一个好习惯。
7 Pi Time(到了pi的时 间 了 )
我们代码现在写完了,现在开始生成二进制的机器码。保存main.s,后到template目录下,
输入:make后,得到机器码kernel.img。下面是如何把这里生成的kernel.img文件。放到sd卡中。然后点亮led.
在ubuntu12.10平台下:
如果你的sdcard已经有原来的能在树莓派上跑的os下面就不用看了,直接插上sd到电脑,把sd中的kernel.img改个名字,再把刚才生成的kernel.img放到sd卡中,然后sd卡插到树莓派就可以看到OKLED亮起来了。
1:先看一下你的硬盘分区是如何:
df -h
2:
插入你的sd卡到电脑,再看下你的sd卡叫什么:
df -h
3:
对比1操作和2操作,会看到你的sd卡在/dev/下面的什么名字
4:把sd卡umount掉,后在sd卡上写入操作系统镜像(系统镜像在百度云盘中有),命令如下:(后面的of=/dev/mmcblk0是你的sd的名字)
unzip2013-02-09-wheezy-raspbian.zip
sudodd bs=2M if=2013-02-09-wheezy-raspbian.img of=/dev/mmcblk0
5:写完后同步一下:
sudo sync
6:拔掉sd卡重新插入电脑,把sd中的kernel.img改个名字,再把刚才我们在template目录下生成的kernel.img复制到到sd卡中,然后sd卡插到树莓派就可以看到OKLED亮起来了。(就是ACT灯)
下一课lesson2:OK02,我们将让LED闪起来。
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok02.html#waiting