How can I make an array of PORTS

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For example, I have 4 output pins which are randomly assigned due to board routing/component placement. I would like to create some sort of array or structure where I can set or clear the bits in a for loop. For example:

#define OUTPUT1 PB4
#define OUTPUT2 PD2
#define OUTPUT3 PC3
#define OUTPUT4 PB0

 

 

In the simplest of terms, what I want to be able to do is this: I'll have a BCD value which I want to check the 4 LSBs, and set the outputs to correspond to these bits. 

port[4] = {PORTB, PORTD, PORTC, PORTB};
pin[4] = {OUTPUT1, OUTPUT2, OUTPUT3, OUTPUT4};

for(int i = 0; i <= 3; i++){
    for(int j = 0; j <= 3; j++){
        if((0xA3 >> j) & 0x01){
          port[i] |= 1 << pin[i];  
        }
        else{
            port[i] &= ~(1 << pin[i])
        }
    }
}

To me, it makes sense to have it in loops like this. I made some progress, but I can not get the port macros in an array. For example, PORTB |= 1 << pin[1] sets PB4 high. However, port[i] |= 1 << pin[1] does not work. Is there some other way to access the ports from an array, maybe with their addresses? Is there a completely different approach that would work better overall?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to look up what PORTB actually means, off in include/avr/ioXXXX.h.   For example, from iom328p.h, we have:

#define PORTB _SFR_IO8(0x05)

and chasing it down, in sfr_iodefs.h there is:

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
//  ... and ...
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

So "PORTB" isn't a variable at all; it's a dereference of a pointer to io memory.  Which is completely sensible, but a bit messy to deal with in C.

You make your array be of pointers to io memory, and then when you access them, you explicitly dereference the pointers.  Something  like this:

#include <avr/io.h>

volatile uint8_t *myports[] = { &PORTD, &PORTB, &PORTC };

int main() {
    uint8_t i,j;
        for (i=0; i < 3; i++) {
        j = *(myports[i]);
    }
}

For another example, you can look at the Arduino code that implements digitalWrite() and similar.  They use exactly such arrays to do the mapping between the arduino-numbered "pin numbers" and the AVR Port and bit position.  (although they put them in flash, which adds some additional code.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For example, I have 4 output pins which are randomly assigned due to board routing/component placement.

1)  AVR architecture/instruction set just plain isn't suited to efficient  "generic" I/O port operations.  Toy apps, and some others, don't care if it takes 30 or 100 or more cycles to do a port operation, versus a couple cycles and a couple words.  Perhaps you don't care about RMW aspects that pop up with generic access, but which do not arise when the end result is a single SBI/CBI.  (There is a thread from the past week where "interrupts don't work" traced down to the side effects of an LED toggle in an ISR.)

 

2)  I question your postulate.  Are you saying that you build e.g.  100 bare circuit boards with a set of Gerber files, and then they are "randomly" populated?  I guess then you have the same  overall approach as Arduino, and you have to live with it.  But in any of my real (mostly industrial) apps, while there are stuffing options on some, the firmare is built for each model and port operations done at compile time, not run time.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Upon a re-read, it seems that it >>is<< compile-time resolution; e.g.

#define OUTPUT1 PB4

So then resolve it at compile time, with small and efficient code...  #defines for LED_PORT, LED_PIN, LED_ON(), ....

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got it working by using pointers as westfw mentioned. I'm not quite grasping what you mean by resolve it at compile time using #defines...

 

If I did #define LEDPORT1 PORTB and #define LEDPORT2 PORTD, and made an array of LEDPORTS = {LEDPORT1, LEDPORT2} and used LEDPORT[i] in my loop, I'd end up with the same issues as using port[i] as I mentioned in my original post. I'm probably misunderstanding what you meant...

 

To expand a bit more on what I meant by random, random was a poor choice of word. It's not random, I know what the corresponding ports are, and they are static. However, they aren't nicely laid out such as LED1 PD1, LED2 PD2, LED3 PD3, LED4 PD4 where I could simply perform an incremental bit shift operation to set the bits. If they were, I could just make an array of pins like LED = {LED1, LED2, LED3, LED4} and reference that array in a loop such as PORTD |= 1 << LED[i] since they are all PORTD. However, in my case, LED1-LED4 are on different ports, and I need to keep track of which port corresponds to which LED.

 

There's a good change I'm overcomplicating this. Hopefully it makes more sense now, and maybe there's an easier, more efficient way of doing it. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

what you mean by resolve it at compile time using #defines...

... I know what the corresponding ports are, and they are static.

 If the ports/bits are actually static, and you reference them statically, then there should always be a way of resolving them at compile time to simple in/out/sbi/cbi/sbis/etc instructions.  Sometimes ugly, sometimes not so ugly.  I mentioned Arduino before; you can check out some of the "FastDigitalWrite()" implementations that have been suggested - they all manager to reduce digitalWrite(13, 1) to a single instruction (on AVR), even though "13" has to be mapped from its "arbitrary integer" status to a port and a bit.

 

My current favorite method stems from the fact that avr-gcc has some pretty fancy optimization.  It will do array dereferences at compile time, if it thinks it can.  If you were to take my earlier example and modify it slightly:

volatile uint8_t *myports[] = { &PORTD, &PORTB, &PORTC };

int main() {
    uint8_t i,j;
    for (i=0; i < 3; i++) {
      *(myports[i]) |= 0x10;  // set this bit in each port
    }
}

It would normally compile to reasonable array-like code with indirect IO pointers.

If you make the declaration "static"

static volatile uint8_t *myports[] = { &PORTD, &PORTB, &PORTC };

the compiler gets clever and produces three sbi instructions for all of main().  (I feel like there SHOULD be a "const" in there as well, but it's not intuitive where it belongs, and doesn't seem to be needed.)

 

So if you are carefully accurate about what is static, and locate/define your functions so that they can be inlined, you can get better code than you might have expected.

 

(segue into flame about vendor libraries that define functions that COULD benefit from compiler optimization, but are pushed off into a library where that isn't possible.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
(segue into flame about vendor libraries that define functions that COULD benefit from compiler optimization, but are pushed off into a library where that isn't possible.)

The ASF "libraries" do an awful lot of inline stuff