在Cortex-M3上使用带预编址地址的带状宏时会出现问题

时间:2012-06-04 17:36:50

标签: c casting c-preprocessor cortex-m3 memory-mapping

TL; DR:

  1. 为什么(unsigned long)(0x400253FC)不等同于(unsigned long)((*((volatile unsigned long *)0x400253FC)))
  2. 如何制作一个与前者合作的宏?
  3. 背景资料

    环境

    我正在使用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宏工作?或者,如果用一个预先存在的转换器给出一个参数,如何编写宏来执行相同的任务但不会失败?

1 个答案:

答案 0 :(得分:6)

  

1-为什么不(无符号长)(0x400253FC)等效于(无符号长整数)((*((volatile unsigned long *)0x400253FC)))?

前者是整数字面值,其值为0x400253FCul,而后者是存储在(内存或GPIO)地址unsigned long

中的0x400253FC
  

2-如何使现有的HWREGBITW宏工作?或者,如何在执行相同任务时编写宏,但在给出与预先存在的强制转换的参数时不会失败?

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