3
\$\begingroup\$

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?

\$\endgroup\$
5
\$\begingroup\$

Your problem:

Your DDR setting is incorrect: You've written

DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3) & _BV(PC2);

but you want

DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3)
DDRC &= ~_BV(PC2);

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 simply 1 << x. If you write _BV(5), the macro will evaluate to the number 0b 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:

   0b 0000 0001  // PC0
|  0b 0000 0010  // PC1
|  0b 0000 1000  // PC3
&  0b 0000 0100  // Misconception: AND does not cause this to output a zero at the set bit
|= ------------
   0b 0000 1011

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

DDRC |= _BV(PC0) | _BV(PC1) | ( _BV(PC3) & _BV(PC2) );

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:

   0b ???? ???1  // Set PC0
|  0b ???? ??10  // Set PC1 
|  0b ???? 1?00  // Set PC3
|= ------------
   0b ???? 1?11

   0b ???? 1?11
&  0b 1111 1011  // Clear PC2
&= ------------
   0b ???? 1011

In code, these equations are:

DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3)
DDRC &= ~_BV(PC2);

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:

DDRC = (DDRC & 0xF0) | ( ( _BV(PC0) | _BV(PC1) | _BV(PC3) ) & ~_BV(PC2) );

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:

DDRC |= _BV(PC0)   // RELAY STATUS LED - O/P
DDRC |= _BV(PC1)   // RELAY CTL LINE - O/P
DDRC &= ~_BV(PC2)  // PUSH BUTTON - I/P
DDRC |= _BV(PC3)   // SPARE - O/P

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.

\$\endgroup\$
  • \$\begingroup\$ thanks for pointing that out and the indepth explanation .. much appreciated! however that also did not fix it. i recoded the in the simplest possible way using DDRC = 0b00000001; PORTC |= _BV(PC0); but still the LED is off. i have double checked my connections so thats not the issue. also tried another ATMEGA8 just to be sure. same result \$\endgroup\$ – Ankit Apr 13 '12 at 21:05
  • \$\begingroup\$ nvm .. works now ... turns out i had lines in my code above this one referring to Peter fleury's LCD library that might be interfering with it ... when i uncomment those lines everything works as expected. now off to find out how those lines are messing with PORTC ! \$\endgroup\$ – Ankit Apr 13 '12 at 21:14
1
\$\begingroup\$

What microcontroller you are using? is it Atmega32 or 16 or any other?, than check its Datasheet, either they have JTAG interface or not. If they have JTAG, than you may not be able to use PortC Few pins as JTAG is enabled by default from factory.

For this you have to Disable JTAG from Fuses, by disabling JTAGEN fuse bit

or

writing this in you initialization or start of your code

MCUCSR|= (1<<JTD);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.