Reading ports via PIN Commands

Just want to hang out with other Makers and chat about stuff? This is the place to do it.
Post Reply
avrdude
Posts: 32
Joined: Mon Sep 30, 2013 3:42 pm

Reading ports via PIN Commands

Post by avrdude » Sat Mar 15, 2014 5:12 am

Hi,
I know you can read the state of ports with PINx, where x is the port register. However i am unsure how to manipulate this to read a single port.
What I wish to do is read digital 3 with the PINB (or is it PIND?) command, and then compare whether it does not equal a boolean value. How can this be done, since other pins will be high, and might interfere?
Thanks

angusgr
Freetronics Staff
Freetronics Staff
Posts: 853
Joined: Tue Apr 09, 2013 11:19 pm
Location: Melbourne, Australia
Contact:

Re: Reading ports via PIN Commands

Post by angusgr » Sun Mar 16, 2014 11:58 pm

Hi avrdude,

The short answer is, this is essentially what digitalRead() is doing already. But I'm guessing you want lower overhead/higher performance.

Another thing you can try is using a library like digitalWriteFast or this one called digitalIOPerformance that I put together last year (not Freetronics-related):
https://github.com/projectgus/digitalIOPerformance

However, to answer your actual question - you need to use bitwise operators to see the actual value.

On Eleven/Uno, we can see that digital pin 3 is PD3 (Port D bit 3.) There are lots of ways to figure this out, I looked at the Eleven PDF https://github.com/freetronics/Eleven/b ... evenR3.pdf but there are other diagrams out there.

So if we read PIND:

Code: Select all

int x = PIND;
We get all 8 input values from port D as a byte. We want to look to see whether bit 3 is set. Bit 3 is value 8 (We count bits from zero and they double each time - so values are 1,2,4,8.) You can also show this as "1 left shifted 3 times", or 1 << 3 in C, which is convenient to read because that "3" matches exactly to the 3 in "PD3". Some explanation can be found here.

So we can do a bitwise AND to expose just that value:

Code: Select all

int x = PIND & 8;
or

Code: Select all

int x = PIND & (1 << 3)
Suppose the whole PIND value (all 8 pins) looks like this in binary

Code: Select all

01011010
The bit we want is bit 3, the value of 8:

Code: Select all

01011010
    ^
    this one
(Just to confuse you here we're counting bits from the right hand end here not the left hand end, because the right-most digit in a number has the lowest value! So rightmost is 1, next left is 2, next left is 4, next left is 8.)

So if we bitwise AND the pin value with the bit for the pin we can are about, we get this in binary

Code: Select all

01011010 &
00001000
Which equals 00001000 (8) if the pin is high and the bit is set as shown above, or it would equal 00000000 if the pin was low and the bit was not set. Generally you can test for x != 0 to determine if the pin was high or not.

Hopefully that makes some sense. If you're looking for more insight into how to understand this, "bitwise operations" are the thing to read up on.

- Angus

avrdude
Posts: 32
Joined: Mon Sep 30, 2013 3:42 pm

Re: Reading ports via PIN Commands

Post by avrdude » Mon Mar 17, 2014 1:55 pm

Thanks for the reply, I am familiar with both the binary system and with bitshift, but your information was nonetheless very good. So if we do
if( (PIND << 3) & 00001000)
We can check if pin three is high.
However, what would be the equivalent of
if ( digitalRead(3) != prevClock)
Where prevClock is a Boolean of value either high or low (obviously, but the point is that it will change).
So how can we check this? I have experimented with some of the bitwise operators, but I am yet to find out how to do it.

Thanks

angusgr
Freetronics Staff
Freetronics Staff
Posts: 853
Joined: Tue Apr 09, 2013 11:19 pm
Location: Melbourne, Australia
Contact:

Re: Reading ports via PIN Commands

Post by angusgr » Sun Mar 23, 2014 11:26 pm

Hi avrdude,

Sorry for the slow reply.

Probably the easiest thing you can do is just store the value as a number in both places:

Code: Select all

byte last_value;

void loop() {
  byte current_value = PIND & (1<<3);  // is pin 3 set?
  if(current_value != last_value) {
    // something has changed
    if(current_value) {
       Serial.println("Pin is high!");
    } else {
       Serial.println("Pin is low!");
    }
    last_value = current_value;
  }
}
This works because you can also use numbers as boolean values (non-zero meaning true, zero meaning false.)

The other thing you can do is cast the number to a bool value:

Code: Select all

bool last_value;

void loop() {
  byte current_value = PIND & (1<<3);  // is pin 3 set?
  if((bool)current_value != last_value) {
    // something has changed
    if(current_value) {
       Serial.println("Pin is high!");
    } else {
       Serial.println("Pin is low!");
    }
    last_value = current_value;
  }
}
- Angus

avrdude
Posts: 32
Joined: Mon Sep 30, 2013 3:42 pm

Re: Reading ports via PIN Commands

Post by avrdude » Mon Mar 24, 2014 1:00 pm

Thanks angusgr, I'll probably use the bool example so I can easily manipulate the value with the ! operator.

Post Reply