What I am trying to do is set pin PC0 of PORT C as output to light an LED.
The code I am using for that is:
//SET PORT C
//PC0 = RELAY STATUS LED - O/P
//PC1 = RELAY CTL LINE - O/P
//PC2 = PUSH BUTTON - I/P
//PC3 = SPARE - O/P
DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3) & _BV(PC2);
//SET PC0 = HIGH
PORTC |= _BV(PC0);
But the above is not working. I have checked my connections and can't figure why is it not working. It should be a fairly simple thing.
The only reason that I think could be affecting it is that I also have an ISP connected to the AVR ⇒ PC6 being used as the reset line by it.
Any ideas why the above won't work?
Best Answer
Your problem:
Your DDR setting is incorrect: You've written
but you want
You say you want more detail because you just kinda emulated other DDRC assignments and don't really understand what you wrote? OK, here goes:
How to set registers with bitwise operators on the AVR.
Let's unpack the
_BV()
macro and PCx definitions for a moment._BV(x)
is simply1 << x
. If you write_BV(5)
, the macro will evaluate to the number0b 0001 0000
. PCx is simply a macro defined to the value of x, so_BV(PC5)
will evaluate to the same thing as_BV(5)
. You're probably expecting something like the following to happen:However, you're not clearing PC2 correctly. You're applying a binary AND at the end. AND has a higher precedence than OR in C, so your operation is actually
PC3 is 0b 0000 1000, and PC2 is 0b 0000 0100, so the parenthetical section evaluates to zero. This is actually fortunate for PC2, but unfortunate for PC3, which remains clear when you wanted it set. To clear a bit, you do use
&
, but you need to take the complement of the number using the~
operator. In the following equations, I've used?
for bits which we don't want to change:In code, these equations are:
as shown above. You could either do this in two steps as I did, or apply the previous value of DDRC with a bit mask:
0xF0, or 0b 1111 0000, when AND'ed with the previous value of DDRC, returns 0b ???? 0000, to which we can OR our lower four bits. I think the two-step method is clearer: Your inputs and outputs are on different lines (note that you can add additional inputs with, for example,
DDRC &= ~_BV(PC2) & ~_BV(PC4)
). Sometimes, I'll even break it up onto more lines:That, to me, is the most clear mechanism possible, and the easiest for future readers to understand. It's also the most verbose, but who cares? The compiler will optimize all three mechanisms to the same code.