#define vs. enums用于寻址外设

我必须在基于ARM9的微控制器中编程外设寄存器。

例如,对于USART,我将相关的内存地址存储在enum

 enum USART { US_BASE = (int) 0xFFFC4000, US_BRGR = US_BASE + 0x16, //... }; 

然后,我在函数中使用指针来初始化寄存器:

 void init_usart (void) { vuint* pBRGR = (vuint*) US_BRGR; *pBRGR = 0x030C; //... } 

但我的老师说我最好使用#define ,例如:

 #define US_BASE (0xFFFC4000) #define US_BRGR (US_BASE + 0x16) #define pBRGR ((vuint*) US_BRGR) void init_usart (void) { *pBRGR = 0x030C; } 

他说,就像这样,你没有在堆栈中分配指针的开销。

就个人而言,我不太喜欢#define ,也不喜欢其他预处理器指令。 所以问题是,在这种特殊情况下,# #define非常值得使用而不是enum和堆栈分配指针?


相关问题: 想在基于ARM9的芯片中配置特定的外设寄存器

我一直偏好的方法是首先定义一个反映外设寄存器布局的结构

 typedef volatile unsigned int reg32; // or other appropriate 32-bit integer type typedef struct USART { reg32 pad1; reg32 pad2; reg32 pad3; reg32 pad4; reg32 brgr; // any other registers } USART; USART *p_usart0 = (USART * const) 0xFFFC4000; 

然后在代码中我可以使用

 p_usart0->brgr = 0x030C; 

当您有多个相同类型的外设的实例时,这种方法会更清晰:

 USART *p_usart1 = (USART * const) 0xFFFC5000; USART *p_usart2 = (USART * const) 0xFFFC6000; 

用户sbass提供了Dan Saks 出色专栏的链接, 该专栏提供了有关此技术的更多详细信息,并指出了其优于其他方法的优势。

如果你很幸运能够使用C ++,那么你可以为外设上的所有常见操作添加方法,并很好地封装设备的特性。

我担心enum对于这样的任务来说是死路一条。 标准将enum常量定义为int类型,因此通常它们与指针兼容。

有一天,在32位int和64位指针的架构上,你可能有一个不适合int的常量。 尚未明确定义将会发生什么。

另一方面, enum将在堆栈上分配内容的论点无效。 它们是编译时常量,与函数堆栈无关,或者与通过宏指定的任何常量无关。

Dan Saks为嵌入式系统编程撰写了许多专栏文章。 这是他最新的一个 。 他讨论了C,C ++,枚举,定义,结构,类等,以及为什么你可能会在另一个上面。 绝对值得一读,总是很好的建议。

根据我的经验,将#define用于此类事情的一个重要原因是它更多地是嵌入式社区中使用的标准习惯用法。

使用枚举而不是#define将从教师(以及将来的同事)生成问题/评论,即使使用其他技术可能具有其他优势(例如不会踩到全局标识符命名空间)。

我个人喜欢使用枚举来表示数字常量,但有时你需要按照习惯做什么和你在哪里工作。

但是,性能不应成为问题。

答案总是做老师想要的任何事情,然后通过课程然后根据自己的问题解决所有事情,并找出他们的理由是否有效并形成自己的意见。 你不能赢得反对学校,不值得。

在这种情况下,很容易编译为汇编程序或反汇编程序,以查看enum和define之间的差异。

我建议定义enum,让编译器对枚举感到不适。 我非常不鼓励使用指针,因为我看到每个编译器都无法准确地生成所需的指令,这种情况很少见,但是当它发生时,你会想知道你的最后几十年的编码是如何工作的。 指向结构或其他任何东西都要糟糕得多。 我经常因此受到抨击,并期待这一次。 在块周围太多英里,用这些问题修复了太多破碎的代码,忽略了根本原因。

我不一定会说两种方式都更好。 这只是个人偏好。 至于你教授的论点,这真的是一个没有实际意义的问题。 在堆栈上分配变量是一条指令,无论有多少,通常以sub esp, 10h的forms。 因此,如果你有一个本地或20,那么仍然只有一条指令为所有这些分配空间。

我想说#include的一个优点是,如果出于某种原因,你想改变访问指针的方式,你只需要在一个位置更改它。

我倾向于使用枚举,因为将来可能与C ++代码兼容。 我这样说是因为在我的工作中,我们在项目之间共享了很多C头文件,其中一些使用C代码,其中一些使用C ++。 对于那些使用C ++的人,我们经常喜欢将定义包装在命名空间中,以防止符号屏蔽,但是您不能将#define分配给命名空间。