This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

direct referrence to GPIO pins

another newbie question.

I have a piece of code that I am porting from PICC. In PICC, you can make reference to specific pins ("#define sclk GPIO5" for example), and in your code, you can write to sclk to change the pin output on GPOI5. This makes porting code or changing pin layout so much easier as you can simple redefine sclk to a different pin to make the code work.

ARM seems to prefer to change its pin output through IOSET / IOCLR.

To make the existing PICC code work, I would prefer to be able to define certain pins logically, and change their states by referencing their logic names.

how do you do that in ARM? Thanks in advance.

  • we have discussed all along why it is not possible to do the PIC-styled direct port access in this thread. the reasons given range from "it is ARM's business model, stupid" to "it is not good for your health", :).

    here is a little piece of code that does direct port access on LPC210x chips and you can expand it to other ARM chips too.

    [code]
    #include <LPC210x.H>
    #include "myioconfig.h"

    //hardware configuration
    #define LEDPort IOPIN_bit
    #define LED P0_8 //LED connected to P0.8
    //end hardware configuration

    #define ON 1
    #define OFF 0
    #define LED_STATE LEDPort.LED
    //#define LED_ON IOSET_bit.LED = 1 //turn on the LED
    //#define LED_OFF IOCLR_bit.LED = 1 //turn off the LED
    #define LED_ON LEDPort.LED=ON
    #define LED_OFF LEDPort.LED=OFF

    void delay(unsigned long dly);
    void init_mcu(void);

    void delay(unsigned long dly) { for(; dly>0; dly--) ;
    }

    void init_mcu(void) { PINSEL0 = 0x0; //??????GPIO IODIR_bit.LED = 1; //set the led pin as output
    }

    int main(void)
    {

    init_mcu(); //initialize the mcu LED_OFF; //turn the led off

    while(1) { delay(100000); (LED_STATE)? (LED_OFF):(LED_ON); //toggle the led pin }
    } [/code]

    enjoy.

    it is basically done by redefining the GPIO registers in the header file.

  • Sigh. Instructions for posting code were right there above the message box where you entered that mess.

    www.danlhenry.com/.../keil_code.png

  • I'm surprised. Did you not know about C bit fields? They are part of the ANSI/ISO C standard.

    Bit fields are not the "PIC approach". They can be used with any memory cell in any C program with any processor that has a C or C++ compiler.

    The PIC approach is - at least in my book - the unique feature available in some few processor cores (PIC, 8051, ...) of being able to create bit variables and perform true bit updates. That PIC approach can only be duplicated in processors that has the same type of core solution, where you have true bit-addressable memory.

    If you by "The PIC approach" do mean bit fields, it should have been enough to know that you can do it with an ARM processor by reading my comment way up in this thread:
    "If your ARM has a PIN register, then you can directly assign values to the port pins, i.e. some pins may be set while others may be cleared."

    That clearly tells you that you can do direct I/O to ports for some ARM chips. But you can not do direct bit operations to ARM processor pins, unless using bit banding. Bit fields will not do direct bit operations on processor pins. It is just a non-portable C construct making it look like direct bit operations. If you can live with the non-portability, you can do it with any ARM chip that has the already mentioned PIN register.

    Your big problem is that you got stuck at:
    "I never understood the way ARM did it: using ioset/ioclr. to me, the pic approach is so much easier and more intuitive."

    Even if you don't like this, and think it is irrelevant, it really is important for you to grasp the difference.

    It sounds like you are thinking about using a Cortex-M3. If you don't care about the difference, then you can use bit fields. If you care about the difference, you will look closer at bit banding. But there is a big but. The bit banding is still only a work-around. It may in some situations remove a core stall, but the Cortex-M3 bit banding will still require a read to write a bit, even if the read/modify/write isn't done inside the processor core but somewhere in the memory controller. So if the processor has SET and CLEAR registers, you can gain a lot from using them when you know that you will only set - or only clear - one or more bits. SET and CLEAR can result in large speed improvements.

    But this is not the PIC way. This is not the 8051 way. They can do a write without a read. They do not need to suffer a core stall when doing a bit write since they need not care about the other bits in the same word/container. All because of bit-addressable memory.

    I don't know how much you have read up on bit fields. They are part of the the C standard, but they have a number of problems, which is the reason why the Keil 8051 processor created SBIT as a datatype instead of using bit fields. Some PIC compilers has a bit datatype. Some has a #bit datatype. Some just rely on bit fields. But the common part for both 8051 and PIC compilers is that they do true bit accesses.

    The ARM has to do it the standard C way. And a bitfield in standard C is a language rewrite of a standard mask operation, i.e.

    port.led1 = 1;
    


    is actually a

    port |= LED1_BITMASK;
    


    or possibly

    port |= 1u << LED1_BIT;
    

    What is so important to remember when using the non-standardized C bit fields is that a write to a bitfield looks like a write, but is actually a read, a modify and a write. When the bit field or the bit field container is defined as volatile, the compiler will perform exactly one (1) read of the full bit field container, and exactly one (1) write of the full bit field container. Depending on how far away bus-wise the memory cell is, the processor core may stall for a large number of clock cycles.

    Another important thing. Few C programmers are using bit fields. Because of this, some compilers may produce very lousy code for bit fields. But for an ARM chip, the compiler may never produce better code for a bit field than it may produce if you do a manual mask operation directly on the PIN register. The PIC compiler on the other hand can convert a one bit wide bit field access to a native bit-addressed memory access depending on location. But the PIC way is to produce a:

    bsf portb,2
    


    The 8051 way is:

    setb P2.2
    


    The ARM/Cortex way (with a PIN register) is:

    4816      LDR      r0,[pc,#88]   ; Load base address of GIO
    6941      LDR      r1,[r0,#0x14] ; Get current PIN values
    F0410104  ORR      r1,r1,#0x04   ; Set bit 2
    6141      STR      r1,[r0,#0x14] ; Store back
    


    The ARM/Cortex way with SET/CLEAR registers is:

    491C      LDR      r1,[pc,#112]  ; Load base address
    2004      MOVS     r0,#0x04      ; Want to set bit 2
    6188      STR      r0,[r1,#0x18] ; Set bit (no read involved)
    


    The ARM/Cortex way with bit banding and PIN register:

    490E      LDR      r1,[pc,#56]   ; Load base address
    2001      MOVS     r0,#0x01      ; Should set bit
    6008      STR      r0,[r1,#0x00] ; Set the bit (hidden read/modify/write)
    


    The ARM/Cortex way with bit banding and SET/CLEAR register.

    490B      LDR      r1,[pc,#44]   ; Load base address
    2001      MOVS     r0,#0x01      ; Should set bit
    6008      STR      r0,[r1,#0x00] ; Set the bit (no hidden read)
    


    The ARM/Cortex way with bit fields - note same code as manual bit manipulation:

    4808      LDR      r0,[pc,#32]   ; Load base address
    6941      LDR      r1,[r0,#0x14] ; Load current pin state
    F0410104  ORR      r1,r1,#0x04   ; Want to set pin 2
    6141      STR      r1,[r0,#0x14] ; Write back changes
    


    The ARM way with PIN register:

    E59F0024  LDR       R0,[PC,#0x0024]   ; Load base address
    E5901034  LDR       R1,[R0,#0x0034]   ; Load current pin bits
    E3811004  ORR       R1,R1,#0x00000004 ; Set bit 2
    E5801034  STR       R1,[R0,#0x0034]   ; Store change
    


    The ARM way with SET/CLEAR register:

    E59F1034  LDR       R1,[PC,#0x0034]   ; Load base address
    E3A00004  MOV       R0,#0x00000004    ; Should set bit 2
    E5810038  STR       R0,[R1,#0x0038]   ; Set bit 2 (no read involved)
    

    That is the reason why so many ARM manufacturers decided on SET and CLEAR registers. Possibly in combination with a PIN register. The SET/CLEAR registers removes the need for a read instruction, and they removes the execution stall while waiting for the result of the read. And yes, NXP - as has already been established - does have PIN registers on a number of their chips. My examples are for NXP LPC1766 (Cortex-M3) and NXP LPC2366 (ARM7TDMI-S).

    The question now is: Are you going to make use of this thread to learn and improve as developer?

  • "But this is not the PIC way. ... They can do a write without a read."

    PIC instructions allow one to specify a bit to effect, but the actual core operation is an 8-bit read-modify-write.

    From the "PICmicro Mid-Range MCU Family Reference Manual":

    "All bit manipulation instructions will first read the entire register, operate on the selected bit and then write the result back (read-modify-write (R-M-W)) the specified register. The user should keep this in mind when operating on some special function registers, such as ports."

  • Yes, this is a common problem.

    Some processors can completely remove the read+modify by having the operation performed all the way down at the specific memory cell - in this case GPIO - where there isn't a normal RAM address, but a combination of SET/CLEAR/DATA latches for each bit. This is very expensive, so it can't be expanded to a general range of memory, but works wonders for GPIO.

    The 8051 on the other hand has settled for a very narrow range of bytes that can also be bit-addressd. The disadvantage here - besides the limited range - is that there is no indirect or indexed bit addressing.

    The bit banding of the Cortex-M3 is a generalization with a 1MB address range spanned by a 32MB alias range, so the Cortex-M3 can't do it with just a write but have to suffer a more general read/modify/write, even if the read+modify is outside the core and not visible in the code.

    The important thing is however where the read+modify happens. Using a PIN register with an ARM, it will happen inside the execution unit, so the core will always have to wait for the read before being able to do the modify. When done in the memory controller, it can be masked, allowing the core to continue full-speed, as long as the write buffer FIFO doesn't get full. But the read operation will slow down the emptying of the FIFO, increasing the probability of a write stall. And a processor without a write buffer FIFO will suffer on next access. If happening all the way at the memory cell, it will be identical to a normal write - the dedicated hardware will perform the OR or AND operation on-the-fly. This is the reason why a lot of manufacturers have added SET and CLEAR registers for the GPIO.