Search This Blog

Showing posts with label motor shield. Show all posts
Showing posts with label motor shield. Show all posts

Friday, 2 January 2015

Teensy 3.1 based Repstrap control board initial wiring completed - test #1

Sorry that this is taking so long, but... (continued from last week...)
I finally have enough of the control board wired up that I can test the I2C library with Adafruit's Motor Shield V2 as well as the Adafruit LCD Backpack.
I simply took the DCMotorTest2 arduino example from the Motor Shield library, and added the LCD functionality. 

Note: Both the Motor Shield Library, as well as the LiquidCrystal libraries needed to be modified to work with Teensy 3.1.

As the Teensy is not AVR based, the I2C functions a bit different (better, trust me!)


In both instances, references to the "Wire" library had to be replaced with Teensy's  "I2C_T3" library.  ie:

//#include <Wire.h>
#include <i2c_t3.h>    // Replacement I2C library for Teensy 3.1


Also in the Motorshield library as well as it's underlying PWMServoDriver library, I had to force it to use the correct I2C channel.   As the ARM processor in the Teensy has two separate I2C channels, it was defaulting to the second one.


I found this little snippet at the beginning of each library, and modified it to use the first I2C channel.

#include "Adafruit_MotorShield.h"
#include <Adafruit_PWMServoDriver.h>
#ifdef __AVR__                        // Teensy definitely is not AVR, so it defaults
 #define WIRE Wire
#else // Arduino Due               // to wire1 as per the next line.
 // #define WIRE Wire1           // Wire1 in Teensy world is the second I2C
 #define WIRE Wire                // So i simply commented out and replaced.
#endif                                      // Not elegant, but...

And here is the example code to simply ramp the DC motor from 0-255 forward, back down to 0 and then do it again in reverse.  All the while displaying status on the 20x4 LCD panel.



/*
This is a test sketch for the Adafruit assembled Motor Shield for Arduino v2
It won't work with v1.x motor shields! Only for the v2's with built in PWM
control

For use with the Adafruit Motor Shield v2
---->    http://www.adafruit.com/products/1438
*/

//#include <Wire.h>
#include <i2c_t3.h> 
// wire for Teensy 3.1 per https://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Or, create it with a different I2C address (say for stacking)
// Adafruit_MotorShield AFMS = Adafruit_MotorShield(0x61);

// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);
// You can also make another motor on port M2
//Adafruit_DCMotor *myOtherMotor = AFMS.getMotor(2);

/*
Using with the Adafruit LCD Backpack to display information
---->    http://www.adafruit.com/products/292
*/

#include "LiquidCrystal.h"

// Connect LiquidCrystal display via i2c, default address #0x20 (A0-A2 not jumpered)
LiquidCrystal lcd(0);

int spd = 150;          // Motor PWM speed from 0 - 255
int mState = FORWARD;   // State of the motor

void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Adafruit Motorshield v2 - DC Motor test!");

  // set up the LCD's number of rows and columns:
  lcd.begin(20, 4);
  lcd.print("Adafruit Motorshield v2");
  lcd.setCursor(0, 1);
  lcd.print("DC Motor test!");

  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
 
  // Set the speed to start, from 0 (off) to 255 (max speed)
  myMotor->setSpeed(spd);
  myMotor->run(mState);
  mState = RELEASE;
  // turn on motor
  myMotor->run(mState);
}

void loop() {
 
  Serial.print("tick");
  lcd.setCursor(0, 3);
  lcd.print("tick");

  mState = FORWARD;
  myMotor->run(mState);
  lcd.setCursor(9, 2);
  lcd.print(" Dir = FWD ");         // Display direction on LCD
 
  for (spd=0; spd<255; spd++) {
    myMotor->setSpeed(spd); 
    lcd.setCursor(0, 2);
    lcd.print("PWM = "); lcd.print(spd); lcd.print(" ");
  }
  for (spd=255; spd!=0; spd--) {
    myMotor->setSpeed(spd); 
    lcd.setCursor(0, 2);
    lcd.print("PWM = "); lcd.print(spd); lcd.print(" ");
  }
 
  Serial.print("tock");
  lcd.setCursor(0, 3);
  lcd.print("tock");

  mState = BACKWARD;
  myMotor->run(mState);
  lcd.setCursor(9, 2);
  lcd.print(" Dir = REV ");         // Display direction on LCD

  for (spd=0; spd<255; spd++) {
    myMotor->setSpeed(spd); 
    lcd.setCursor(0, 2);
    lcd.print("PWM = "); lcd.print(spd); lcd.print(" ");
  }
  for (spd=255; spd!=0; spd--) {
    myMotor->setSpeed(spd); 
    lcd.setCursor(0, 2);
    lcd.print("PWM = "); lcd.print(spd); lcd.print(" ");
  }
 

  Serial.print("tech");
  lcd.setCursor(0, 3);
  lcd.print("tech");
 
  myMotor->run(RELEASE);
  delay(1000);
}

