Nov 1, 2015

generating RC Control signals (Pulse Position) directly from Arduino

Referred to as Pulse Position or PWM, but technically it is not either in normal sense, as it's not a duty cycle of pulse rate.

This is explained in many locations in more detail such as here on Wikipedia, but briefly, the min to max range on servo (typically 90 degrees total) by producing a pulse at around 50 Hz (20ms) whose width varies from 1000us to 2000us.


  • Rate of pulses affects speed of motor response. My maximum was with an interval of 8-10 ms. 
  • The exact time for min and max and actual angle of rotation vary for different motors, and usually need trimming to offset the exact pulse durations and a measurment to find angles.
  • Motor rotation rates vary a lot and are specified in time per 60deg turn.
  • 0.15 - 0.25 is typical; less than 0.1 is actually "fast," although lots of parts are referred to as fast are 0.18 - 0.2/60deg. 0.06 is the very fastest I've seen on more expensive, fast servos, at their highest input voltage. Price jumps hugely from $15 to $200 for ones I've found so far for a seemingly small speed increase. (ex: Futaba $$$)


Programming

Directly coding using time will work. Here is the code I used to try out control; it sweeps the value up and down to spin motor from ~min to ~max position, with a little margin near edges.

To control more than one motor, use one of the RC Servo libraries (such as the one supplied with Arduino examples).

Higher resolution control with library:  https://www.pjrc.com/teensy/td_libs_PulsePosition.html



// Control an RC servo by directly setting times
// filename: ds_servo_motion_direct_timer

#include <FlexiTimer2.h>

int t_on = 1500; // ms
int led_pin = LED_BUILTIN;
int pulseInterval = 10;     // this is the interval between variable pulses. 8 is near minimum time on my tests. nominal is 5 - 25ms  https://en.wikipedia.org/wiki/Servo_control

void setup() {
  pinMode(led_pin, OUTPUT);

  FlexiTimer2::set(pulseInterval, 1.0 / 1000, pulse); // call every n 1ms "ticks"
  FlexiTimer2::start();
}

void loop() {
  ;
}

// ISR
void pulse() {
  float rate_factor = 0.10;   // adjust this to change servo rotation rate
  static float t = 0;
  float t_step = 500.0 / 1000; // ms
  float f = 1.0;  // Hz
  int i = 0;
  // ideal range: 1500 +/- ~500
  t_on = 1500 + 50 + int( 400 * sin(rate_factor * t)); // fraction controls time; interacts with pulse interval above
  if (i++ % 4 == 0) {
    Serial.print(t_on); Serial.print("  ");
    Serial.println(90.0 * (t_on - 1000) / 1000); // print as degrees. print less often if fast output
  }

  //make high and low pulse durations (constant total time) 1000us min; 2000us max
  digitalWriteFast(led_pin, 1);
  delayMicroseconds(t_on);
  digitalWriteFast(led_pin, 0);
  delayMicroseconds(2000 - t_on);
  t += t_step;
}
EOF

No comments: