Tag Archives: DRV8871

Replacing Wall-E2’s L298N Motor Drivers with Adafruit DRV8871

Posted 07 September 2020,

I’ve been having some ‘issues’ with driving Wall-E2’s Pololu 20D 125:1 12V metal geared motors with the old L298N motor drivers, so I thought it was time to replace them with the Adafruit DRV8871 models used in ‘re-motoring’ my two-wheel robot (see this post for some of the details).

The first step was to review the work I had done earlier replacing the L298N on my two-wheel robot with the same DRV8871 Driver. The two-wheel robot uses a UNO controller, while Wall-E2 uses a Mega, but they are similar enough so porting the wiring and code should be simple enough. The two-wheel robot uses UNO pins 5, 6, 9 & 10 (all PWM lines) for direction and speed control, while Wall-E2 uses pins 8-13 and 36, 38, 40, 42, 44 & 46 for controlling the two L298N motor drivers. My plan is to try using just two motor drivers, one for both left motors, and another for both right motors. If this works, I’ll need just 4 lines, say 8-11 (If I later need to use four drivers vs two, I’ll use 12/13 for one of the ‘extras’ and 6/7 for the other one. Currently 6 is unused and 7 drives the red laser diode, so moving it shouldn’t be a big problem).

So, I replaced the two L298N modules with two DRV8871 modules, and wired both left motors into one driver and both right motors into the other, as shown in the following photos

‘before’ – dual L298N motor drivers
‘after’ – Dual Adafruit DRV8871 motor drivers

Since I previously replaced an L298N with a DRV8871 in my two-wheel robot, I had already modified all the relevant motor driver code to use the DRV8871 module vs the L298N, so all I had to do was replace the low-level motor interface modules in my four-wheel robot project with the corresponding ones from my two-wheel robot project. The replaced modules were:

  • SetLeftMotorDirAndSpeed
  • SetRightMotorDirAndSpeed
  • StopBothMotors
  • MoveAhead
  • MoveReverse

Note that the four-wheel robot code uses separate SetLeft/RightMotorDir & SetLeft/RightMotorSpeed functions, so these needed to be modified or replaced.

SetLeft/RightMotorSpeed() is called from RunBothMotors() & SpinTurn(). I could modify SpinTurn() to call RunBothMotors() instead of calling SetLeft/RightMotorSpeed() directly.

RunBothMotors() is called from RunBothMotorsMsec(), Setup(), MoveReverse() and MoveForward(). Every call to RunBothMotors() is paired with calls to SetLeft/RightMotorDir(), so I could replace Each set of calls with a single call to SetLeft/RightMotorDirAndSpeed(). The only issue with this are the RunBothMotorsMsec() calls in Setup() & ExecDisconManeuver().

I decided to modify the RunBothMotors() & RunBothMotorsMsec() functions to take a direction parameter, and then the functions will simply call SetLeft/RightDirAndSpeed().

10 September 2020 Update:

I got lost in the details of the Wall-E2 code, so I decided to simplify things to check out the new DRV8871-based motor setup. I modified my original ‘Adafruit_DRV8871_Driver_Test’ project to run both motor sets forward and backward from the minimum PWM value (about 50) to max (255) while monitoring the total current for all four motors, as shown in the following Excel plot

Total motor current for motor speed commands from 50-255

As can be seen from the above plot, the total motor current for all four motors is right around 0.8A or about 0.4A per driver – well within the current limits for the DRV8871 module. As a side note, the TO-3 cans on the modules barely got warm, so this looks like a real winner.

Now that I have the technical issues sorted out with respect to the driver replacement project, I can get back to the main project of improving Wall-E2’s wall-following ability.

Stay tuned!

Frank

13 September 2020 Update:

Unfortunately, when I started running the full Wall-E2 code, the right set of motors would go backwards, but not forwards – awkward to say the least. After a lot of troubleshooting, it finally dawned on me that the problem was being caused by my introduction of a TIMER1-based interrupt a while ago to manage ‘stuck’ detection. TIMER1 controls PWM on pins 11 & 12, and one of those pins was being used by the right motor – bummer!

After a LOT of screwing around, I finally decided that the only way to really figure things out was to remove everything but the timer and motor driver code from the program to figure out what timer (if any) I can use for the ‘stuck’ detection interrupt and still have proper motor control.

To that end, I created a new Arduino program ‘TimerISRvsPWMTest.ino’ as shown below:

