Skip to content

Commit a9ef35b

Browse files
authored
Merge pull request #54 from CedarGroveStudios/master
Add Recirculation Current Decay Mode Setter/Getter
2 parents 160ed3f + 413c3c3 commit a9ef35b

File tree

3 files changed

+78
-11
lines changed

3 files changed

+78
-11
lines changed

adafruit_motor/motor.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
1+
# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries
22
#
33
# SPDX-License-Identifier: MIT
44

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

26+
FAST_DECAY = 0
27+
"""Recirculation current fast decay mode (coasting)"""
28+
29+
SLOW_DECAY = 1
30+
"""Recirculation current slow decay mode (braking)"""
31+
2632

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

4157
@property
4258
def throttle(self):
4359
"""Motor speed, ranging from -1.0 (full speed reverse) to 1.0 (full speed forward),
44-
or ``None``.
60+
or ``None`` (controller off).
4561
If ``None``, both PWMs are turned full off. If ``0.0``, both PWMs are turned full on.
4662
"""
4763
return self._throttle
4864

4965
@throttle.setter
5066
def throttle(self, value):
5167
if value is not None and (value > 1.0 or value < -1.0):
52-
raise ValueError("Throttle must be None or between -1.0 and 1.0")
68+
raise ValueError("Throttle must be None or between -1.0 and +1.0")
5369
self._throttle = value
54-
if value is None:
70+
if value is None: # Turn off motor controller (high-Z)
5571
self._positive.duty_cycle = 0
5672
self._negative.duty_cycle = 0
57-
elif value == 0:
73+
elif value == 0: # Brake motor (low-Z)
5874
self._positive.duty_cycle = 0xFFFF
5975
self._negative.duty_cycle = 0xFFFF
6076
else:
6177
duty_cycle = int(0xFFFF * abs(value))
62-
if value < 0:
63-
self._positive.duty_cycle = 0
64-
self._negative.duty_cycle = duty_cycle
65-
else:
66-
self._positive.duty_cycle = duty_cycle
67-
self._negative.duty_cycle = 0
78+
if self._decay_mode == SLOW_DECAY: # Slow Decay (Braking) Mode
79+
if value < 0:
80+
self._positive.duty_cycle = 0xFFFF - duty_cycle
81+
self._negative.duty_cycle = 0xFFFF
82+
else:
83+
self._positive.duty_cycle = 0xFFFF
84+
self._negative.duty_cycle = 0xFFFF - duty_cycle
85+
else: # Default Fast Decay (Coasting) Mode
86+
if value < 0:
87+
self._positive.duty_cycle = 0
88+
self._negative.duty_cycle = duty_cycle
89+
else:
90+
self._positive.duty_cycle = duty_cycle
91+
self._negative.duty_cycle = 0
92+
93+
@property
94+
def decay_mode(self):
95+
"""Motor controller recirculation current decay mode. A value of ``motor.FAST_DECAY``
96+
sets the motor controller to the default fast recirculation current decay mode
97+
(coasting); ``motor.SLOW_DECAY`` sets slow decay (braking) mode."""
98+
return self._decay_mode
99+
100+
@decay_mode.setter
101+
def decay_mode(self, mode=FAST_DECAY):
102+
if mode in (FAST_DECAY, SLOW_DECAY):
103+
self._decay_mode = mode
104+
else:
105+
raise ValueError(
106+
"Decay mode value must be either motor.FAST_DECAY or motor.SLOW_DECAY"
107+
)
68108

69109
def __enter__(self):
70110
return self

examples/motor_pca9685_dc_motor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
# See here for more info: https://learn.adafruit.com/adafruit-motor-shield-v2-for-arduino/faq#faq-13
3333
pca.channels[7].duty_cycle = 0xFFFF
3434
motor4 = motor.DCMotor(pca.channels[5], pca.channels[6])
35+
motor4.decay_mode = (
36+
motor.SLOW_DECAY
37+
) # Set motor to active braking mode to improve performance
3538

3639
print("Forwards slow")
3740
motor4.throttle = 0.5

tests/test_stepper.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
#
33
# SPDX-License-Identifier: Unlicense
44

5+
"""
6+
`test_stepper`
7+
====================================================
8+
9+
Tests stepper functionality.
10+
11+
* Author(s): ladyada
12+
"""
13+
14+
__version__ = "1.0.0"
15+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Motor.git"
16+
517
import os
618
import sys
719
from unittest.mock import MagicMock
@@ -14,17 +26,23 @@
1426

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

29+
# pylint: disable=consider-using-in
30+
1731

1832
class Coil:
33+
"""Class Coil"""
34+
1935
def __init__(self):
2036
self._duty_cycle = 0
2137

2238
@property
2339
def frequency(self):
40+
"""Default frequency setting"""
2441
return 1500
2542

2643
@property
2744
def duty_cycle(self):
45+
"""16-bit duty cycle value"""
2846
return self._duty_cycle
2947

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

3553

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

4867

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

6383

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

90111

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

122144

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

155178

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

0 commit comments

Comments
 (0)