diff --git a/micropython/modules/motor/README.md b/micropython/modules/motor/README.md index 752babe1..8028c041 100644 --- a/micropython/modules/motor/README.md +++ b/micropython/modules/motor/README.md @@ -1,38 +1,58 @@ # Motors and Motor 2040 -The Motor library lets you drive DC motors from a Raspberry Pi Pico or any other RP2040-based board, such as the [Pimoroni Motor 2040](https://pimoroni.com/servo2040). +The Motor library lets you drive DC motors from a Raspberry Pi Pico or any other RP2040-based board via connected h-bridge drivers. +An easy way to add an h-bridge driver to the Pico is to attach a [Pimoroni Pico Motor Shim](https://pimoroni.com/picomotorshim). +Alternatively, an RP2040-based board with integrated drivers could be used, such as the [Pimoroni Motor 2040](https://pimoroni.com/motor2040) (coming soon). This library offers two motor implementations: * a `Motor` class that uses hardware PWM to drive a single motor, with support for up to 8 motors. -* a `MotorCluster` class that uses Programmable IO (PIO) hardware to drive up to 15 servos. +* a `MotorCluster` class that uses Programmable IO (PIO) hardware to drive up to 15 motors. ## Table of Content -- [Motor 2040](#motor-2040) +- [Pico Motor Shim](#pico-motor-shim) - [Reading the User Button](#reading-the-user-button) - - [Reading the Sensors](#reading-the-sensors) - - [Fault Sensing and Configuring Pulls](#fault-sensing-and-configuring-pulls) - - [Controlling the LED](#controlling-the-led) - [Pin Constants](#pin-constants) - - [Motor Pin Tuples](#motor-pin-tuples) - - [Encoder Pin Tuples](#encoder-pin-tuples) - - [LED Pin](#led-pin) + - [Motor Pins](#motor-pins) - [I2C Pins](#i2c-pins) - [Button Pin](#button-pin) + - [Other Constants](#other-constants) + +- [Motor 2040](#motor-2040) + - [Reading the User Button](#reading-the-user-button-1) + - [Reading the Sensors](#reading-the-sensors) + - [Configuring Pulls](#configuring-pulls) + - [Fault Sensing](#fault-sensing) + - [Reading the Encoders](#reading-the-encoders) + - [Controlling the LED](#controlling-the-led) + - [Pin Constants](#pin-constants-1) + - [Motor Pins](#motor-pins-1) + - [Encoder Pins](#encoder-pins-1) + - [LED Pin](#led-pin) + - [I2C Pins](#i2c-pins-1) + - [Button Pin](#button-pin-1) - [Address Pins](#address-pins) - [ADC Pins](#adc-pins) - - [Other Constants](#other-constants) + - [Other Constants](#other-constants-1) - [Counts](#counts) - [Addresses](#addresses) - [Sensing](#sensing) - [Motor](#motor) - [Getting Started](#getting-started) - [Control by Speed](#control-by-speed) + - [Full Speed](#full-speed) + - [Stopping](#stopping) + - [Calibration](#calibration) - [Control by Percent](#control-by-percent) - [Control by Duty Cycle](#control-by-duty-cycle) + - [Duty Deadzone](#duty-deadzone) - [Frequency Control](#frequency-control) - [Configuration](#configuration) + - [Direction](#direction) + - [Decay Mode](#decay-mode) + - [Driver Type](#driver-type) - [Function Reference](#function-reference) + - [Constants Reference](#constants-reference) - [PWM Limitations](#pwm-limitations) - [MotorCluster](#motorcluster) - [Getting Started](#getting-started-1) @@ -47,9 +67,76 @@ This library offers two motor implementations: - [PIO Limitations](#pio-limitations) +## Pico Motor Shim + +Pico Motor Shim (or Motor Shim for Pico, if you prefer) is a small board featuring a DRV8833 dual h-bridge motor driver IC, a user button, and a Qw/st connector. It attaches to the underside of a Raspberry Pi Pico towards the USB end, and connects to two motors with Motor Connector Shims (MCS) attached via 2 pin JST-ZH cables. + +For more information on this board and its accessories, check out the store page: [pimoroni.com/picomotorshim](https://pimoroni.com/picomotorshim). + + +### Reading the User Button + +The Pico Motor Shim has a handy button onboard, offering a tactile way to interact with your program. To simplify the use of this and other buttons, the `pimoroni` module contains a `Button` class that handles button debounce and auto-repeat. + +```python +Button(button, invert=True, repeat_time=200, hold_time=1000) +``` + +To set up the user button, first import the `Button` class from the `pimoroni` module and the pin constant for the button from `motor`: +```python +from pimoroni import Button +from motor import picomotorshim +``` + +Then create an instance of `Button` for the user button: +```python +button_a = Button(picomotorshim.BUTTON_A) +``` + +To get the button state, call `.read()`. If the button is held down, then this will return `True` at the interval specified by `repeat_time` until `hold_time` is reached, at which point it will return `True` every `repeat_time / 3` milliseconds. This is useful for rapidly increasing/decreasing values: + +```python +state = button_a.read() +``` + +It is also possible to read the raw button state without the repeat feature, if you prefer: +```python +state = button_a.raw() +``` + + +### Pin Constants + +The `motor` module contains a `picomotorshim` sub module with constants for the motor and button pins. + + +#### Motor Pin Tuples + +* `MOTOR_1` = `(6, 7)` +* `MOTOR_2` = `(27, 26)` + + +#### I2C Pins + +* `SDA` = `4` +* `SCL` = `5` + + +#### Button Pin + +* `BUTTON_A` = `2` + + +### Other Constants + +The `picomotorshim` sub module also contains a constant for the number of motors on Pico Motor Shim: + +* `NUM_MOTORS` = `2` + + ## Motor 2040 -Motor 2040 is a **standalone motor controller** for driving DC motors with encoder feedback. It has JST-SH connectors for plugging in up to 4 motors, with additional bells and whistles like sensor headers, current monitoring, an RGB LED, and a user button that make it ideal for many robotics projects! +Motor 2040 is a **standalone motor controller** for driving DC motors with encoder feedback. It has JST-SH connectors for plugging in up to 4 motors, with additional bells and whistles like sensor headers, current and voltage monitoring, an RGB LED, and a user button that make it ideal for many robotics projects! For more information on this board, check out the store page: [pimoroni.com/motor2040](https://pimoroni.com/motor2040). @@ -97,7 +184,7 @@ These could be used just for monitoring, or as the trigger to turn off motors sa To allow for all of these inputs, Motor 2040 has an onboard analog multiplexer that expands a single analog pin into eight, by use of three digital address pins that select a single input at a time. As such, the setup for these sensors is more involved than it would be to just read eight analog pins directly. -To begin reading these inputs, first import the `Analog` and `AnalogMux` classes from `pimoroni` and the pin, address, and gain constants from `servo`: +To begin reading these inputs, first import the `Analog` and `AnalogMux` classes from `pimoroni` and the pin, address, and gain constants from `motor`: ```python from pimoroni import Analog @@ -107,7 +194,7 @@ from motor import motor2040 Then set up three instances of `Analog` for the sensor and fault, voltage, and current sensing: ```python -sen_fault_adc = Analog(motor2040.SHARED_ADC) +sen_adc = Analog(motor2040.SHARED_ADC) vol_adc = Analog(motor2040.SHARED_ADC, motor2040.VOLTAGE_GAIN) cur_adc = Analog(motor2040.SHARED_ADC, motor2040.CURRENT_GAIN, motor2040.SHUNT_RESISTOR, motor2040.CURRENT_OFFSET) @@ -127,7 +214,7 @@ To read the two sensor headers: ```python for addr in range(motor2040.NUM_SENSORS): mux.select(addr + motor2040.SENSOR_1_ADDR) - print("Sensor", addr + 1, "=", sen_fault_adc.read_voltage()) + print("Sensor", addr + 1, "=", sen_adc.read_voltage()) ``` To read the voltage sense: @@ -146,47 +233,64 @@ for addr in range(motor2040.NUM_MOTORS): #### Configuring Pulls -For some sensors, as well as the internal fault sensor, you may need to have the input be pulled high or low before taking a reading. To support this there is an optional `muxed_pin` parameter that can be passed into the `AnalogMux` when creating it, which gives the multiplexer access to the pin to control the pulls. +For the internal fault sensor, as well as some external sensors, you may need to have the input be pulled high or low before taking a reading. To support this there is an optional `muxed_pin` parameter that can be passed into the `AnalogMux` when creating it, which gives the multiplexer access to the pin to control the pulls. ```python mux = AnalogMux(motor2040.ADC_ADDR_0, motor2040.ADC_ADDR_1, motor2040.ADC_ADDR_2, - muxed_pin=Pin(servo2040.SHARED_ADC)) + muxed_pin=Pin(motor2040.SHARED_ADC)) ``` From there the pull state of each of the multiplexer's addresses can be configured independently by calling `.configure_pull()`, with the address and the pull state (either `Pin.PULL_UP`, `Pin.PULL_DOWN`, or `None`). The below example shows how to set both sensor addresses to have pull-downs: ```python -sensor_addrs = list(range(motor2040.SENSOR_1_ADDR, motor2040.SENSOR_2_ADDR + 1)) -for addr in sensor_addrs: - mux.configure_pull(addr, Pin.PULL_DOWN) +for addr in range(motor2040.NUM_SENSORS): + mux.configure_pull(addr + motor2040.SENSOR_1_ADDR, Pin.PULL_DOWN) ``` #### Fault Sensing -The drivers on Motor 2040 can detect when there is a fault with their connected motors, such as if a short occurs, and shut themselves off for safety. As part of this they also signal that a fault has occurred, which can be read. The way they signal this is by pulling the line to ground. This means that the line needs to be high, and so the 'AnalogMux' needs to be configured accordingly. +The drivers on Motor 2040 can detect when there is a fault with their connected motors and shut themselves off for safety. When this occurs they also signal the fault by pulling a line to ground, but requires that the line be pulled up by default. + +The `AnalogMux` can be set with a pull up on the fault sensor line, by doing the following: ```python -adc_as_io = Pin(motor2040.SHARED_ADC) -mux = AnalogMux(motor2040.ADC_ADDR_0, motor2040.ADC_ADDR_1, motor2040.ADC_ADDR_2, - muxed_pin=adc_as_io) mux.configure_pull(motor2040.FAULT_SENSE_ADDR, Pin.PULL_UP) ``` -Then in your main code: +Then the fault can be read as a digital value calling `.read()` on the `AnalogMux` itself, with the value being inverted with `not` to give `True` for when a fault has occurred: ```python mux.select(motor2040.FAULT_SENSE_ADDR) -if not adc_as_io.value(): - print("Fault!") -else: - print("No Fault") +print("Fault =", not mux.read()) ``` -//TODO make this neater +This internally reads the value of the `Pin` provided to `muxed_pin` during the creation of the `mux` object, so could be used to read any digital sensors attached to the external sensor headers of Motor 2040 too. -#### Controlling the LED +### Reading the Encoders -Between Motor 2040's for motor connectors is a single addressable RGB LEDs. This works using the same chainable 1-wire signalling as WS2812 LED's, commonly known as Neopixels. As such, it can be controlled using the same Plasma Library used by the [Pimoroni Plasma 2040](https://shop.pimoroni.com/products/plasma-2040). +On the top edge of Motor 2040 are four JST-SH 6 pin connectors for N20 style motors with magnetic encoders. With these encoders you can measure the position and speed or your motors, opening up many advanced possibilities! + +To start using encoders, you will need to first import the `Encoder` class. +```python +from encoder import Encoder +``` + +To create your encoder, specify the PIO, PIO state-machine and GPIO pins it will be connected to, and pass them into `Encoder`. For this example we will use one of the handy constants of the `motor2040`. +```python +from encoder import MMME_CPR +from motor import motor2040 +enc = Encoder(0, 0, motor2040.ENCODER_A, counts_per_rev=MMME_CPR, count_microsteps=True) +``` +Motor encoders often have a different number of counts per revolution than a traditional rotary encoder. For our MMME shims the `MMME_CPR` constant is provided. Also, as motor encoders lack the tactile steps of rotary encoders, their counting resolution can be increased by a factor of four by including each microstep. + +With the created encoder class, the current position can be read by calling `.revolutions()`, `.degrees()` or `.radians()`. + +For full details on encoders, including how to read speeds, please refer to the [Encoder Library](https://github.com/pimoroni/pimoroni-pico/tree/main/micropython/modules/encoder). + + +### Controlling the LED + +Between Motor 2040's four motor connectors is a single addressable RGB LEDs. This works using the same chainable 1-wire signalling as WS2812 LED's, commonly known as Neopixels. As such, it can be controlled using the Plasma Library, as used by the [Pimoroni Plasma 2040](https://shop.pimoroni.com/products/plasma-2040). To set up the LED, first import the `WS2812` class from the `plasma` module and the pin constants for the LED from `motor`: ```python @@ -212,7 +316,7 @@ For more information on how to control the LED, please refer to the [Plasma Libr The `motor` module contains a `motor2040` sub module with constants for the motor, encoder, LED, sensor and button pins. -#### Motor Pin Tuples +#### Motor Pins * `MOTOR_A` = `(4, 5)` * `MOTOR_B` = `(6, 7)` @@ -220,7 +324,7 @@ The `motor` module contains a `motor2040` sub module with constants for the moto * `MOTOR_D` = `(10, 11)` -#### Encoder Pin Tuples +#### Encoder Pins * `ENCODER_A` = `(0, 1)` * `ENCODER_B` = `(2, 3)` @@ -295,8 +399,8 @@ The `motor2040` sub module also contains other constants to help with the contro * `VOLTAGE_GAIN` = `0.28058` * `SHUNT_RESISTOR` = `0.47` -* `CURRENT_GAIN` = `5` -* `CURRENT_OFFSET` = `-0.02` +* `CURRENT_GAIN` = `1` +* `CURRENT_OFFSET` = `-0.005` ## Motor @@ -307,7 +411,7 @@ To start using motors with your Motor 2040, you will need to first import the `M ```python from motor import Motor, motor2040 ``` -If you are using another RP2040 based board, then `motor2040` can be omitted from the above line +If you are using another RP2040 based board, then `motor2040` can be omitted from the above line. To create your motor, choose which GPIO pins it will be connected to, and pass that into `Motor`. For this example we will use one of the handy constants of the `motor2040`. ```python @@ -331,70 +435,117 @@ From here the motor can be controlled in several ways. These are covered in more ### Control by Speed -The most intuitive way of controlling a motor is by speed. Speed can be any number that has a real-world meaning for that type of motor, for example revolutions per minute, or the linear or angular speed of the mechanism it is driving. By default the speed is a value ranging from `-1.0` to `1.0` but this can be changed by setting a new `speed_scale`. See [Configuration](#configuration) for more details. +The most intuitive way of controlling a motor is by speed. Speed can be any number that has a real-world meaning for that type of motor, for example revolutions per minute, or the linear or angular speed of the mechanism it is driving. By default the speed is a value ranging from `-1.0` to `1.0` but this can be changed by setting a new `speed_scale`. See [Calibration](#calibration) for more details. The speed of a motor can be set by calling `.speed(speed)`, which takes a float as its `speed` input. If the motor is disabled, this will enable it. The resulting duty cycle will also be stored. To read back the current speed of the motor, call `.speed()` without any input. If the motor is disabled, this will be the last speed that was provided when enabled. -At this time the speed of a motor is the same as `m.duty() * m.speed_scale()`, though there could be the option in the future to add a curve to a motor's speed that make the relationship between speed and duty cycle become non-linear. + +#### Full Speed + +To simplify certain motion patterns, a motor can be commanded to its full negative, and full positive speeds. These are performed by calling `.full_negative()`, and `.full_positive()`, respectively. If the motor is disabled, these will enable it. + +The value of the full negative and full positive speed can be read back using `.speed_scale()`. This can be useful as an input to equations that provide numbers directly to `.speed(speed)`, for example. -#### Common Speeds +#### Stopping -To simplify certain motion patterns, a motor can be commanded to three common speeds. These are, full negative, full positive, and stopped. These are performed by calling `.full_negative()`, `.full_positive()`, and `.stop()`, respectively. If the motor is disabled, these will enable it. +The easiest way to stop a motor is by calling `.stop()`. This is equivalent to calling `.speed(0.0)` and stops the motor using the currently assigned decay mode of the `Motor` object. See [Decay Modes](#decay-modes) for more details. -The full negative and full positive speed can be read back using `.speed_scale()`. This can be useful as an input to equations that provide numbers directly to `.speed(speed)`, for example. +It is also possible to explicitly have the motor coast or brake to a stop by calling `.coast()` or `.brake()`. + +If the motor is disabled, these will enable it. + + +### Calibration + +It is very rare for a motor to perfectly drive at the speed we want them to. As such, the `Motor` class offers two parameters for adjusting how the value provided to `.speed(speed)` is converted to the PWM duty cycle that is actually sent to the motor, a speed scale, and a zeropoint. + +Speed scale, as the name implies, is a value that scalues the duty cycle up or down to better reflect the measured speed of the motor when driving at `.full_negative()` or `.full_positive()`. This can be set by calling `.speed_scale(speed_scale)`, which accepts a value greater than `0.0`. The current speed scale can also be read by calling `.speed_scale()`. + +Zeropoint is a value that sets what duty cycle should count as the zero speed of the motor. By default this is `0.0` and usually it is fine to leave it at that, but there are cases at low speeds where the expected speed does not match the measured speed, which small adjustments to the zeropoint will fix. This can be set by calling `.zeropoint(zeropoint)`, which accepts a value from `0.0` to less than `1.0`. The current zeropoint can also be read by calling `.zeropoint()`. + +Both parameters can also be provided during the creation of a new `Motor` object. ### Control by Percent Sometimes there are projects where a motor needs to drive based on the reading from a sensor or another device, but the numbers given out are not easy to convert to speeds the motor accepts. To overcome this the library lets you drive the motor at a percent between its negative and positive speeds, or two speeds provided, based on that input. -With an input between `-1.0` and `1.0`, a motor can be driven at a percent between its negative and positive speeds using `.at_percent(in)`. +With an input between `-1.0` and `1.0`, a motor can be set to a percent between its negative and positive speeds using `.to_percent(in)`. -With an input between a provided min and max, a motor can be driven at a percent between its negative and positive speeds using `.at_percent(in, in_min, in_max)`. +With an input between a provided min and max, a motor can be set to a percent between its negative and positive speeds using `.to_percent(in, in_min, in_max)`. -With an input between a provided min and max, a motor can be driven at a percent between two provided speeds using `.at_percent(in, in_min, value_min, value_max)`. +With an input between a provided min and max, a motor can be set to a percent between two provided speeds using `.to_percent(in, in_min, value_min, value_max)`. If the motor is disabled, these will enable it. ### Control by Duty Cycle -At a hardware level DC motors operate by receiving a voltage across their two terminals, with positive causing a motion in one direction and negative causing a motion in the other. To avoid needing a negative voltage supply, motor drivers employ a H-Bridge arrangement of transistors or mosfets to flip which side of the motor is connected to ground and which is connected to power. By rapidly turing these transistors or mosfets on and off both the speed and direction of the motor can be varied. The common way this is achieved is by using a pair of pulse width modulated signals, where the duty cycle of the active signal controls the speed, and which signal is active controls the direction. Braking can also be controlled (see //TODO) +Motor drivers accept pulse width modulated (PWM) signals to control the speed and direction of their connected motors. The percentage of time that these signals are active for is know as their duty cycle. This is typically measured as a value between `0.0` and `1.0`, but as motors use two pins for their control signals, here negative values are added to denote the reverse direction. The duty cycle of a motor can be set by calling `.duty(duty)`, which takes a float from `-1.0` to `1.0` as its `duty` input. If the motor is disabled this will enable it. This function will also recalculate the related speed. To read back the current duty cycle of the motor, call `.duty()` without any input. If the motor is disabled, this will be the last duty that was provided when enabled. -At this time the duty cycle of a motor is the same as `m.speed() / m.speed_scale()`, though there could be the option in the future to add a curve to a motor's speed that make the relationship between speed and duty cycle become non-linear. + +#### Duty Deadzone + +Most motors have a duty cycle value below which their is too much friction for them to move. This may not be a concern, except for when running motors at audable frequencies, where the buzzing of the motor trying to move without success can get annoying. + +To overcome this, a duty cycle deadzone can be set on a per motor basis by calling `.deadzone(deadzone)`, which accepts a float from `0.0` to less than `1.0`. Whenever a duty cycle is set, either directly or via speed or percent functions, it will only be output to the motor if it is greater than or equal to the deadzone. If it's below, the motor will be stopped instead. By default the deadzone is `0.05`. + +To read back the current duty deadzone of the motor, call `.deadzone()` without any input. + +Note that the motor keeps track of its duty cycle, so if the deadzone is changed the check will be performed again, and may either start a motor that was previously stopped or visa versa. ### Frequency Control -Motors can be driven at a variety of frequencies, with a common values being above the range of human hearing. As such this library uses 25KHz as its default, but this can easily be changed. +Motors can be driven at a variety of frequencies, with common values being above the range of human hearing. As such this library uses 25KHz as its default, but this can easily be changed. -The frequency (in Hz) of a motor can be set by calling `.frequency(freq)`, which takes a float as its `freq` input. //TODO The supported range of this input is `10` to `350` Hz. +The frequency (in Hz) of a motor can be set by calling `.frequency(freq)`, which takes a float as its `freq` input. The supported range of this input is `10` Hz to `400` KHz, though not all motor drivers can handle the very high frequencies. To read back the current frequency (in Hz) of the motor, call `.frequency()` without any input. -Note that changing the frequency does not change the duty cycle sent to the motors, only how frequently pulses are sent. - +Note that changing the frequency does not change the duty cycle or speed sent to the motors, only how frequently pulses are sent. ### Configuration -There are several options for configuring a motor. As mentioned in earlier sections, the first is to adjust their speed scale. There is also the option to change their direction, change their decay mode, and add in a dead-zone. This is a percentage of the duty cycle below which the motor will be stopped. This is included to avoid annoying situations where the duty cycle is too low for a motor to move, but enough for it to make an audable sound. +### Direction -Decay mode is //TODO `.brake()` `.coast()` +The driving direction of a motor can be changed either by providing `direction=REVERSED_DIR` when creating the `Motor` object, or by calling `.direction(REVERSED_DIR)` at any time in code. The `REVERSED_DIR` constant comes from the `pimoroni` module. There is also a `NORMAL_DIR` constant, though this is the default. + + +### Decay Mode + +If you have ever had a motor directly connected to a power source and turned the power off, or disconnected the wire, you may have noticed that the motor continues to spin for a second or two before it reaches a stop. This is because the magnetic field the power source was generating has decayed away quickly, so the only thing slowing the motor down is friction. This results in the motor coasting to a stop. + +By contrast, if you were to wire your circuit up such that instead of disconnecting the power, the off position joined the two ends of the motor together, it would take longer for the magnetic field to decay away. This has the effect of braking the motor, causing it to stop quicker than with friction alone. + +These examples describe the two decay modes supported by the `Motor` class, `FAST_DECAY`, and `SLOW_DECAY`, respectively. Generally slow decay offers better motor performance, particularly with low speeds, so this is the default when creating a new `Motor`. + +If fast decay is wanted then it can either be changed by providing `mode=FAST_DECAY` during the class creation or by calling `.decay_mode(FAST_DECAY)`. The current decay mode can also be read with `.decay_mode()`. + +For more information about motor decay modes, it's highly recommended that you check out the Adafruit Learn Guide titled [Improve Brushed DC Motor Performance](https://learn.adafruit.com/improve-brushed-dc-motor-performance) + + +### Driver Type + +There are two common types of DC motor drivers, based on the signals they expect for controlling the motor. +* Dual PWMs, where both pins control the speed, direction, and decay mode of the motor. +* Phase/Enable, where a single PWM pin controls speed and a single GPIO pin controls direction. There is no decay mode control. + +By default all `Motor` objects are initialised for dual PWM drivers, but if you are using the other type of driver this can be configured by providing `ph_en_driver=True` during object creation. ### Function Reference Here is the complete list of functions available on the `Motor` class: ```python -//TODO -Motor(pins, calibration=ANGULAR, freq=50) # calibration can either be an integer or a Calibration class +Motor(pins, direction=NORMAL_DIR, speed_scale=1.0, zeropoint=0.0, deadzone=0.05, freq=25000, mode=SLOW_DECAY, ph_en_driver=False) pins() enable() disable() @@ -410,19 +561,33 @@ coast() brake() full_negative() full_positive() -at_percent(in) -at_percent(in, in_min, in_max) -at_percent(in, in_min, in_max, speed_min, speed_max) +to_percent(in) +to_percent(in, in_min, in_max) +to_percent(in, in_min, in_max, speed_min, speed_max) direction() direction(direction) speed_scale() speed_scale(speed_scale) +zeropoint() +zeropoint(zeropoint) deadzone() deadzone(deadzone) decay_mode() decay_mode(mode) ``` +### Constants Reference + +Here is the complete list of constants on the `motor` module: + +* `FAST_DECAY` = `0` +* `SLOW_DECAY` = `1` + +Here are useful constants from the `pimoroni` module: + +* `NORMAL_DIR` = `0` +* `REVERSED_DIR` = `1` + ### PWM Limitations diff --git a/micropython/modules/motor/motor.c b/micropython/modules/motor/motor.c index f3d6ee53..e1d1731d 100644 --- a/micropython/modules/motor/motor.c +++ b/micropython/modules/motor/motor.c @@ -78,6 +78,7 @@ STATIC const mp_rom_map_elem_t Motor_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_to_percent), MP_ROM_PTR(&Motor_to_percent_obj) }, { MP_ROM_QSTR(MP_QSTR_direction), MP_ROM_PTR(&Motor_direction_obj) }, { MP_ROM_QSTR(MP_QSTR_speed_scale), MP_ROM_PTR(&Motor_speed_scale_obj) }, + { MP_ROM_QSTR(MP_QSTR_zeropoint), MP_ROM_PTR(&Motor_zeropoint_obj) }, { MP_ROM_QSTR(MP_QSTR_deadzone), MP_ROM_PTR(&Motor_deadzone_obj) }, { MP_ROM_QSTR(MP_QSTR_decay_mode), MP_ROM_PTR(&Motor_decay_mode_obj) }, }; diff --git a/micropython/modules/servo/README.md b/micropython/modules/servo/README.md index 7fe0890b..4ebaf934 100644 --- a/micropython/modules/servo/README.md +++ b/micropython/modules/servo/README.md @@ -172,9 +172,9 @@ for addr in sensor_addrs: ``` -#### Controlling the LED Bar +### Controlling the LED Bar -Alongside Servo 2040's six sensor headers are six addressable RGB LEDs. These work using the same chainable 1-wire signalling as WS2812 LED's, commonly known as Neopixels. As such, they can be controlled using the same Plasma Library used by the [Pimoroni Plasma 2040](https://shop.pimoroni.com/products/plasma-2040). +Alongside Servo 2040's six sensor headers are six addressable RGB LEDs. These work using the same chainable 1-wire signalling as WS2812 LED's, commonly known as Neopixels. As such, they can be controlled using the Plasma Library, as used by the [Pimoroni Plasma 2040](https://shop.pimoroni.com/products/plasma-2040). To set up the LED bar, first import the `WS2812` class from the `plasma` module and the pin constants for the LEDs from `servo`: ```python @@ -294,7 +294,7 @@ To start using servos with your Servo 2040, you will need to first import the `S ```python from servo import Servo, servo2040 ``` -If you are using another RP2040 based board, then `servo2040` can be omitted from the above line +If you are using another RP2040 based board, then `servo2040` can be omitted from the above line. To create your servo, choose which GPIO pin it will be connected to, and pass that into `Servo`. For this example we will use one of the handy constants of the `servo2040`. ```python