diff --git a/.vscode/settings.json b/.vscode/settings.json index 5445861278ef6f8fe6dedd99a58a85aaf5e3b6f9..66dd1b5820d9d5c4a599602b87f23eac419e33df 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -228,6 +228,8 @@ "mavlinkdriver", "MEKF", "microcontrollers", + "Microstep", + "Microsteps", "MINC", "miosix", "mkdir", diff --git a/CMakeLists.txt b/CMakeLists.txt index fed706ded258d6c0b5b70991a7271d5948ee55b3..8859335116208741211e067fce992995e81be572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,10 @@ add_executable(test-buzzer src/tests/actuators/test-buzzer.cpp) sbs_target(test-buzzer stm32f429zi_hre_test_stand) add_executable(test-stepper src/tests/actuators/test-stepper.cpp) -sbs_target(test-stepper stm32f429zi_stm32f4discovery) +sbs_target(test-stepper stm32f767zi_nucleo) + +add_executable(test-stepper-pwm src/tests/actuators/test-stepper-pwm.cpp) +sbs_target(test-stepper-pwm stm32f767zi_nucleo) #-----------------------------------------------------------------------------# # Tests - Algorithms # @@ -218,6 +221,9 @@ sbs_target(test-MBLoadCell stm32f407vg_stm32f4discovery) add_executable(test-pwm src/tests/drivers/timer/test-pwm.cpp) sbs_target(test-pwm stm32f429zi_stm32f4discovery) +add_executable(test-counted-pwm src/tests/drivers/timer/test-counted-pwm.cpp) +sbs_target(test-counted-pwm stm32f429zi_stm32f4discovery) + add_executable(test-spi src/tests/drivers/spi/test-spi.cpp) sbs_target(test-spi stm32f407vg_stm32f4discovery) diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 1972d025dbc27ad2fcc6b21c1960876f1da6eb63..43a510b067c0d8d8de3bffa666ed8f14b1ab224a 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -30,6 +30,8 @@ foreach(OPT_BOARD ${BOARDS}) # Actuators ${SBS_BASE}/src/shared/actuators/HBridge/HBridge.cpp ${SBS_BASE}/src/shared/actuators/Servo/Servo.cpp + ${SBS_BASE}/src/shared/actuators/Stepper.cpp + ${SBS_BASE}/src/shared/actuators/StepperPWM.cpp # Algorithms ${SBS_BASE}/src/shared/algorithms/ADA/ADA.cpp @@ -52,6 +54,7 @@ foreach(OPT_BOARD ${BOARDS}) ${SBS_BASE}/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp ${SBS_BASE}/src/shared/drivers/interrupt/external_interrupts.cpp ${SBS_BASE}/src/shared/drivers/timer/PWM.cpp + ${SBS_BASE}/src/shared/drivers/timer/CountedPWM.cpp ${SBS_BASE}/src/shared/drivers/timer/TimestampTimer.cpp ${SBS_BASE}/src/shared/drivers/runcam/Runcam.cpp ${SBS_BASE}/src/shared/drivers/spi/SPITransaction.cpp diff --git a/src/shared/actuators/Stepper.cpp b/src/shared/actuators/Stepper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..00a2166cdaaa011026386b92723ef382614ac00b --- /dev/null +++ b/src/shared/actuators/Stepper.cpp @@ -0,0 +1,163 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Authors: Alberto Nidasio, Emilio Corigliano + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "Stepper.h" + +namespace Boardcore +{ + +Stepper::Stepper(miosix::GpioPin stepPin, miosix::GpioPin directionPin, + float speed, float stepAngle, bool revertDirection, + uint16_t microStep, PinConfiguration pinConfiguration, + miosix::GpioPin enablePin) + : stepPin(stepPin), directionPin(directionPin), speed(speed), + stepAngle(stepAngle), revertDirection(revertDirection), + microStep(microStep), pinConfig(pinConfiguration), enablePin(enablePin), + currentDirection(Direction::CLOCKWISE) +{ + if (this->speed < 0) + this->speed = 0; + + Stepper::setMicroStepping(microStep); + + // Start with the motor disabled + disable(); +} + +void Stepper::enable() +{ + if (pinConfig == PinConfiguration::COMMON_CATHODE) + { + enablePin.low(); + } + else + { + enablePin.high(); + } +} + +void Stepper::disable() +{ + if (pinConfig == PinConfiguration::COMMON_CATHODE) + { + enablePin.high(); + } + else + { + enablePin.low(); + } +} + +void Stepper::setDirection() +{ + // Following the connections written in the stepper-driver datasheet for + // moving the stepper clockwise we have that: + // directionPin high: stepper turns clockwise; + // directionPin low: stepper turns counterclockwise; + // + // The revertDirection flag is used just for accounting for an inverted + // polarity in the configuration. + if (currentDirection == Direction::CLOCKWISE) + { + // To set the stepper-driver to turn CLOCKWISE we have to set the pin + // high in common cathode mode and low in common anode mode (the + // resulting potential difference should lead to a high logic value) + if ((!revertDirection && + (pinConfig == PinConfiguration::COMMON_CATHODE)) || + (revertDirection && (pinConfig == PinConfiguration::COMMON_ANODE))) + { + directionPin.high(); + } + else + { + directionPin.low(); + } + } + else + { + // To set the stepper-driver to turn COUNTER-CLOCKWISE we have to set + // the pin low in common cathode mode and high in common anode mode (the + // resulting potential difference should lead to a low logic value) + if ((!revertDirection && + (pinConfig == PinConfiguration::COMMON_CATHODE)) || + (revertDirection && (pinConfig == PinConfiguration::COMMON_ANODE))) + { + directionPin.low(); + } + else + { + directionPin.high(); + } + } +} + +void Stepper::move(int16_t steps) +{ + if (speed == 0) + return; + + unsigned int halfStepDelay = 1e6 / (speed * 360 / stepAngle * microStep); + int16_t stepsAbs; + if (steps > 0) + { + // Go forward + currentDirection = Direction::CLOCKWISE; + setDirection(); + stepsAbs = steps; + } + else + { + // Go backwards + currentDirection = Direction::COUNTER_CLOCKWISE; + setDirection(); + stepsAbs = -steps; + } + + miosix::delayUs(halfStepDelay); + + for (int i = 0; i < stepsAbs; i++) + { + if (pinConfig == PinConfiguration::COMMON_CATHODE) + { + stepPin.high(); + } + else + { + stepPin.low(); + } + + miosix::delayUs(halfStepDelay); + + if (pinConfig == PinConfiguration::COMMON_CATHODE) + { + stepPin.low(); + } + else + { + stepPin.high(); + } + miosix::delayUs(halfStepDelay); + } + + currentPositionDeg += steps * stepAngle / microStep; +} + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/actuators/Stepper.h b/src/shared/actuators/Stepper.h index b08639af1075f1d3534df3fd5543f3fe0e5e6b25..d93a5c7dcfc3da67c3b1eeb3347c5f949c3dbc23 100644 --- a/src/shared/actuators/Stepper.h +++ b/src/shared/actuators/Stepper.h @@ -1,5 +1,5 @@ /* Copyright (c) 2022 Skyward Experimental Rocketry - * Author: Alberto Nidasio + * Authors: Alberto Nidasio, Emilio Corigliano * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,13 +32,10 @@ namespace Boardcore class Stepper { public: - enum class Microstep + enum class PinConfiguration { - MICROSTEP_1, - MICROSTEP_2, - MICROSTEP_4, - MICROSTEP_8, - MICROSTEP_16, + COMMON_ANODE, ///< All + signals connected to Vdd (3v3) + COMMON_CATHODE, ///< All - signals connected to Gnd }; /** @@ -53,14 +50,12 @@ public: * @param stepAngle Angle covered by one motor step. * @param revertDirection Whether or not revert the direction signal. */ - Stepper(miosix::GpioPin stepPin, miosix::GpioPin directionPin, - float speed = 1, float stepAngle = 1.8, - bool revertDirection = false, - Microstep microstep = Microstep::MICROSTEP_1, - miosix::GpioPin enablePin = MockGpioPin(), - miosix::GpioPin ms1Pin = MockGpioPin(), - miosix::GpioPin ms2Pin = MockGpioPin(), - miosix::GpioPin ms3Pin = MockGpioPin()); + Stepper( + miosix::GpioPin stepPin, miosix::GpioPin directionPin, float speed = 1, + float stepAngle = 1.8, bool revertDirection = false, + uint16_t microStep = 1, + PinConfiguration pinConfiguration = PinConfiguration::COMMON_CATHODE, + miosix::GpioPin enablePin = MockGpioPin()); void enable(); @@ -68,24 +63,28 @@ public: /** * @brief Changes the stepper motor speed. + * + * @param speed Speed in rotation per second. [rev/s] */ - void setSpeed(float speed); + virtual void setSpeed(float speed); /** - * @brief Set the motor driver microstepping configuration. + * @brief Set the motor driver micro stepping configuration. + * + * This method has no effect if micro steps are changed via physical + * switches. */ - void setMicrostepping(Microstep microstep); + virtual void setMicroStepping(uint16_t microStep); /** - * @brief Zeros the driver's internal position. - * + * @brief Overrides the driver's internal position counter. */ - void zeroPosition(); + void zeroPosition(float degrees = 0); /** * @brief Move the stepper motor by the specified amount of steps. */ - void move(int32_t steps); + virtual void move(int16_t steps); /** * @brief Move the stepper motor by the specified amount of degrees. @@ -97,168 +96,89 @@ public: * * Note that this function uses delayUs, thus it blocks execution based on * the stepper motor speed and movement. - * - * @param degrees Position in steps. */ - void setPosition(int32_t steps); + virtual void setPosition(int16_t steps); /** * @brief Set the position of the stepper motor. * * Note that this function uses delayUs, thus it blocks execution based on * the stepper motor speed and movement. - * - * @param degrees Position in degrees. */ void setPositionDeg(float degrees); - uint32_t getCurrentPosition(); + int16_t getCurrentPosition(); - float getCurrentDegPosition(); + /** + * @brief Returns the current absolute position of the stepper in degrees + * [deg]. + */ + virtual float getCurrentDegPosition(); -private: - int getMicrosteppingValue(); +protected: + /** + * @brief Sets the directionPin to the right value to go in the direction + * stored in `currentDirection`. + */ + void setDirection(); + + enum Direction : int8_t + { + CLOCKWISE = 1, // To move the stepper clockwise seeing it from the top + // of the stepper + COUNTER_CLOCKWISE = -1, // To move the stepper counter-clockwise seeing + // it from the top of the stepper + }; + + // This class is not copyable! + Stepper& operator=(const Stepper&) = delete; + Stepper(const Stepper& p) = delete; miosix::GpioPin stepPin; miosix::GpioPin directionPin; - float speed; - float stepAngle; ///< Degrees per step. + float speed; // [rev/s] + float stepAngle; // [deg/step] bool revertDirection; - Microstep microstep; + uint16_t microStep; + PinConfiguration pinConfig; miosix::GpioPin enablePin; - miosix::GpioPin ms1Pin; - miosix::GpioPin ms2Pin; - miosix::GpioPin ms3Pin; - int32_t currentPosition = 0; + Direction currentDirection; // Direction of the stepper + float currentPositionDeg = 0; // Absolute position counter [degrees] }; -inline Stepper::Stepper(miosix::GpioPin stepPin, miosix::GpioPin directionPin, - float speed, float stepAngle, bool revertDirection, - Microstep microstep, miosix::GpioPin enablePin, - miosix::GpioPin ms1Pin, miosix::GpioPin ms2Pin, - miosix::GpioPin ms3Pin) - : stepPin(stepPin), directionPin(directionPin), speed(speed), - stepAngle(stepAngle), revertDirection(revertDirection), - microstep(microstep), enablePin(enablePin), ms1Pin(ms1Pin), - ms2Pin(ms2Pin), ms3Pin(ms3Pin) -{ - if (speed < 0) - speed = 0; - - setMicrostepping(microstep); - - // Start with the motor disabled - disable(); -} - -inline void Stepper::enable() { enablePin.low(); } - -inline void Stepper::disable() { enablePin.high(); } - inline void Stepper::setSpeed(float speed) { this->speed = speed; } -inline void Stepper::setMicrostepping(Microstep microstep) +inline void Stepper::setMicroStepping(uint16_t microStep) { - this->microstep = microstep; - - switch (microstep) - { - case Microstep::MICROSTEP_1: - ms1Pin.low(); - ms2Pin.low(); - ms3Pin.low(); - break; - case Microstep::MICROSTEP_2: - ms1Pin.high(); - ms2Pin.low(); - ms3Pin.low(); - break; - case Microstep::MICROSTEP_4: - ms1Pin.low(); - ms2Pin.high(); - ms3Pin.low(); - break; - case Microstep::MICROSTEP_8: - ms1Pin.high(); - ms2Pin.high(); - ms3Pin.low(); - break; - case Microstep::MICROSTEP_16: - ms1Pin.high(); - ms2Pin.high(); - ms3Pin.high(); - break; - } + this->microStep = microStep; } -void Stepper::zeroPosition() { currentPosition = 0; } - -inline void Stepper::move(int32_t steps) +inline void Stepper::zeroPosition(float degrees) { - if (speed == 0) - return; - - unsigned int halfStepDelay = - 1e6 / (speed * 360 / stepAngle * getMicrosteppingValue()); - - if (revertDirection == (steps >= 0)) - directionPin.low(); - else - directionPin.high(); - miosix::delayUs(halfStepDelay); - - int32_t stepsAbs = steps > 0 ? steps : -steps; - for (int i = 0; i < stepsAbs; i++) - { - stepPin.high(); - miosix::delayUs(halfStepDelay); - stepPin.low(); - miosix::delayUs(halfStepDelay); - } - - currentPosition += steps; + currentPositionDeg = degrees; } inline void Stepper::moveDeg(float degrees) { - move(degrees / stepAngle * getMicrosteppingValue()); + move(degrees * microStep / stepAngle); } -inline void Stepper::setPosition(int32_t steps) +inline void Stepper::setPosition(int16_t steps) { - move(steps - currentPosition); + setPositionDeg(steps * stepAngle / microStep); } inline void Stepper::setPositionDeg(float position) { - setPosition(position / stepAngle * getMicrosteppingValue()); + moveDeg(position - getCurrentDegPosition()); } -inline uint32_t Stepper::getCurrentPosition() { return currentPosition; } - -inline float Stepper::getCurrentDegPosition() +inline int16_t Stepper::getCurrentPosition() { - return currentPosition * stepAngle / getMicrosteppingValue(); + return getCurrentDegPosition() * microStep / stepAngle; } -inline int Stepper::getMicrosteppingValue() -{ - switch (microstep) - { - case Microstep::MICROSTEP_1: - return 1; - case Microstep::MICROSTEP_2: - return 2; - case Microstep::MICROSTEP_4: - return 4; - case Microstep::MICROSTEP_8: - return 8; - case Microstep::MICROSTEP_16: - return 16; - } - - return 1; -} +inline float Stepper::getCurrentDegPosition() { return currentPositionDeg; } -} // namespace Boardcore +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/actuators/StepperPWM.cpp b/src/shared/actuators/StepperPWM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9eccf5ffa73aac7ba0c8b8ef5ca13668dfa7ff5 --- /dev/null +++ b/src/shared/actuators/StepperPWM.cpp @@ -0,0 +1,99 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Authors: Alberto Nidasio, Emilio Corigliano + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "StepperPWM.h" + +namespace Boardcore +{ + +StepperPWM::StepperPWM(CountedPWM &pwm, miosix::GpioPin stepPin, + miosix::GpioPin directionPin, float speed, + float stepAngle, bool revertDirection, + uint16_t microStep, PinConfiguration pinConfiguration, + miosix::GpioPin enablePin) + : Stepper(stepPin, directionPin, speed, stepAngle, revertDirection, + microStep, pinConfiguration, enablePin), + pwm(pwm) +{ + if (this->speed < 0) + this->speed = 0; + + Stepper::setDirection(); + StepperPWM::setMicroStepping(microStep); + + // Start with the motor disabled + disable(); +} + +void StepperPWM::setSpeed(float speed) +{ + this->speed = speed; + pwm.setFrequency(speed * 360 / stepAngle * microStep); +} + +void StepperPWM::setMicroStepping(uint16_t microStep) +{ + // Resize the pulses to generate and the current pulses generated to use the + // new microsteps (keeping constant the angle of movement) + pwm.updateTargetCount(pwm.getTargetCount() * this->microStep / microStep); + pwm.updateCurrentCount(pwm.getCurrentCount() * this->microStep / microStep); + + Stepper::setMicroStepping(microStep); + pwm.setFrequency(speed * 360 / stepAngle * microStep); +} + +void StepperPWM::move(int16_t steps) +{ + if (speed == 0 || steps == 0) + return; + + // First update currentPositionDeg. This method corrects the initial + // position saved with the current state of the CountTimer counter register, + // so that we account only for the pulses actually generated. + currentPositionDeg = getCurrentDegPosition(); + + if (steps > 0) + { + // Go forward + currentDirection = Direction::CLOCKWISE; + Stepper::setDirection(); + + // Move + pwm.generatePulses(steps); + } + else + { + // Go backwards + currentDirection = Direction::COUNTER_CLOCKWISE; + Stepper::setDirection(); + + // Move + pwm.generatePulses(-steps); + } +} + +float StepperPWM::getCurrentDegPosition() +{ + return currentPositionDeg + + currentDirection * pwm.getCurrentCount() * stepAngle / microStep; +} + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/actuators/StepperPWM.h b/src/shared/actuators/StepperPWM.h new file mode 100644 index 0000000000000000000000000000000000000000..6c91c24b7316a219684f2725728081d80ea32e5d --- /dev/null +++ b/src/shared/actuators/StepperPWM.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Authors: Alberto Nidasio, Emilio Corigliano + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <interfaces-impl/gpio_impl.h> +#include <interfaces/delays.h> +#include <utils/TestUtils/MockGpioPin.h> + +#include "Stepper.h" +#include "drivers/timer/CountedPWM.h" + +namespace Boardcore +{ + +class StepperPWM : public Stepper +{ +public: + /** + * @brief Construct a new StepperPWM object. + * + * By default the direction pin is held low when moving forward. + * If the given speed is negative the motor wont move. + * + * @param pwm The CountedPWM object that will generate the N steps. + * @param stepPin Pin connected to the step signal of the driver. + * @param directionPin Pin connected to the direction signal of the driver. + * @param speed Number of rotations per second. + * @param stepAngle Angle covered by one motor step. + * @param revertDirection Whether or not revert the direction signal. + */ + StepperPWM( + CountedPWM &pwm, miosix::GpioPin stepPin, miosix::GpioPin directionPin, + float speed = 1, float stepAngle = 1.8, bool revertDirection = false, + uint16_t microStep = 1, + PinConfiguration pinConfiguration = PinConfiguration::COMMON_CATHODE, + miosix::GpioPin enablePin = MockGpioPin()); + + /** + * @brief Changes the stepper motor speed. + * + * @param speed Speed in rotation per second. [rev/s] + */ + void setSpeed(float speed) override; + + /** + * @brief Set the motor driver micro stepping configuration. + * + * If micro steps are changed via physical switches we have to call this + * method with the correct configuration to keep consistency in the speed of + * the stepper. + * @param microStep The micro steps that the stepper performs. + */ + void setMicroStepping(uint16_t microStep) override; + + /** + * @brief Move the stepper motor by the specified amount of steps. + */ + void move(int16_t steps) override; + + /** + * @brief Returns the current angle of the stepper. + * + * To calculate the correct angle of the stepper we add the angle reached in + * the last actuation (saved in the variable `currentPositionDeg`) with the + * conversion in angle of the actual stepper position (so, the number of + * pulses actuated by the timer till now, that corresponds to the counter + * register of the CounterTimer). + * + * Notice that on each actuation (calling one of the move or setPosition + * methods) we save the angle reached using this method. + */ + float getCurrentDegPosition() override; + +private: + // This class is not copyable! + StepperPWM &operator=(const StepperPWM &) = delete; + StepperPWM(const StepperPWM &p) = delete; + + void setDirection(); + + CountedPWM &pwm; +}; + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/drivers/timer/CountedPWM.cpp b/src/shared/drivers/timer/CountedPWM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c49b3e85638bf007d33fe531c0b5a47ca768e5f --- /dev/null +++ b/src/shared/drivers/timer/CountedPWM.cpp @@ -0,0 +1,147 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Emilio Corigliano, Alberto Nidasio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "CountedPWM.h" + +namespace Boardcore +{ + +CountedPWM::CountedPWM(TIM_TypeDef* const pulseTimer, + TimerUtils::Channel const pulseChannel, + TimerUtils::TriggerSource const pulseTriggerSource, + TIM_TypeDef* const counterTimer, + TimerUtils::Channel const counterChannel, + TimerUtils::TriggerSource const counterTriggerSource) + : pulseTimer(pulseTimer), pulseChannel(pulseChannel), + pulseTriggerSource(pulseTriggerSource), counterTimer(counterTimer), + counterChannel(counterChannel), counterTriggerSource(counterTriggerSource) +{ + // Erase the previous timer configuration + this->pulseTimer.reset(); + this->counterTimer.reset(); + + configureTimers(); + + // Keep the timers always enabled. The clock of the pwm timer will be + // enabled based on the output of the counter timer + this->pulseTimer.enable(); + this->counterTimer.enable(); +} + +CountedPWM::~CountedPWM() +{ + pulseTimer.reset(); + counterTimer.reset(); +} + +void CountedPWM::setFrequency(unsigned int pulseFrequency) +{ + this->pulseFrequency = pulseFrequency; + pulseTimer.setFrequency(pulseFrequency * dutyCycleResolution); + pulseTimer.setAutoReloadRegister( + TimerUtils::getFrequency(pulseTimer.getTimer()) / pulseFrequency); +} + +void CountedPWM::setDutyCycle(float dutyCycle) +{ + if (dutyCycle >= 0 && dutyCycle <= 1) + { + this->dutyCycle = dutyCycle; + pulseTimer.setCaptureCompareRegister( + pulseChannel, + static_cast<uint16_t>( + dutyCycle * pulseTimer.readAutoReloadRegister() + 0.5)); + } +} + +void CountedPWM::setDutyCycleResolution(unsigned int dutyCycleResolution) +{ + this->dutyCycleResolution = dutyCycleResolution; + setFrequency(pulseFrequency); +} + +void CountedPWM::generatePulses(uint16_t pulses) +{ + // Reset the counters + pulseTimer.setCounter(0); + counterTimer.setCounter(0); + + // Set the capture and compare register to the number of pulses to generate + counterTimer.setCaptureCompareRegister(counterChannel, pulses); +} + +bool CountedPWM::isGenerating() +{ + return counterTimer.readCounter() != + counterTimer.readCaptureCompareRegister(counterChannel); +} + +void CountedPWM::configureTimers() +{ + // PWM timer + { + setFrequency(pulseFrequency); + + // Output/Master: Select TRGO source + pulseTimer.setMasterMode(masterModeFromChannel(pulseChannel)); + + // Input/Slave: Enable the pulseTimer when the output from the + // counterTimer is high + pulseTimer.setTriggerSource(pulseTriggerSource); + pulseTimer.setSlaveMode(TimerUtils::SlaveMode::GATED_MODE); + + // Capture Compare Channel setup in PWM mode + pulseTimer.setOutputCompareMode( + pulseChannel, TimerUtils::OutputCompareMode::PWM_MODE_1); + setDutyCycle(dutyCycle); + pulseTimer.enableCaptureCompareOutput(pulseChannel); + + // Force the timer to update its configuration + pulseTimer.generateUpdate(); + } + + // Counter timer + { + counterTimer.setAutoReloadRegister(-1); + + // Output/Master: Select TRGO source + counterTimer.setMasterMode(masterModeFromChannel(counterChannel)); + + // Input/Slave: Use the output from counterTimer as clock for pulseTimer + counterTimer.setTriggerSource(counterTriggerSource); + counterTimer.setSlaveMode(TimerUtils::SlaveMode::EXTERNAL_CLOCK_MODE_1); + + // Capture Compare Channel setup in PWM mode + counterTimer.setOutputCompareMode( + counterChannel, TimerUtils::OutputCompareMode::PWM_MODE_1); + counterTimer.setCaptureCompareRegister(counterChannel, 0); + counterTimer.enableCaptureCompareOutput(counterChannel); + + // The output is enabled also for the counterTimer if the user wants to + // output an enable signal + + // Force the timer to update its configuration + counterTimer.generateUpdate(); + } +} + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/drivers/timer/CountedPWM.h b/src/shared/drivers/timer/CountedPWM.h new file mode 100644 index 0000000000000000000000000000000000000000..30fe01640ea66cda2970601532c4539b76f36650 --- /dev/null +++ b/src/shared/drivers/timer/CountedPWM.h @@ -0,0 +1,198 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Emilio Corigliano, Alberto Nidasio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <assert.h> +#include <drivers/timer/GeneralPurposeTimer.h> + +#include "PWM.h" +#include "utils/Debug.h" + +namespace Boardcore +{ + +/** + * @brief This class generates a PWM signal for a chosen number of pulses. + * + * Two timers are used: PulseTimer and CounterTimer. + * + * +------------+ OverflowEvent UpdateCounter +--------------+ + * | PulseTimer | ------------------------------> | CounterTimer | + * | | | | + * | | | | + * | PWM, GATED | <------------------------------ | ExtClkMode | + * +------------+ StopCounter OverflowEvent +--------------+ + * + * As seen in the previous scheme, PulseTimer generates the PWM signal. This + * is seen both as output on the respective GPIO and as input for the + * CounterTimer. So, in this way, CounterTimer counts the pulses generated and, + * when a threshold is passed, the CounterTimer changes its output polarity. + * Since PulseTimer has as GATED input the CounterTimer, when the N pulses are + * exceeded, the PulseTimer is stopped and no other cycles are generated. This + * works because since we disable the PulseTimer, the CounterTimer doesn't + * exceed the Capture and Compare value set (which corresponds to the wanted + * pulses). + * + * The requirement for this technique to work is that the two timers should have + * an "internal trigger input" (ITR) connection to each other. + */ +class CountedPWM +{ +public: + /** + * @brief Constructor that initializes the timers. + * + * In particular, the pulseTimer will be used to create the output PWM + * signal while the counterTimer will just count for the occurred pulses. + * The PWM signal will be transmitted on the `pulseChannel` channel while + * the counterTimer signal will be accessible on the `counterChannel` + * channel (for debug purposes). + * @param pulseTimer Timer used for the generation of the PWM signal. + * @param pulseChannel Channel of pulseTimer on which the PWM signal will be + * generated. + * @param pulseTriggerSource The corresponding ITR mode of the pulseTimer + * for having as external trigger input the counterTimer. + * @param counterTimer Timer used for counting the pulses generated by the + * PWM. + * @param counterChannel Channel of pulseTimer on which the counter signal + * will be visible. + * @param counterTriggerSource The corresponding ITR mode of the + * counterTimer for having as external trigger input the pulseTimer. + */ + CountedPWM(TIM_TypeDef* const pulseTimer, + TimerUtils::Channel const pulseChannel, + TimerUtils::TriggerSource const pulseTriggerSource, + TIM_TypeDef* const counterTimer, + TimerUtils::Channel const counterChannel, + TimerUtils::TriggerSource const counterTriggerSource); + + ~CountedPWM(); + + void setFrequency(unsigned int pulseFrequency); + + /** + * @brief Sets the duty cycle for the specified channel. + * + * Changes the duty cycle only if the specified value is in the range [0,1]. + * + * @param dutyCycle Relative duty cycle, ranges from 0 to 1. + */ + void setDutyCycle(float dutyCycle); + + /** + * @brief Sets the granularity of the PulseTimer duty cycle. So it sets the + * Auto Reload Register of the PulseTimer. + */ + void setDutyCycleResolution(unsigned int dutyCycleResolution); + + /** + * @brief Triggers the generation of a specific number of PWM periods. + * + * Note that the function returns immediately and the PWM signal is + * generated in background by the timers. + * + * @param pulses The number of pulses to generate. + */ + void generatePulses(uint16_t pulses); + + /** + * @brief Returns whether the timers are generating the PWM signal or not. + */ + bool isGenerating(); + + /** + * @brief Allows to update the target periods count while the timers are + * generating the signal. + */ + void updateTargetCount(uint16_t pulses); + + /** + * @brief Allows to get the target periods count while the timers are + * generating the signal. + */ + uint16_t getTargetCount(); + + /** + * @brief Allows to update the current periods count while the timers are + * generating the signal. + */ + void updateCurrentCount(uint16_t count); + + /** + * @brief Returns the number of periods counted until now. + */ + uint16_t getCurrentCount(); + + /** + * @brief Stops the PWM signal generation if it is ongoing. + */ + void stop(); + +private: + // This class is not copyable! + CountedPWM& operator=(const CountedPWM&) = delete; + CountedPWM(const CountedPWM& p) = delete; + + void configureTimers(); + + GP16bitTimer pulseTimer; + TimerUtils::Channel pulseChannel; + TimerUtils::TriggerSource pulseTriggerSource; + + GP16bitTimer counterTimer; + TimerUtils::Channel counterChannel; + TimerUtils::TriggerSource counterTriggerSource; + + unsigned int pulseFrequency = 50; ///< Frequency of the pulses [Hz] + float dutyCycle = 0.5; ///< DutyCycle of the pulses [%] + unsigned int dutyCycleResolution = + 20000; ///< Basically this is the Auto Reload Register of the + ///< PulseTimer +}; + +inline void CountedPWM::updateTargetCount(uint16_t pulses) +{ + counterTimer.setCaptureCompareRegister(counterChannel, pulses); +} + +inline uint16_t CountedPWM::getTargetCount() +{ + return counterTimer.readCaptureCompareRegister(counterChannel); +} + +inline void CountedPWM::updateCurrentCount(uint16_t count) +{ + return counterTimer.setCounter(count); +} + +inline uint16_t CountedPWM::getCurrentCount() +{ + return counterTimer.readCounter(); +} + +inline void CountedPWM::stop() +{ + counterTimer.setCaptureCompareRegister(counterChannel, getCurrentCount()); +} + +} // namespace Boardcore \ No newline at end of file diff --git a/src/tests/actuators/test-stepper-pwm.cpp b/src/tests/actuators/test-stepper-pwm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1a2101a4bb496758a57eacf6cc9f21745d4049e --- /dev/null +++ b/src/tests/actuators/test-stepper-pwm.cpp @@ -0,0 +1,147 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Emilio Corigliano + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <actuators/StepperPWM.h> +#include <drivers/timer/CountedPWM.h> +#include <miosix.h> + +#include <thread> + +using namespace miosix; +using namespace Boardcore; + +GpioPin directionPin = GpioPin(GPIOC_BASE, 12); +GpioPin enablePin = GpioPin(GPIOC_BASE, 10); + +GpioPin stepPin = GpioPin(GPIOB_BASE, 4); +GpioPin ms1Pin = GpioPin(GPIOE_BASE, 6); +GpioPin ms2Pin = GpioPin(GPIOE_BASE, 2); +GpioPin ms3Pin = GpioPin(GPIOE_BASE, 4); + +void setMicroStepping(StepperPWM& stepper, int16_t microStep) +{ + switch (microStep) + { + case 1: + ms1Pin.low(); + ms2Pin.low(); + ms3Pin.low(); + break; + case 2: + ms1Pin.high(); + ms2Pin.low(); + ms3Pin.low(); + break; + case 4: + ms1Pin.low(); + ms2Pin.high(); + ms3Pin.low(); + break; + case 8: + ms1Pin.high(); + ms2Pin.high(); + ms3Pin.low(); + break; + case 16: + ms1Pin.high(); + ms2Pin.high(); + ms3Pin.high(); + break; + default: + printf("Microsteps value not available!\n"); + return; + } + stepper.setMicroStepping(microStep); +} + +void doRoutine(StepperPWM& stepper) +{ + stepper.enable(); + + for (int i = 1; i < 5; i++) + { + stepper.setSpeed(i); + + stepper.setPositionDeg(180); + delayMs(2 * 1000); + stepper.setPositionDeg(90); + delayMs(2 * 1000); + stepper.setPositionDeg(180); + delayMs(2 * 1000); + stepper.setPositionDeg(90); + delayMs(2 * 1000); + stepper.setPosition(0); + delayMs(2 * 1000); + stepper.moveDeg(-180); + delayMs(2 * 1000); + stepper.moveDeg(-360); + delayMs(2 * 1000); + stepper.setPosition(0); + delayMs(5 * 1000); + } + + stepper.disable(); +} + +int main() +{ + // Configure stepper motor pins + directionPin.mode(Mode::OUTPUT); + enablePin.mode(Mode::OUTPUT); + ms3Pin.mode(Mode::OUTPUT); + ms2Pin.mode(Mode::OUTPUT); + ms1Pin.mode(Mode::OUTPUT); + stepPin.mode(Mode::ALTERNATE); + stepPin.alternateFunction(2); + + CountedPWM pwm( + TIM3, TimerUtils::Channel::CHANNEL_1, TimerUtils::TriggerSource::ITR3, + TIM4, TimerUtils::Channel::CHANNEL_1, TimerUtils::TriggerSource::ITR2); + + StepperPWM stepper(pwm, stepPin, directionPin, 1, 1.8, false, 8, + Stepper::PinConfiguration::COMMON_CATHODE, enablePin); + setMicroStepping(stepper, 8); + + printf("The stepper is now disabled, waiting 2 seconds...\n"); + delayMs(2 * 1000); + + std::thread th( + [&stepper]() + { + while (true) + { + printf("Current position: %.2f\n", + stepper.getCurrentDegPosition()); + Thread::sleep(100); + } + }); + th.detach(); + + while (true) + { + printf("Press something to start\n"); + scanf("%*s"); + + doRoutine(stepper); + Thread::sleep(10 * 1000); + } +} \ No newline at end of file diff --git a/src/tests/actuators/test-stepper.cpp b/src/tests/actuators/test-stepper.cpp index 707f227892df7545e5aec8b6a9ef174322ed8e46..6ee595f8ce2737dae6f0b4950bf44dc836d8c35a 100644 --- a/src/tests/actuators/test-stepper.cpp +++ b/src/tests/actuators/test-stepper.cpp @@ -25,15 +25,51 @@ using namespace miosix; using namespace Boardcore; -miosix::GpioPin directionPin = miosix::GpioPin(GPIOC_BASE, 13); -miosix::GpioPin stepPin = miosix::GpioPin(GPIOC_BASE, 14); -miosix::GpioPin resetPin = miosix::GpioPin(GPIOE_BASE, 5); -miosix::GpioPin ms1Pin = miosix::GpioPin(GPIOE_BASE, 6); -miosix::GpioPin ms2Pin = miosix::GpioPin(GPIOE_BASE, 2); -miosix::GpioPin ms3Pin = miosix::GpioPin(GPIOE_BASE, 4); -miosix::GpioPin enablePin = miosix::GpioPin(GPIOC_BASE, 15); - -void doRoutine(Stepper stepper) +GpioPin directionPin = GpioPin(GPIOC_BASE, 12); +GpioPin stepPin = GpioPin(GPIOB_BASE, 4); +GpioPin enablePin = GpioPin(GPIOC_BASE, 10); +GpioPin resetPin = GpioPin(GPIOE_BASE, 5); +GpioPin ms1Pin = GpioPin(GPIOE_BASE, 6); +GpioPin ms2Pin = GpioPin(GPIOE_BASE, 2); +GpioPin ms3Pin = GpioPin(GPIOE_BASE, 4); + +void setMicroStepping(Stepper& stepper, int16_t microStep) +{ + switch (microStep) + { + case 1: + ms1Pin.low(); + ms2Pin.low(); + ms3Pin.low(); + break; + case 2: + ms1Pin.high(); + ms2Pin.low(); + ms3Pin.low(); + break; + case 4: + ms1Pin.low(); + ms2Pin.high(); + ms3Pin.low(); + break; + case 8: + ms1Pin.high(); + ms2Pin.high(); + ms3Pin.low(); + break; + case 16: + ms1Pin.high(); + ms2Pin.high(); + ms3Pin.high(); + break; + default: + printf("Microsteps value not available!\n"); + return; + } + stepper.setMicroStepping(microStep); +} + +void doRoutine(Stepper& stepper) { stepper.enable(); @@ -71,18 +107,17 @@ void doRoutine(Stepper stepper) int main() { - directionPin.mode(miosix::Mode::OUTPUT); - stepPin.mode(miosix::Mode::OUTPUT); - resetPin.mode(miosix::Mode::OUTPUT); + directionPin.mode(Mode::OUTPUT); + stepPin.mode(Mode::OUTPUT); + resetPin.mode(Mode::OUTPUT); resetPin.high(); - ms3Pin.mode(miosix::Mode::OUTPUT); - ms2Pin.mode(miosix::Mode::OUTPUT); - ms1Pin.mode(miosix::Mode::OUTPUT); - enablePin.mode(miosix::Mode::OUTPUT); + ms3Pin.mode(Mode::OUTPUT); + ms2Pin.mode(Mode::OUTPUT); + ms1Pin.mode(Mode::OUTPUT); + enablePin.mode(Mode::OUTPUT); - Stepper stepper(stepPin, directionPin, 1, 1.8, false, - Stepper::Microstep::MICROSTEP_1, enablePin, ms1Pin, ms2Pin, - ms3Pin); + Stepper stepper(stepPin, directionPin, 1, 1.8, false, 8, + Stepper::PinConfiguration::COMMON_CATHODE, enablePin); printf("The stepper is now disabled, waiting 2 seconds...\n"); delayMs(2 * 1000); @@ -90,38 +125,38 @@ int main() while (true) { printf("Press something to start\n"); - getchar(); + scanf("%*s"); - printf("\t1x microstepping\n"); - stepper.setMicrostepping(Stepper::Microstep::MICROSTEP_1); + printf("\t1x micro stepping\n"); + setMicroStepping(stepper, 8); doRoutine(stepper); printf("The stepper is now disabled\n"); delayMs(1000); - printf("\t2x microstepping\n"); - stepper.setMicrostepping(Stepper::Microstep::MICROSTEP_2); + printf("\t2x micro stepping\n"); + setMicroStepping(stepper, 8); doRoutine(stepper); printf("The stepper is now disabled\n"); delayMs(1000); - printf("\t4x microstepping\n"); - stepper.setMicrostepping(Stepper::Microstep::MICROSTEP_4); + printf("\t4x micro stepping\n"); + setMicroStepping(stepper, 8); doRoutine(stepper); printf("The stepper is now disabled\n"); delayMs(1000); - printf("\t8x microstepping\n"); - stepper.setMicrostepping(Stepper::Microstep::MICROSTEP_8); + printf("\t8x micro stepping\n"); + setMicroStepping(stepper, 8); doRoutine(stepper); printf("The stepper is now disabled\n"); delayMs(1000); - printf("\t16x microstepping\n"); - stepper.setMicrostepping(Stepper::Microstep::MICROSTEP_16); + printf("\t16x micro stepping\n"); + setMicroStepping(stepper, 8); doRoutine(stepper); printf("The stepper is now disabled\n"); } diff --git a/src/tests/drivers/timer/test-counted-pwm.cpp b/src/tests/drivers/timer/test-counted-pwm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..497f33a868dc16002ab73a87f5e23613fa1eaeca --- /dev/null +++ b/src/tests/drivers/timer/test-counted-pwm.cpp @@ -0,0 +1,93 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Emilio Corigliano, Alberto Nidasio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <drivers/timer/CountedPWM.h> +#include <miosix.h> + +#include <thread> + +using namespace miosix; +using namespace Boardcore; + +/** + * This test shows how to use a CountedPWM to generate N pulses all done by + * hardware! + */ + +typedef Gpio<GPIOB_BASE, 4> tim3ch1; // AF2 +typedef Gpio<GPIOD_BASE, 12> tim4ch1; // AF2 + +int main() +{ + tim3ch1::mode(Mode::ALTERNATE); + tim3ch1::alternateFunction(2); + + tim4ch1::mode(Mode::ALTERNATE); + tim4ch1::alternateFunction(2); + + CountedPWM pwm( + TIM3, TimerUtils::Channel::CHANNEL_1, TimerUtils::TriggerSource::ITR3, + TIM4, TimerUtils::Channel::CHANNEL_1, TimerUtils::TriggerSource::ITR2); + pwm.setFrequency(2); + pwm.setDutyCycle(0.5); + + std::thread th( + [&]() + { + while (true) + { + printf("[%.2f] Counter: %d\tTarget: %ld\tIs generating: %d\n", + getTick() / 1000.0, pwm.getCurrentCount(), TIM4->CCR1, + pwm.isGenerating()); + Thread::sleep(250); + } + }); + th.detach(); + + Thread::sleep(1000); + + pwm.generatePulses(4); + Thread::sleep(1000); + pwm.generatePulses(6); + Thread::sleep(1000); + pwm.generatePulses(3); + + Thread::sleep(5 * 1000); + + pwm.generatePulses(4); + Thread::sleep(1000); + pwm.updateTargetCount(6); + + Thread::sleep(5 * 1000); + + pwm.generatePulses(6); + while (pwm.getCurrentCount() < 3) + ; + pwm.stop(); + + pwm.setFrequency(10); + while (true) + { + pwm.generatePulses(8); + Thread::sleep(2500); + } +}