|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识


编辑:陈萍
树莓派虽小 , 小到仅有信用卡大小 , 但功能却和普通电脑无异 , 可以将其连接电视、显示器、键盘鼠标等设备使用 。 也可以处理文字、电子表格、媒体甚至是游戏 。 那么这个神奇的小电脑 , 怎样用它来进行编程呢?下面介绍一篇利用树莓派进行裸机编程的教程 , 顺便学习一下接口、硬件等知识 。

|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识
本文插图
近日 , 有人在 GitHub 上开源了一个关于树莓派的教程 。 不同于以往的树莓派开发 , 这篇教程的核心内容是讨论如何在树莓派上进行裸机编程 。

|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识
本文插图
教程地址:https://github.com/bztsrc/raspi3-tutorial
在树莓派 3 上进行裸机编程
该系列教程面向那些想要编译自己的树莓派裸机应用程序的人 , 具体目标受众是那些对树莓派硬件不熟悉 , 但在业余时间又爱好 OS 的开发人员 。 在这篇教程里 , 作者给出了一些示例来完成基本的操作 , 比如:将代码写入串行控制台、从串行控制台中读取按键、设置屏幕分辨率并绘制到线性帧缓冲区 。 此外 , 作者还展示了如何获取硬件的序列号、硬件支持的随机数 , 以及如何从启动分区读取文件 。
需要注意的是:这篇教程没有涉及编写 OS 。 诸如内存管理、虚拟文件系统、实现多任务处理之类的主题也不会介绍 。 该教程将重点介绍与硬件的接口 , 而不是关于 OS 的理论 。 此外 , 该教程假设你具有一定的 GNU/Linux 知识 , 对编译程序、创建磁盘和文件系统镜像有一定的了解 。
对于为何选择树莓派 3 , 作者给出了解释:首先 , 它既便宜又容易买到 。 第二 , 它是 64 位的 , 拥有非常大的地址空间 。 第三 , 它只使用 MMIO , 这使得编程更容易 。
该教程使用 C 语言进行开发 , 因为 C 语言能够直接对硬件进行开发 。
预备知识
在开始前 , 你将需要在 FAT 文件系统上使用交叉编译器(有关详细信息 , 请参见 00_crosscompiler 目录)和带有固件文件的 Micro SD 卡 。
每个目录都有一个 Makefile.gcc 以及 Makefile.clang 。 确保 Makefile 符号链接根据你自己选择的交叉编译器指向版本 。
作者给出的建议是买一个 Micro SD 卡 USB 适配器(许多制造商都会提供这种适配器的 SD 卡) , 这样就可以像 USB 一样将该卡连接到任何台式计算机上 , 而不需要特殊的读卡器接口(尽管现在很多笔记本电脑都有这种接口) 。 如果你不喜欢 dd 命令 , 你也可以选择 USBImager , 这是一个简单的 GUI 应用程序 , 具有可移植的可执行文件 , 可用于 Windows、MacOSX 和 Linux 操作系统 。

|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识
本文插图
Micro-SD 卡 USB 适配器 。
在带有 LBA FAT32(类型 0x0C)分区的 SD 卡上创建 MBR 分区方案 , 并对其格式化 , 然后将 bootcode.bin、start.elf 以及 fixup.dat 复制到其中 。 或者 , 你可以下载一个 raspbian 镜像 , dd 命令烧写到 SD 卡 , mount 挂载并删除不必要的. img 文件 。 不管你喜欢哪种方法 , 重点是你将使用这些教程创建 kernel8.img , 而且必须复制到 SD 卡的根目录中 , 后者不应该存在其他. img 文件 。
建议使用 USB 串行调试电缆 。 把它连接到 GPIO 引脚 14/15 , 然后在电脑上按如下方式运行 minicom:

|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识
本文插图
USB 串行调试电缆
仿真
不幸的是 , 官方的 qemu 二进制文件还不支持树莓派 3 。 但作者已经实现了 , 并将很快发布(更新:在 qemu2.12 中提供)(https://wiki.qemu.org/ChangeLog/2.12#ARM) 。 在此之前 , 你必须从最新的源代码编译 qemu 。 编译后 , 可进行如下操作: 分页标题
或者:
-M raspi3:让 qemu 仿真树莓派 3 硬件 。
-kernel kernel8.img:告知要使用的内核文件名 。
-drive file=$(yourimagefile),if=sd,format=raw:在第二种情况下 , 该参数为 SD 卡镜像 , 它也可以是标准的 rasbian 镜像 。
-serial stdio
-serial null -serial stdio:将模拟的 UART0 重定向到运行 qemu 的终端的标准输入 / 输出 , 以便显示发送到串行线路的所有内容 , 并且 vm 会接收终端中键入的每个键 。 该操作仅适用于教程 05 及更高版本 , 因为默认情况下不会重定向 UART1 。 为此 , 必须添加一些类似于 - chardev socket , host=localhost , port=1111 , id=aux -serial chardev:aux 的参数 , 或者简单地使用两个 -serial 参数 。
硬件资源
下面简单介绍一下所需硬件资源 , BCM2837 SoC 芯片 。 包括:
VideoCore GPU;
ARM-Cortex-A53 CPU (ARMv8);
MMIO 映射外部设备 。
有趣的是 , CPU 不是主板上的主处理器 。 当它通电后 , 第一个 GPU 运行 。 当初始化完成时 , 通过执行 bootcode.bin , 它将加载并执行 start.elf 。 这不是一个 ARM 可执行文件 , 而是专门为 GPU 编译的 。 比较有意思的是 , start.elf 寻找不同的 ARM 可执行文件 , 都以 kernel 开头 , 以. img 结尾 。 由于要在 AArch64 模式下对 CPU 进行编程 , 因此只需要 kernel8.img , 这也是最后一个要查找的 。 加载后 , GPU 触发 ARM 处理器上的复位线 , 开始在地址 0x80000(或更准确地说是 0)处执行代码 。
RAM(1G Raspberry Pi3)在 CPU 和 GPU 之间共享 , 这意味着一个可以读取另一个写入内存的内容 。 为了避免混淆 , 需要定义好 mailbox 接口 。 CPU 将消息写入 mailbox , 并通知 GPU 读取它 。 GPU(知道消息完全在内存中)解释它 , 并将响应消息放在同一个地址 。 CPU 必须循环访问内存以知道 GPU 何时完成 , 然后它才能读取响应 。
相似的 , 所有外部设备都在内存中与 CPU 通信 。 每个设备都有从 0x3F000000 开始的专用内存地址 , 但是它不在真实的 RAM 中(称为内存映射 IO) 。 现在没有用于外围设备的 mailbox , 而是每个设备都有其自己的协议 。 这些设备的共同点是:必须以 32 位为单位在 4 个字节对齐的地址(所谓的字)上读取和写入其内存 , 并且每个设备都有控制 / 状态和数据字(data words) 。 不幸的是 , Broadcom(SoC 芯片的制造商)在记录产品方面很差 。 现在所拥有的最好的是 BCM2835 文档 , 这个文档就足够了 。
CPU 中还有一个内存管理单元 , 允许创建虚拟地址空间 。 这可以通过特定的 CPU 寄存器进行编程 , 并且在将这些 MMIO 地址映射到虚拟地址空间时必须小心 。
一些更有趣的 MMIO 地址是:
【|OS开发爱好者福利:树莓派上编译C语言,顺便掌握一波硬件知识】更多信息 , 请参见:https://github.com/raspberrypi