诚然,我是C 的新手。不幸的是,我看到的大多数代码要么使用asm
调用或定义extern
函数,其主体在汇编文件中。
这就是为什么我非常退出以找到以下代码的原因。我已经研究了代码库3天了。
句法我了解下面代码的每一行;除了重要的是因为我不明白它的工作方式!
- types.h定义
u32
和uintptr
(我已经看过它们) - 当C和C 代码混合时,需要IFDEF __cplusplus。特别是因为
extern C
是C 特异的。如果GCC具有-fno-exceptions
参数
,则可以省略 -
volatile
用于防止编译器进行任何优化,因为该地址必须是完美的,因为它是写入/从寄存器中/读取的。
所有的话,我仍然不知道该代码实际上是如何写或从寄存器中读取的。
#include <circle/types.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline u32 read32 (uintptr nAddress)
{
return *(u32 volatile *) nAddress;
}
static inline void write32 (uintptr nAddress, u32 nValue)
{
*(u32 volatile *) nAddress = nValue;
}
#ifdef __cplusplus
}
#endif
#endif
*(unsigned int *)
是什么意思?这是如何用于读写寄存器的?nAddress
是否必须是物理地址
您正在寻找"内存映射输入输出"。
CPU与外部硬件交谈的最常见方法是通过内存总线 - 用于访问正常内存的相同总线。
首先,请记住,CPU与内存的互动不仅涉及读取和写入,还涉及诸如总线错误处理(无效访问),仲裁(多个访问相同内存的设备)和路由之类的东西(CPU可能希望访问多个多个内存设备)。为了处理此问题,使用A BUS协议。
要写或读取外部存储器CPU必须启动A 交易。它的确切顺序是由使用的总线协议定义的,但通常涉及以下步骤:
- 发送转移地址,类型,长度等。
- 接收响应 - 允许或拒绝(总线错误)。
- 如果允许交易传输实际数据。
启动转移的设备称为 master 或启动器,而负责处理交易的设备称为 slave 或目标。
确定从属处理事务的设备称为解码器或路由器。因此,交易通常会从主转到解码器,然后再到从设备。
总线协议当然提供了一种将数据传输到设备或从设备传输的方法。该设备可以是存储设备或其他任何设备。对于存储设备,其控制器通过编写或从内存单元格数字编写或读取数据来处理转换。
如果您熟悉面向对象的编程,则可以通过接口来考虑CPU连接到外部设备, 允许在指定地址读取和写作。该接口的实现可以做任何事情。这是完成内存映射I/O的方式 - CPU连接到一堆设备,每个设备都在特定地址范围内接收交易。将数据写入一个地址,将通过存储设备接收该数据,该数据将其存储到内存单元格数组,将数据写入另一个地址,可以通过SD Controller接收,并将其解释为" send send_status命令到SD卡"。
如果您也熟悉现代操作系统,则可以想到"一切都是文件"抽象。有些文件只是普通文件,例如他们充当记忆。其他文件不同。就像在Linux上阅读/proc/cpuinfo
一样,您可以为您提供有关CPU的信息一样,在某些地址上阅读可以为您提供有关IRQ当前正在待处理的信息或告诉您邮箱目前有多少消息的信息。
公交协议的示例是AXI和AHB。AHB更简单,AXI更复杂,更快。在覆盆子Pi的情况下,很可能是将CPU连接到硬件的AXI协议。
因此,关于您的问题,这两个功能用于通过内存映射的I/O访问外部设备的寄存器。您正确正确的一切:
-
volatile
用于防止编译器删除,重新排序或以任何方式更改这些内存访问 - 没有此硬件将无法执行我们想要的操作。 -
u32
之所
(unsigned int *)
是一种铸造到unsigned int
指针的类型。取消运算符*
用于访问该指针指向的内存位置。