Search code examples
c++cembeddedgpioattiny

ATtiny85 digital "on" output fails to deliver 5 V


I am using an ATtiny85 as a micro-controller. I am trying to read two inputs at around 3 V each and output 5 V for every "on" input (more than 1V). I'm using PINB0 & PINB1 for input and PINB3 & PINB4 for output. The problem is when both PINB0 & PINB1 are on, I get two 5 V outputs, but when only one of the is on, I only get 2 V, I'm trying to fix that so I get 5V output.

Here is my code:

#inlude <avr/io.h>
#include <stdint.h>

int main(void)
{
// set pin 0 to input (pi signal 0)
DDRB &= ~(1 << PINB0);
PORTB &= 0 << PINB0;

// set pin 1 to input (pi signal 1)
DDRB &= ~(1 << PINB1);
PORTB &= 0 << PINB1;

//set pin 3 to output of 0
DDRB |= 1 << PINB3;
PORTB &= 0 << PINB3;

//set pin 4 to output of 1
DDRB |= 1 << PINB4;
PORTB &= 0 << PINB4;

while (1)
{
    if (bit_is_clear(PINB, 0) && bit_is_clear(PINB, 1))
    {
        PORTB &= 0 << PINB3;    //output zero volts 
        PORTB &= 0 << PINB4;    //output zero volts
    }
    else if (bit_is_clear(PINB, 0) && !(bit_is_clear(PINB, 1)))
    {
        PORTB &= 0 << PINB3;    //output zero volts
        PORTB |= 1 << PINB4;    //output 5 volts
    }
    else if (!(bit_is_clear(PINB, 0)) && bit_is_clear(PINB, 1))
    {
        PORTB |= 1 << PINB3;    //output 5 volts
        PORTB &= 0 << PINB4;    //output zero volts
    }
    else
    {
        PORTB |= 1 << PINB3;    //output 5 volts
        PORTB |= 1 << PINB4;    //output 5 volts
    }
}
}

Solution

  • With the code you posted, when only one input is set, the corresponding output is rapidly toggled on and off in your loop rather than remaining on, giving an output voltage that averages somewhere between that of a high output and that of a low one. This happens because although you correctly set the output high, you also set it low when you clear the other output immediately before or after. For example, when only pin 1 is high, you run this code in your loop:

        PORTB &= 0 << PINB3;    //output zero volts
        PORTB |= 1 << PINB4;    //output 5 volts
    

    Since shifting 0 by PINB3 bits (or any other amount) gives zero, which you then AND with PORTB, the first line clears all bits of PORTB turning off both outputs. Then in the next line, you turn pin 4 back on.

    Similarly, when only pin 0 is high, you run the following:

        PORTB |= 1 << PINB3;    //output 5 volts
        PORTB &= 0 << PINB4;    //output zero volts
    

    In this case the first line turns on pin 3, but the second line again turns off both outputs.

    Instead of trying to shift 0 to the right bit position, try shifting 1 and then inverting the bits. For example, to turn off pin 4:

        PORTB &= ~(1 << PINB4);
    

    ...and to turn off pin 3:

        PORTB &= ~(1 << PINB3);
    

    That way you AND PORTB with a value with all bits set except the one you wish to clear, instead of a value with no bits set.