Search This Blog

Showing posts with label L293D. Show all posts
Showing posts with label L293D. Show all posts

Tuesday, 29 April 2014

Arduino Sketch to manage high resolution - high speed linear encoders


In the ongoing saga of building my RepStrap 3D printer from salvaged printer parts...

The first axis is sorted out:



This is the printhead carriage salvaged out of a inkjet printer.  It basically consists of a DC gear motor driving a tensioned belt, pulling the print carriage across a high resolution optical encoder strip





There have been plenty of people do this before me, but this is my kick at repurposing salvaged printer parts.  


This is the print head circuit board from the carriage.  In the center, you will see the solder pads for the Optical Encoder Sensor.  My original intent was to keep the flat cables intact, and pull the signals off of those, but at about 50mil pitch... I can't even think about soldering that... 
(yeah, I'm getting old)

As they have already done the work of wiring the IR LED with a resistor to VCC, all I really need is four wires.  VCC/GND/Encoder Phase A and B. 
The Optical Encoder provides logic level outputs to the Interrupt pins on the Arduino.  The sketch below only addresses the X-AXIS for demonstration purposes, and only uses one Interrupt on Phase A, while polling the signal level on Phase B.  This provides half the resolution capable from this Quadrature encoder. 

In a future article, we will demonstrate using PinChange Interrupts to get the full resolution from this arrangement.

/***************************************************************************************
*  Lin_Enc_01.ino   04-29-2014   unix_guru at hotmail.com   @unix_guru on twitter
*  http://arduino-pi.blogspot.com
*
*  This sketch allows you to run two salvaged printer carriages for X/Y axis using their 
*  linear encoder strips for tracking. 
*  Both interrupt routines are below, but for the sake of demonstration, I've only set up
*  the X-AXIS for now.
*
*****************************************************************************************/

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

#define frontstop = 100            // Right most encoder boundary
#define backstop = 3600            // Left most encoder boundary


// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);


const int encoder1PinA = 2;        // X-AXIS  encoder 1 on pins 2 and 4
const int encoder1PinB = 4;
volatile int encoder1Pos = 0;

const int encoder2PinA = 3;        // Y-AXIS  encoder 2 on pins 3 and 5
const int encoder2PinB = 5;
volatile int encoder2Pos = 0;

boolean CarriageDir = 0;           // Carriage Direction '0' is Right to left
byte spd = 220;                    // Carriage speed from 0-255
int newpos = 0;                    // Taget position for carriage
int posrchd = 1;                   // Flag for target reached

int Pos1, Pos2;


void setup() {
  Serial.begin(115200);
  Serial.println("Linear Encoder Test  04-29-2014");

  AFMS.begin();  // Set up Motors
  
  myMotor->run(BACKWARD);        // Bring carriage to home position. 
  myMotor->setSpeed(spd); 
  delay(100); 
  myMotor->run(FORWARD);        // Bring carriage to home position. 
  myMotor->setSpeed(0); 
  
  attachInterrupt(0, doEncoder1, CHANGE);  // encoder pin on interrupt 0 (pin 2)
  // attachInterrupt(1, doEncoder2, CHANGE);  // encoder pin on interrupt 1 (pin 3)
  
  randomSeed(analogRead(0));
}

void loop() {
static int oldPos1, oldPos2;
uint8_t oldSREG = SREG;

uint8_t i;

  cli();
  Pos1 = encoder1Pos;  
  Pos2 = encoder2Pos;
  SREG = oldSREG;
  
  if(Pos1 != oldPos1){
     Serial.print("Encoder 1=");
     Serial.println(Pos1,DEC);
     oldPos1 = Pos1;
  }
  if(Pos2 != oldPos2){
     Serial.print("Encoder 2=");
     Serial.println(Pos2,DEC);
     oldPos2 = Pos2;
  }  
  

  //sweep_carriage();
  
  if(posrchd) {                           // If target has been reached clear flag, and get new target
    newpos =  random(200,3500);
    posrchd = 0;
  }    
    
  posrchd = go_to_target(newpos);
  
}


/***************************************************************************************
The following code was taken from   http://forum.arduino.cc/index.php?topic=41615.20;wap2
to utilize the fast port based encoder logic.  Thank you Lefty!
please go there for a full explanation of how this works.  I have truncated the comments 
here for brevity.
***************************************************************************************/
void doEncoder1() {                                  // ************** X- AXIS ****************
    if (PIND & 0x04) {                              // test for a low-to-high interrupt on channel A, 
        if ( !(PIND & 0x10)) {                      // check channel B for which way encoder turned, 
           encoder1Pos = ++encoder1Pos;               // CW rotation
           PORTD = PIND | 0x40;                     // set direction output pin to 1 = forward, 
          }
        else {
           encoder1Pos = --encoder1Pos;               // CCW rotation
           PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, 
          }
    }
    else {                                          // it was a high-to-low interrupt on channel A
        if (PIND & 0x10) {                          // check channel B for which way encoder turned, 
           encoder1Pos = ++encoder1Pos;               // CW rotation
           PORTD = PIND | 0x40;                     // Set direction output pin to 1 = forward, 
           }
        else {
           encoder1Pos = --encoder1Pos;               // CCW rotation
           PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, 
           }
         }
    PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   generate step pulse high
    PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   add a small delay
    PORTD = PIND & 0x7F;                            //  digitalWrite(encoderstep, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #1
                                                   
void doEncoder2(){                                  // ************** X- AXIS ****************
  if (PIND & 0x08) {                                // test for a low-to-high interrupt on channel A, 
     if (!(PIND & 0x20)) {                          // check channel B for which way encoder turned, 
      encoder2Pos = ++encoder2Pos;                  // CW rotation
      PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, 
     }
     else {
      encoder2Pos = --encoder2Pos;                  // CCW rotation
      PORTD =PIND & 0xFE;                           // Set direction output pin to 0 = reverse, 
     }
  }
  else {                                            // it was a high-to-low interrupt on channel A
     if (PIND & 0x20) {                             // check channel B for which way encoder turned, 
      encoder2Pos = ++encoder2Pos;                  // CW rotation
      PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, 
      }
     else {
      encoder2Pos = --encoder2Pos;                  // CCW rotation
      PORTB =PINB & 0xFE;                           // Set direction output pin to 0 = reverse, 
     }
  }
  PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   generate step pulse high
  PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   used to add a small delay
  PORTB = PINB & 0xFD;                              // digitalWrite(encoder2step, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #2



/***************************************************************************************
go_to_target() determines the distance and direction from current position to target 
position, then maps speed to decellerate close to the target so as not to overshoot.
***************************************************************************************/


int go_to_target(int target)
{
  int temp = 0;
  int delta = abs(Pos1-target);                   // Distance to target
  spd = map(delta,3600,0,255,150);                // Decellerate as you get closer
  if(target < 3600 && target > 100) {
     if(Pos1 < target) {
       myMotor->run(FORWARD);
       myMotor->setSpeed(spd); 
       temp = 0;
     } else if(Pos1 > target) {
       myMotor->run(BACKWARD);
       myMotor->setSpeed(spd); 
       temp = 0;
     }  else temp =1;
  }
  return temp;
}


/***************************************************************************************
sweep_carriage() is just a test routine to track back and forth testing overshoot. 
I will likely remove it soon.
***************************************************************************************/

void sweep_carriage()
{
    if(CarriageDir == 0) {              // Carriage Moving Right to Left
    if (Pos1 < 3600) {      
      myMotor->run(FORWARD);
      if (Pos1 > 3400) { 
       myMotor->setSpeed(spd-80); 
      } else myMotor->setSpeed(spd);  
    } else {      
      myMotor->setSpeed(0);  
      CarriageDir = !CarriageDir;  
    }
  } else {                              // Carriage Moving Left to Right
    if (Pos1 > 100) {
      myMotor->run(BACKWARD);
      if (Pos1 < 300) { 
       myMotor->setSpeed(spd-80); 
      } else myMotor->setSpeed(spd);  
     } else {      
      myMotor->setSpeed(0);  
      CarriageDir = !CarriageDir;  
    }
  }
}



This is the endstop sensor circuit.  I haven't wired it up to the Arduino as yet, but it is pretty straight forward. 







In the true nature of this project, I searched for a used motor controller to go with my Arduino .   Pictured here, is the Adafruit I2C Motor Shield V2 that was on my original robot.  This project will breath new life into it. 

This is a wonderful board in that it contains TWO dual h-bridge FET drivers for four DC motors, or two steppers, or one stepper and two motors... 



Wiring up the Optical Encoder.  Only four wires are needed...



12volt DC gear motor used to drive the carriage assembly.








References:

http://playground.arduino.cc/Main/RotaryEncoders#OnSpeed
http://reprap.org/wiki/Optical_encoders_01
http://mechatronics.mech.northwestern.edu/design_ref/sensors/encoders.html
http://forum.arduino.cc/index.php/topic,17695.0.html

Wednesday, 23 April 2014

Using DC Motors and Encoders for 3D printer: Challenging the norm!

http://www.nextdayreprap.co.uk/wiring-reprap-prusa-mendel-build-manual/Every 3D printer I've seen 
(please correct me if I've missed something!) 
uses stepper motors for X/Y/Z axis. 



The RepRap firmware assume that you are using steppers in your build.


That said, RepRap does introduce the concept of "RepStrap

(from http://reprap.org/wiki/Category:RepStrap)


repstrap is a 3D printer cobbled together from whatever parts you can find, which will eventually allow you to print the parts for a reprap machine, or to simply use as a stand alone machine. Derived from the term bootstrap, as in "to pull yourself up by your bootstraps"A RepStrap is a open-hardware rapid prototyping machine which is made by fabrication processes which aren't under the RepRap umbrella yet. These are becoming less and less common as RepRap printed parts become more available, but are still an option. You can build a 3D printer RepStrap using a tablesaw, orusing a lasercutter, and use this to make fun, beautiful, useful things.


Old commercial ink/laser printers used to use stepper motors too.   

These printers typically got resolutions of 300dpi (0.08mm)  or 600dpi (0.04mm)




But....  Newer printers, say within the last decade, use DC motors with a "linear strip encoder".   And these printers typically get better than 1200dpi (0.02mm) 


(yes, I know they use interpolation to get this resolution, but work with me here...)





According to WikiPedia:  Optical linear encoders[1][2] dominate the high resolution market and may employ shuttering/MoirĂ©diffraction or holographic principles. Typical incremental scale periods vary from hundreds down to sub-micrometre and following interpolation can provide resolutions as fine as a nanometre.
And... 

Reprap already has a reference to these... 


OverviewFor those who enjoy scavenging, many components useful for constructing 3D printers can be found in inkjet printers. This often includes optical encoders and strips. This page gives information on finding and using these items.
Finding printers with linear optical encoders in themCheap inkjet printers can be obtained from garage sales or recycling centers. Do not get laser printers, since they do not have the right optical components in them. Not all inkjet printers have optical encoders and strips in them. It is easy to tell by opening the lid (as if to change the ink). You should see a grey plastic strip close to the shiny metal rod and running parallel to it. The printers that people sell cheaply or recycle generally are somewhat inky inside. Do not get ink on the optical strip, though you may be able to clean it off.
The strip runs through the optical sensor, which may be quite hidden. It is probably on the back side of the assembly that holds the ink cartridges.

So...

I'm upping my game.  My original goal was to simply copy a basic 3D printer using as much salvaged parts as I could, a few stepper motors, linear rails, switches, etc...  

Had I done my research up front, I probably would not have even started this project, however... I have started, and am facing a new challenge...

My NEW GOAL is to create a 3D printer using DC motors and the salvaged Optical Encoder strips.  

A simple test on the arduino with a pololu dual h-bridge quickly had two printer heads tracking back and forth on their carriages within minutes of wiring them up to their native cables.  I haven't accounted for overrun yet, so they oscillate like crazy before getting to their destination, but this is DEFINITELY doable.


I will likely start with Marlin Firmware and write a hardware abstraction to convert stepper motor output (steps/inch, etc...) to run a closed loop DC motor with Encoder Strip feedback.  



Any suggestions or prior art welcome! 


Let's call these two videos  --- 

Inspiration....    


References:

http://www.nextdayreprap.co.uk/wiring-reprap-prusa-mendel-build-manual/
http://en.wikipedia.org/wiki/Stepper_motor
http://reprap.org/wiki/Firmware
http://reprap.org/wiki/Category:RepStrap
http://benkrasnow.blogspot.ca/2010/02/linear-position-tracking-with.html
http://hackaday.com/2009/11/12/linear-optical-encoder/
http://reprap.org/wiki/Optical_encoders_01
http://www.electromate.com/db_support/downloads/lin.pdf
http://mil.ufl.edu/projects/gnuman/gnuman_pre2005/spec_sheets/heds_encoder.pdf
https://www.youtube.com/watch?v=0QLZCfqUeg4
http://makezine.com/2009/11/11/linear-optical-encoder-from-printer/
http://junkplusarduino.blogspot.ca/p/svg-image-plotter.html
http://madpenguin.ca/blog/2011/05/14/use-an-inkjet-printer-to-learn-emc2-and-servo-motor-control-part-1/
Arduino.cc: Agilent Optical encoder



Thursday, 14 November 2013

Out with the Serial, In with I2C - Multimaster - that is...

So I've whined a bit here about some of my hurdles communicating between the Raspberry Pi and the Arduino's, as well as managing critical timing issues on the Arduino.


I spent roughly $30 CDN on the Arduino branded Motor Shield V3 which uses the L293D darlington H bridge driver to provide control over 2 DC motors.

To provide this control, it consumes 8 pins of the Arduino.  6 Digital, and two Analog.

From http://www.instructables.com/id/Arduino-Motor-Shield-Tutorial/ :
There are pins on the Arduino that are always in use by the shield. By addressing these pins you can select a motor channel to initiate, specify the motor direction (polarity), set motor speed (PWM), stop and start the motor, and monitor the current absorption of each channel .
The pin breakdown is as follows:
FunctionChannel AChannel B
DirectionDigital 12Digital 13
Speed (PWM)Digital 3Digital 11
BrakeDigital 9Digital 8
Current SensingAnalog 0Analog 1


It also provides a few handy headers for sensors....  but..... it is not an intelligent device, rather requiring all control code to be written in the Arduino Sketch.




For the same $30 CDN, I just purchased AdaFruit's Motor Shield V2 which uses a pair of Mosfet TB6612 H-Bridges for higher current capabilities to drive four DC motors, or two Stepper motors, or one Stepper and two DC motors...  

These are controlled through the I2C interface.... so not taking up any other Arduino resources.
There are also pin headers that bring Arduino Pins 9 and 10 up for two 5v servos, but these are not I2C controlled...  Maybe on the next revision???  Please Adafruit?

I know, I know, I can add a Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface  or stack a Adafruit 16-Channel 12-bit PWM/Servo Shield - I2C interface on top of this one....




Anyway, I expect to have good results out of this controller by the weekend, and fully expect that by using the Servo Timer1 library, I will  remove my timer0 issues regarding  delay() and millis().



References: