NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » 1value error

July 25, 2011
by jasongillikin
jasongillikin's Avatar

I am trying to write a while statement that checks PD3 and PD4 to be 0... but I keep getting this 1value error. I assume its because I am not comparing them correctly...

here is my code

while (PORTD&=(0<<PD3)&&PORTD&=(0<<PD4)){

what is the correct syntax?

Thanks again guys :) everyone is awesome for helping out on this site

July 25, 2011
by Noter
Noter's Avatar

while((PORTD&(1<<PD3)==0)&&(PORTD&(1<<PD4)==0))

July 25, 2011
by bretm
bretm's Avatar

That won't work, because for input pins PORTD represents the state of the pull-up resistors, not the input state. You need PIND instead of PORTD.

Also it's reading PORTD twice which is unnecessary. The compiler can't optimize that out, either, because of how PORTD is defined.

I would do this:

#define MY_TWO_PINS ((1<<PD3) | (1<<PD4))

while ((PIND & MY_TWO_PINS) == 0) {

You could also omit the #define and just put the whole thing in one big expression.

Whenever you see "0<<" somewhere, such as in (0<<PD3), it's almost always wrong. Zero shifted left any number of bit positions is still zero. (0<<PD3) is zero, (0<<PD4) is also zero, so PORTD&(0<<PD3) is always zero. So your original attempt was equivalent to

while (0) {

which means it would never execute the loop body. You always have to mask (with &) using (1<<PDx) first, and then you can look to see whether you got non-zero or zero.

More than that, you were using "&=" which would actually modify the value of PORTD, setting it to zero. If they're configured as output pins then that's harmless, but any PORTD pins configured as input pins would have had their pull-up resistors turned off by that statement.

July 25, 2011
by Noter
Noter's Avatar

while((PIND&(1<<PD3)==0)&&(PIND&(1<<PD4)==0))

July 25, 2011
by bretm
bretm's Avatar

That may or may not work depending on the application. It will read one of the pins at a different time than the other pin, not quite simultaneously. If the application can't tolerate that, use

while ((PIND & ((1<<PD3) | (1<<PD4))) == 0)
July 25, 2011
by Noter
Noter's Avatar

How much time does it take to read a pin?

July 25, 2011
by Noter
Noter's Avatar

I think I found the answer. The instruction to read the port takes 1 clock cycle so for the nerdkit that would be 1/14745600 seconds or about 68 nanoseconds to do another read of the pin.

July 25, 2011
by bretm
bretm's Avatar

But you're doing more than just reading the port -- you're reading it and testing one of the bits. That's at least two instructions and possibly more depending on how the compiler does it.

July 26, 2011
by Noter
Noter's Avatar

Unless I'm missing something it's just one more instruction - here's the assembly from the compiler.

ii=PIND&(1<<PD1);
 606:   89 b1           in  r24, 0x09   ; 9
ii=(PIND&(1<<PD1))&(PIND&(1<<PD1));
 608:   89 b1           in  r24, 0x09   ; 9
 60a:   89 b1           in  r24, 0x09   ; 9
ii=((PIND&(1<<PD1))==0)&((PIND&(1<<PD1))==0);
 60c:   89 b1           in  r24, 0x09   ; 9
 60e:   89 b1           in  r24, 0x09   ; 9

ii=((PIND&((1<<PD1)|(1<<PD2)))==0);
 610:   89 b1           in  r24, 0x09   ; 9
July 26, 2011
by Noter
Noter's Avatar

Here it is again with optimization turned off - 2ms vs 1ms. So, I suppose it's safe to conclude that if a microsecond makes a difference in your application then use the combined statement otherwise either approach is fine.

ii=((PIND&(1<<PD1))==0)&((PIND&(1<<PD1))==0);
2582:   e9 e2           ldi r30, 0x29   ; 41
2584:   f0 e0           ldi r31, 0x00   ; 0
2586:   80 81           ld  r24, Z
2588:   88 2f           mov r24, r24
258a:   90 e0           ldi r25, 0x00   ; 0
258c:   82 70           andi    r24, 0x02   ; 2
258e:   90 70           andi    r25, 0x00   ; 0
2590:   1d a6           std Y+45, r1    ; 0x2d
2592:   00 97           sbiw    r24, 0x00   ; 0
2594:   11 f4           brne    .+4         ; 0x259a <main+0x5c>
2596:   81 e0           ldi r24, 0x01   ; 1
2598:   8d a7           std Y+45, r24   ; 0x2d
259a:   e9 e2           ldi r30, 0x29   ; 41
259c:   f0 e0           ldi r31, 0x00   ; 0
259e:   80 81           ld  r24, Z
25a0:   88 2f           mov r24, r24
25a2:   90 e0           ldi r25, 0x00   ; 0
25a4:   82 70           andi    r24, 0x02   ; 2
25a6:   90 70           andi    r25, 0x00   ; 0
25a8:   1c a6           std Y+44, r1    ; 0x2c
25aa:   00 97           sbiw    r24, 0x00   ; 0
25ac:   11 f4           brne    .+4         ; 0x25b2 <main+0x74>
25ae:   91 e0           ldi r25, 0x01   ; 1
25b0:   9c a7           std Y+44, r25   ; 0x2c
25b2:   8d a5           ldd r24, Y+45   ; 0x2d
25b4:   9c a5           ldd r25, Y+44   ; 0x2c
25b6:   89 23           and r24, r25
25b8:   88 2f           mov r24, r24
25ba:   90 e0           ldi r25, 0x00   ; 0
25bc:   9c a3           std Y+36, r25   ; 0x24
25be:   8b a3           std Y+35, r24   ; 0x23

ii=((PIND&((1<<PD1)|(1<<PD2)))==0);
25c0:   e9 e2           ldi r30, 0x29   ; 41
25c2:   f0 e0           ldi r31, 0x00   ; 0
25c4:   80 81           ld  r24, Z
25c6:   88 2f           mov r24, r24
25c8:   90 e0           ldi r25, 0x00   ; 0
25ca:   86 70           andi    r24, 0x06   ; 6
25cc:   90 70           andi    r25, 0x00   ; 0
25ce:   1c a2           std Y+36, r1    ; 0x24
25d0:   1b a2           std Y+35, r1    ; 0x23
25d2:   00 97           sbiw    r24, 0x00   ; 0
25d4:   21 f4           brne    .+8         ; 0x25de <main+0xa0>
25d6:   81 e0           ldi r24, 0x01   ; 1
25d8:   90 e0           ldi r25, 0x00   ; 0
25da:   9c a3           std Y+36, r25   ; 0x24
25dc:   8b a3           std Y+35, r24   ; 0x23
July 26, 2011
by Rick_S
Rick_S's Avatar

Eek Dang Noter... You remind me of myself sometimes, taking something as far as you take it to prove your point. LOL

I have seen myself doing the same thing... I bet Jason never thought his innocent question would have turned into what it did! Neutral

It seems as though this became a bit of a back & forth, with bretm correcting your error, you fixing your error, bretm showing an optimized version, and on and on...

I think what it boils down to is either form will work 99.9999% of the time. While you've shown one is sligtly more optimized, I agree that no-one would probably notice the difference in the end. It would only be an issue if the finished code were right at the limits of available memory, or a lot of these checks were being done. Essentially, it becomes a matter of preference or programming styles. Of which we all are unique.

Rick

July 26, 2011
by Noter
Noter's Avatar

Not working a point, I just like to understand the details of choosing one method over another. Now if I ever have a situation where an extra microsecond or ~15 bytes will save the day, I know what to do. I don't see that being likely on the Atmega but I suppose it could happen with the smaller Atiny.

July 26, 2011
by Rick_S
Rick_S's Avatar

Sorry if I mis-spoke. Just reading through the thread that was how it appeared.

Cheers

Rick

July 26, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Jason,

The very helpful folks above gave you some very good information, I just want to make sure you understood what they said and got the answer to your question. Feel free to ask any more questions that crop up if you need some clarification.

Humberto

July 26, 2011
by bretm
bretm's Avatar

Wait... you're saying the compiler generates

60c:   89 b1           in  r24, 0x09   ; 9
60e:   89 b1           in  r24, 0x09   ; 9

So it actually reads the port twice but throws away the first result, overwriting it with the second one? If that's the case, then it's using only the second value, and the reads are "simultaneous" in the sense that both comparisons only use the second read. Which means there's no timing issue at all, except that the code is just slower.

So the only differences are 1) unnecessary extra time spent, 2) unnecessary extra program space consumed, 3) side-effects of reading the port twice.

