Able to read only one pin state

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm learning to use AVRs (Attiny 2313A) with a simple led-blinking program:

#define F_CPU 10000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	DDRB = 0xFF;//B output
	DDRD = 0x00;//D input
	PORTB = 0xFF;//LED off
	PORTD = 0x11111111;
	while(1) {
	if(PIND & (1 << 0) == 1) {
		PORTB &= ~(1<<PB0); /* LED on */
		_delay_ms(100);
		PORTB |= 1<<PB0; /* LED off */
		_delay_ms(100);
	}}
	return 0;
}

It works fine. Led blinks as long as I don't touch the buton. When I press it the led goes off.

However there are couple of problems.

First, if I move the button to pin PD1 and change 

PIND & (1 << 0)

to

PIND & (1 << 1)

the code works no more. The led stays off.

 

Second problem is that I can't make led blink while I press the button. I've tried

if(PIND & (1 << 0) == 0)

but the led stays off again.

 

This is the first program made (mostly) on my own so there is probably some beginner's mistake in there.

I hope it isn't very stupid one.frown

This topic has a solution.
Last Edited: Wed. Nov 5, 2014 - 08:10 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Common mistake - did this myself as a newb.  PIND & (1<<1) will NEVER == 1.  It can only ever be 1<<1 (2) or 0.

 

To test if a pin is high, just write

if (PIND & (1<<1))
{
    ...do hi pin thing...
}

To test if a pin is low, you can do one of three things

if ((PIND & (1<<1)) == 0)
{
    ...do lo pin thing...
}

__or__

if (!(PIND & (1<<1)))
{
    ...do lo pin thing...
}

__or__

if (~PIND & (1<<1))
{
    ...do lo pin thing...
}

The key point, when using using masks and looking at bits, is to only compare to 0.  Only ==0 or !=0 should be used (and !=0 is implied if you just leave it off, as I did in the 1st, 3rd and 4th examples).

 

Last Edited: Wed. Nov 5, 2014 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You normally wire buttons as 'active-low'.    i.e. when you press the button it reads 0.

You use a pull-up resistor to ensure that an 'unpressed' button reads 1.

 

This means that you test for 0.

 

Since this is rather unintuitive,   I normally invert the PIND value.    So I can test for !0.

 

int main(void) {
	DDRB = 0xFF;             //B output
	DDRD = 0x00;             //D input
	PORTB = 0xFF;            //LED off (active-low)
	PORTD = 0b11111111;      //pull-ups on PORTD
	while(1) {
	   if(PIND & (1 << 0) != 0) {   //not pressed
		PORTB &= ~(1<<PB0); /* LED on */
		_delay_ms(100);
		PORTB |= 1<<PB0;    /* LED off */
		_delay_ms(100);
	   }
        }
	return 0;
}

Always test for 0 or for !0.     This means that you can test for a button on any pin.

Your test will only work with a button on PD0 because you are comparing with 1.

Using a != 0 test means you can test any button.

	   if(PIND & (1 << 5) != 0) {   //button on PD5 not pressed

I understand that active-low is good for electronics but not good for the human brain.

So I would rearrange logic:

          uint8_t pressed = ~PIND;      //invert the button state
          if (pressed & (1<<1)) do_option1();
          if (pressed & (1<<0)) do_option0();
          if (pressed & (1<<5)) do_option5();
          if (pressed & (1<<7)) do_option7();
      

 

David.

 

Edit.   I have corrected my wrong hexadecimal to a correct binary constant.

Last Edited: Wed. Nov 5, 2014 - 11:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got it working now.

These bit operations are quite tricky. One strange thing for me is that.

DDRB 0x00000001 //B0 is output others are input

but

PINB & (1<<0) //Checks PB0

Why bits are in opposite order?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A couple of things:

DDRB 0x00000001 

I bet you meant:

DDRB 0b00000001 

There's quite a difference between a "'b' and an 'x' there!! Anyway assuming you meant 0b00000001 then:

PINB & (1<<0) //Checks PB0

What do you actually think (1 << 0) means? It means take a 1 bit and move it 0 places to the left - that is do not move it at all. So in 0b notation this value is 0b00000001 which is exactly the same as was used with DDRB. So we're talking about the same bit!

 

A better example might be if you talking about the 5th bit (say). The value in DDRB would then be:

DDRB 0b00100000

which is a byte that has bit 5 (counting up from 0) set. The equivalent PINB line would then be:

PINB & (1<<5) //Checks PB5

And in this (1 << 5) means take a bit in position 0 and move it 5 places to the left. That is 00100000. In fact for n=0..7 you would have:

n  (1 << n)
0  0b00000001
1  0b00000010
2  0b00000100
3  0b00001000
4  0b00010000
5  0b00100000
6  0b01000000
7  0b10000000

EDIT: PS just noticed where you b/x confusion came from. David wrote:

	PORTD = 0x11111111;      //pull-ups on PORTD

Naughty, naughty Mr Prentice!! He did of course mean:

	PORTD = 0b11111111;      //pull-ups on PORTD

Note that 0x is common to all C compilers but 0b support is a non-standad extension. You can use it in avr-gcc within Studio but if you try to do this with other C compilers it may not work though I have a feeling that one of the later C/C++ standards has now adopted it but C89/C99 compilers won't necessarily support it. (it is very useful when programming microcontrollers though and a lot of C compilers for controllers (not application CPUs) will tend to have it).

 

EDIT2: actually for completeness I suppose I should have added 0x as well as 0b to my table above in which case it would be:

n  (1 << n)    in 0x notation
0  0b00000001     0x01
1  0b00000010     0x02
2  0b00000100     0x04
3  0b00001000     0x08
4  0b00010000     0x10
5  0b00100000     0x20
6  0b01000000     0x40
7  0b10000000     0x80

Last Edited: Wed. Nov 5, 2014 - 11:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't follow your question.

 

THe AVR's DDRx registers have a 1 bit for output.   (Data Direction Register)

M*crochip has a TRISx register that is 1 for input.   (Tristate register)

 

Your example shows a 64-bit hexadecimal constant.    You generally use an 8-bit binary (or hex) constant for an 8-bit value.

 

Yes,   the convention is to write number as MSD first.     e.g. $1234.56 instead of c65.4321

You have 1 thousand + 2 hundred + 3 tens + 4 units.     e.g. a thousand is 10 to the power of 3.   1 is 10 to the power of 0.

 

These are just concepts that you will have to learn to live with.     After all,   I think we have only had decimal numbers for 2000 years or so.

It is difficult to imagine how the Dinosaurs did their shopping.

 

David.

 

p.s.   Thankyou Cliff,   I have now edited my incorrect HEX constant.

Last Edited: Wed. Nov 5, 2014 - 11:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kestis wrote:

Got it working now.

These bit operations are quite tricky. One strange thing for me is that.

DDRB 0x00000001 //B0 is output others are input

but

PINB & (1<<0) //Checks PB0

Why bits are in opposite order?

In addition to the other answers, you can certainly write

DDRB = (1<<0); //B0 is output

and/or

PINB & 0b00000001  // checks PB0

Anyway, glad you got your program working.