I have a Pi Pico hooked up to an L298N motor controller, -- all of it, i.e. IN1, IN2, ENA, IN3, IN4, ENB. And all is well up to a point; the voltage across OUT1 & OUT2 (going to one motor), likewise across OUT3 & OUT4 (going to the other motor), are as expected.
The trouble is that when I turn off one of the motors, its OUT pins are not at ground, but about the voltage I'm applying to the L298N's "5V" pin (the one in the bank of three terminal block connectors the other two of which are for ground and 12V).
This would not be a problem if I was driving two independent motors. Unfortunately what I'm doing with the L298N is powering the rails of a model train layout. The trains draw their power from the rails, and that would be fine too except that there are places where one set of tracks intersects another (or merges into another) and where that happens it's essential for all the rails' grounds to be the same.
Is this the expected behavior of an L298N motor controller? If it isn't, then that's a sign that the Micropython code I wrote for the Pico is wrong. But if it is expected behavior, can anyone think of a way out?
--------- new material added after reading David C. Rankin's comments -------
D-oh! I misunderstood what the "EN-5V jumper" meant. So thanks. But now (with a fresh, undamaged motor controller in place) I think this is a software issue now (and therefore appropriate for this StackExchange). In brief: I've found that trying to control the L298N with three pins -- IN1&IN2 for the direction, and ENA for PWM -- gives crazy results (the ones I mentioned regarding inconsistent ground voltages). And that the solution is to put the jumper back on ENA, and apply PWM to IN1 (or IN2, according to the desired motor direction). This solution, as it happens, appears to be the way the gpiozero module (unfortunately, available only on the "big" Raspberry Pis, not the Pico) does motor control.
Here's my code, written to make it easy to switch between the three-pin method (which gave me my weird results) and the two-pin "gpiozero" method (which is working perfectly), in case anyone besides me cares to experiment:
#
# Micropython code -- part of a RPi Pico's main.py.
#
from machine import Pin, PWM
# Can switch from PWM to plain old Pin
class MyPWM:
# public
def __init__(self, pin_num):
self.pin_num = pin_num
self.pin = Pin(pin_num, Pin.OUT) # default -- will change
def logical_off(self):
if type(self.pin) == PWM:
self.pwm2pin()
self.pin.off()
def logical_on(self):
if type(self.pin) == PWM:
self.pwm2pin()
self.pin.on()
def pulsate(self, duty):
if not 0 <= duty <= 1:
raise Exception('duty must be on [0,1]')
if type(self.pin) == Pin:
self.pin2pwm()
self.pin.duty_u16(int((pow(2,16)-1)*duty))
# private
def pin2pwm(self):
self.pin.off()
self.pin = PWM(self.pin)
self.pin.freq(1000)
def pwm2pin(self):
self.pin.duty_u16(0)
self.pin.deinit()
self.pin = Pin(self.pin_num, Pin.OUT)
class MyDCMotor:
def __init__(self, *, mode, fwd_pin_num, bk_pin_num, en_pin_num=None):
# argument validation
if not mode in ('two_pin_pwm', 'two_pin_logical', 'three_pin'):
raise Exception('Invalid MyDCMotor mode "' + mode + '"')
self.mode = mode
self.fwd_pin_num = fwd_pin_num
self.fwd_pin = MyPWM(fwd_pin_num)
self.bk_pin_num = bk_pin_num
self.bk_pin = MyPWM(bk_pin_num)
self.en_pin_num = en_pin_num
self.en_pin = MyPWM(en_pin_num)
def drive(self, *, direction, speed):
if not 0 <= speed <= 1.0:
print('Error: speed must be within [0,1].')
if direction == 'forward':
if self.mode == 'two_pin_pwm':
self.bk_pin.logical_off()
self.fwd_pin.pulsate(speed)
else:
self.bk_pin.logical_off()
self.fwd_pin.logical_on()
if self.mode == 'three_pin':
self.en_pin.pulsate(speed)
elif direction == 'backward':
if self.mode == 'two_pin_pwm':
self.fwd_pin.logical_off()
self.bk_pin.pulsate(speed)
else:
self.fwd_pin.logical_off()
self.bk_pin.logical_on()
if self.mode == 'three_pin':
self.en_pin.pulsate(speed)
elif direction == 'stop':
self.fwd_pin.logical_off()
self.bk_pin.logical_off()
else:
raise Exception('Invalid direction: ' + direction)
def forward(self, *, speed):
self.drive(direction='forward', speed=speed)
def backward(self, *, speed):
self.drive(direction='backward', speed=speed)
def stop(self):
self.drive(direction='stop', speed=0)
g_dcmotor = (MyDCMotor(mode='two_pin_pwm',
fwd_pin_num=12, bk_pin_num=13, en_pin_num=11),
MyDCMotor(mode='two_pin_pwm',
fwd_pin_num=19, bk_pin_num=18, en_pin_num=20))
OK, I was unaware of the fact not all the Pi Pico's PWM pins are actually independent. Specifically, the track that was exhibiting the strange behavior was under the control of a PWM pin that was, under the hood, the same as the PWM pin I was using to control another (third, unmentioned in my description above) track. Everyone who plans to use the Pi Pico's PWM functionality should read up on its "PWM slices". In a nutshell, though there are about 26 pins you can use for PWM, the total number of fully independent PWM pins is only eight (or sixteen if you're ok with quasi-independence).