0

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
    }
}
}
asf
  • 3
  • 2
  • Where are you measuring this voltage? Anyway, what you are doing probably does not make sense. You can't put the outputs in series, and if you put them in parallel you might as well just do the necessary logic in software and simplify to a single output. – nobody Mar 03 '15 at 01:21
  • This is not a software issue - you cannot control the level output from a GPIO pin, it is determined by (and more-or-less equal to) the supply voltage to the chip. This is therefore not an appropriate question for SO. The likelihood is that what ever circuit these outputs are connected to is dragging these signals down (by presenting too low and impedance for example and drawing too much current); what are you trying to drive? – Clifford Mar 03 '15 at 09:51
  • 1
    @Clifford the output might seem to be 2v if the code is (inadvertently) doing PWM with a ratio 2:3. – Weather Vane Mar 03 '15 at 18:41
  • @WeatherVane : Good point, but one can only go on the information given. – Clifford Mar 03 '15 at 20:48
  • @Clifford But the code shown in the question *does* inadvertently do PWM when exactly one input is set... either setting then immediately clearing pin 3, or clearing then immediately setting pin 4 in a loop. – Dmitri Mar 03 '15 at 22:46
  • @Dmitri : Ah I see what you mean. I took asf at his word that he was *seeing* 2V assuming he'd observed it with a scope, or exercised the code in a debugger before posting perhaps. If you added an explanation for the observation to your answer rather that simply suggesting a fix I'd vote for it since it would be more instructive. – Clifford Mar 03 '15 at 22:56
  • If there is no hardware timers, you aren't really doing PWM, but rather some garage hack port toggling, where timing depends on the amount of assembler instructions generated by the C code... Don't do this. Use the on-chip hardware timer and/or PWM hardware. – Lundin Mar 05 '15 at 12:47

1 Answers1

3

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.

Dmitri
  • 9,175
  • 2
  • 27
  • 34