# 外设

## 什么是外围设备？

大多数微控制器都是SoC,不仅仅具有CPU，RAM或闪存，芯片内部还集成了各种设备,用于与外部系统进行交互. 比如通过传感器，电机控制器直接或间接与周围环境进行交互。 或其他的人机界面，例如显示器或键盘。这些组件统称为外围设备。

这些外围设备很有用，因为它们使开发人员可以将一部分工作分派出去，而不必全部由软件来实现。与台式机开发人员将图形处理任务分派给显卡的方式类似，嵌入式开发人员可以将某些任务分派到外围设备，从而使CPU可以将时间花在更重要的事情上，或者不做任何事以节省功耗。

如果您看一下1970年代或1980年代的老式家用计算机中的主板(实际上，以前的台式机与今天的嵌入式系统相去不远)，您会看到：

* 处理器
* RAM芯片
* ROM芯片
* I/O控制器

RAM芯片，ROM芯片和I/O控制器(此系统中的外围设备)通过“总线”连接到处理器。处理器通过地址总线选择与哪个设备进行通信，通过数据总线传输数据。在我们的嵌入式微控制器中，原理都是一样的--只是将所有内容包装在一块芯片内。

但是与显卡不同的是,显卡一般提供了像Vulkan，Metal或OpenGL之类的软件API，而嵌入式外设通过内存映射的方式,直接将硬件接口暴露给我们的微控制器。

## 线性和物理内存地址空间

在微控制器上，将一些数据写入任意地址，例如`0x4000_0000`或`0x0000_0000`，也可能是完全有效的操作。

与嵌入式系统不同,在台式机系统上，对内存的访问由内存管理单元(MMU)严格控制,MMU有两个主要职责：强制执行对内存的访问权限(防止一个进程读取或修改另一进程的内存)；并将物理内存的地址重新映射到软件中使用的虚拟内存地址。微控制器通常没有MMU，而仅使用实际物理地址。

尽管32位微控制器具有从0x0000_0000到0xFFFF_FFFF的物理和线性地址空间，但它们通常仅使用该范围的几百K字节作为实际内存。这留下了大量的可用地址空间。在前面的章节中，我们讨论了位于地址“0x2000_0000”上的RAM。如果我们的RAM大小为64 KiB(即最大地址为0xFFFF)，则地址“0x2000_0000”到“0x2000_FFFF”将对应于我们的RAM。当我们写入位于地址“0x2000_1234”的变量时， 某些逻辑检测地址的上半部分(在此示例中为0x2000)，然后激活RAM控制器，由RAM控制器来处理地址的下半部分(在这种情况下为0x1234)。在Cortex-M上，我们将Flash ROM映射到地址“0x0000_0000”到地址“0x0007_FFFF”之间(如果我们有512 KiB Flash ROM)。微控制器设计人员没有忽略这两个区域之间的剩余地址空间，而是将某些内存位置映射给了外设。最终看起来像这样：

![](../assets/nrf52-memory-map.png)

[Nordic nRF52832手册(pdf)]

## 内存映射的外围设备

乍看之下，与这些外设的交互非常简单--将正确的数据写入正确的地址。例如，通过串行端口发送32位数据可能与将32位数据写入某个内存地址一样直接。写入后，串口外设将接管并自动发送数据。

这些外设的配置工作与内存操作类似。无需调用函数来进行配置一个外设，而是公开了一块用作配置硬件的内存区域。比如将“0x8000_0000”写入SPI频率配置寄存器，SPI端口将以每秒8兆位的速度发送数据。将“0x0200_0000”写入相同的地址，SPI端口将以每秒125 Kilobits的速度发送数据。这些配置寄存器看起来像这样：

![](../assets/nrf52-spi-frequency-register.png)

[Nordic nRF52832手册(pdf)]

无论使用哪种语言，无论该语言是Assembly，C还是Rust，都是这样与硬件进行交互。

[Nordic nRF52832手册(pdf)]: http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf