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.
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.
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.
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.
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
Top Comments