With the TIMER1 setup commented out, both motors rotate forwards and backwards no problem. However, as soon as the TIMER1 code was un-commented, the motor driver on pins 10/11 would turn one way but not the other. With an O’scope I could see the PWM waveform on pin 10 but not on pin 11. This is consistent with Timer1 controlling PWM on pins 11,12 & 13.

So, I moved the wire connected to DRV8871 IN2 from 11 to 3 and changed the code to use pins 10 & 3 vs 10 & 11. Now the motor connected to these pins rotates both forward and backwards

Then I went back to my WallE2_V6 project and tried the same trick – moving the IN2 pin from 11 to 3; nope – still doesn’t work. In fact, moving from 11 to any other PWM pin doesn’t work in the WallE2 project, but does in the TimerPWM test project – weird.

So, thinking that maybe there were some timer dependencies hidden in one or more of the libraries being used for the WallE2 project, I copied them all over to the TimerPWM project folder, added them to the project in VS2019, and added them to the project code. After I got everything to compile, I ran the TimerPWM test project successfully using 10 & 7, 10 & 3, 10 & 2, etc (but 10 & 11 still doesn’t work with TIMER1 ISR enabled).

So, it’s not the libraries. Next I changed the timer interrupt code to use TIMER5 instead of TIMER1, moving the pin dependency from 11-13 to 44-46. I confirmed this by changing ‘In1_Right‘ to 44, 45, 46 in turn and moving the physical In1_Right connection to the corresponding pin, noting that the motors don’t rotate properly when driven from any of the affected pins.

Next, I changed the timer interrupt code in WallE2_V6 from TIMER1 to TIMER5 to see if I can get back pin 11 as a PWM pin. Nope – it still doesn’t work in the WallE2 code, nor does pin 7.

Back to the test program. Copied all the ‘pre-setup’ code from WallE2 to the test program; no change – test program still works properly.

After a few back-and-forths of this nature, I eventually narrowed the problem down to the driver modules themselves. A physical inspection revealed that I had forgotten to solder the second pin on both 2-pin screw terminals on one of the drivers – oops! So, like many seemingly intractable technical problems, this one was caused by two independent issues; the use of TIMER1 caused pins 10-12 to be unavailable for motor drive PWM, and the intermittent connections to the left-side motors complicated the symptoms. This was a perfect example of why ‘cutting the problem in half’ (in my case, by eliminating all the Wall-E2 hardware from the problem) is so effective in troubleshooting.

Stay tuned!

Frank

MPU6050 IMU Motor Noise Troubleshooting

Posted 24 July 2019

For a while now I’ve been investigating ways of improving the wall following performance of my autonomous wall-following robot Wall-E2.  At the heart of the plan is the use of a MPU6050 IMU to sense relative angle changes of the robot so that changes in the distance to the nearest wall due only to the angle change itself can be compensated out, leaving only the actual offset distance to be used for tracking.

As the test vehicle for this project, I am using my old 2-motor robot, fitted with new Pololu 125:1 metal-geared DC motors and Adafruit DRV8871 motor drivers, as shown in the photo below.

2-motor test vehicle on left, Wall-E2 on right

The DFRobots MPU6050 IMU module is mounted on the green perfboard assembly near the right wheel of the 2-motor test robot, along with an Adafruit INA169 high-side current sensor and an HC-05 Bluetooth module used for remote programming and telemetry.

This worked great at first, but then I started experiencing anomalous behavior where the robot would lose track of the relative heading and start turning in circles.  After some additional testing, I determined that this problem only occurred when the motors were running.  It would work fine as long as the motors weren’t running, but since the robot had to move to do its job, not having the ability to run the motors was a real ‘buzz-kill’.  I ran some experiments on the bench to demonstrate the problem, as shown in the Excel plots below:

Troubleshooting:

There were a number of possibilities for the observed behavior:

  1. The extra computing load required to run the motors was causing heading sensor readings to get missed (not likely, but…)
  2. Motor noise of some sort was feeding back into the power & ground lines
  3. RFI created by the motors was getting into the MPU6050 interrupt line to the Arduino Mega and causing interrupt processing to overwhelm the Mega
  4. RFI created by the motors was interfering with I2C communications between the Mega and the MPU6050
  5. Something else

Extra Computing Load:

This one was pretty easy to eliminate.  The main loop does nothing most of the time, and only updates system parameters every 200 mSec.  If the extra computing load was the problem, I would expect to see no ‘dead time’ between adjacent adjustment function blocks.  I had some debug printing code in the program that displayed the result of the ‘millis()’ function at various points in the program, and it was clear that there was still plenty of ‘dead time’ between each 200 mSec adjustment interval.