Again, trivial, but it validated that my wiring is correct this far, and that the I2C libraries are functional.

Over the weekend, I'll get the Quadrature Decoders and PID control running, and post another update then.


Monday, 29 December 2014

Prototype Printer Controller Cont'd - Teensy 3.1 w/DC motor/encoders


Here's some more shots of the controller prototype..

I've added a DC/DC converter for clean 5v power to the electronics (5amp).  I've also added a separate 5v linear regulator for the Extruder stepper circuitry.
To get access to the extra pins on the bottom of the teensy, I used a dual row header, and bent the inside pins at a 90 degree angle, trimmed, and soldered.
The shot on the right shows the Real Time Clock crystal soldered into place.

And of course I use my 
legacy 3D extruder 
religiously to lay down layers of plastic "wire holders". 







This is where I wish I had kept all of my old wire wrap tools... Who knew I'd pick up electronics again after almost a 20 year hiatus.

Anyway, power and ground... check. 

Now to the rest of the wires..
 

Sunday, 28 December 2014

Prototype Board: DC motor/Encoder - Teensy 3.1 based 3D printer controller

This will be a short post today.  

I had mentioned putting up pictures as I go along, so I took this to show the first prototype of the controller board.

Here is the layout of my Teensy 3.1 based 3D printer controller.


As of this shot, I have not yet wired it.  Nor have I installed the analog components (power supply, heater drivers, pullup resistors, filter capacitors, etc...).  It is also missing the connector for the extruder stepper motor. 

As I described in my previous post, my motor control design is based on Adafruit's Motor Shield V2.3.  For my prototype... well... I'm using their shield, pilfered from one of my older robots. This fantastic design employs an NXP PCA9865 16 channel 12 bit PWM controller, intended to drive LEDs, but instead to  driving a pair of dual Mosfet H bridge TB6612FNG motor drivers.

The Library for this Shield works with the Teensy 3.1 just fine. (It is just I2C after all).

Ok... I'm off to wire this up... wish me luck.
 





 



Monday, 12 May 2014

Using the Arduino PID Library for position control of X and Y axis on RepScrap printer

I've updated the test code I'm using to manage my X and Y axis DC motor / linear encoder closed loop controller. 



I am currently using the Arduino PID Library by Brett Beauregard  for this, and having great success.  Videos to come tomorrow. 

I am *NOT* going to explain what PID is, or how PID works.... I couldn't possibly do it justice.  I'll simply point you to Brett's wonderful explanation:



In the following example, I set up two axis, X and Y, each using a DC motor run from the Adafruit Motor Shield V2.  This shield provides PWM control for up to four separate DC motors via I2C communications.

I then set up two Quadrature encoders, one for each axis, using the Hardware Interrupts 0 and 1 (Arduino digital pins 2 and 3) and high speed digital port reads for one half of each encoder, and then validate the state of the other phase pin of the encoder during the interrupt routine: 
Graciously borrowed from http://forum.arduino.cc/index.php?topic=41615.20;wap2

The ZERO endstop for each axis is set up using the Arduino PinChangeInterrupt library watching a pin attached to a photo-interrupter.


I would certainly accept any advice on a proper sequence to initialize each axis to the ZERO endstop.

Right now, I arbitrarily send the carriage forward for 100ms assuming this is enough time to get on the positive side of the endstop, if we were beyond it.  Then I set my current position to the maximum possible location, and start travelling back to the endstop, knowing that once I actually reach it, the interrupt routine will Zero out my position, and initialize my PID setpoint to zero as well, thus stopping travel at ZERO. 
Is there a more efficient way of doing this? 


