Skip to content

Commit

Permalink
Merge pull request #54 from CedarGroveStudios/master
Browse files Browse the repository at this point in the history
Add Recirculation Current Decay Mode Setter/Getter
  • Loading branch information
tannewt authored Mar 18, 2021
2 parents 160ed3f + 413c3c3 commit a9ef35b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 11 deletions.
62 changes: 51 additions & 11 deletions adafruit_motor/motor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries
#
# SPDX-License-Identifier: MIT

Expand All @@ -23,11 +23,26 @@
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git"

FAST_DECAY = 0
"""Recirculation current fast decay mode (coasting)"""

SLOW_DECAY = 1
"""Recirculation current slow decay mode (braking)"""


class DCMotor:
"""DC motor driver. ``positive_pwm`` and ``negative_pwm`` can be swapped if the motor runs in
the opposite direction from what was expected for "forwards".
Motor controller recirculation current decay mode is selectable and defaults to
``motor.FAST_DECAY`` (coasting). ``motor.SLOW_DECAY`` is recommended to improve spin
threshold, speed-to-throttle linearity, and PWM frequency sensitivity.
Decay mode settings only effect the operational performance of controller chips such
as the DRV8833, DRV8871, and TB6612. Either decay mode setting is compatible
with discrete h-bridge controller circuitry such as the L9110H and L293D; operational
performance is not altered.
:param ~pwmio.PWMOut positive_pwm: The motor input that causes the motor to spin forwards
when high and the other is low.
:param ~pwmio.PWMOut negative_pwm: The motor input that causes the motor to spin backwards
Expand All @@ -37,34 +52,59 @@ def __init__(self, positive_pwm, negative_pwm):
self._positive = positive_pwm
self._negative = negative_pwm
self._throttle = None
self._decay_mode = FAST_DECAY

@property
def throttle(self):
"""Motor speed, ranging from -1.0 (full speed reverse) to 1.0 (full speed forward),
or ``None``.
or ``None`` (controller off).
If ``None``, both PWMs are turned full off. If ``0.0``, both PWMs are turned full on.
"""
return self._throttle

@throttle.setter
def throttle(self, value):
if value is not None and (value > 1.0 or value < -1.0):
raise ValueError("Throttle must be None or between -1.0 and 1.0")
raise ValueError("Throttle must be None or between -1.0 and +1.0")
self._throttle = value
if value is None:
if value is None: # Turn off motor controller (high-Z)
self._positive.duty_cycle = 0
self._negative.duty_cycle = 0
elif value == 0:
elif value == 0: # Brake motor (low-Z)
self._positive.duty_cycle = 0xFFFF
self._negative.duty_cycle = 0xFFFF
else:
duty_cycle = int(0xFFFF * abs(value))
if value < 0:
self._positive.duty_cycle = 0
self._negative.duty_cycle = duty_cycle
else:
self._positive.duty_cycle = duty_cycle
self._negative.duty_cycle = 0
if self._decay_mode == SLOW_DECAY: # Slow Decay (Braking) Mode
if value < 0:
self._positive.duty_cycle = 0xFFFF - duty_cycle
self._negative.duty_cycle = 0xFFFF
else:
self._positive.duty_cycle = 0xFFFF
self._negative.duty_cycle = 0xFFFF - duty_cycle
else: # Default Fast Decay (Coasting) Mode
if value < 0:
self._positive.duty_cycle = 0
self._negative.duty_cycle = duty_cycle
else:
self._positive.duty_cycle = duty_cycle
self._negative.duty_cycle = 0

@property
def decay_mode(self):
"""Motor controller recirculation current decay mode. A value of ``motor.FAST_DECAY``
sets the motor controller to the default fast recirculation current decay mode
(coasting); ``motor.SLOW_DECAY`` sets slow decay (braking) mode."""
return self._decay_mode

@decay_mode.setter
def decay_mode(self, mode=FAST_DECAY):
if mode in (FAST_DECAY, SLOW_DECAY):
self._decay_mode = mode
else:
raise ValueError(
"Decay mode value must be either motor.FAST_DECAY or motor.SLOW_DECAY"
)

def __enter__(self):
return self
Expand Down
3 changes: 3 additions & 0 deletions examples/motor_pca9685_dc_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
# See here for more info: https://learn.adafruit.com/adafruit-motor-shield-v2-for-arduino/faq#faq-13
pca.channels[7].duty_cycle = 0xFFFF
motor4 = motor.DCMotor(pca.channels[5], pca.channels[6])
motor4.decay_mode = (
motor.SLOW_DECAY
) # Set motor to active braking mode to improve performance

print("Forwards slow")
motor4.throttle = 0.5
Expand Down
24 changes: 24 additions & 0 deletions tests/test_stepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
#
# SPDX-License-Identifier: Unlicense

"""
`test_stepper`
====================================================
Tests stepper functionality.
* Author(s): ladyada
"""

__version__ = "1.0.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git"

import os
import sys
from unittest.mock import MagicMock
Expand All @@ -14,17 +26,23 @@

from adafruit_motor import stepper # pylint: disable-msg=wrong-import-position

# pylint: disable=consider-using-in


class Coil:
"""Class Coil"""

def __init__(self):
self._duty_cycle = 0

@property
def frequency(self):
"""Default frequency setting"""
return 1500

@property
def duty_cycle(self):
"""16-bit duty cycle value"""
return self._duty_cycle

@duty_cycle.setter
Expand All @@ -34,6 +52,7 @@ def duty_cycle(self, value):


def test_single_coil():
"""Tests single coil"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
Expand All @@ -47,6 +66,7 @@ def test_single_coil():


def test_double_coil():
"""Tests double coil"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
Expand All @@ -62,6 +82,7 @@ def test_double_coil():


def test_interleave_steps():
"""Tests interleave steps"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
Expand Down Expand Up @@ -89,6 +110,7 @@ def test_interleave_steps():


def test_microstep_steps():
"""Tests microsteps"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3], microsteps=2)
Expand Down Expand Up @@ -121,6 +143,7 @@ def test_microstep_steps():


def test_double_to_single():
"""Tests double to single movement"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
Expand Down Expand Up @@ -154,6 +177,7 @@ def test_double_to_single():


def test_microstep_to_single():
"""Tests microsteps to single movement"""
coil = (Coil(), Coil(), Coil(), Coil())
# We undo the coil order so our tests make more sense.
motor = stepper.StepperMotor(coil[2], coil[0], coil[1], coil[3])
Expand Down

0 comments on commit a9ef35b

Please sign in to comment.