How to amplify a low voltage signal for digitalRead()?

Hi,
I have this optical rotary encoder for my stepper motor:
https://nodna.de/images/manufacturers/Phidgets/3531/3531_0_Datasheet.pdf
With an output of 2-12mA and -0.5V.
Now I am wondering how I can boost the voltage above 2V so that my Arduino 101 can read it? The negative voltage output is also confusing me. The normal Forum advice is to connect to the analogue pins but those are all in use and I need the encoder to attach to the interrupt pins.
I have thought about transistors, maybe several in a row, but they all seem to need a base voltage of 5V and SMD parts are not really an option for me. How do I pick the right one? I have also read about OP's. Would that be an option?

The datasheet says the output voltage is from -0.5V to Vcc, with VH >= 0.85*Vcc and VL < 0.3V.

I'm not familiar with the Arduino 101, but that voltage range is good for the digital inputs of Arduinos for which I am familiar.

The datasheet clearly shows the supply voltage as 5V dc and the output voltage as 0.5 V to 5.0 V dc.

"Signal level VH 85% Vcc, VL <0.3V dc. "

Based on the datasheet, the output voltage is a TTL level signal compatible with the arduino GPIO pins.

You will not be able to measure it with a meter because it will average the high/low and report 0v.
You can however , prove to yourself that the level is TTL level by writing a sketch that turns on a led (d13)for 300 mS if it detects a HIGH and turns it off if it detects a low. The led should cycle on and off. If you had an oscilloscope you could see the signal is 0V to 5V, meaning it is either HIGH or LOW , but never in between, as is normal for a TTL level signal. Try writing the sketch using

int val = digitalRead(pin);
if (val == HIGH)
{
  digitalWrite(ledpin,HIGH);
  delay(300);
}

the LOW detect routine is just the opposite.

I just checked out the Arduino 101. Interesting, an Intel based Arduino -> http://www.arduino.cc/en/uploads/Main/Arduino101-REV4Schematic.pdf

It's a bit annoying they don't have a part number on the schematic for the Intel processor, but I think this is the right datasheet for it -> http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/quark-d1000-datasheet.pdf

From Table 8.0 of the datahseet, it seems that processor has a max supply voltage of 3.63 max. Hopefully your encoder has a 3.3V model.

Also from Table 8 you can see the GPIO input range is -0.5 to VDD+0.5V, so you should be fine with your sensor as long as it's a 3.3V model.

Hi thank you for the Quick reply! :slight_smile:
Ok I am not good at reading datasheets. I had tried to read the encoder before posting, but didn't get any signal. I used a 10k pullup and also tried connecting the signal wires directly. I will try a lower Pullup when I get home.
@BigBobby: All Pins are protected against 5V. So that is not a problem.

Ah, I see. Yes the LSF0108s on the schematic will turn 5V signals into 3.3V signals for your processors. You should be all set then.

Where does it say that you need pullups on the ChA and ChB signals? From the datasheet it seems like the signals are able to output 0V-5V with a current of 2-12mA?

2-12mA is sort of a strange spec really. You'd like to think that the output current capability doesn't vary that much, but your 10kΩ pullup is only drawing 0.5mA when the encoder output is low. I find it hard to believe that's a problem.

Ok I am not good at reading datasheets. I had tried to read the encoder before posting, but didn't get any signal. I used a 10k pullup and also tried connecting the signal wires directly. I will try a lower Pullup when I get home.

The datasheet is lousy, but it doesn't say, or imply that you need pull-ups. If it does, 10K would be fine, or you can enable the Arduino's internal pull-ups, and a pull-up resistor wouldn't hurt anything if it's not needed.

How are you measuring the signal? Did you try the test-program raschemmel gave you? If you don't get anything, try disconnecting the encoder and alternately ground and connect the input to Vcc to see if the LED reacts.

The normal Forum advice is to connect to the analogue pins but those are all in use and I need the encoder to attach to the interrupt pins.

I recommend a simpler approach before playing around with interrupts. And, make a little encoder-test program without the analog stuff and whatever else your program is doing.

