8
\$\begingroup\$

I have a TowerPro MG90D servo (Manufactuer Link) (ServoDatabase Link).
It has a 180deg range (non-continuous).

TowerPro MG90D

It responds great to my servo tester:
Servo tester

Observe the following 7% Duty Cycle (about 90 deg) on the tester:

scope servo tester

scope servo tester

Servo responds fine.


However, when I use servo.write() with my Arduino Mega 2560 clone, the servo does not respond to any angle output. I have several other servos that work just fine with the same code on the same pins.

Observe the following 7% Duty Cycle on the Arduino with servo.write(90):

scope arduino servo 90deg

No response. The servo is "limp"; it is not holding any position.


While I was writing this question, I thought of trying servo.writeMicroseconds().

Here is servo.writeMicroseconds(1450):

scope arduino servo 1450ms

Servo responds!

Here is servo.writeMicroseconds(1472) (working), which has the same time intervals as the prevoius non-working servo.write(90)!

scope arduino servo 1472ms

servo.writeMicroseconds(1550) (working):

scope arduino servo 1550ms


What is the difference?
The servo tester worked at 49.5Hz, while servo.write() failed at 49.9Hz. I wondered if somehow that 0.4Hz made a difference, but then I see that servo.writeMicroseconds() worked at 49.9Hz too.

In the above scope captures, it can be seen that both servo.write(90) and servo.writeMicroseconds(1472) have the same time intervals:
 1,474,560ns HIGH
18,544,640ns LOW

The signals are so similar... What could cause servo.write() not to work?

My code is as basic as possible:

#include <Servo.h>
Servo serv1;

void setup() {
  serv1.attach(3); // Pin 3
}

void loop() {
  serv1.write(90); // No response
  delay(3000);

  serv1.writeMicroseconds(1472); // Works
  delay(3000);

  serv1.write(0); // No response
  delay(3000);

  serv1.writeMicroseconds(1800); // Works
  delay(3000);
}

schematic

simulate this circuit – Schematic created using CircuitLab

\$\endgroup\$
14
  • \$\begingroup\$ I have tried both a benchtop linear power supply, and I've also tried using a buck converter to step down from 9V. \$\endgroup\$ – Bort Dec 6 '16 at 14:34
  • 1
    \$\begingroup\$ Are you sure you have a steady wave for the full 3 seconds when you use write? There's really no reason for the servo not to work, so I'd question your signals. \$\endgroup\$ – Dmitry Grigoryev Dec 6 '16 at 16:32
  • 1
    \$\begingroup\$ @Bort Then I'm having a hard time believing your story. \$\endgroup\$ – Dmitry Grigoryev Dec 6 '16 at 16:53
  • 2
    \$\begingroup\$ @BigHomie: The signals are identical as far as the scope images go. There's something going on besides the signal. Poor ground is one thing that could cause different reactions to nominally identical signals. \$\endgroup\$ – JRE Dec 7 '16 at 17:59
  • 2
    \$\begingroup\$ If possible, capture two scope traces on the servo signal line with the servo connected. One trace with serv1.write() only and one trace with serv1.writeMicroseconds() only. Post both traces. For extra charm throw in a third trace from your tester with the servo connected. \$\endgroup\$ – neonzeon Dec 17 '16 at 13:51
0
\$\begingroup\$

First, a quick aside. You seem to have a slight misunderstanding as to how servos work. Servos are not controlled by PWM, and they do not know or care that you are sending pulses at 49.whatever Hz. They do not know the pulse is some percentage of some arbitrary period. The servo couldn't care less what the time between pulses is. I say this because you seem unusually focused on things that don't actually matter.

Servos don't even really know or care that the voltage is high or low at a given time. They only care about one thing: the time between a rising edge and falling edge.

The servo is controlled by detecting a rising voltage edge, and measuring the time until there is a falling edge. Valid times are usually between 1.0 to 2.0ms, but it can vary from servo to servo.

You can control it at 1Hz, 10Hz, 50Hz, 100Hz. Most will respond to even higher pulse rates, but again this is variable. What I am trying to say is that the frequency, duty cycle, duration between pulses, all could not be less relevant to your problem, which is that the servo isn't responding when you expect it to.

The only thing that is relevant is edges of your pulse, which you have not given any attention to. If you want to figure this out, please start by looking at the things that matter, give close up captures of your pulse edges, that sort of thing. You've captured nothing useful in those screen shots, which is probably why there doesn't appear to be a problem or difference. There are a lot of problems or differences that would never be visible with what you've measured.

