I'm programming the Atmega328P in embedded C. I'm going to use 16 pins for turning on LEDs, and I have to use PORTB, PORTD and PORTC for this. I would like to just iterate a pointer so I can turn them on/off instead having to deal with what port each pin relates to. So an example would be how I need three if statements to iterate through the 16 pins like shown below:
// Enable outputs
DDRB = 0b00111111; // 6 outputs
DDRC = 0b00110000; // 2 outputs
DDRD = 0xFF; // 8 outputs
// LED pins
uint8_t ledpin[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint8_t idx = 0;
// Set LEDs
if (idx < 8)
for(int i = 0; i < 8; i++)
PORTD |= (1 << ledpin[i]);
if( idx > 8 && idx < 14)
for(int i = 8; i < 14 ; i++)
PORTB |= (1 << ledpin[i]);
if (idx >= 14)
for(int i = 14; i < 16; i++)
PORTC |= (1 << ledpin[i]);
I would rather use a memory map where I could just say "pin0 address + offset" like this:
// Base address of ports
uint8_t base 0x00
uint8_t offset = 0x04;
// LED pins
uint8_t ledpin[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
// Set LEDs
for(int i = 0; i < 16; i++){
(base + offset) |= (1 << ledpin[i]); // Pseudo code
offset += 0x04;
}
The code above is obviously not optimized, nor totally functioning code but I think it gets my point accross as to what I want to achieve. Ultimately I will use this in a code to control a LED cube which will work quite differently, but it's essential that I don't need to decode what port each pin belong to all the time. Is there a good way to solve this with virtually no overhead?
EDIT
After investigating and trying out a lot of stuff with good help from the answers I have found that it's not possible to address individual bits in the AVR ports. Each PORTB/C/D have their own address, but it's not possible to manipulate the bits with memory mapping, like incrementing a pointer address. Atmega328P datasheet page 280 shows the memory map of the IO ports (image below).
Best Answer
There is no practical way to get 'virtually no overhead' in C on AVR. Code that looks like it should have low overhead often doesn't, and you have to look at the disassembly to see the real code.
AVR has a RISC architecture that needs many instructions to do 'simple' operations. IN, OUT, and SBI/CBI (bit set/clear) instructions take literal arguments only, which must be 'hard-coded' at compile time. But there is no barrel shifter, so bit masks computed at run time can take many instructions to produce. Pointers are made from 8 bit registers that are loaded one byte at a time, so pointer operations may use more cycles than port I/O with conditional code.
Here's the best I have managed to produce so far (It's basically the same as your code except the pin masks are precomputed, and it's wrapped in a function instead of inlined)...
...which generates the following machine code:-
That's 12 or 14 machine code instructions executed depending on the port, which is probably about as close as you can get to 'virtually no overhead' without coding in optimized assembler.