July 26, 2011
by jasongillikin
jasongillikin's Avatar

maybe I should have stated what I am doing... I have two buttons one on PD3 and one on PD4.

1) I assume when the ciruit is open then PORTD==(0<<PD3 | 0<<PD4).
and when they are pressed PORTD==(1<<PD3 |1<< PD4)

2) while none of the buttons are pressed, I want it to run a routine.

efficiency really doesn't matter. what I am doing is making an LED display that toggles through letters. (thats what one button is for) the other button just toggles 1 LED.

so, when the Letter toggle button is pressed. the routine stops (temporarily) and incriments 1. then resumes. The code I want to use for that is (and correct me if I'm wrong) while (PORTD==(1<<PD3))

can you use PORTD or do you have to use PIND? I believe I asked this question be for and the answer was either will work.

Thanks again for the help.

oh yeah, what does this line of code do? "ii=((PIND&(1<<PD1))==0)&((PIND&(1<<PD1))==0);"

July 26, 2011
by bretm
bretm's Avatar

No, you have to use PIND. Using PORTD will just tell you the state of the pull-up resistors, not the state of the digital inputs.

Whether or not the bits are 0's or 1's when the buttons are pressed depends on how the circuit is wired. Usually buttons are wired up connect to ground with the internal pull-up resistors enabled, so the bit is actually 0 when the button is pressed.

We showed code for testing whether the bits are zero since that's what you asked about. If things are wired differently, the code may have to be changed.

That line of code you quoted sets a variable call "ii" to 1 if PD1 is zero and to 0 if PD1 is 1. I think he meant to use PD1 and PD2 instead of PD1 twice. In that case it would set it to 1 only if both bits were zero.

July 26, 2011
by bretm
bretm's Avatar

Correcting you if you're wrong:

(and correct me if I'm wrong) while (PORTD==(1<<PD3))

Needs to be PIND, not PORTD. And PIND will only equal 1<<PD3 if PD3 is high and PD0,PD1,PD2,PD4,PD5,PD6,PD7 are low. That's why you have to use "&" examine just the single pin.

July 26, 2011
by jasongillikin
jasongillikin's Avatar

Sorry, but now I am confused.

is it "while (PIND==(1<<PD3|1<<PD4)" to see if a button is pressed (assuming wired correctly). I am only running two buttons on PORTD. and whether they are high or low I get how to change that.

what does PIND&(1<<PD4)==0 do?

I don't understand how the '&' operator is used when comparing. Isn't the the comparing operator "&&". as in PIND&(1<<PD4) only keeps the 1 in the PD4 bit... i.e

original PIND=

PIND=11010100 & (1<<PD4)

new PIND= PIND=00010000


where as PIND=11010100 |(0<<PD4)

PIND=11000100

so how is PIND&(1<<PD4)==0 ever true? shouldn't it always be 16?

sorry its taking me a while on this, its new.

July 26, 2011
by bretm
bretm's Avatar

(1<<PD4) is just 16.

PIND & 16 can either be 16 (if the PD4 bit is set) or 0 (if the PD4 bit is not set). Example:

Original PIND = 11010100 (bit PD4 is set)
    PIND & 16 = 00010000 (16)

Original PIND = 11000100 (bit PD4 is not set, but some other ones are)
    PIND & 16 = 00000000 (zero)

So to test if PD4 is set, you check to see if the result is zero (or not). 16 counts as "not", obviously, so you don't have to check for 16 specifically. No matter what the pin bit position is, the result after "&" will either be zero, or not zero, and that's what you look for.

What does PIND&(1<<PD4)==0 do? It reads PIND (which contains the state of all eight pins on port D), nukes all of them except PD4, and compares the result with zero. If it's equal zero it returns yes (1), otherwise it returns no (0).

July 26, 2011
by jasongillikin
jasongillikin's Avatar

OMG!!! I finally got it working @ 1:20pm EST... Thanks for everyones help.

Post a Reply

Please log in to post a reply.

Did you know that binary numbers use base 2 to represent numbers, and these are important for understanding microcontroller registers? Learn more...