Electronic – ATmega328p PIN reading

atmegaatmelcmicrocontrollerpins

int main(void)
{

DDRD = 0x00 ;
DDRB = DDRB | 0b00100000 ;
while (1)
{
     while(!(PIND & (1<<6))){
       PORTB |= 1<<PORTB5 ;  
    }
    while((PIND & (1<<6)))  {
        PORTB &= ~(1<<PORTB5) ;
    }

}
return 0;
}

I am trying to create a basic security system by using a switch that when broken is picked up by PIND7, then sets PB5. And Vicd versa, when the switch is set, the low signal is picked up by PIND7 then clears the bit on PB5.
The micro-controller is not having a problem when the switch is set, it clears the bit in PB5 immediately, but when the switch breaks, it takes about 30 seconds for the bit in PB5 to be set.
Thank you for any help you can give, I apologize if my code is sloppy I am just a beginner

Best Answer

A simple truth table usually helps a lot.

Here's my try at deciphering it from your text:

PIND6 | PORTB5
  0   |   1
  1   |   0

This truth table simply says, if PIND6 is 0, then PORTB5 should be 1, and "vice versa". In proper terms, PORTB5 is the logical NOT of PIND6.

This is the mathematical equation for what we want:
PORTB5 = !PIND6 which is the same as \$\text{PORTB}_5 = \overline{\text{PIND}_6}\$.


I don't think you want to have two while loops in your major while loop. That's not a good way for checking a state, since you can get stuck in them. As I believe you are getting.

What you should do instead is to either use an if statement or simply assigning PORTD, or use either of them together with a debounced input. I will show all three of them with some space between. You should not keep all three, you should choose one of them.

  • The one-liner makes it hard to understand.
  • The if statement is much easier to udnerstand.
  • The debouncer detects when the switch changes the input, the input can change rapidly to a logic 1, a logic 0, hundreds of times within a couple of microseconds when it's transitioning, and you don't want your output to also change in that manner. So instead we detect a change and then look some time later when the switching noise has settled.

Here's some untested code:

int main(void)
{
    DDRD = 0x00 ;
    DDRB = DDRB | 0b00100000 ;
    unsigned int debounce = 0; //remove this line if you don't care about debouncing
    char old_PIND = PIND;//remove this line if you don't care about debouncing
    while (1)
    {
        ////////////////////////////////////////////////
        //You can do either this:
        ////////////////////////////////////////////////
        PORTB = (PORTB&(~(1<<PORTB5)))|(~((PIND&(1<<6))>>1));
        //(PORTB&(~(1<<PORTB5))) => PORTB but with a 0 on bit 5
        //(~((PIND&(1<<6))>>1)) => PIND, but only 6th bit, inverted and shifted 1 step ->
        //And then just OR them together
        //You could probably remove some parenthesis, but I prefer having more than fewer
        ////////////////////////////////////////////////
        //Or you can do this, it will give you the same result
        ////////////////////////////////////////////////
        if(PIND&(1<<6)){//If it's a 1
            PORTB &= ~(1<<PORTB5);//then set PORTB5 to 0
        }else{
            PORTB |= (1<<PORTB5); //if PIND6 is a 0, set PORTB5 to 1
        }

        ////////////////////////////////////////////////
        //Or you can do this if you want debouncing
        ////////////////////////////////////////////////
        if(debounce==0){
            if(old_PIND!=PIND){
                //if we're in here, it means that the input has changed
                //and that the input may bounce a lot for the next couple of microseconds. 

                //we want to wait some time and then use the value we get later
                //because that value will (hopefully) be stable, without any burst noise. 

                debounce = 10000;//This sets the debounce-timer
                //if you want to do this "properly", then you should use one of 
                //the built in timers that Atmega328P supports,
                //instead of using a variable like I do. But this will work.               
            }else{
                //if we're in here, it means that the input has stayed the same 
                //for one debounce-timer, so now it's stable.

                //same code as before: 
                if(PIND&(1<<6)){//If it's a 1
                    PORTB &= ~(1<<PORTB5);//then set PORTB5 to 0
                }else{
                    PORTB |= (1<<PORTB5); //if PIND6 is a 0, set PORTB5 to 1
                }
            }
            old_PIND = PIND;//update old_PIND so we can detect changes
        }else{
            debounce-=1;
        }


    }
    return 0;
}

So technically, just replace your while statements with if statements and learn the word "logical invert" and you'll be on the good side.

If you'd want to be pedantic about it, or "proper", then you should use interrupts together with internal timers, because they make the code more efficient. But this code will solve the problem as you have presented it.