Motor noise feeding back into power/ground:

I poked around on the power lines with my O’scope with the motors running and not running, but didn’t find anything spectacular; there was definitely some noise, but IMHO not enough to cause the problems I was seeing.  So, in an effort to completely eliminate this possibility, I removed the perfboard sub-module from the robot entirely, and connected it to a separate Mega microcontroller. Since this setup used completely different power circuits (the onboard battery for the robot, PC USB cable for the second Mega), power line feedback could not possibly be a factor.  With this setup I was able to demonstrate that the MPU6050 output was accurate and reasonable until I placed the perfboard sub-module in close proximity to the robot; then it started acting up just as it did when mounted on the robot.

So it was clear that the interference is RFI, not conducted through any wiring.

RFI created by the motors was getting into the MPU6050 interrupt line to the Arduino Mega and causing interrupt processing to overwhelm the Mega

This one seemed very possible.  The MPU6050 generates interrupts at a 20Hz rate, but I only use measurements at a 5Hz (200mSec) rate.  Each interrupt causes the Interrupt Service Routine (ISR) to fire, but the actual heading measurement only occurs every 200 mSec. I reasoned that if motor-generated RFI was causing the issue, I should see many more activations of the ISR than could be explained by the 20Hz MPU6050 interrupt generation rate.  To test this theory, I placed code in the ISR that pulsed a digital output pin, and then monitored this pin with my O’scope.  When I did this, I saw many extra ISR activations, and was convinced I had found the problem.  In the following short video clip, the top trace is the normal interrupt line pulse frequency, and the bottom trace is the ISR-generated pulse train.  In normal operation, these two traces would be identical, but as can be seen, many extra ISR activations are occurring when the motors are running.

So now I had to figure out what to do with this information.  After Googling around for a while, I ran across some posts that described using the MPU6050/DMP setup without using the interrupt output line from the module; instead, the MPU6050 was polled whenever a new reading was required.  As long as this polling takes place at a rate greater than the normal DMP measurement frequency, the DMP’s internal FIFO shouldn’t overflow.  If the polling rate is less than the normal rate, then FIFO management is required.  After thinking about this for a while, I realized I could easily poll the MPU/DMP at a higher rate than the configured 20Hz rate by simply polling it each time through the main loop – not waiting for the 200mSec/5Hz motor speed adjustment interval.  I would simply poll the MPU/DMP as fast as possible, and whenever new data was ready I would pull it off the FIFO and put it into a global variable.  The next time the motor adjustment function ran, it would use the latest relative heading value and everyone would be happy.

So, I implemented this change and tested it off the robot, and everything worked OK, as shown in the following Excel plot.

And then I put it on the robot and ran the motors

Crap!  I was back to the same problem!  So, although I had found evidence that the motor RFI was causing additional ISP activations, that clearly wasn’t the entire problem, as the polling method completely eliminates the ISP.

RFI created by the motors was interfering with I2C communications between the Mega and the MPU6050

I knew that the I2C control channel could experience corruption due to noise, especially with ‘weak’ pullup resistor values and long wire runs.  However, I was using short (15cm) runs and 2.2K pullups on the MPU6050 end of the run, so I didn’t think that was an issue.  However, since I now knew that the problem wasn’t related to wiring issues or ISR overload, this was the next item on the list.  So, I shortened the I2C runs from 15cm to about 3cm, and found that this did indeed suppress (but not eliminate) the interference.  However, even with this modification and with the MPU6050 module located as far away from the motors as possible, the interference was still present.

Something else

So, now I was down to the ‘something else’ item on my list, having run out of ideas for suppressing the interference.  After letting this sit for a few days, I realized that I didn’t have this problem (or at least didn’t notice it) on my 4-motor Wall-E2 robot, so I started wondering about the differences between the two robot configurations.

  1. Wall-E2 uses plastic-geared 120:1 ‘red cap’ motors, while the 2-motor robot uses pololu 125:1 metal-geared motors
  2. Wall-E2 uses L298N linear drivers while the 2-motor version uses the Adafruit DRV8871 switching drivers.

So, I decided to see if I could isolate these two factors and see if it was the motors, or the drivers (or both/neither?) responsible for the interference. To do this, I used my new DPS5005 power supply to generate a 6V DC source, and connected the power supply directly to the motors, bypassing the drivers entirely.  When I did this, all the interference went away!  The motors aren’t causing the interference – it’s the drivers!

