From fac3e7365a37ba180e3071877d953554a6206e55 Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Thu, 12 May 2022 13:20:52 +0100 Subject: [PATCH] Ported shim examples to C++, and tidy up --- examples/motor2040/README.md | 36 ++-- .../motor2040/motor2040_position_control.cpp | 2 +- ...motor2040_position_on_velocity_control.cpp | 2 +- .../motor2040_position_on_velocity_tuning.cpp | 2 +- .../motor2040/motor2040_position_tuning.cpp | 2 +- .../motor2040_quad_position_wave.cpp | 2 +- .../motor2040_quad_velocity_sequence.cpp | 2 +- .../motor2040/motor2040_reactive_encoder.cpp | 2 +- examples/motor2040/motor2040_single_motor.cpp | 2 +- .../motor2040/motor2040_velocity_control.cpp | 2 +- .../motor2040/motor2040_velocity_tuning.cpp | 2 +- examples/pico_motor_shim/CMakeLists.txt | 7 +- examples/pico_motor_shim/README.md | 33 ++++ .../motorshim_dual_motors.cmake | 12 ++ .../pico_motor_shim/motorshim_dual_motors.cpp | 105 +++++++++++ .../motorshim_motor_song.cmake | 13 ++ .../demo.cpp => motorshim_motor_song.cpp} | 85 ++++----- .../pico_motor_shim/motorshim_movements.cmake | 12 ++ .../pico_motor_shim/motorshim_movements.cpp | 126 +++++++++++++ .../motorshim_single_motor.cmake | 12 ++ .../motorshim_single_motor.cpp | 78 ++++++++ .../pico_motor_shim/sequence/CMakeLists.txt | 16 -- examples/pico_motor_shim/sequence/demo.cpp | 170 ------------------ examples/pico_motor_shim/song/CMakeLists.txt | 16 -- .../examples/pico_motor_shim/README.md | 40 +++++ .../examples/pico_motor_shim/motor_song.py | 2 + 26 files changed, 508 insertions(+), 275 deletions(-) create mode 100644 examples/pico_motor_shim/README.md create mode 100644 examples/pico_motor_shim/motorshim_dual_motors.cmake create mode 100644 examples/pico_motor_shim/motorshim_dual_motors.cpp create mode 100644 examples/pico_motor_shim/motorshim_motor_song.cmake rename examples/pico_motor_shim/{song/demo.cpp => motorshim_motor_song.cpp} (53%) create mode 100644 examples/pico_motor_shim/motorshim_movements.cmake create mode 100644 examples/pico_motor_shim/motorshim_movements.cpp create mode 100644 examples/pico_motor_shim/motorshim_single_motor.cmake create mode 100644 examples/pico_motor_shim/motorshim_single_motor.cpp delete mode 100644 examples/pico_motor_shim/sequence/CMakeLists.txt delete mode 100644 examples/pico_motor_shim/sequence/demo.cpp delete mode 100644 examples/pico_motor_shim/song/CMakeLists.txt create mode 100644 micropython/examples/pico_motor_shim/README.md diff --git a/examples/motor2040/README.md b/examples/motor2040/README.md index ad152b7b..17b07b81 100644 --- a/examples/motor2040/README.md +++ b/examples/motor2040/README.md @@ -5,7 +5,6 @@ - [Multiple Motors](#multiple-motors) - [Motor Cluster](#motor-cluster) - [Motor Wave](#motor-wave) - - [Stop Motors](#stop-motors) - [Function Examples](#function-examples) - [Read Sensors](#read-sensors) - [Read Encoders](#read-encoders) @@ -28,25 +27,25 @@ ## Motor Examples ### Single Motor -[motor2040_single_motor.py](motor2040_single_motor.py) +[motor2040_single_motor.cpp](motor2040_single_motor.cpp) Demonstrates how to create a Motor object and control it. ### Multiple Motors -[motor2040_multiple_motors.py](motor2040_multiple_motors.py) +[motor2040_multiple_motors.cpp](motor2040_multiple_motors.cpp) Demonstrates how to create multiple Motor objects and control them together. ### Motor Cluster -[motor2040_motor_cluster.py](motor2040_motor_cluster.py) +[motor2040_motor_cluster.cpp](motor2040_motor_cluster.cpp) Demonstrates how to create a MotorCluster object to control multiple motors at once. ### Motor Wave -[motor2040_motor_wave.py](motor2040_motor_wave.py) +[motor2040_motor_wave.cpp](motor2040_motor_wave.cpp) An example of applying a wave pattern to a group of motors and the LEDs. @@ -54,26 +53,26 @@ An example of applying a wave pattern to a group of motors and the LEDs. ## Function Examples ### Read Sensors -[motor2040_read_sensors.py](motor2040_read_sensors.py) +[motor2040_read_sensors.cpp](motor2040_read_sensors.cpp) Shows how to initialise and read the 2 external and 6 internal sensors of Motor 2040. ### Read Encoders -[motor2040_read_encoders.py](motor2040_read_encoders.py) +[motor2040_read_encoders.cpp](motor2040_read_encoders.cpp) Demonstrates how to read the angles of Motor 2040's four encoders. ### Motor Profiler -[motor2040_motor_profiler.py](motor2040_motor_profiler.py) +[motor2040_motor_profiler.cpp](motor2040_motor_profiler.cpp) A program that profiles the speed of a motor across its PWM duty cycle range using the attached encoder for feedback. ### LED Rainbow -[motor2040_led_rainbow.py](motor2040_led_rainbow.py) +[motor2040_led_rainbow.cpp](motor2040_led_rainbow.cpp) Displays a rotating rainbow pattern on the Motor 2040's onboard LED. @@ -81,36 +80,37 @@ Displays a rotating rainbow pattern on the Motor 2040's onboard LED. ## Control Examples ### Position Control -[motor2040_position_control.py](motor2040_position_control.py) +[motor2040_position_control.cpp](motor2040_position_control.cpp) An example of how to move a motor smoothly between random positions, with the help of it's attached encoder and PID control. ### Velocity Control -[motor2040_velocity_control.py](motor2040_velocity_control.py) +[motor2040_velocity_control.cpp](motor2040_velocity_control.cpp) An example of how to drive a motor smoothly between random speeds, with the help of it's attached encoder and PID control. ### Position on Velocity Control -[motor2040_position_on_velocity_control.py](motor2040_position_on_velocity_control.py) +[motor2040_position_on_velocity_control.cpp](motor2040_position_on_velocity_control.cpp) An example of how to move a motor smoothly between random positions, with velocity limits, with the help of it's attached encoder and PID control. ### Reactive Encoder -[motor2040_reactive_encoder.py](motor2040_reactive_encoder.py) +[motor2040_reactive_encoder.cpp](motor2040_reactive_encoder.cpp) A demonstration of how a motor with an encoder can be used as a programmable rotary encoder for user input, with force-feedback for arbitrary detents and end stops. ### Quad Position Wave -[motor2040_quad_position_wave.py](motor2040_quad_position_wave.py) +[motor2040_quad_position_wave.cpp](motor2040_quad_position_wave.cpp) A demonstration of driving all four of Motor 2040's motor outputs between positions, with the help of their attached encoders and PID control. + ### Quad Velocity Sequence -[motor2040_quad_velocity_sequence.py](motor2040_quad_velocity_sequence.py) +[motor2040_quad_velocity_sequence.cpp](motor2040_quad_velocity_sequence.cpp) A demonstration of driving all four of Motor 2040's motor outputs through a sequence of velocities, with the help of their attached encoders and PID control. @@ -118,18 +118,18 @@ A demonstration of driving all four of Motor 2040's motor outputs through a sequ ## Tuning Examples ### Position Tuning -[motor2040_position_tuning.py](motor2040_position_tuning.py) +[motor2040_position_tuning.cpp](motor2040_position_tuning.cpp) A program to aid in the discovery and tuning of motor PID values for position control. It does this by commanding the motor to move repeatedly between two setpoint angles and plots the measured response. ### Velocity Tuning -[motor2040_velocity_tuning.py](motor2040_velocity_tuning.py) +[motor2040_velocity_tuning.cpp](motor2040_velocity_tuning.cpp) A program to aid in the discovery and tuning of motor PID values for velocity control. It does this by commanding the motor to drive repeatedly between two setpoint speeds and plots the measured response. ### Position on Velocity Tuning -[motor2040_position_on_velocity_tuning.py](motor2040_position_on_velocity_tuning.py) +[motor2040_position_on_velocity_tuning.cpp](motor2040_position_on_velocity_tuning.cpp) A program to aid in the discovery and tuning of motor PID values for position on velocity control. It does this by commanding the motor to move repeatedly between two setpoint angles and plots the measured response. diff --git a/examples/motor2040/motor2040_position_control.cpp b/examples/motor2040/motor2040_position_control.cpp index 99e94a0c..925d336a 100644 --- a/examples/motor2040/motor2040_position_control.cpp +++ b/examples/motor2040/motor2040_position_control.cpp @@ -31,7 +31,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_position_on_velocity_control.cpp b/examples/motor2040/motor2040_position_on_velocity_control.cpp index 43c76a6b..3bd8c9a8 100644 --- a/examples/motor2040/motor2040_position_on_velocity_control.cpp +++ b/examples/motor2040/motor2040_position_on_velocity_control.cpp @@ -31,7 +31,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_position_on_velocity_tuning.cpp b/examples/motor2040/motor2040_position_on_velocity_tuning.cpp index c2db35a4..04b8b1a4 100644 --- a/examples/motor2040/motor2040_position_on_velocity_tuning.cpp +++ b/examples/motor2040/motor2040_position_on_velocity_tuning.cpp @@ -33,7 +33,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_position_tuning.cpp b/examples/motor2040/motor2040_position_tuning.cpp index 28358262..f1c52d31 100644 --- a/examples/motor2040/motor2040_position_tuning.cpp +++ b/examples/motor2040/motor2040_position_tuning.cpp @@ -33,7 +33,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_quad_position_wave.cpp b/examples/motor2040/motor2040_quad_position_wave.cpp index 8fa2428c..0f9c6d65 100644 --- a/examples/motor2040/motor2040_quad_position_wave.cpp +++ b/examples/motor2040/motor2040_quad_position_wave.cpp @@ -23,7 +23,7 @@ constexpr float GEAR_RATIO = 50.0f; constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_quad_velocity_sequence.cpp b/examples/motor2040/motor2040_quad_velocity_sequence.cpp index dd4598c3..6da67661 100644 --- a/examples/motor2040/motor2040_quad_velocity_sequence.cpp +++ b/examples/motor2040/motor2040_quad_velocity_sequence.cpp @@ -29,7 +29,7 @@ constexpr float GEAR_RATIO = 50.0f; constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_reactive_encoder.cpp b/examples/motor2040/motor2040_reactive_encoder.cpp index 8a2317f2..2c282bc1 100644 --- a/examples/motor2040/motor2040_reactive_encoder.cpp +++ b/examples/motor2040/motor2040_reactive_encoder.cpp @@ -33,7 +33,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_single_motor.cpp b/examples/motor2040/motor2040_single_motor.cpp index bda6ed44..d93c3ed3 100644 --- a/examples/motor2040/motor2040_single_motor.cpp +++ b/examples/motor2040/motor2040_single_motor.cpp @@ -17,7 +17,7 @@ const uint STEPS = 10; // The time in milliseconds between each step of the sequence const uint STEPS_INTERVAL_MS = 500; -// How far from zero to move the motor when sweeping +// How far from zero to drive the motor when sweeping constexpr float SPEED_EXTENT = 1.0f; diff --git a/examples/motor2040/motor2040_velocity_control.cpp b/examples/motor2040/motor2040_velocity_control.cpp index 09e18509..64e17c61 100644 --- a/examples/motor2040/motor2040_velocity_control.cpp +++ b/examples/motor2040/motor2040_velocity_control.cpp @@ -31,7 +31,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/motor2040/motor2040_velocity_tuning.cpp b/examples/motor2040/motor2040_velocity_tuning.cpp index 1348fe54..ab7ddc76 100644 --- a/examples/motor2040/motor2040_velocity_tuning.cpp +++ b/examples/motor2040/motor2040_velocity_tuning.cpp @@ -33,7 +33,7 @@ constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; const Direction DIRECTION = NORMAL_DIR; // The scaling to apply to the motor's speed to match its real-world speed -float SPEED_SCALE = 5.4f; +constexpr float SPEED_SCALE = 5.4f; // How many times to update the motor per second const uint UPDATES = 100; diff --git a/examples/pico_motor_shim/CMakeLists.txt b/examples/pico_motor_shim/CMakeLists.txt index 91e9e29f..04d2d981 100644 --- a/examples/pico_motor_shim/CMakeLists.txt +++ b/examples/pico_motor_shim/CMakeLists.txt @@ -1,3 +1,4 @@ -add_subdirectory(balance) -add_subdirectory(sequence) -add_subdirectory(song) \ No newline at end of file +include(motorshim_dual_motors.cmake) +include(motorshim_movements.cmake) +include(motorshim_motor_song.cmake) +include(motorshim_single_motor.cmake) \ No newline at end of file diff --git a/examples/pico_motor_shim/README.md b/examples/pico_motor_shim/README.md new file mode 100644 index 00000000..5fd51595 --- /dev/null +++ b/examples/pico_motor_shim/README.md @@ -0,0 +1,33 @@ +# Pico Motor Shim C++ Examples + +- [Examples](#examples) + - [Single Motor](#single-motor) + - [Dual Motors](#dual-motors) + - [Movements](#movements) + - [Motor Song](#motor-song) + + +## Motor Examples + +### Single Motor +[motorshim_single_motor.cpp](motorshim_single_motor.cpp) + +Demonstrates how to create a Motor object and control it. + + +### Dual Motors +[motorshim_dual_motors.cpp](motorshim_dual_motors.cpp) + +Demonstrates how to create two Motor objects and control them together. + + +### Movements +[motorshim_movements.cpp](motorshim_movements.cpp) + +An example of how to perform simple movements of a 2-wheeled driving robot. + + +### Motor Song +[motorshim_motor_song.cpp](motorshim_motor_song.cpp) + +A fun example of how to change a motor's frequency to have it play a song. diff --git a/examples/pico_motor_shim/motorshim_dual_motors.cmake b/examples/pico_motor_shim/motorshim_dual_motors.cmake new file mode 100644 index 00000000..7ef38692 --- /dev/null +++ b/examples/pico_motor_shim/motorshim_dual_motors.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME motorshim_dual_motors) +add_executable(${OUTPUT_NAME} motorshim_dual_motors.cpp) + +target_link_libraries(${OUTPUT_NAME} + pico_stdlib + pico_motor_shim + ) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_motor_shim/motorshim_dual_motors.cpp b/examples/pico_motor_shim/motorshim_dual_motors.cpp new file mode 100644 index 00000000..4bae2021 --- /dev/null +++ b/examples/pico_motor_shim/motorshim_dual_motors.cpp @@ -0,0 +1,105 @@ +#include +#include "pico/stdlib.h" + +#include "pico_motor_shim.hpp" + +/* +Demonstrates how to create two Motor objects and control them together. +*/ + +using namespace motor; + +// How many sweeps of the motors to perform +const uint SWEEPS = 2; + +// The number of discrete sweep steps +const uint STEPS = 10; + +// The time in milliseconds between each step of the sequence +const uint STEPS_INTERVAL_MS = 500; + +// How far from zero to drive the motors when sweeping +constexpr float SPEED_EXTENT = 1.0f; + +// Create an array of motor pointers +const pin_pair motor_pins[] = {pico_motor_shim::MOTOR_1, pico_motor_shim::MOTOR_2}; +const uint NUM_MOTORS = count_of(motor_pins); +Motor *motors[NUM_MOTORS]; + + +int main() { + stdio_init_all(); + + // Fill the array of motors, and initialise them. Up to 8 motors can be created + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m] = new Motor(motor_pins[m]); + motors[m]->init(); + } + + // Uncomment the below lines to reverse + // the driving direction of a motor + // motors[0].direction(REVERSED_DIR); + // motors[1].direction(REVERSED_DIR); + + // Enable all motors + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->enable(); + } + sleep_ms(2000); + + // Drive at full positive + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->full_positive(); + } + sleep_ms(2000); + + // Stop all moving + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->stop(); + } + sleep_ms(2000); + + // Drive at full negative + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->full_negative(); + } + sleep_ms(2000); + + // Coast all to a gradual stop + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->coast(); + } + sleep_ms(2000); + + // Do a sine speed sweep + for(auto j = 0u; j < SWEEPS; j++) { + for(auto i = 0u; i < 360; i++) { + float speed = sin(((float)i * (float)M_PI) / 180.0f) * SPEED_EXTENT; + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->speed(speed); + } + sleep_ms(20); + } + } + + // Do a stepped speed sweep + for(auto j = 0u; j < SWEEPS; j++) { + for(auto i = 0u; i < STEPS; i++) { + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->to_percent(i, 0, STEPS, 0.0 - SPEED_EXTENT, SPEED_EXTENT); + } + sleep_ms(STEPS_INTERVAL_MS); + } + for(auto i = 0u; i < STEPS; i++) { + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->to_percent(i, STEPS, 0, 0.0 - SPEED_EXTENT, SPEED_EXTENT); + } + sleep_ms(STEPS_INTERVAL_MS); + } + } + + // Disable the motors + for(auto m = 0u; m < NUM_MOTORS; m++) { + motors[m]->disable(); + } +} diff --git a/examples/pico_motor_shim/motorshim_motor_song.cmake b/examples/pico_motor_shim/motorshim_motor_song.cmake new file mode 100644 index 00000000..3f2518fa --- /dev/null +++ b/examples/pico_motor_shim/motorshim_motor_song.cmake @@ -0,0 +1,13 @@ +set(OUTPUT_NAME motorshim_motor_song) +add_executable(${OUTPUT_NAME} motorshim_motor_song.cpp) + +target_link_libraries(${OUTPUT_NAME} + pico_stdlib + pico_motor_shim + button + ) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_motor_shim/song/demo.cpp b/examples/pico_motor_shim/motorshim_motor_song.cpp similarity index 53% rename from examples/pico_motor_shim/song/demo.cpp rename to examples/pico_motor_shim/motorshim_motor_song.cpp index 584dd3a0..134295fc 100644 --- a/examples/pico_motor_shim/song/demo.cpp +++ b/examples/pico_motor_shim/motorshim_motor_song.cpp @@ -1,13 +1,13 @@ #include -#include "pico_motor_shim.hpp" +#include "pico/stdlib.h" -#include "common/pimoroni_common.hpp" -#include "motor.hpp" +#include "pico_motor_shim.hpp" #include "button.hpp" /* -Play a song using a motor! Works by setting the PWM duty cycle to 50% and changing the frequency on the fly. -Plug a motor into connector 1, and press "A" to start the song playing (does not loop). Press the button again will stop the song early. +A fun example of how to change a motor's frequency to have it play a song. + +Press "A" to start or stop the song. */ using namespace pimoroni; @@ -21,43 +21,41 @@ constexpr float SONG[] = {1397, 1397, 1319, 1397, 698, 0, 698, 0, 1047, 0, 1175, 1319, 1397, 1319, 1175, 1047, 1175, 1047, 932, 880, 932, 880, 784, 698, 784, 698, 659, 587, 523, 587, 659, 698, 784, 932, 880, 784, 880, 698, 0, 698}; -constexpr uint SONG_LENGTH = sizeof(SONG) / sizeof(float); +constexpr uint SONG_LENGTH = count_of(SONG); -//The time (in milliseconds) to play each note for. Change this to make the song play faster or slower -static const uint NOTE_DURATION_MS = 150; +// The time (in seconds) to play each note for. Change this to make the song play faster or slower +const uint NOTE_DURATION_MS = 150; -//Uncomment this lineto have the song be played without the motor turning -//Note, this will affect the audio quality of the sound produced -//#define STATIONARY_PLAYBACK +// The time (in microseconds) between each direction switch of the motor when using STATIONARY_PLAYBACK +const uint STATIONARY_TOGGLE_US = 2000; -//The time (in microseconds) between each direction switch of the motor when using STATIONARY_PLAYBACK -static const uint STATIONARY_TOGGLE_US = 2000; +// Whether to play the song with or without the motors spinning +const bool STATIONARY_PLAYBACK = false; -//Uncomment this line to use the fast decay (coasting) motor mode. -//This seems to produce a louder sound with STATIONARY_PLAYBACK enabled, but will make movement poorer when STATIONARY_PLAYBACK is disabled -//#define USE_FAST_DECAY +// The motor decay mode to use, either FAST_DECAY (0) or SLOW_DECAY (1). Affects the song's quality +const DecayMode DECAY_MODE = SLOW_DECAY; -Button button_a(pico_motor_shim::BUTTON_A, Polarity::ACTIVE_LOW, 0); +// Create two motor objects with a given decay mode Motor motor_1(pico_motor_shim::MOTOR_1); Motor motor_2(pico_motor_shim::MOTOR_2); -static bool button_toggle = false; +// Create the user button +Button button_a(pico_motor_shim::BUTTON_A, Polarity::ACTIVE_LOW, 0); -/** - * Checks if the button has been pressed, toggling a value that is also returned. - */ +// Variable for recording if the button has been toggled +// Starting as true makes the song play automatically +static bool button_toggle = true; + +// Checks if the button has been pressed, toggling a value that is also returned. bool check_button_toggle() { - bool button_pressed = button_a.read(); - if(button_pressed) { + if(button_a.read()) { button_toggle = !button_toggle; } return button_toggle; } -/** - * The entry point of the program. - */ + int main() { stdio_init_all(); @@ -65,28 +63,30 @@ int main() { gpio_init(PICO_DEFAULT_LED_PIN); gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); - #ifdef USE_FAST_DECAY - motor_1.decay_mode(FAST_DECAY); - motor_2.decay_mode(FAST_DECAY); - #endif + // Se the two motor's decay modes + motor_1.decay_mode(DECAY_MODE); + motor_2.decay_mode(DECAY_MODE); - //Initialise the motor + // Initialise the motors if(!motor_1.init() || !motor_2.init()) { printf("Cannot initialise the motors. Check the provided parameters\n"); return 0; } while(true) { + // Has the button been toggled? if(check_button_toggle()) { - //Turn the Pico's LED on to show that the song has started + // Turn the Pico's LED on to show that the song has started gpio_put(PICO_DEFAULT_LED_PIN, true); - //Play the song + // Play the song for(uint i = 0; i < SONG_LENGTH && check_button_toggle(); i++) { - if(motor_1.frequency(SONG[i]) && motor_2.frequency(SONG[i])) { - #ifdef STATIONARY_PLAYBACK - //Set the motors to 50% duty cycle to play the note, but alternate - //the direction so that the motor does not actually spin + // Get the frequency of the note and attempt to set the motors to it + float freq = SONG[i]; + if(motor_1.frequency(freq) && motor_2.frequency(freq)) { + if(STATIONARY_PLAYBACK) { + // Set the motors to 50% duty cycle to play the note, but alternate + // the direction so that the motor does not actually spin uint t = 0; while(t < NOTE_DURATION_MS * 1000) { motor_1.duty(0.5f); @@ -99,15 +99,16 @@ int main() { sleep_us(STATIONARY_TOGGLE_US); t += STATIONARY_TOGGLE_US; } - #else - //Set the motors to 50% duty cycle to play the note + } + else { + // Set the motors to 50% duty cycle to play the note motor_1.duty(0.5f); motor_2.duty(0.5f); sleep_ms(NOTE_DURATION_MS); - #endif + } } else { - //The frequency was invalid, so we are treating that to mean this is a pause note + // The frequency was invalid, so we are treating that to mean this is a pause note motor_1.stop(); motor_2.stop(); sleep_ms(NOTE_DURATION_MS); @@ -115,7 +116,7 @@ int main() { } button_toggle = false; - //The song has finished, so turn off the Pico's LED and disable the motors + // The song has finished, so turn off the Pico's LED and disable the motors gpio_put(PICO_DEFAULT_LED_PIN, false); motor_1.disable(); motor_2.disable(); diff --git a/examples/pico_motor_shim/motorshim_movements.cmake b/examples/pico_motor_shim/motorshim_movements.cmake new file mode 100644 index 00000000..e606e2da --- /dev/null +++ b/examples/pico_motor_shim/motorshim_movements.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME motorshim_movements) +add_executable(${OUTPUT_NAME} motorshim_movements.cpp) + +target_link_libraries(${OUTPUT_NAME} + pico_stdlib + pico_motor_shim + ) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_motor_shim/motorshim_movements.cpp b/examples/pico_motor_shim/motorshim_movements.cpp new file mode 100644 index 00000000..aea27567 --- /dev/null +++ b/examples/pico_motor_shim/motorshim_movements.cpp @@ -0,0 +1,126 @@ +#include +#include "pico/stdlib.h" + +#include "pico_motor_shim.hpp" + +/* +An example of how to perform simple movements of a 2-wheeled driving robot. +*/ + +using namespace motor; + +// The scaling to apply to each motor's speed to match its real-world speed +constexpr float SPEED_SCALE = 5.4f; + +// The speed to drive the wheels at, from 0.0 to SPEED_SCALE +constexpr float DRIVING_SPEED = SPEED_SCALE; + + +// Create the left and right motors with a given speed scale +// Swap the numbers and directions if this is different to your setup +Motor left(pico_motor_shim::MOTOR_1, NORMAL_DIR, SPEED_SCALE); +Motor right(pico_motor_shim::MOTOR_2, REVERSED_DIR, SPEED_SCALE); + + +// Helper functions for driving in common directions +void forward(float speed=DRIVING_SPEED) { + left.speed(speed); + right.speed(speed); +} + +void backward(float speed=DRIVING_SPEED) { + left.speed(-speed); + right.speed(-speed); +} + +void turn_left(float speed=DRIVING_SPEED) { + left.speed(-speed); + right.speed(speed); +} + +void turn_right(float speed=DRIVING_SPEED) { + left.speed(speed); + right.speed(-speed); +} + +void curve_forward_left(float speed=DRIVING_SPEED) { + left.speed(0.0); + right.speed(speed); +} + +void curve_forward_right(float speed=DRIVING_SPEED) { + left.speed(speed); + right.speed(0.0); +} + +void curve_backward_left(float speed=DRIVING_SPEED) { + left.speed(0.0); + right.speed(-speed); +} + +void curve_backward_right(float speed=DRIVING_SPEED) { + left.speed(-speed); + right.speed(0.0); +} + +void stop() { + left.stop(); + right.stop(); +} + +void coast() { + left.coast(); + right.coast(); +} + + +int main() { + stdio_init_all(); + + // Initialise the motors + if(!left.init() || !right.init()) { + printf("Cannot initialise the motors. Check the provided parameters\n"); + return 0; + } + + // Demo each of the move methods + forward(); + sleep_ms(1000); + + backward(); + sleep_ms(1000); + + curve_forward_right(); + sleep_ms(1000); + + curve_forward_left(); + sleep_ms(1000); + + turn_right(); + sleep_ms(1000); + + forward(0.5 * DRIVING_SPEED); // Half speed + sleep_ms(1000); + + turn_left(0.5 * DRIVING_SPEED); // Half speed + sleep_ms(1000); + + curve_backward_right(0.75 * DRIVING_SPEED); // Three quarters speed + sleep_ms(1000); + + forward(); // Full speed + sleep_ms(500); + + coast(); // Come to a halt gently + sleep_ms(1000); + + forward(); + sleep_ms(500); + + stop(); // Apply the brakes + sleep_ms(1000); + + // Disable the motors + left.disable(); + right.disable(); +} diff --git a/examples/pico_motor_shim/motorshim_single_motor.cmake b/examples/pico_motor_shim/motorshim_single_motor.cmake new file mode 100644 index 00000000..18562424 --- /dev/null +++ b/examples/pico_motor_shim/motorshim_single_motor.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME motorshim_single_motor) +add_executable(${OUTPUT_NAME} motorshim_single_motor.cpp) + +target_link_libraries(${OUTPUT_NAME} + pico_stdlib + pico_motor_shim + ) + +# enable usb output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_motor_shim/motorshim_single_motor.cpp b/examples/pico_motor_shim/motorshim_single_motor.cpp new file mode 100644 index 00000000..9df3a840 --- /dev/null +++ b/examples/pico_motor_shim/motorshim_single_motor.cpp @@ -0,0 +1,78 @@ +#include +#include "pico/stdlib.h" + +#include "pico_motor_shim.hpp" + +/* +Demonstrates how to create a Motor object and control it. +*/ + +using namespace motor; + +// How many sweeps of the motor to perform +const uint SWEEPS = 2; + +// The number of discrete sweep steps +const uint STEPS = 10; + +// The time in milliseconds between each step of the sequence +const uint STEPS_INTERVAL_MS = 500; + +// How far from zero to drive the motor when sweeping +constexpr float SPEED_EXTENT = 1.0f; + + +// Create a motor +Motor m = Motor(pico_motor_shim::MOTOR_1); + + +int main() { + stdio_init_all(); + + // Initialise the motor + m.init(); + + // Enable the motor + m.enable(); + sleep_ms(2000); + + // Drive at full positive + m.full_positive(); + sleep_ms(2000); + + // Stop moving + m.stop(); + sleep_ms(2000); + + // Drive at full negative + m.full_negative(); + sleep_ms(2000); + + // Coast to a gradual stop + m.coast(); + sleep_ms(2000); + + + // Do a sine speed sweep + for(auto j = 0u; j < SWEEPS; j++) { + for(auto i = 0u; i < 360; i++) { + m.speed(sin(((float)i * (float)M_PI) / 180.0f) * SPEED_EXTENT); + sleep_ms(20); + } + } + + // Do a stepped speed sweep + for(auto j = 0u; j < SWEEPS; j++) { + for(auto i = 0u; i < STEPS; i++) { + m.to_percent(i, 0, STEPS, 0.0 - SPEED_EXTENT, SPEED_EXTENT); + sleep_ms(STEPS_INTERVAL_MS); + } + for(auto i = 0u; i < STEPS; i++) { + m.to_percent(i, STEPS, 0, 0.0 - SPEED_EXTENT, SPEED_EXTENT); + sleep_ms(STEPS_INTERVAL_MS); + } + } + + // Disable the motor + m.disable(); +} diff --git a/examples/pico_motor_shim/sequence/CMakeLists.txt b/examples/pico_motor_shim/sequence/CMakeLists.txt deleted file mode 100644 index eafeebe8..00000000 --- a/examples/pico_motor_shim/sequence/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(OUTPUT_NAME motor_shim_sequence) - -add_executable( - ${OUTPUT_NAME} - demo.cpp -) - -# enable usb output, disable uart output -pico_enable_stdio_usb(${OUTPUT_NAME} 1) -pico_enable_stdio_uart(${OUTPUT_NAME} 1) - -# Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_motor_shim motor button) - -# create map/bin/hex file etc. -pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_motor_shim/sequence/demo.cpp b/examples/pico_motor_shim/sequence/demo.cpp deleted file mode 100644 index 61ffda82..00000000 --- a/examples/pico_motor_shim/sequence/demo.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include "pico_motor_shim.hpp" - -#include "common/pimoroni_common.hpp" -#include "motor.hpp" -#include "button.hpp" - -/* -Program showing how the two motors of the Pico Motor Shim can be perform a sequence of movements. -Press "A" to start and stop the movement sequence -*/ - -using namespace pimoroni; -using namespace motor; - -static constexpr float TOP_SPEED = 1.0f; //A value between 0 and 1 -static const uint32_t ACCELERATE_TIME_MS = 2000; -static const uint32_t WAIT_TIME_MS = 1000; -static const uint32_t STOP_TIME_MS = 1000; - - -Button button_a(pico_motor_shim::BUTTON_A, Polarity::ACTIVE_LOW, 0); - -Motor motor_1(pico_motor_shim::MOTOR_1);//, Motor::DEFAULT_PWM_FREQUENCY, Motor::DEFAULT_DECAY_MODE); -Motor motor_2(pico_motor_shim::MOTOR_2);//, Motor::DEFAULT_PWM_FREQUENCY, Motor::DEFAULT_DECAY_MODE); - -static bool button_toggle = false; - -/** - * Checks if the button has been pressed, toggling a value that is also returned. - */ -bool check_button_toggle() { - bool button_pressed = button_a.read(); - if(button_pressed) { - button_toggle = !button_toggle; - } - return button_toggle; -} - -/** - * Waits for a given amount of time (in milliseconds). - * Exits early if the user presses the button to stop the sequence, returning false. - */ -bool wait_for(uint32_t duration_ms) { - uint32_t start_time = millis(); - uint32_t ellapsed = 0; - - //Loops until the duration has elapsed, checking the button state every millisecond - while(ellapsed < duration_ms) { - if(!check_button_toggle()) - return false; - - sleep_ms(1); - ellapsed = millis() - start_time; - } - return true; -} - -/** - * Accelerate/Decelerate the motors from their current speed to the target speed over the given amount of time (in milliseconds). - * Exits early if the user presses the button to stop the sequence, returning false. - */ -bool accelerate_over(float left_speed, float right_speed, uint32_t duration_ms) { - uint32_t start_time = millis(); - uint32_t ellapsed = 0; - - //Get the current motor speeds - float last_left = motor_1.speed(); - float last_right = motor_2.speed(); - - //Loops until the duration has elapsed, checking the button state every millisecond, and updating motor speeds - while(ellapsed <= duration_ms) { - if(!check_button_toggle()) - return false; - - //Calculate and set the new motor speeds - float percentage = (float)ellapsed / (float)duration_ms; - motor_1.speed(((left_speed - last_left) * percentage) + last_left); - motor_2.speed(((right_speed - last_right) * percentage) + last_right); - - sleep_ms(1); - ellapsed = millis() - start_time; - } - - //Set the final motor speeds as loop may not reach 100% - motor_1.speed(left_speed); - motor_2.speed(right_speed); - - return true; -} - -/** - * The function that performs the driving sequence. - * Exits early if the user presses the button to stop the sequence, returning false. - */ -bool sequence() { - printf("accelerate forward\n"); - if(!accelerate_over(-TOP_SPEED, TOP_SPEED, ACCELERATE_TIME_MS)) - return false; //Early exit if the button was toggled - - printf("driving forward\n"); - if(!wait_for(WAIT_TIME_MS)) - return false; //Early exit if the button was toggled - - printf("deccelerate forward\n"); - if(!accelerate_over(0.0f, 0.0f, ACCELERATE_TIME_MS)) - return false; //Early exit if the button was toggled - - printf("stop\n"); - motor_1.stop(); - motor_2.stop(); - if(!wait_for(STOP_TIME_MS)) - return false; //Early exit if the button was toggled - - printf("accelerate turn left\n"); - if(!accelerate_over(TOP_SPEED * 0.5f, TOP_SPEED * 0.5f, ACCELERATE_TIME_MS * 0.5f)) - return false; //Early exit if the button was toggled - - printf("turning left\n"); - if(!wait_for(WAIT_TIME_MS)) - return false; //Early exit if the button was toggled - - printf("deccelerate turn left\n"); - if(!accelerate_over(0.0f, 0.0f, ACCELERATE_TIME_MS * 0.5f)) - return false; //Early exit if the button was toggled - - printf("stop\n"); - motor_1.stop(); - motor_2.stop(); - if(!wait_for(STOP_TIME_MS)) - return false; //Early exit if the button was toggled - - //Signal that the sequence completed successfully - return true; -} - -/** - * The entry point of the program. - */ -int main() { - stdio_init_all(); - - //Initialise the LED. We use this to indicate that the sequence is running. - gpio_init(PICO_DEFAULT_LED_PIN); - gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); - - //Initialise the two motors - if(!motor_1.init() || !motor_2.init()) { - printf("Cannot initialise motors. Check the provided parameters\n"); - return 0; - } - - while(true) { - //Has the user has pressed the button to start the sequence - if(check_button_toggle()) { - - //Turn the Pico's LED on to show that the sequence has started - gpio_put(PICO_DEFAULT_LED_PIN, true); - - //Run the sequence in a perpetual loop, exiting early if the button is pressed again - while(sequence()); - } - - //The sequence loop has ended, so turn off the Pico's LED and disable the motors - gpio_put(PICO_DEFAULT_LED_PIN, false); - motor_1.disable(); - motor_2.disable(); - } - return 0; -} diff --git a/examples/pico_motor_shim/song/CMakeLists.txt b/examples/pico_motor_shim/song/CMakeLists.txt deleted file mode 100644 index b5adb316..00000000 --- a/examples/pico_motor_shim/song/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(OUTPUT_NAME motor_shim_song) - -add_executable( - ${OUTPUT_NAME} - demo.cpp -) - -# enable usb output, disable uart output -pico_enable_stdio_usb(${OUTPUT_NAME} 1) -pico_enable_stdio_uart(${OUTPUT_NAME} 1) - -# Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_motor_shim motor button breakout_msa301) - -# create map/bin/hex file etc. -pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/micropython/examples/pico_motor_shim/README.md b/micropython/examples/pico_motor_shim/README.md new file mode 100644 index 00000000..9bc04211 --- /dev/null +++ b/micropython/examples/pico_motor_shim/README.md @@ -0,0 +1,40 @@ +# Pico Motor Shim C++ Examples + +- [Examples](#examples) + - [Single Motor](#single-motor) + - [Dual Motors](#dual-motors) + - [Movements](#movements) + - [Motor Song](#motor-song) + - [Stop Motors](#stop-motors) + + +## Motor Examples + +### Single Motor +[single_motor.py](single_motor.py) + +Demonstrates how to create a Motor object and control it. + + +### Dual Motors +[dual_motors.py](dual_motors.py) + +Demonstrates how to create two Motor objects and control them together. + + +### Movements +[movements.py](movements.py) + +An example of how to perform simple movements of a 2-wheeled driving robot. + + +### Motor Song +[motor_song.py](motor_song.py) + +A fun example of how to change a motor's frequency to have it play a song. + + +### Stop Motors +[stop_motors.py](motorshim_motor_song.py) + +A simple program that stops the motors. diff --git a/micropython/examples/pico_motor_shim/motor_song.py b/micropython/examples/pico_motor_shim/motor_song.py index 299cb979..0bfc5b3a 100644 --- a/micropython/examples/pico_motor_shim/motor_song.py +++ b/micropython/examples/pico_motor_shim/motor_song.py @@ -6,6 +6,8 @@ from pimoroni import Button """ A fun example of how to change a motor's frequency to have it play a song. + +Press "A" to start or stop the song. """ # This handy dictionary converts notes into frequencies