kopia lustrzana https://github.com/micropython/micropython-lib
Porównaj commity
3 Commity
367819117e
...
15f091a897
Autor | SHA1 | Data |
---|---|---|
Ihor Nehrutsa | 15f091a897 | |
Damien George | 45ead11f96 | |
Ihor Nehrutsa | abdab7dbbc |
|
@ -0,0 +1,3 @@
|
|||
*.mp4
|
||||
*.png
|
||||
*.jpg
|
|
@ -0,0 +1,229 @@
|
|||
## Stepper motor PWM-Counter driver
|
||||
|
||||
This MicroPython software driver is designed to control stepper motor using STEP/DIR hardware driver.
|
||||
|
||||
![stepper_motor_driver](https://github.com/IhorNehrutsa/micropython-lib/assets/70886343/27933a08-7225-4931-a1ee-8e0042d0b822)
|
||||
|
||||
* The driver signal "STEP" (aka "PULSE") is intended for clock pulses. In one pulse, the motor rotor turns one step. The higher the frequency of pulses, the higher the speed of rotation of the rotor.
|
||||
|
||||
* The driver signal "DIR" is intended to select the direction of rotation of the engine ("1" - in one direction, "0" - in the other direction).
|
||||
|
||||
![forward_reverse](https://github.com/IhorNehrutsa/micropython-lib/assets/70886343/f1986469-6fca-4d10-a6f2-262020a19946)
|
||||
|
||||
### Hardware
|
||||
|
||||
As an example of a STEP / DIR hardware driver:
|
||||
|
||||
* [TMC2209](https://wiki.fysetc.com/Silent2209) module, TB6612,
|
||||
|
||||
* [TB6560-V2](https://mypractic.com/stepper-motor-driver-tb6560-v2-description-characteristics-recommendations-for-use) module,
|
||||
|
||||
* [TB6600](https://mytectutor.com/tb6600-stepper-motor-driver-with-arduino) based driver,
|
||||
|
||||
* DM860H, DM556 etc.
|
||||
|
||||
### Software
|
||||
|
||||
The main feature of this driver is that the generation and counting of pulses are performed by hardware, which frees up time in the main loop. PWM will start pulses and Counter will stop pulses in irq handler.
|
||||
|
||||
The PWM unit creates STEP pulses and sets the motor speed.
|
||||
|
||||
The GPIO unit controls the DIR pin, the direction of rotation of the motor.
|
||||
|
||||
The Counter unit counts pulses, that is, the actual position of the stepper motor.
|
||||
|
||||
![stepper_motor_pwm_counter](https://github.com/IhorNehrutsa/micropython-lib/assets/70886343/4e6cf4b9-b198-4fa6-8bcc-51d873bf74ce)
|
||||
|
||||
In general case MicroPython ports need 4 pins: PWM STEP output, GPIO DIR output, Counter STEP input, Counter DIR input (red wires in the image).
|
||||
|
||||
The ESP32 port allows to connect Counter inputs to the same outputs inside the MCU(green wires in the picture), so 2 pins are needed.
|
||||
|
||||
This driver requires PR's:
|
||||
|
||||
[esp32/PWM: Reduce inconsitencies between ports. #10854](https://github.com/micropython/micropython/pull/10854)
|
||||
|
||||
[ESP32: Add Quadrature Encoder and Pulse Counter classes. #8766](https://github.com/micropython/micropython/pull/8766)
|
||||
|
||||
Constructor
|
||||
-----------
|
||||
|
||||
class:: StepperMotorPwmCounter(pin_step, pin_dir, freq, reverse)
|
||||
|
||||
Construct and return a new StepperMotorPwmCounter object using the following parameters:
|
||||
|
||||
- *pin_step* is the entity on which the PWM is output, which is usually a
|
||||
:ref:`machine.Pin <machine.Pin>` object, but a port may allow other values, like integers.
|
||||
- *freq* should be an integer which sets the frequency in Hz for the
|
||||
PWM cycle i.e. motor speed.
|
||||
- *reverse* reverse the motor direction if the value is True
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
property:: StepperMotorPwmCounter.freq
|
||||
|
||||
Get/set the current frequency of the STEP/PWM output.
|
||||
|
||||
property:: StepperMotorPwmCounter.steps_counter
|
||||
|
||||
Get current steps position form the Counter.
|
||||
|
||||
property:: StepperMotorPwmCounter.steps_target
|
||||
|
||||
Get/set the steps target.
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
method:: StepperMotorPwmCounter.deinit()
|
||||
|
||||
Disable the PWM output.
|
||||
|
||||
method:: StepperMotorPwmCounter.go()
|
||||
|
||||
Call it in the main loop to move the motor to the steps_target position.
|
||||
|
||||
method:: StepperMotorPwmCounter.is_ready()
|
||||
|
||||
Return True if steps_target is achieved.
|
||||
|
||||
Tested on ESP32.
|
||||
|
||||
**Simple example is:**
|
||||
```
|
||||
# stepper_motor_pwm_counter_test1.py
|
||||
|
||||
from time import sleep
|
||||
|
||||
from stepper_motor_pwm_counter import StepperMotorPwmCounter
|
||||
|
||||
try:
|
||||
motor = StepperMotorPwmCounter(26, 23, freq=10_000)
|
||||
print(motor)
|
||||
|
||||
motor.steps_target = 8192
|
||||
while True:
|
||||
if not motor.is_ready():
|
||||
motor.go()
|
||||
print(f'motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}')
|
||||
else:
|
||||
print()
|
||||
print(f'motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}')
|
||||
print('SET steps_target', -motor.steps_target)
|
||||
print('sleep(1)')
|
||||
print()
|
||||
sleep(1)
|
||||
motor.steps_target = -motor.steps_target
|
||||
motor.go()
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
finally:
|
||||
try:
|
||||
motor.deinit()
|
||||
except:
|
||||
pass
|
||||
```
|
||||
|
||||
**Output is:**
|
||||
```
|
||||
StepMotorPWMCounter(pin_step=Pin(26), pin_dir=Pin(23), freq=10000, reverse=0,
|
||||
pwm=PWM(Pin(26), freq=10000, duty_u16=0),
|
||||
counter=Counter(0, src=Pin(26), direction=Pin(23), edge=Counter.RISING, filter_ns=0))
|
||||
motor.steps_target=8192, motor.steps_counter=2, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=1025, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=2048, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=3071, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=4094, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=5117, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=6139, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=7162, motor.is_ready()=False
|
||||
motor.steps_target=8192, motor.steps_counter=8185, motor.is_ready()=False
|
||||
irq_handler: steps_over_run=6, counter.get_value()=8204
|
||||
|
||||
motor.steps_target=8192, motor.steps_counter=8204, motor.is_ready()=True
|
||||
SET steps_target -8192
|
||||
sleep(1)
|
||||
|
||||
motor.steps_target=-8192, motor.steps_counter=7200, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=6176, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=5153, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=4130, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=3107, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=2084, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=1061, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=37, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-986, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-2009, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-3032, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-4054, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-5077, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-6100, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-7123, motor.is_ready()=False
|
||||
motor.steps_target=-8192, motor.steps_counter=-8146, motor.is_ready()=False
|
||||
irq_handler: steps_over_run=4, counter.get_value()=-8188
|
||||
motor.steps_target=-8192, motor.steps_counter=-8189, motor.is_ready()=False
|
||||
|
||||
motor.steps_target=-8192, motor.steps_counter=-9209, motor.is_ready()=True
|
||||
SET steps_target 8192
|
||||
sleep(1)
|
||||
|
||||
motor.steps_target=8192, motor.steps_counter=-8205, motor.is_ready()=False
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 25, in <module>
|
||||
KeyboardInterrupt:
|
||||
```
|
||||
|
||||
**Example with motor speed acceleration/deceleration:**
|
||||
```
|
||||
# stepper_motor_pwm_counter_test2.py
|
||||
|
||||
from time import sleep
|
||||
|
||||
from stepper_motor_pwm_counter import StepperMotorPwmCounter
|
||||
|
||||
|
||||
try:
|
||||
motor = StepperMotorPwmCounter(26, 23)
|
||||
print(motor)
|
||||
|
||||
f_min = 3_000
|
||||
f_max = 50_000
|
||||
df = 1_000
|
||||
motor.freq = f_min
|
||||
motor_steps_start = motor.steps_counter
|
||||
motor.steps_target = 8192 * 10
|
||||
while True:
|
||||
if not motor.is_ready():
|
||||
motor.go()
|
||||
else:
|
||||
print()
|
||||
print(f'motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}')
|
||||
print('SET steps_target', -motor.steps_target)
|
||||
print('sleep(1)')
|
||||
print()
|
||||
sleep(1)
|
||||
motor_steps_start = motor.steps_target
|
||||
motor.steps_target = -motor.steps_target
|
||||
motor.go()
|
||||
|
||||
|
||||
m = min(abs(motor.steps_counter - motor_steps_start), abs(motor.steps_target - motor.steps_counter))
|
||||
motor.freq = min(f_min + df * m // 1000, f_max)
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
finally:
|
||||
try:
|
||||
motor.deinit()
|
||||
except:
|
||||
pass
|
||||
|
||||
```
|
||||
[Motor speed acceleration/deceleration video](https://drive.google.com/file/d/1HOkmqnaepOOmt4XUEJzPtQJNCVQrRUXs/view?usp=drive_link)
|
|
@ -0,0 +1,164 @@
|
|||
from utime import ticks_diff, ticks_us, ticks_ms
|
||||
from machine import Pin, PWM, Counter
|
||||
|
||||
|
||||
class StepperMotorPwmCounter:
|
||||
def __init__(self, pin_step, pin_dir, freq=5_000, reverse=0, counter=None, pwm=None):
|
||||
if isinstance(pin_step, Pin):
|
||||
self.pin_step = pin_step
|
||||
else:
|
||||
self.pin_step = Pin(pin_step, Pin.OUT)
|
||||
|
||||
if isinstance(pin_dir, Pin):
|
||||
self.pin_dir = pin_dir
|
||||
else:
|
||||
self.pin_dir = Pin(pin_dir, Pin.OUT, value=0)
|
||||
|
||||
self.freq = freq
|
||||
|
||||
self.reverse = reverse # reverse the direction of movement of the motor
|
||||
|
||||
if isinstance(counter, Counter):
|
||||
self._counter = counter
|
||||
else:
|
||||
self._counter = Counter(-1, src=pin_step, direction=pin_dir)
|
||||
|
||||
self._steps_target = 0
|
||||
self._match = 0
|
||||
self._steps_over_run = 0
|
||||
|
||||
self._direction = 0 # the current direction of movement of the motor (-1 - movement in the negative direction, 0 - motionless, 1 - movement in the positive direction)
|
||||
|
||||
# must be after Counter() initialization!
|
||||
if isinstance(pwm, PWM):
|
||||
self._pwm = pwm
|
||||
else:
|
||||
self._pwm = PWM(pin_step, freq=self._freq, duty_u16=0) # 0%
|
||||
|
||||
def __repr__(self):
|
||||
return f"StepMotorPWMCounter(pin_step={self.pin_step}, pin_dir={self.pin_dir}, freq={self._freq}, reverse={self._reverse}, pwm={self._pwm}, counter={self._counter})"
|
||||
|
||||
def deinit(self):
|
||||
try:
|
||||
self._pwm.deinit()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self._counter.irq(handler=None)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self._counter.deinit()
|
||||
except:
|
||||
pass
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@property
|
||||
def reverse(self):
|
||||
return self._reverse
|
||||
|
||||
@reverse.setter
|
||||
def reverse(self, reverse: int):
|
||||
self._reverse = 1 if bool(reverse) else 0
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@property
|
||||
def freq(self):
|
||||
return self._freq
|
||||
|
||||
@freq.setter
|
||||
def freq(self, freq):
|
||||
self._freq = freq if freq > 0 else 1 # pulse frequency in Hz
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@property
|
||||
def direction(self) -> int:
|
||||
return self._direction
|
||||
|
||||
@direction.setter
|
||||
def direction(self, delta: int):
|
||||
if delta > 0:
|
||||
self._direction = 1
|
||||
self.pin_dir(1 ^ self._reverse)
|
||||
elif delta < 0:
|
||||
self._direction = -1
|
||||
self.pin_dir(0 ^ self._reverse)
|
||||
else:
|
||||
self._direction = 0
|
||||
# print(f'Set direction:{delta} to {self._direction}')
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@property
|
||||
def steps_counter(self) -> int:
|
||||
return self._counter.get_value()
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
@property
|
||||
def steps_target(self) -> int:
|
||||
return self._steps_target
|
||||
|
||||
@steps_target.setter
|
||||
def steps_target(self, steps_target):
|
||||
# Set the target position that will be achieved in the main loop
|
||||
if self._steps_target != steps_target:
|
||||
self._steps_target = steps_target
|
||||
|
||||
delta = self._steps_target - self._counter.get_value()
|
||||
if delta > 0:
|
||||
self._match = self._steps_target - self._steps_over_run # * 2
|
||||
self._counter.irq(
|
||||
handler=self.irq_handler, trigger=Counter.IRQ_MATCH1, value=self._match
|
||||
)
|
||||
elif delta < 0:
|
||||
self._match = self._steps_target + self._steps_over_run # * 2
|
||||
self._counter.irq(
|
||||
handler=self.irq_handler, trigger=Counter.IRQ_MATCH1, value=self._match
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def irq_handler(self, obj):
|
||||
self.stop_pulses()
|
||||
self._steps_over_run = (
|
||||
self._steps_over_run + abs(self._counter.get_value() - self._match)
|
||||
) // 2
|
||||
print(
|
||||
f" irq_handler: steps_over_run={self._steps_over_run}, counter.get_value()={self._counter.get_value()}"
|
||||
)
|
||||
|
||||
def start_pulses(self):
|
||||
self._pwm.freq(self._freq)
|
||||
self._pwm.duty_u16(32768)
|
||||
|
||||
def stop_pulses(self):
|
||||
self._pwm.duty_u16(0)
|
||||
|
||||
def stop(self):
|
||||
self.stop_pulses()
|
||||
self._steps_target = self._counter.get_value()
|
||||
|
||||
def go(self):
|
||||
delta = self._steps_target - self._counter.get_value()
|
||||
if delta > 0:
|
||||
self.direction = 1
|
||||
self.start_pulses()
|
||||
elif delta < 0:
|
||||
self.direction = -1
|
||||
self.start_pulses()
|
||||
else:
|
||||
self.stop_pulses()
|
||||
# print(f" go: delta={delta}, steps_target={self._steps_target}, match={self._match}, counter.get_value()={self._counter.get_value()}, direction={self._direction}, steps_over_run={self._steps_over_run}, freq={self._freq}")
|
||||
|
||||
def is_ready(self) -> bool:
|
||||
delta = self._steps_target - self._counter.get_value()
|
||||
# print(f" is_ready: delta={delta}, counter.get_value()={self._counter.get_value()}, steps_target={self._steps_target}, direction={self._direction}")
|
||||
if self._direction > 0:
|
||||
if delta <= 0:
|
||||
self.stop_pulses()
|
||||
return True
|
||||
elif self._direction < 0:
|
||||
if delta >= 0:
|
||||
self.stop_pulses()
|
||||
return True
|
||||
else:
|
||||
return delta == 0
|
||||
return False
|
|
@ -0,0 +1,40 @@
|
|||
# stepper_motor_pwm_counter_test1.py
|
||||
|
||||
from time import sleep
|
||||
|
||||
from stepper_motor_pwm_counter import StepperMotorPwmCounter
|
||||
|
||||
|
||||
try:
|
||||
motor = StepperMotorPwmCounter(26, 23, freq=10_000)
|
||||
print(motor)
|
||||
|
||||
motor.steps_target = 8192
|
||||
while True:
|
||||
if not motor.is_ready():
|
||||
motor.go()
|
||||
print(
|
||||
f"motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}"
|
||||
)
|
||||
else:
|
||||
print()
|
||||
print(
|
||||
f"motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}"
|
||||
)
|
||||
print("SET steps_target", -motor.steps_target)
|
||||
print("sleep(1)")
|
||||
print()
|
||||
sleep(1)
|
||||
motor.steps_target = -motor.steps_target
|
||||
motor.go()
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
finally:
|
||||
try:
|
||||
motor.deinit()
|
||||
except:
|
||||
pass
|
|
@ -0,0 +1,49 @@
|
|||
# stepper_motor_pwm_counter_test2.py
|
||||
|
||||
from time import sleep
|
||||
|
||||
from stepper_motor_pwm_counter import StepperMotorPwmCounter
|
||||
|
||||
|
||||
try:
|
||||
motor = StepperMotorPwmCounter(26, 23)
|
||||
print(motor)
|
||||
|
||||
f_min = 3_000
|
||||
f_max = 50_000
|
||||
df = 1_000
|
||||
motor.freq = f_min
|
||||
motor_steps_start = motor.steps_counter
|
||||
motor.steps_target = 8192 * 10
|
||||
while True:
|
||||
if not motor.is_ready():
|
||||
motor.go()
|
||||
else:
|
||||
print()
|
||||
print(
|
||||
f"motor.steps_target={motor.steps_target}, motor.steps_counter={motor.steps_counter}, motor.is_ready()={motor.is_ready()}"
|
||||
)
|
||||
print("SET steps_target", -motor.steps_target)
|
||||
print("sleep(1)")
|
||||
print()
|
||||
sleep(1)
|
||||
motor_steps_start = motor.steps_target
|
||||
motor.steps_target = -motor.steps_target
|
||||
motor.go()
|
||||
|
||||
m = min(
|
||||
abs(motor.steps_counter - motor_steps_start),
|
||||
abs(motor.steps_target - motor.steps_counter),
|
||||
)
|
||||
motor.freq = min(f_min + df * m // 1000, f_max)
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
finally:
|
||||
try:
|
||||
motor.deinit()
|
||||
except:
|
||||
pass
|
|
@ -1,3 +1,3 @@
|
|||
metadata(version="0.2.0")
|
||||
metadata(version="0.2.1")
|
||||
|
||||
module("ssl.py", opt=3)
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import tls
|
||||
from tls import (
|
||||
CERT_NONE,
|
||||
CERT_OPTIONAL,
|
||||
CERT_REQUIRED,
|
||||
MBEDTLS_VERSION,
|
||||
PROTOCOL_TLS_CLIENT,
|
||||
PROTOCOL_TLS_SERVER,
|
||||
)
|
||||
from tls import *
|
||||
|
||||
|
||||
class SSLContext:
|
||||
|
|
Ładowanie…
Reference in New Issue