2

TL;博士:

  1. 为什么不(unsigned long)(0x400253FC)等于(unsigned long)((*((volatile unsigned long *)0x400253FC)))
  2. 如何制作与前者一起使用的宏与后者一起使用?

背景资料

环境

我正在使用 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用于按位算术(然后应该可以正常工作)。任何地方都没有隐含的东西,没有浮点到指针的转换,没有精度/范围的变化......最左边的演员应该简单地取消右边的演员。

我的问题(最后......)

  1. 为什么不(unsigned long)(0x400253FC)等于(unsigned long)((*((volatile unsigned long *)0x400253FC)))
  2. 如何使现有的HWREGBITW宏工作?或者,如何编写宏来执行相同的任务,但在给定具有预先存在的演员表的参数时不会失败?
4

1 回答 1

6

1- 为什么 (unsigned long)(0x400253FC) 不等于 (unsigned long)((*((volatile unsigned long *)0x400253FC)))?

前者是一个整数文字,它的值是0x400253FCul,而后者是unsigned long存储在(内存或 GPIO)地址中的值0x400253FC

2- 如何使现有的 HWREGBITW 宏工作?或者,如何编写宏来执行相同的任务,但在给定具有预先存在的演员表的参数时不会失败?

改为使用HWREGBITW(&GPIO_PORTF_DATA_R, 0) = 1;

于 2012-06-04T17:41:10.100 回答