Updated 2 Jan 2020:  Embedded 2nd video

 

I am starting to plan ahead for a robot that will have image recognition using a Raspberry Pi.  Image recognition being computationally intensive, the plan is to offload the Pi with microcontrollers connected with I2C.  In previous post an I2C template for connecting a Raspberry Pi with Python to a microcontroller with the Arduino IDE is described.  This post is the first of two parts describing how a microcontroller can be used for motor control over I2C.

 

Introduction

 

While there are motor driver hats for the Raspberry Pi I plan to develop a separate board with microcontroller and motor controller connected to the Pi with I2C.  This should unload the Pi and keep pins and other resources available for other duties.  In any event microcontrollers are well suited to the task and easy enough to implement.   Encoders will be used to obtain more accurate speed control and estimation of distance travel and turning angle.  This accruacy is desirable for example when making a pen bot.

Robot with Motors

In this first post the motors, encoders, and motor driver board will be tested.  In a future post proportional and integral control will be added along with connection to the Raspberry Pi.

 

Encoders

 

The small geared brushed DC motors used in these tests have rotary encoders attached to the shafts and are included in the Arduino Engineering KitArduino Engineering Kit. In the view below, the encoder disk is attached to a shaft extension at the bottom of the motor.  A piece of tape has been attached to the main shaft to make rotation more visible.

The end view below looks directly at the encoder disk from the back.  The encoder uses hall effect sensors to detect the passage of four magnets which are encased in the disk as seen in the photo.

Encoder View

Power to the motor is provided by the red and black wires on the right.  The brown (Vcc) and green (GND) provide power to the two hall effect sensors.  The blue and purple wires are the output for the two hall effect channels of the encoder.  This type encoder is incremental - that is it reports changes in position but not absolute position.  From it the rotational speed and direction of the motor shaft can be determined.  Knowing the gear ratio and size of the wheels on the robot (plus a little math) the RPM of the wheel and distance travelled over time can be determined.

 

Simple Motor Test

 

The goal of the test is to make sure the motors and encoders are performing as expected.  Somewhat unusual for these small motors, a motor performance curve is available.  The motor is rated for up to 12 V but the plan is to use it at 6 V.  Power during the tests will be provided by a bench power supply set at 6V and current limited to 100 mA.  Encoder output is sent to the oscilloscope.

 

The motor and encoder perform as expected.

 

Motor Driver

 

The motor driver used is a Toshiba TB6612FNG capable of controlling two brushed DC motors and came on a small board from Pololu.  It is installed on a bit of Veroboard and was used on the first robot I designed about four years ago.  The controller can provide sustained current up to 1.2 A per channel and voltages up to 15 V.  Functions include forward and reverse direction, braking, thermal shutdown, and standby power saving mode.  Speed is individually controlled to the motors with PWM.  Motor power supply comes in to the connectors bottom left and output to the two motors is bottom right.  Input to the motor controller is on the 0.1" header here and is described in the microcontroller section below.

TB6612FNG Motor Controller Board

 

Microcontroller

 

For these tests an Adafruit M4 Feather Express will be used.  This is way more microcontroller than needed but it is a simple task to substitute something else.  The following code tests all of the functions available on the Toshiba TB6612FNG and was written in the Arduino IDE.  Only pins need be modified to run on another Arduino capable microcontroller with sufficient pins and PWM.

 

/*
 * Robot_SimpleMotorDrive
 * 
 * Adafruit Feather M4 using Pololu TB6612FNG motor controller
 * 
 * Motor Control Table
 * XIN1   XIN2    Effect
 * Low    Low     Brake
 * Low    High    Forward
 * High   Low     Reverse
 * 
 * Free to use for all
 * F Milburn, January 2020
 */
// Pins used to control motors
const uint16_t PWMA = 5;         // Motor A PWM control     Orange
const uint16_t AIN2 = 6;         // Motor A input 2         Brown
const uint16_t AIN1 = 9;         // Motor A input 1         Green
const uint16_t BIN1 = 10;        // Motor B input 1         Yellow
const uint16_t BIN2 = 11;        // Motor B input 2         Purple
const uint16_t PWMB = 12;        // Motor B PWM control     White
const uint16_t STBY = 13;        // Standby                 Brown
// Constants
const uint16_t ANALOG_WRITE_BITS = 8;
const uint16_t MAX_PWM = pow(2, ANALOG_WRITE_BITS);
const uint16_t MIN_PWM = MAX_PWM / 3;    // Make sure motor turns
void setup(){
   initMotors();
 }