You probably don't need pullups . If you don't have electronics experience don't just start picking pullup/pulldown resistor values at random. First try the digitalRead example I gave you. Make another routine to detect LOW . Try that FIRST, BEFORE trying to read the encoder values. You need to first verify that the arduino can detect the signal levels and then you can try the encoder code. Also, DO NOT USE ANY RESISTOR VALUES LESS THAN 10 k for pullup or pulldown resistors.

BigBobby:
Where does it say that you need pullups on the ChA and ChB signals? From the datasheet it seems like the signals are able to output 0V-5V with a current of 2-12mA?

It doesn't have to, encoders are often open collector, you would assume its likely (and pull-ups dont cause
any harm if the output happens to be push-pull.)

Some encoders have two differential outputs per signal, so A+, A-, B+, B-, Z+, Z-, but that is
obvious from the pinout.

If the outputs are open collector you need 10 k to 20 k pullups (or internal) .
Try the example sketch WITHOUT pullups FIRST.

MarkT:
It doesn't have to, encoders are often open collector, you would assume its likely (and pull-ups dont cause
any harm if the output happens to be push-pull.)

Some encoders have two differential outputs per signal, so A+, A-, B+, B-, Z+, Z-, but that is
obvious from the pinout.

That's what I was thinking, the outputs are probably push-ull and the pullups aren't necessary.

Although that 2-12mA spec is weird. If their max output current can range anywhere from 2mA to 12mA that's a huge tolerance, and you'd think the manufacturer would be better off just saying 2mA since that's what their customers would need to design to anyway.

If those outputs are weird, however, and they actually need between 2mA-12mA of load to have sharp edges or something then maybe he actually needs a 1.2kΩ pulldown to ground. After all, a 10kΩ pullup to 5V is actually causing -0.5mA to come from the ChA and ChB pins.

The 2mA lower limit is perhaps to guarantee bandwidth - the outputs are likely phototransistors, which
switch slower with low current (since they are more heavily saturated then) - the higher collector
current clears the stored charge faster.

That's what I was thinking, the outputs are probably open collector and the pullups aren't necessary.

I'm sure you know that if they are open collector the pullups are mandatory so I am assuming what you meant is that the pullups are probably integrated onboard.

Sorry for bothering you. Turns out the ground of the encoder cable was broken. I didn't check because I thought the Voltage was the problem. Right now it is running without pullups.
Now it works perfectly. Thank you! :smiley:

Sorry for bothering you. Turns out the ground of the encoder cable was broken. I didn't check because I thought the Voltage was the problem. Right now it is running without pullups.
Now it works perfectly. Thank you! :smiley:

No bother at all. Congratulations on the successful troubleshooting . Maybe you learned something anyway.

Do you mind posting your code for others to use ?

raschemmel:
I'm sure you know that if they are open collector the pullups are mandatory so I am assuming what you meant is that the pullups are probably integrated onboard.

Oh jeez, you're correct I meant push-pull. I'll correct my post. Thanks for pointing out the mistake.

Yes I have learned something. :smiley:
Here is my code:

const uint8_t encoderMotorPinA = 10;
const uint8_t encoderMotorPinB = 11;
const uint8_t stepperPin = 15;
const uint8_t directionPin = 16;
const uint8_t OKButton = 4;
volatile boolean encoderMotorPastA = false;
volatile boolean encoderMotorPastB = false;
volatile int encoderMotorPosition = 0;
volatile int allenc = 0;
uint16_t motorPhaseDelay = 500;

void setup() {
  Serial.begin(9600);
  while(!Serial){}
  pinMode(encoderMotorPinA, INPUT); 
  pinMode(encoderMotorPinB, INPUT);
  pinMode(OKButton, INPUT);
  pinMode(stepperPin,OUTPUT);
  pinMode(directionPin, OUTPUT);
  
  encoderMotorPastA = digitalRead(encoderMotorPinA);
  encoderMotorPastB = digitalRead(encoderMotorPinB);
  
  attachInterrupt(encoderMotorPinA, doEncoderMotorA, CHANGE);
  attachInterrupt(encoderMotorPinB, doEncoderMotorB, CHANGE);
  
  Serial.println("Encoder TEST:");  
}