What I can see is that the capture of the non-working pulse train is noticeably dirtier, both the pulse and the ground, than any of the others. Which is odd, as it should be calling the same function as the others. Why is that one so much noisier?

More importantly, in the nonworking capture, look at the 'fall time'. 809µs? Your oscilloscope thinks it sees a fall time lasting 0.8ms. That is...bad. Clearly that is incorrect, but the fact remains, that is what it measures.

That is a classic sign of a dirty edge. Think about it. If this pulse is fooling your high end piece of test equipment that is your oscilloscope into seeing a ridiculously long edge or fall time, or maybe so dirty it just can't correctly detect the falling edge all the time (or who knows), then what chance does that poor crappy little $8 servo have of picking up a decent falling edge?

If a servo doesn't get a valid pulse (like if the falling edge takes too long, is too dirty, or is missed) within the acceptable pulse range, and by the servos reckoning by the edges which may or may not have anything to do with what you consider the pulse edges to be, then it responds just as if it were off.

In other words, not only does it not move, but it will not resist its shaft moving. It will simply be limp, exactly as you're seeing.

Now, this begs the question.... why would calling servo.write effect the edge quality?

You said a clone. Like this one? enter image description here

These clones in particular tend to behave erratically due to the incredibly poor decoupling. There should be decoupling capacitors on every power pin, and as close as possible to the mega2560. And on the actual arduino, indeed there are. On these clones however, they are much too far away, or perhaps missing, it is hard to tell. Its obvious from looking at the board that it will not behave reliably, that is the important thing.

So what is the difference though?

When you call servo.write, it pushes the stack higher than if you call writeMicroseconds. Given the mega2560's 3 byte stack pointer (17 bits), it is having to flip a bunch of critical bits that it doesn't have to when you call writemicroseconds. I know this seems like an unlikely difference, but I've experienced my fair share of poorly decoupled microcontrollers, and atmegas in particular seem to exhibit odd behavior specifically when using timers and/or pushing or popping the stack. Something similar happened for me, only the stack was corrupted when I was trying to drive LEDs with PWM, but if I put everything inline without pushing the stack, it worked. Poor decoupling was ultimately the problem.

I would fully expect poor decoupling to be able to, for reasons known to that atmega2560 and no one else, are having a detrimental effect on the edge quality of that pulse, but only when you are pushing the stack right before. This servo is just not quite able to handle the way those edges are being sullied, so it sees no valid pulses in that case. Other servos obviously manage it.

Decoupling stuff is always bizarre and hyper specific like this. Which is why decoupling is so important. Keep the nightmarish hell of problems lack of capacitance can cause you at bay with nice fat ceramic caps and as close to the chip as is physically possible.

\$\endgroup\$
0
\$\begingroup\$

This may be related to the output pin settings done by the .write() routine. Please try using a 1K pull-down resistor, it it doesn't work then remove it and use it pull-up resistor. this will balance the effect of any internal week pull-up/pull-down resistor that may be set by the routine. When you are measuring the signal with your scope the internal resistance of the probe is acting as a pull-down.
Most servos will also release the power to the internal motor if the signal in not preset for 10 pulses in a row. This is used to save power.

\$\endgroup\$
4
  • \$\begingroup\$ I'm afraid your answer is not correct. servo.write() takes an angle as input, not time. People shouldn't be voting up answers blindly. \$\endgroup\$ – Bort Jan 4 '17 at 14:59
  • \$\begingroup\$ Yes, My mistake, i didn't read the code well. this may be related to the output pin settings done by the .write() routine. \$\endgroup\$ – 555 Jan 4 '17 at 15:07
  • 2
    \$\begingroup\$ This would have been my first thought, the scope traces seem to refute this though. A missing ground and a different pull-up/-down situation might account for the same signal being sent but not getting there (after the probe location). \$\endgroup\$ – KalleMP Feb 5 '17 at 21:21
  • \$\begingroup\$ I think @KallieMP is probably right. Pullup/down resisters might very well be the answer. They could limit the current. The pulses may be formed correctly yet with not enough drive. So no matter the particular function of servo.write() the current levels must precisely match the output of the servo tester in order to obtain the same result. \$\endgroup\$ – SDsolar Feb 20 '17 at 4:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.