TL;博士:
- 为什么不
(unsigned long)(0x400253FC)
等于(unsigned long)((*((volatile unsigned long *)0x400253FC)))
? - 如何制作与前者一起使用的宏与后者一起使用?
背景资料
环境
我正在使用 ARM Cortex-M3 处理器,TI 的LM3S6965,以及他们的StellarisWare(免费下载,出口控制)定义。我正在使用 gcc 4.6.1 版(Sourcery CodeBench Lite 2011.09-69)。Stellaris 在“inc/lm3s6965.h”中提供了大约 5,000 个寄存器和内存地址的定义,我真的不想重做所有这些。但是,它们似乎与我要编写的宏不兼容。
位带
在 ARM Cortex-M3 上,一部分内存与外设和 RAM 内存空间的每位 32 位字混叠。将地址 0x42000000 的内存设置为 0x00000001 会将地址 0x40000000 的内存的第一位设置为 1,但不会影响字的其余部分。要更改第 2 位,请将 0x42000004 处的字更改为 1。这是一个简洁的功能,并且非常有用。根据 ARM 技术参考手册,计算地址的算法是:
bit_word_offset = (byte_offset x 32) + (bit_number × 4)
bit_word_addr = bit_band_base + bit_word_offset
在哪里:
bit_word_offset
是目标位在位带内存区域中的位置。bit_word_addr
是别名内存区域中映射到目标位的字的地址。bit_band_base
是别名区域的起始地址。byte_offset
是包含目标位的位带区域中的字节数。bit_number
是目标位的位位置,0 到 7
位带的实现
该"inc/hw_types.h"
文件包括以下实现此算法的宏。需要明确的是,它是为一个基于字的模型实现的,该模型接受 4 字节对齐的字和 0-31 位偏移量,但结果地址是等价的:
#define HWREGBITB(x, b) \
HWREGB(((unsigned long)(x) & 0xF0000000) | 0x02000000 | \
(((unsigned long)(x) & 0x000FFFFF) << 5) | ((b) << 2))
该算法采用 SRAM 中 0x20000000 或外设存储器空间 0x40000000 的基数,并将其与 0x02000000 进行或运算,加上位带基数偏移。然后,它将基数的偏移量乘以 32(相当于左移五位)并加上位数。
引用的 HWREG 仅执行必要的转换以写入内存中的给定位置:
#define HWREG(x) \
(*((volatile unsigned long *)(x)))
这对于像这样的任务非常有效
HWREGBITW(0x400253FC, 0) = 1;
其中 0x400253FC 是内存映射外设的幻数,我想将此外设的位 0 设置为 1。上面的代码计算(当然是在编译时)位偏移并将该字设置为 1。
什么不起作用
不幸的是,“inc/lm3s6965.h”中的上述定义已经执行了由 HWREG 完成的转换。我想避免使用幻数,而是使用提供的定义,例如
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
尝试将其粘贴到 HWREGBITW 会导致宏不再工作,因为强制转换会干扰:
HWREGBITW(GPIO_PORTF_DATA_R, 0) = 1;
预处理器生成以下混乱(添加了缩进):
(*((volatile unsigned long *)
((((unsigned long)((*((volatile unsigned long *)0x400253FC)))) & 0xF0000000)
| 0x02000000 |
((((unsigned long)((*((volatile unsigned long *)0x400253FC)))) & 0x000FFFFF) << 5)
| ((0) << 2))
)) = 1;
注意两个实例
(((unsigned long)((*((volatile unsigned long *)0x400253FC)))))
我相信这些额外的演员是导致我的过程失败的原因。以下预处理结果HWREGBITW(0x400253FC, 0) = 1;
确实有效,支持我的断言:
(*((volatile unsigned long *)
((((unsigned long)(0x400253FC)) & 0xF0000000)
| 0x02000000 |
((((unsigned long)(0x400253FC)) & 0x000FFFFF) << 5)
| ((0) << 2))
)) = 1;
强制(type)
转换运算符具有从右到左的优先级,因此最后一个强制转换应该适用并unsigned long
用于按位算术(然后应该可以正常工作)。任何地方都没有隐含的东西,没有浮点到指针的转换,没有精度/范围的变化......最左边的演员应该简单地取消右边的演员。
我的问题(最后......)
- 为什么不
(unsigned long)(0x400253FC)
等于(unsigned long)((*((volatile unsigned long *)0x400253FC)))
? - 如何使现有的
HWREGBITW
宏工作?或者,如何编写宏来执行相同的任务,但在给定具有预先存在的演员表的参数时不会失败?