将指针的地址转换为int

时间:2014-02-27 20:05:02

标签: c arm keil

ARM的新手(以及针对该问题的编程)并发现I / O端口的位寻址令人困惑。您可以在特定端口引脚上定义常量,但仍必须写入其位值以进行设置。例如:

#define MyOutput    (*((volatile unsigned long *)0x40025040)) //PF4
// But to set this bit you must write
MyOutput = 0x10;

这对我来说很奇怪。如果我寻址某个引脚,我应该能够设置它1.为了防止我忘记我必须写出它的位值,我想为我做一个函数。我已经提出了以下内容,但是我的指针语法或指针转换为int我遇到了麻烦。

int SetOutput(volatile unsigned long* PIN), int ONOFF){ //ON defined as 1, OFF defined as 0
    volatile unsigned long PortBit = (PIN & 0xFF);
    if (ONOFF){
        return ((PortBit & 0xFF)>>2);
    } else {
        return 0;
    }
}

//Called by
MyOutput = SetOutput(&MyOutput, ON);

有人有任何想法或建议吗?谢谢!

2 个答案:

答案 0 :(得分:2)

克里斯,   我不是Cortex-M专家,所以你的MMV。但根据您上面的描述,您试图使用一种非常复杂的方法来修改外设寄存器中的单个位。有两种方法可以正常处理它:

  1. 使用一组宏(我最熟悉的)。这将减少每次读/写引脚的调用函数的空间和CPU时间开销,因为宏在编译期间直接转换为所需的精确值/操作。

  2. 使用PF4寄存器的位带地址(从未使用过Cortex-M)。这看起来是使用您的架构的首选方式。

  3. 根据ARM Cortex-M4技术参考手册的3.4节,您可以使用位带别名地址来修改PF4位。有关其工作原理的详细信息,请参见TRM的第3.7节。

    根据您的上述代码,PF4为地址0x40025040的第4位,位带公式给出(取自TRM的合理使用):

    bit_band_base是别名区域的起始地址。 (0x42000000) •byte_offset是包含目标位的位带区域中的字节数。 (0x00025040) •bit_number是目标位的位位置0到7。 (为0x4)

    bit_word_offset = (byte_offset x 32) + (bit_number × 4)
    
    bwo = 0x25040* 0x20 + 0x4 * 0x4 = 0x004A0810
    
    bit_word_addr = bit_band_base + bit_word_offset
    
    bwa = 0x42000000 + 0x4A0810 = 0x424A0810
    

    bit_word_offset是位带存储区中目标位的位置。 •bit_word_addr是别名内存区域中映射到的字的地址 目标位。

    所以

    *(volatile unsigned long *)0x424A0810 = 0x1;
    

    与写作相同

    *MyOutput |= 0x10;
    

    如果您真的想要使用函数和直接写入的路径,请尝试相反(仅限PF31,如果PF需要高于31,则实现留给读者);此代码包含基于PC的测试#define,因此您可以在命令行上使用gcc编译它。

    #include <inttypes.h>
    #include <stdio.h>
    
    #define PC_TESTING 1
    
    #if PC_TESTING
        unsigned long FAKE_PFBASE;
        #define PFBASE &FAKE_PFBASE
    #else
        #define PFBASE (volatile unsigned long *) 0x40025040
    #endif
    
    #define SUCCESS 0
    #define ERROR_INVALID_PIN -1
    #define ERROR_INVALID_STATE -2
    
    typedef enum {OFF = 0, ON} ONOFF_t;
    
    typedef enum { PF0 = 0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15, PF16, PF17, PF18, PF19, PF20, PF21, PF22, PF23, PF24, PF25, PF26, PF27, PF28, PF29, PF30, PF31 } PIN_t;
    
    int SetOutput( PIN_t PIN, ONOFF_t ONOFF)
    {
        uint32_t mask, value;
    
        // Implementing more than 32 pins is exercise for the reader
        if (PIN > 31)
            return ERROR_INVALID_PIN;
    
        // In case someone did something wrong
        if (ONOFF != OFF && ONOFF != ON)
            return ERROR_INVALID_STATE;
    
        //Broken into separate steps for ease of reading
        //Select the bit of interest
        mask = 1 << PIN;
    
        //Clear the bit of interest
        value = *PFBASE & ~mask;  //~ is a bit-wise invert.  0x0000 0010 becomes 0xFFFF FFEF
    
        //Set the bit of interest if that is requested
        if( ON == ONOFF)
            value |= mask;
    
        *PFBASE = value;
    
        return SUCCESS;
    }
    
    int main()
    {
    int success = 0;
    
    FAKE_PFBASE = 0l;
    
    success = SetOutput( PF4, ON);
    printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE );
    
    success = SetOutput(PF0, ON );
    printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE );
    
    success = SetOutput(PF0, OFF );
    printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE );
    
    //Error handling left to reader
    success = SetOutput(33, OFF );
    printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE );
    
    success = SetOutput(PF2, 2 );
    printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE );
    
    return 0;
    }
    

    示例输出:

    $ ./a.out
     Success = 0, *PFBASE = 0x00000010
     Success = 0, *PFBASE = 0x00000011
     Success = 0, *PFBASE = 0x00000010
     Success = -1, *PFBASE = 0x00000010
     Success = -2, *PFBASE = 0x00000010
    

答案 1 :(得分:1)

你不能解决个别位; C中(通常在硬件中)的最小可寻址单位是一char,即通常是8位的字节。

典型的方法是编写包装器宏或函数。至于你的SetOutput,它似乎很破碎,例如,它试图从void函数返回一个值,0xFF掩码隔离8位,而不是1位(这是pin可能是),它从不写入输出寄存器。如果位0x10控制您想要的引脚,则典型的方法是:

MyOutput |= 0x10; // set bit
MyOutput &= ~0x10; // unset bit
MyOutput ^= 0x10; // toggle bit

您可以根据需要在这些周围创建宏。要检查输入寄存器中相应位的状态,可以使用:

if (MyInput & 0x10) {
    // bit is set
}