Inside the loop portion of my code, I run the PID controls as per the library, providing motor speed control via the Adafruit motor class, and periodically check to see if both X and Y axis have reached their goal.  At which time, I randomly select a new target for each.  When I get to the real application of this, the random selection of X and Y axis targets will be replaced by GRBL coordinates. 


And without further ado, here is my working code for precise position control in X and Y axis using the Arduino PID library:

/***************************************************************************************
*  Lin_Enc_02.ino   05-12-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. 
*  This example uses the Arduino PID Library found at:
*  https://github.com/br3ttb/Arduino-PID-Library/archive/master.zip
*
*  Hardware Interrupt 0 on Digital pin2 is used to determine X-Axis position
*  Hardware Interrupt 1 on Digital pin3 is used to determine Y-Axis position
*  PinchangeInterrupt is used to identify the Zero Endstop for X and Y axis

*****************************************************************************************/

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <PID_v1.h> 
#include <PinChangeInt.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 *XaxisMotor = AFMS.getMotor(1);
Adafruit_DCMotor *YaxisMotor = AFMS.getMotor(2);


const int XaxisENCPinA = 2;                  // X-AXIS  encoder 1 on pins 2 and 4
const int XaxisENCPinB = 4;
const int XaxisENDSTOP = 10;               // Endstop photointerrupter for X-Axis
volatile double XaxisENCPos = 0;

const int YaxisENCPinA = 3;                  // Y-AXIS  encoder 2 on pins 3 and 5
const int YaxisENCPinB = 5;
const int YaxisENDSTOP = 11;               // Endstop photointerrupter for Y-Axis
volatile double YaxisENCPos = 0;


double XaxisSpd,  YaxisSpd;                  // Carriage speed from 0-255
double XaxisPos, YaxisPos;                   // Current Carriage position

/*working variables for PID routines*/
// Tuning parameters
float KpX=0,  KpY=0;                          //Initial Proportional Gain 
float KiX=10, KiY=10;                         //Initial Integral Gain 
float KdX=0,  KdY=0;                          //Initial Differential Gain 

double XaxisSetpoint, YaxisSetpoint;      // Taget position for carriage

// Instantiate X and Y axis PID controls
PID XaxisPID(&XaxisPos, &XaxisSpd, &XaxisSetpoint, KpX, KiX, KdX, DIRECT); 
PID YaxisPID(&YaxisPos, &YaxisSpd, &YaxisSetpoint, KpY, KiY, KdY, DIRECT); 
const int sampleRate = 1; 

long int reportTime;

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

  AFMS.begin();  // Set up Motors
  
  XaxisMotor->run(BACKWARD);                  // Bring carriage to home position. 
  XaxisMotor->setSpeed(70); 
  delay(100);                                                // Get endstop limiter working here
  XaxisMotor->run(FORWARD);                    // Bring carriage to home position. 
  XaxisMotor->setSpeed(0); 
  

  YaxisMotor->run(BACKWARD);                  // Bring carriage to home position. 
  YaxisMotor->setSpeed(70); 
  delay(100);                                                // Get endstop limiter working here
  YaxisMotor->run(FORWARD);                    // Bring carriage to home position. 
  YaxisMotor->setSpeed(0); 
  
  attachInterrupt(0, doXaxisENC, CHANGE);     // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doYaxisENC, CHANGE);     // encoder pin on interrupt 1 (pin 3)

  PCintPort::attachInterrupt(XaxisENDSTOP,doXaxisEndstop,FALLING); //X-axis Endstop ISR
  PCintPort::attachInterrupt(YaxisENDSTOP,doYaxisEndstop,FALLING); //Y-axis Endstop ISR

  randomSeed(analogRead(0));                          // Used to select random setpoints for testing

  XaxisPID.SetMode(AUTOMATIC);                //Turn on the PID loop 
  XaxisPID.SetSampleTime(sampleRate);         //Sets the sample rate 

  YaxisPID.SetMode(AUTOMATIC);                //Turn on the PID loop 
  YaxisPID.SetSampleTime(sampleRate);         //Sets the sample rate 

  reportTime = millis()+2000;
}