In the first plot above, I used a short (3cm) I2C wire pair and the module was located near, but not on, the robot. As can be seen, no interference occurred when the motors were run.  In the second plot I used a long (15cm) I2C wire pair and mounted the module directly on the robot in its original position.  Again, no interference when the motors were run.

So, at this point it was pretty definite that the main culprit in the MPU6050 interference issue is the Adafruit DRV8871 switch-mode driver.  Switch-mode drivers are much more efficient than L298N linear-mode drivers, but the cost is high switching transients and debilitating interference to any I2C peripherals.

As an experiment, I tried reducing the cable length from the drivers to the motors, reasoning that the cables must be acting like antennae, and reducing their length should reduce the strength of the RFI.  I re-positioned the drivers from the top surface of the robot to the bottom right next to the motors, thereby reducing the drive cable length from about 15cm to about 3 (a 5:1 reduction).  Unfortunately, this did not significantly reduce the interference.

So, at this point I’m running out of ideas for eliminating the MPU6050 interference due to switch-mode driver use.

  • I read at least one post where the poster had eliminated motor interference by eliminating the I2C wiring entirely – he used a MPU6050 ‘shield’ where the I2C pins on the MPU6050 were connected directly to the I2C pins on the Arduino microcontroller.  The poster didn’t mention what type of motor driver (L298N linear-mode style or DRV8871 switch-mode style), but apparently a (near) zero I2C cable length worked for him.  Unfortunately this solution won’t work for me as Wall-E2 uses three different I2C-based sensors, all located well away from the microcontroller.
  • It’s also possible that the motors and drivers could be isolated from the rest of the robot by placing them in some sort of metal box that would shield the rest of the robot from the switching transients caused by the drivers.  That seems a bit impractical, as it would require metal fabricating unavailable to me.  OTOH, I might be able to print a plastic enclosure, and then cover it with metal foil of some sort.  If I go this route, I might want to consider the use of optical isolators on the motor control lines, in order to break any conduction path back to the microcontroller, and capacitive feed-throughs for the power lines.

27 July 19 Update:

I received a new batch of GY-521 MPU6050 breakout boards, so I decided to try a few more experiments.  With one of the GY-521 modules, I soldered the SCL/SDA header pins to the ‘bottom’ (non-label side) and the PWR/GND pins to the ‘top’.  With this setup I was able to plug the module directly into the Mega’s SCL/SDA pins, thereby reducing the I2C cable length to zero.  The idea was that if the I2C cable length was contributing significantly to RFI susceptibility, then a zero length cable should reduce this to the minimum  possible, as shown below:

MPU6050 directly on Mega pins, normal length power wiring

In the photo above, the Mega with the MPU6050 connected is sitting atop the Mega that is running the motors. The GND and +5V leads are normal 15cm jumper wires.  As shown in the plots below, this configuration did reduce the RFI susceptibility some, but not enough to allow normal operation when lying atop the robot’s Mega.

GY-521 MPU6050 module mounted directly onto Mega, normal length power leads

I was at least a little encouraged by this plot, as it showed that the MPU6050 (and/or the Mega) was recovering from the RFI ‘flooding’ more readily than before.  In previous experiments, once the MPU6050/Mega lost sync, it never recovered.

Next I tried looping the power wiring around an ‘RF choke’ magnetic core to see if raising the effective impedance of the power wiring to high-frequency transients had any effect, as shown in the following photo.

GND & +5V leads looped through an RF Choke.

Unfortunately, as far as I could tell this had very little positive effect on RFI susceptibility.

Next I tried shortening the GND & +5V leads as much as possible.  After looking at the Mega pinout diagram, I realized there was GND & +5V very close to the SCL/SDA pins, so I fabricated the shortest possible twisted-pair cable and installed it, as shown in the following photo.

MPU6050 directly on Mega pins, shortest possible length power wiring

With this configuration, I was actually able to get consistent readings from the MPU6050, whether or not the motors were running – yay!!

In the plot above, the vertical scale is only from -17 deg to -17.8 deg, so all the variation is due to the MPU6050, and there is no apparent deleterious effects due to motor RFI – yay!

So, at this point it’s pretty clear that a significant culprit in the MPU6050’s RFI susceptibility is the GND/+5V and I2C cabling acting as antennae and  conducting the RFI into the MPU6050 module.  Reducing the effective length of the antennas was effective in reducing the amount of RFI present on the module.

With the above in mind, I also tried adding a 0.01uF ‘chip’ capacitor directly at the power input leads, thinking this might be just as effective (if not more so) than shortening the power cabling.  Unfortunately, this experiment was inconclusive. The normal length power cabling with the capacitor seemed to cause just as much trouble as the setup without the cap, as shown in the following plot.

