Then follow the wiring instructions in the comments at the top of
the sketch. Remember not to ever connect or disconnect a stepper
motor from a driver board with power applied. In the video, I've
wired the 5V output from the Big Easy Driver to power both the
Fubarino Mini and the RC receiver, just cause it was simple.
I hope you enjoy this sketch - let me know if you make anything
really cool with it!!
(This sketch has been tested to work in Arduino 1.8.9 + chipKIT core. It uses straight
up register manipulation to set up the timer and output compare.
Copy and paste this into Arduino IDE, or download the
RCServoInStepperOut.ino file below.
/* Example sketch for reading RC servo input and driving a stepper motor in speed mode
* Written by Brian Schmalz of Schmalz Haus brian@schmalzhaus.com
* This sketch runs on a Fubarino Mini, and reads an RC servo type pulse input on pin 17
* And then scales that input based on the constants below, and outputs a step/dir type stepper
* output stream to be used with a driver like the Big Easy Driver or Easy Driver from Sparkfun.
* The step signal is on pin 9 and the direction signal is on pin 8.
*
* The whole idea here is to allow somebody with an RC transmitter to precisely control the
* speed and direction of a stepper motor, with configurable limits so that the max and min speed
* can be changed for the application.
*
* This sketch is in the public domain, and anybody can do anything with it. No warranty, etc.
*
* Hardware setup:
* Take a Fubarino Mini, a stepper motor appropriate for your driver (like a NEMA 17 for Easy Driver)
* a power supply (like 12V DC, 2A) and a stepper motor driver like the Big Easy Driver or Easy Driver.
* Also you'll need some source of RC servo signals - like an RC receiver.
* Download this sketch to the Fubarino Mini.
* Connect grounds of Fubarino Mini, stepper driver, RC receiver, and power supply together.
* Connect motor to stepper driver.
* Connect 5V output from Big Easy Driver to battery input of RC receiver, as well as VIN pin of Mini.
* (You can use another 5V source if you want.)
* Connect RC receiver servo output signal to Mini pin 17.
* Connect Mini pin 8 to stepper driver DIRECTION input.
* Connect Mini pin 9 to stepper driver STEP input.
* Power up system and watch stepper motor move based on stick input.
*
* Notes:
* This sketch controls the speed of the stepper motor, not it's position. In other words, it will NOT
* operate like an RC servo (that would just take a little different math actually). It basically
* turns the stepper motor into a DC motor who's speed and direction you can control with an RC servo input.
* If the stick is centered (within the dead zone) then the stepper motor will not move.
* If the stick is advanced towards RC_MAX_US, the speed of the stepper motor will linearly increase in forward direction.
* If the stick is pulled back towards RC_MIN_US, the speed of the stepper will increase in reverse.
* There are limits to the maximum number of steps per second that your driver/motor can achieve. So don't set
* MAX_SPEED_SPS too high. Experiment a lot. Remember that torque is approximately inversely proportional
* to speed with steppers, so your torque will drop off the faster you go.
* Because we are using a hardware timer and output compare to generate the step signal in this sketch, it can
* go _very_ fast, with zero jitter.
* This sketch can also be used to drive ANYTHING that takes step/dir inputs (many servo motor controllers
* can do this).
*/
// These are the user modifiable defines that you can use to tweak the performance of the system
#define MAX_SPEED_SPS 12000 // Units of steps per second, maximum stepper speed (at RC_MAX_US and RC_MIN_US)
#define MIN_SPEED_SPS 1400 // Units of steps per second, minimum stepper speed (at +/-RC_DEAD_US from RC_CENTER_US)
#define RC_MAX_US 2300 // Units of uS, maximum RC servo input pulse width accepted
#define RC_MIN_US 1300 // Units of uS, minimum RC servo input pulse width accepted
#define RC_DEAD_US 50 // Units of uS, value on either side of RC_CENTER_US to be considered 'no motor movement'
#define RC_INPUT_PIN 17 // This can be any pin
#define MOTOR_STEP_PIN 9 // This is not arbitrary, modify code below to use different Output Compare if you need to change this
#define MOTOR_DIR_PIN 8 // This can be any pin
// These are defines that the user should not ever have to modify
#define RC_CENTER_US ((RC_MAX_US + RC_MIN_US)/2) // Half way between Min and Max RC value
#define PR2_MAX (F_CPU / 2 /(MIN_SPEED_SPS * 2)) // Minimum speed for PR2 register
#define PR2_MIN (F_CPU / 2 /(MAX_SPEED_SPS * 2)) // Maximum speed for PR2 register
void setup() {
// Set up timer2 and timer3 to be a 32 bit timer
// This 32 bit timer will be used to drive OC4 on
// on pin RB2 (Fubarino Mini pin 9)
T2CONbits.ON = 0; // Turn off the timers
T3CONbits.ON = 0;
T2CONbits.TCS = 0; // Internal peripheral clock source
T2CONbits.T32 = 1; // 32-bit mode turned on for Timer2 and 3
T2CONbits.TCKPS = 0b001; // 1:2 prescale
TMR2 = 0x00000000;
PR2 = 0x00000000;
// Set up OC4 to be PWM clocked on TMR2/3
OC4CONbits.OCM = 0b011; // Compare toggles OC4 output
OC4CONbits.OCTSEL = 0; // Timer2 is clock source
OC4CONbits.OC32 = 1; // Use 32-bit mode
OC4RS = 0x00000000;
OC4CONbits.ON = 1; // Turn OC4 on
T2CONbits.ON = 1; // Turn the timer(s) on and start them running
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
// Re-route PPS ofo OC4 to pin RB2 (Mini pin 9)
mapPps(9, PPS_OUT_OC4);
pinMode(17, INPUT);
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
// Take an RC value in, which ranges from 2320 to 1260,
// and map it to a PR2 value (in units of 1/24000000 s)
// Add a sign for direction, and handle dead zone.
int calc_speed(int rc_in) {
int temp;
// If the receiver is turned off, it won't be generating
// any pulses, so rc_in will be zero. In that case, return
// zero
if (rc_in == 0) {
return (0);
}
// If the receiver is turned on, but the transmitter is not,
// the receiver may be sending us runt pulses that we need
// to ignore
if (rc_in < 1000) {
return (0);
}
// First limit the input
if (rc_in > RC_MAX_US) {
rc_in = RC_MAX_US;
}
else if (rc_in < RC_MIN_US) {
rc_in = RC_MIN_US;
}
// If rc_in is in the dead zone, then return zero
if (
(rc_in < (RC_CENTER_US + RC_DEAD_US))
&&
(rc_in > (RC_CENTER_US - RC_DEAD_US))
) {
return (0);
}
// Break apart the math into above and below center
if (rc_in >= (RC_CENTER_US + RC_DEAD_US)) {
// Bring it down so it starts at zero to (RC_MAX_US - RC_CENTER_US - RC_DEAD_US)
rc_in = rc_in - (RC_CENTER_US + RC_DEAD_US);
// Now scale so that rc_in of 0 becomes PR2_MIN and
// rc_in of (RC_MAX_US - RC_CENTER_US - RC_DEAD_US) becomes PR2_MAX
rc_in = (RC_MAX_US - (RC_CENTER_US + RC_DEAD_US)) - rc_in;
temp = rc_in * ((PR2_MAX - PR2_MIN)/(RC_MAX_US - (RC_CENTER_US + RC_DEAD_US)));
temp = temp + PR2_MIN;
}
else {
// Bring rc_in down so it starts at zero to (RC_CENTER_US - RC_DEAD_US)
rc_in = rc_in - RC_MIN_US;
// Now scale so that rc_in of 0 becomes PR2_MIN and
// rc_in of (RC_MAX_US - RC_CENTER_US - RC_DEAD_US) becomes PR2_MAX
temp = rc_in * ((PR2_MAX - PR2_MIN)/(RC_MAX_US - (RC_CENTER_US + RC_DEAD_US)));
temp = temp + PR2_MIN;
temp = -temp;
}
return (temp);
}
// Take a new value for PR2 and strip off the sign, handle
// the direction bit, and update PR2 with the new value.
void update_speed(int new_speed) {
// Only update timer if it's not running
T2CONbits.ON = 0;
// Read sign for direction, and make new_speed positive
if (new_speed < 0) {
new_speed = -new_speed;
digitalWrite(8, HIGH);
}
else {
digitalWrite(8, LOW);
}
// This is a 32 bit write
PR2 = new_speed;
// And if TMR2 is already higher than the new PR2, zero it out
// so it can count up and match PR2
if (TMR2 >= PR2) {
TMR2 = 0x00000000;
}
T2CONbits.ON = 1;
}
void loop() {
int ch1, new_speed;
static int blink_time = 0;
ch1 = pulseIn(17, HIGH, 25000);
new_speed = calc_speed(ch1);
update_speed(new_speed);
Serial.print("In:");
Serial.print(ch1);
Serial.print(" Out:");
Serial.println(new_speed);
blink_time++;
if (blink_time > 100)
{
digitalWrite(LED_BUILTIN, HIGH);
}
else
{
digitalWrite(LED_BUILTIN, LOW);
}
if (blink_time > 200)
{
blink_time = 0;
}
}
Download the above sketch here.