可移植性
通过一个被叫做硬件抽象层或者HAL的层去均等化这种差异是一种常见的方法。
在这方面嵌入式系统有点特别,因为我们通常没有操作系统和用户可安装的软件,而是只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有效,但是它不是确保可移植性最有效的方法。
简而言之,它是一组traits,其定义了HAL implementations,驱动,应用(或者固件) 之间的实现约定(implementation contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,HAL implementation就提供了某个功能)和方法(即,如果你要构造一个实现了某个trait的类型,约定保障你肯定有在trait中指定的方法)。
典型的分层可能如下所示:
一些在embedded-hal中被定义的traits:
- Serial communication
- I2C
- SPI
- Timers/Countdowns
- Analog Digital Conversion
像上面说的,HAL有三个主要用户:
一个HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成:
- 生成和初始化这个类型的函数,其经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。)
- 与那个类型有关的一个或者多个 embedded-hal traits 的
这样的一个 HAL implementation 可以有各种类型:
- 通过低级硬件访问,e.g. 通过寄存器。
- 通过操作系统,e.g. 通过使用Linux下的
- 通过适配器,e.g. 一个与单元测试有关的类型的模仿
一个驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多个传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
应用把多个部分结合在一起并确保需要的功能被实现。当在不同的系统间移植时,这部分的适配是花费最多精力的地方,因为应用需要通过HAL implementation正确地初始化真实的硬件,而且不同硬件的初始化也不相同,甚至有时候差别非常大。用户的选择也在其中扮演了非常重大的角色,因为组件能被物理连接到不同的端口,硬件总线有时候需要外部硬件去匹配配置,或者用户在内部外设的使用上有不同的考量。