void loop(){
   uint16_t pwm = 0;
   // test forward
   for (pwm = MIN_PWM; pwm <= MAX_PWM; pwm += 20){
     forwardA(pwm);
     forwardB(pwm);
     delay(2000);
   }
   // brake
   brakeA();
   brakeB();
   delay(500);
   // test reverse(){
   for (pwm = MIN_PWM; pwm <= MAX_PWM; pwm += 20){
     reverseA(pwm);
     reverseB(pwm);
     delay(2000);
   }
   // brake
   brakeA();
   brakeB();
   delay(500);
 }
void forwardA(uint16_t pwm){
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, HIGH);
  if (pwm > MAX_PWM){
    pwm = MAX_PWM;
  }
  if (pwm < MIN_PWM){
    pwm = MIN_PWM;
  }
  analogWrite(PWMA, pwm);
}
void forwardB(uint16_t pwm){
  digitalWrite(BIN1, LOW);
  digitalWrite(BIN2, HIGH);
  if (pwm > MAX_PWM){
    pwm = MAX_PWM;
  }
  if (pwm < MIN_PWM){
    pwm = MIN_PWM;
  }
  analogWrite(PWMB, pwm);
}
void reverseA(uint16_t pwm){
  digitalWrite(AIN1, HIGH);
  digitalWrite(AIN2, LOW);
  if (pwm > MAX_PWM){
    pwm = MAX_PWM;
  }
  if (pwm < MIN_PWM){
    pwm = MIN_PWM;
  }
  analogWrite(PWMA, pwm);
}
void reverseB(uint16_t pwm){
  digitalWrite(BIN1, HIGH);
  digitalWrite(BIN2, LOW);  
  if (pwm > MAX_PWM){
    pwm = MAX_PWM;
  }
  if (pwm < MIN_PWM){
    pwm = MIN_PWM;
  }
  analogWrite(PWMB, pwm);
}
void brakeA(){
  digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, LOW);
}
void brakeB(){
  digitalWrite(BIN1, LOW);
  digitalWrite(BIN2, LOW);
}
void standbyMotors(bool standby){
  if (standby == true){
    digitalWrite(STBY, LOW);
  }
  else{
    digitalWrite(STBY, HIGH);
  }
}
void initMotors(){
  pinMode(AIN1, OUTPUT);
  pinMode(AIN2, OUTPUT);
  pinMode(PWMA, OUTPUT);
  pinMode(BIN1, OUTPUT);
  pinMode(BIN2, OUTPUT);
  pinMode(PWMB, OUTPUT);
  pinMode(STBY, OUTPUT);
  analogWriteResolution(ANALOG_WRITE_BITS);
  standbyMotors(false);
}

 

Lines 6 - 10:  The motor control table documents how to control the direction of the motors.  This table has been coded into functions that control direction and speed by the way of PWM.

Lines 24-26:  Various microcontrollers have different ADC resolution and that is addressed here along with maximum and minimum allowable values.

Lines 31-52:  This is the heart of the program.  It ramps up speed of the motors, pauses, and then ramps them down before starting over again.

Lines 53-112: Functions that control the capabilities of the motor driver - e.g. forward, reverse, stop.  Range is checked on RPM.

Lines 113-122: Initializes the pins used, sets the analog write resolution, and takes the motor out of standby.

 

Motor Controller Test

 

After finding a bug in the code associated with not being able to see the pin labels on the Feather (or for that matter the pin map) the motor control tests went fine.

 

The screenshot below was taken on the oscilloscope during the motor controller test.  Channel A from the encoders on the two separate motors is displayed.  While it would be possible to use both channels on a motor it is probably not necessary.  The motor direction is known since it is set in code and the resolution from a single hall sensor should be adequate to control motor speed.

Oscilloscope Screenshot taken during Motor Control testing

 

Next Steps

 

The last robot I made was during the RoadTest of the Texas Instruments RSLK which used the MSP432.  Since I am familiar with the TI microcontrollers, especially the MSP430 models, I am tempted to use it but have noted that posts I make on those microcontrollers don't get near as much attention as Arduino related material.  Since part of my objective with this hobby is to encourage others to get started with electronics and engineering in general maybe I should stick with Arduino.  In any event, the next step is to implement Proportional and Integral control so as to better control motor speed.  I have been jumping around on this project and should also lay out a plan with objectives and a roadmap.  Committing those things in writing where others can see it tends to keep me better focused.

 

The follow-up blog is here and demonstrates successful use of PID control to match the two motor speeds.

 

As always comments, suggestions, and corrections are appreciated.

 

LInks

Simple Arduino DC Motor Control with Encoder, Part 2

Creating Multi-Purpose I2C Devices with Arduino for use with a Raspberry Pi

Raspberry Pi and Arduino I2C Communication

TI RSLK RoadTest