简体   繁体   中英

Convert address of pointer to an int

New to ARM (and programming for that matter)and find the bit addressing of I/O ports to be confusing. You can define a constant at a specific port pin but must still write its bit value to set it. For example:

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

This feels weird to me. If I address a certain pin I should be able to set it with a 1. So to keep me from forgetting that I must write its bit value I would like to make a function that does this for me. I have come up with the following, but am having trouble with the pointer syntax or pointer conversion to int I think.

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);

Anyone have any thoughts or advice? Thank you!

Chris, I am not a Cortex-M expert, so your MMV. But based on your description above, you are attempting to use a pretty convoluted way to modify a single bit in a peripheral register. There are two ways to normally handle it:

  1. Use a set of macros (what I'm most familiar with). This will reduce your overhead of space and CPU time vs calling functions for each read/write of a pin, since the macros are directly converted during compile time to the exact values/operations needed.

  2. Use the bit-band address for the PF4 register instead (never used a Cortex-M). This looks like the preferred way to do it with your architecture.

Based on the ARM Cortex-M4 Technical Reference Manual, Section 3.4, you can use the bit-band alias address to modify the PF4 bit. Details on how that works can be found in Section 3.7 of the TRM.

Based on your code above, of PF4 being bit 4 in address 0x40025040, the bit-band formula gives (taken from TRM under fair-use):

bit_band_base is the starting address of the alias region. (0x42000000) • byte_offset is the number of the byte in the bit-band region that contains the targeted bit. (0x00025040) • bit_number is the bit position, 0 to 7, of the targeted bit. (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 is the position of the target bit in the bit-band memory region. bit_word_addr is the address of the word in the alias memory region that maps to the targeted bit.

So

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

is identical to writing

*MyOutput |= 0x10;

If you really want to go the route of using a function and direct writes, try this instead( limits to only PF31, if PF needs to go higher than 31, implementation left to the reader ); this code includes a PC-based test #define so you can compile it with gcc on your command line.

#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;
}

Sample output:

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

You can't address individual bits; the minimum addressable unit in C (and usually in hardware) is one char , ie, typically a byte of 8 bits.

The typical approach is to write wrapper macros or functions. As for your SetOutput , it seems to be quite broken, eg, it tries to return a value from a void function, and the 0xFF mask isolates 8 bits, not 1 (which the pin presumably is), and it never writes to the output register. If the bit 0x10 controls the pin you want, the typical way would be:

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

You can create macros around these as necessary. To check the state of the corresponding bit in an input register, you can use:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM