Basic Functions
Each I/O pin Pxn can be controlled by the registers in PORTx. Each pin group x has its own set of PORT
registers. The base address of the register set for pin n is at the byte address PORT + 0x10 + ÝŠ . The
index within that register set is n.
To use pin number n as an output only, write bit n of the PORTx.DIR register to '1'. This can be done by
writing bit n in the PORTx.DIRSET register to '1', which will avoid disturbing the configuration of other pins
in that group. The nth bit in the PORTx.OUT register must be written to the desired output value.
Similarly, writing a PORTx.OUTSET bit to '1' will set the corresponding bit in the PORTx.OUT register to
'1'. Writing a bit in PORTx.OUTCLR to '1' will clear that bit in PORTx.OUT to zero. Writing a bit in
PORTx.OUTTGL or PORTx.IN to '1' will toggle that bit in PORTx.OUT.
To use pin n as an input, bit n in the PORTx.DIR register must be written to '0' to disable the output driver.
This can be done by writing bit n in the PORTx.DIRCLR register to '1', which will avoid disturbing the
configuration of other pins in that group. The input value can be read from bit n in register PORTx.IN as
long as the ISC bit is not set to INPUT_DISABLE.
Writing a bit to '1' in PORTx.DIRTGL will toggle that bit in PORTx.DIR and toggle the direction of the
corresponding pin.
So is that confusing or what? to make a pin low I write to one register but to make it high I write to another. So can that mean the registers conflict or do they just have silicon to spare to make any register a state machine with 2 inputs?
Stuff I was intending to write #defines for seem hardly needed as they have made every action a different register so the define write themselves.....
Though
hans' reply has provided good insights about what [I think] your problem might be, I'm sensing there's something left to be explained further down the line.
What you're quoting is your compiler's convention about manipulating ATmega registers. Your perception of registers is correct. What I find confusing and terribly implemented as well is that one has to write a logical 'one' to actually
clear register bits. And there's also more than one way to do that through the various specific abstract terms. It *does* sound confusing because it is. Besides it gives no real clue what the generated assembly code will look like nor how the actual register works.
Let's take a small detour by the assembly land. Indeed to set a register bit to one, all you have to do is
OR the register with an immediate value where all bits are cleared but the one bit you want to set (assembly code):
ori R15, 80 /* Set bit 7 in R15 */
andi R14, FE /* Clear bit 0 in R14 */
From your quote,
PORTx.DIR actually
represents the direction register for port x, aka
DDRx, e.g.
DDRA in the case of
PORTA. OTOH
PORTx.DIRSET actually is
not a register but some form of compiler-defined "macro" or "symbol" (not very sure what it is exactly though), which translates
write operations into compiled assembly code that properly sets the corresponding register. Same goes with
PORTx.OUTSET,
PORTx.OUTCLR and
PORTx.OUTTGL, which are for setting, clearing and toggling port pins when written logical ones. It might spare you from mentally calculating what masks and hardware registers you need to use to set/clear bits but IMHO it's not very practical and is confusing at best.
In my opinion, this is but a convoluted way to implement a hardware programming interface. If you take a look at
avr-gcc, there's no such complication/confusion: you manipulate those damn registers like... registers!
Example, C-code:
DDRA |= 0x01 /* Set PORTA pin 0 as an output */
PORTA |= 0x01 /* Set PORTA pin 0 high */
PORTA &= ~0x01 /* Set PORTA pin 0 low */
In short, you write a logical 'one' to set a bit and a logical 'zero' to clear it. But to make matters more confusing, the above C code might be translated into assembly language like this:
sbi DDRA, 0 /* Make PORTA pin 0 an output */
sbi PORTA, 0 /* Set PORTA pin 0 high */
cbi PORTA, 0 /* Set PORTA pin 0 low */
As you can see C instructions, which logically translate into a read-modify-write operation, are translated into a single bit-write instruction. This is made possible thanks to the optimizer.
But note that "sbi" and cbi" instructions are only valid for a limited set of AVR registers. There are other registers that require read-modify-write operations to manipulate their bits. But again: the optimizer will do that job.
I guess your compiler has made those operations invisible by providing some form of abstraction... which is not really required since it's the optimizer's job to translate C instructions into the appropriate assembly code. So one assignment instructions in C might translate into assembly "sbi/cbi" or a read-modify-write sequence, depending on the target register.
Anyway, I suggest you took a look at the generated assembly for more details. Also see
AVR instruction set documentation.