void doEncoderMotorA(){
  if(encoderMotorPastA == encoderMotorPastB){
    encoderMotorPosition++;  
  } else{
    encoderMotorPosition--;
  }
  encoderMotorPastA = !encoderMotorPastA; 
  allenc++; 
}

void doEncoderMotorB(){  
  if(encoderMotorPastA != encoderMotorPastB){
    encoderMotorPosition++;  
  } else{
    encoderMotorPosition--;
  }
  encoderMotorPastB = !encoderMotorPastB; 
  allenc++; 
}

void photoStep(uint32_t stepAmount){
  uint32_t i = 0;
  digitalWrite(directionPin, LOW);
  while(i<stepAmount){
    digitalWrite(stepperPin, HIGH);
    delayMicroseconds(motorPhaseDelay);
    digitalWrite(stepperPin, LOW);
    delayMicroseconds(motorPhaseDelay);
    i++;
  }
}

void loop() {
  if(digitalRead(OKButton) == HIGH){
    Serial.println("-------");
    Serial.println(encoderMotorPosition);
    photoStep(3000);
    Serial.println(encoderMotorPosition);
    Serial.println(allenc);
    delay(1000); 
  }
  delay(1000);
}

Some of it is specific to my stepper driver. Since there is no bounce on the optical encoder I am only counting interrupts. Sometimes it misses a few steps. I don't know yet if those are hiccups of the motor or a misreading of the encoder causing it to count backwards for a while. But I will figure that out.

Thanks for posting your code.

Did you write the encoder part of that code or did you find it on the web ?

I found it on the playground a few weeks ago but now I can't find it anymore:
http://playground.arduino.cc/Main/RotaryEncoders

Encoder TEST:
-------
0
17998
18014
-------
18000
35998
36073
-------
36000
53998
54134
-------
54000
71498
71694
-------
71499
89498
89754
-------
89499
107498
107818
-------
107499
124992
125374
-------
124994
142992
143430

Checking the serial output, it seems like the motor is missing steps sometimes. But I am still not sure if I want to rely only on interrupts. One misreading and the encoder starts counting backwards. I am building a camera slider for a photography project. One picture will take several hours. It would be fatal if the encoder reverses after 5 hours. ^^

I don't suppose you can afford an absolute encoder ? Is this rotary encoder built into the motor ?

Did you try this code from here

//PIN's definition
#define encoder0PinA  2
#define encoder0PinB  3


volatile int encoder0Pos = 0;
volatile boolean PastA = 0;
volatile boolean PastB = 0;

void setup() 
{

  pinMode(encoder0PinA, INPUT);
  //turn on pullup resistor
  //digitalWrite(encoder0PinA, HIGH); //ONLY FOR SOME ENCODER(MAGNETIC)!!!! 
  pinMode(encoder0PinB, INPUT); 
  //turn on pullup resistor
  //digitalWrite(encoder0PinB, HIGH); //ONLY FOR SOME ENCODER(MAGNETIC)!!!! 
  PastA = (boolean)digitalRead(encoder0PinA); //initial value of channel A;
  PastB = (boolean)digitalRead(encoder0PinB); //and channel B

//To speed up even more, you may define manually the ISRs
// encoder A channel on interrupt 0 (arduino's pin 2)
  attachInterrupt(0, doEncoderA, RISING);
// encoder B channel pin on interrupt 1 (arduino's pin 3)
  attachInterrupt(1, doEncoderB, CHANGE); 

}


void loop()
{  
 //your staff....ENJOY! :D
}

//you may easily modify the code  get quadrature..
//..but be sure this whouldn't let Arduino back! 
void doEncoderA()
{
     PastB ? encoder0Pos--:  encoder0Pos++;
}

void doEncoderB()
{
     PastB = !PastB; 
}

One misreading and the encoder starts counting backwards. I am building a camera slider for a photography project. One picture will take several hours. It would be fatal if the encoder reverses after 5 hours.

I hope you plan to perform some bench testing before deploying it in the field.