Having determined that the best configuration so far was the zero-length I2C cable and the shortest possible GND/+5V cable, I decided to try moving the MPU6U6050 module from the separate test Mega to the robot’s Mega. This required moving the motor drive lines to different pins, but this was easily accomplished.  Unfortunately, when I got everything together, it was apparent that the steps taken so far were not yet effective enough to prevent RFI problems due the switch-mode motor drivers

The good news, such as it is, is that the MPU6050/Mega seems to recover fairly quickly after each ‘bad data’ excursion, so maybe we are most of the way there!

As a next step, I plan to replace the current DRV8871 switch-mode motor drivers with a single L298N dual-motor linear driver, to see if my theory about the RFI problem being mostly due to the high-frequency transients generated by the drivers and not the motors themselves.  If my theory holds water, replacing the drivers should eliminate (or at least significantly suppress) the RFI problems.

28 July 2019 Update:

So today I got the L298N driver version of the robot running, and I was happy (but not too surprised) to see that the MPU6050 can operate properly with the motors ON  or OFF when mounted on the robot’s Mega controller, as shown in the following photo and Excel plots

2-motor robot with L298N motor driver installed.

However, there does still seem to be one ‘fly in the ointment’ left to consider.  When I re-installed the wireless link to allow me to reprogram the 2-motor robot remotely and to receive wireless telemetry, I found that the MPU6050 exhibited an abnormally high yaw drift rate unless I allowed it to stabilize for about 10 sec after applying power and before the motors started running, as shown in the following plots.

2-motor robot with HC-05 wireless link re-installed.

I have no idea what is causing this behavior.

31 July 2019 Update

So, I found a couple of posts that refer to some sort of auto-calibration process that takes on the order of 10 seconds or so, and that sounds like what is happening with my project.  I constructed the following routine that waited for the IMU yaw output values to settle

This was very effective in determining when the MPU6050 output had settled, but it turned out to be unneeded for my application.  I’m using the IMU output for relative yaw values only, and over a very short time frame (5-10 sec), so even high yaw drift rates aren’t deleterious.  In addition, this condition only lasts for a 10-15 sec from startup, so not a big deal in any case.

At this point, the MPU6050 IMU on my little two-motor robot seems to be stable and robust, with the following adjustments (in no particular order of significance)

  • Changed out the motor drivers from 2ea switched-mode DRV8871 motor drivers to a single dual-channel L298N linear mode motor driver.  This is probably the most significant change, without which none of the other changes would have been effective.  This is a shame, as the voltage drop across the L298N is significantly higher than with the switch-mode types.
  • Shortened the I2C cable to zero length by plugging the GY-521 breakout board directly into the I2C pins on the Mega.  This isn’t an issue on my 2-motor test bed, but will be on the bigger 4-motor robot
  • Shortened the IMU power cable from 12-15cm to about 3cm, and installed a 10V 1uF capacitor right at the PWR & GND pins on the IMU breakout board.  Again, this was practical on my test robot, but might not be on my 4-motor robot.
  • Changed from an interrupt driven architecture to a polling architecture.  This allowed me to remove the wire from the module to the Mega’s interrupt pin, thereby eliminating that possible RF path.  In addition, I revised the code to be much stricter about using only valid packets from the IMU.  Now the code first clears the FIFO, and then waits for a data ready signal from the IMU (available every 50 mSec at the rate I have it configured for).  Once this signal is received, the code immediately reads a packet from the FIFO if and only if it contains exactly one packet (42 bytes in this configuration).  The code shown below is the function that does all of this.

Here’s a short video of the robot making some planned turns using the MPU6050 for turn management.  In the video, the robot executes the following set of maneuvers:

  1. Straight for 2 seconds
  2. CW for 20 deg, starting an offset maneuver to the right
  3. CCW for 20 deg, finishing the maneuver
  4. CCW for 20 deg, starting an offset maneuver to the left
  5. CW for 20 deg, finishing the maneuver
  6. 180 deg turn CW
  7. Straight for 3 sec
  8. 20 deg turn CCW, finishing at the original start point

So, I think it’s pretty safe to say at this point that although both the DFRobots and GY-521 MPU6050 modules have some serious RFI/EMI problems, they can be made to be reasonably robust and reliable, at least with the L298N linear mode motor drivers.  Maybe now that I have killed off this particular ‘alligator’, I can go back to ‘draining the swamp’ – i.e. using relative heading information to make better decisions during wall-following operations.

Stay tuned!

Frank