void loop() {
uint8_t oldSREG = SREG;                           // Store interrupt status register

  cli();
  XaxisPos = XaxisENCPos;  
  YaxisPos = YaxisENCPos;
  SREG = oldSREG;                                    // Restore interrupt status register
  

  // Temporary to create random X and Y axis setpoints for testing
  if(millis() > reportTime) {                               // Only validate this every 2 seconds
    if(XaxisPos == XaxisSetpoint && YaxisPos == YaxisSetpoint) {   
      // If both X-axis and Y-axis have reached their target - get new targets
      XaxisSetpoint =  random(200,3500);             // Keep target within bounds of Endpoints
      YaxisSetpoint =  random(200,3500);             // Keep target within bounds of Endpoints
    }    
    reportTime = millis()+2000;
}
  
  
  // Manage X-axis positioning
  XaxisPID.Compute();                          //Run the PID loop 
  if(XaxisSetpoint < XaxisPos) XaxisMotor->run(BACKWARD);  // Determine direction of travel
  else  XaxisMotor->run(FORWARD);      
  XaxisMotor->setSpeed(XaxisSpd);              // Apply PID speed to motor


  // Manage Y-axis positioning
  YaxisPID.Compute();                          //Run the PID loop 
  if(YaxisSetpoint < YaxisPos) YaxisMotor->run(BACKWARD);  // Determine direction of travel
  else  YaxisMotor->run(FORWARD);      
  YaxisMotor->setSpeed(YaxisSpd);              // Apply PID speed to motor

}


/***************************************************************************************
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 doXaxisENC() {                                  // ************** 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, 
           XaxisENCPos = ++XaxisENCPos;               // CW rotation
          }
        else {
           XaxisENCPos = --XaxisENCPos;               // CCW rotation
          }
    }
    else {                                          // it was a high-to-low interrupt on channel A
        if (PIND & 0x10) {                          // check channel B for which way encoder turned, 
           XaxisENCPos = ++XaxisENCPos;               // CW rotation
           }
        else {
           XaxisENCPos = --XaxisENCPos;               // CCW rotation
        }
     }
}                                                   // End of interrupt code for encoder #1


                                                   
void doYaxisENC(){                                  // ************** 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, 
      YaxisENCPos = ++YaxisENCPos;                  // CW rotation
     }
     else {
      YaxisENCPos = --YaxisENCPos;                  // CCW rotation
     }
  }
  else {                                            // it was a high-to-low interrupt on channel A
     if (PIND & 0x20) {                             // check channel B for which way encoder turned, 
      YaxisENCPos = ++YaxisENCPos;                  // CW rotation
      }
     else {
      YaxisENCPos = --YaxisENCPos;                  // CCW rotation
     }
  }
}                                                   // End of interrupt code for encoder #2


void doXaxisEndstop() {
  XaxisENCPos=0;                                    // X-Axis Endstop indicates ZERO position 
}

void doYaxisEndstop() {
  YaxisENCPos=0;                                    // Y-Axis Endstop indicates ZERO position 
}

            https://github.com/michaeljball/RepScrap

Updated diagram for reference:



References:

DIYDrones: Tutorial series for new Arduino PID library
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
http://robotics.stackexchange.com/questions/1232/how-can-i-use-the-arduino-pid-library-to-drive-a-robot-in-a-straight-line
Tim Wescott's PID without a PHD
http://abigmagnet.blogspot.ca/2008/10/dc-motor-control-part-one.html
http://www.pdx.edu/nanogroup/sites/www.pdx.edu.nanogroup/files/2013_Arduino%20PID%20Lab_0.pdf

https://www.youtube.com/watch?v=ZZYgZjMnGXU
https://www.youtube.com/watch?v=wbmEUi2p-nA
http://blog.solutions-cubed.com/pid-motor-control-with-an-arduino/
http://forum.arduino.cc/index.php/topic,45169.0.html

http://playground.arduino.cc/Code/PIDLibrary
http://playground.arduino.cc/Code/PIDAutotuneLibrary

Arduino Playground: PinChangeInterrupt Library
https://code.google.com/p/arduino-pinchangeint/downloads/list

LetsMakeRobots: PID Control by Big Face
LetsMakeRobots: PID Tutorials for Line Following by Enigmerald
LetsMakeRobots: PID without a PHD by BDK6
LetsMakeRobots: Using Motor Encoders to Control Speed by Oddbot








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: