diff --git a/library/stm32f072xb/encoder/vex-encoder/vex-encoder-init.hpp b/library/stm32f072xb/encoder/vex-encoder/vex-encoder-init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a05f9076e23279595108101391368047df838a2d --- /dev/null +++ b/library/stm32f072xb/encoder/vex-encoder/vex-encoder-init.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include <dma.hpp> +#include <i2c.hpp> + +using namespace stm32f072xb; + +struct VexEncoderInit { + I2c& i2c; + float maxPosition; +}; diff --git a/library/stm32f072xb/encoder/vex-encoder/vex-encoder.cpp b/library/stm32f072xb/encoder/vex-encoder/vex-encoder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a38737d7360b2b64cd1d37fe8aec329c052bb8f --- /dev/null +++ b/library/stm32f072xb/encoder/vex-encoder/vex-encoder.cpp @@ -0,0 +1,109 @@ +#include "vex-encoder.hpp" + +VexEncoder::VexEncoder(VexEncoderInit& encoderInit) + : _i2c(encoderInit.i2c), _address(0), _position(0), _maxPosition(encoderInit.maxPosition) { + uint8_t newAddress = VEX_STARTING_ADDRESS; + if (lastEncoder) { + lastEncoder->unterminate(); + newAddress = lastEncoder->_address + 0x2; + } + + lastEncoder = this; + + setAddress(newAddress); + zero(); +} + +float VexEncoder::getPosition() { + updatePosition(); + return _position; +} + +void VexEncoder::updatePosition() { + _position = MOTOR_393_TORQUE_ROTATION / TICKS * getRawPosition(); +} + +float VexEncoder::getMaxPosition() { + return _maxPosition; +} + +uint8_t VexEncoder::getAddress() { + return _address; +} + +void VexEncoder::setAddress(uint8_t newAddress) { + // Increment I2C by factor of 2 according to datasheet + VexEncoder::_setAddress(newAddress, _i2c); + _address = newAddress; +} + +void VexEncoder::unterminate() { + VexEncoder::_unterminate(_address, _i2c); +} + +void VexEncoder::terminate() { + VexEncoder::_terminate(_address, _i2c); +} + +void VexEncoder::accessRegister(uint8_t reg) { + VexEncoder::_accessRegister(reg, _address, _i2c); +} + +void VexEncoder::zero() { + accessRegister(I2C_ENCODER_ZERO_REGISTER); +} + +void VexEncoder::monitorVexPosition() {} + +long VexEncoder::getRawPosition() { + uint8_t raw_position[4]; + accessRegister(I2C_ENCODER_POSITION_REGISTER); + _i2c.blockingRead(_address, raw_position, sizeof(raw_position)); + + long position = 0; + position |= raw_position[0] << 8; + position |= raw_position[1]; + position |= raw_position[2] << 24; + position |= raw_position[3] << 16; + + // TODO - check for negative numbers (motor is reversed) + return position; +} + +// void VexEncoder::writeToVex(uint8_t reg, uint8_t address, I2c& i2c, uint8_t* bytes, uint8_t bufferSize) { +// // i2c.blockingWrite(address, ®, bufferSize); +// } + +void VexEncoder::_accessRegister(uint8_t reg, uint8_t address, I2c& i2c) { + i2c.blockingWrite(address, ®, 1); +} + +void VexEncoder::_terminate(uint8_t address, I2c& i2c) { + VexEncoder::_accessRegister(I2C_ENCODER_TERMINATE_REGISTER, address, i2c); +} + +void VexEncoder::_unterminate(uint8_t address, I2c& i2c) { + VexEncoder::_accessRegister(I2C_ENCODER_UNTERMINATE_REGISTER, address, i2c); +} + +void VexEncoder::_setAddress(uint8_t newAddress, I2c& i2c) { + // Increment I2C by factor of 2 according to datasheet + uint8_t changeAddressBytes[2] = {I2C_ENCODER_ADDRESS_REGISTER, newAddress}; + i2c.blockingWrite(I2C_ENCODER_DEFAULT_ADDRESS, changeAddressBytes, sizeof(changeAddressBytes)); +} + +void VexEncoder::resetAllEncoders(uint8_t numberOfMotor, I2c& i2c) { + const uint8_t address = 0x20; + for (int i = 0; i < numberOfMotor; i++) { + uint8_t resetBytes[3] = {I2C_ENCODER_RESET_ALL_DEVICES, 0xCA, 0x03}; + i2c.blockingWrite(0x00, resetBytes, sizeof(resetBytes)); + + // Increment I2C by factor of 2 according to datasheet + uint8_t changeAddressBytes[2] = {I2C_ENCODER_ADDRESS_REGISTER, address}; + i2c.blockingWrite(I2C_ENCODER_DEFAULT_ADDRESS, changeAddressBytes, sizeof(changeAddressBytes)); + VexEncoder::_unterminate(address, i2c); + } + + uint8_t resetBytes[3] = {I2C_ENCODER_RESET_ALL_DEVICES, 0xCA, 0x03}; + i2c.blockingWrite(0x00, resetBytes, sizeof(resetBytes)); +} diff --git a/library/stm32f072xb/encoder/vex-encoder/vex-encoder.hpp b/library/stm32f072xb/encoder/vex-encoder/vex-encoder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d33a5753d54ef6cfbf3c06d522b27b7478fed83d --- /dev/null +++ b/library/stm32f072xb/encoder/vex-encoder/vex-encoder.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <i2c.hpp> +#include "vex-encoder-init.hpp" + +using namespace stm32f072xb; + +// The default address for an encoder that's just been turned on. +constexpr int I2C_ENCODER_DEFAULT_ADDRESS = 0x60; +constexpr int VEX_STARTING_ADDRESS = 0x20; + +// Registers for important data +constexpr int I2CENCODER_DEVICE_INFO = 0x20; +constexpr int I2C_ENCODER_ADDRESS_REGISTER = 0x4D; +constexpr int I2C_ENCODER_POSITION_REGISTER = 0x40; +constexpr int I2C_ENCODER_VELOCITY_REGISTER = 0x44; +constexpr int I2C_ENCODER_ZERO_REGISTER = 0x4A; +constexpr int I2C_ENCODER_RESET_ALL_DEVICES = 0x4E; +constexpr int I2C_ENCODER_UNTERMINATE_REGISTER = 0x4B; +constexpr int I2C_ENCODER_TERMINATE_REGISTER = 0x4C; + +// Default number of ticks per encoder revolution +constexpr int TICKS = 8; + +// Encoder revolutions to Output rotations +// 1 (output rotation) / 39.2 (Encoder revolution) +constexpr float MOTOR_393_TORQUE_ROTATION = static_cast<float>(1 / 39.2); + +class VexEncoder { + public: + static inline VexEncoder* lastEncoder = nullptr; + + VexEncoder(VexEncoderInit& encoderinit); + ~VexEncoder() = default; + float getPosition(); + float getMaxPosition(); + uint8_t getAddress(); + void setAddress(uint8_t newAddress); + + void updatePosition(); + void unterminate(); + void terminate(); + void accessRegister(uint8_t reg); + void zero(); + + static void resetAllEncoders(uint8_t numberOfMotor, I2c& i2c); + + private: + I2c& _i2c; + uint8_t _address; + float _position; + float _maxPosition; + + void monitorVexPosition(); + long getRawPosition(); + + static void _accessRegister(uint8_t reg, uint8_t address, I2c& i2c); + static void _terminate(uint8_t address, I2c& i2c); + static void _unterminate(uint8_t address, I2c& i2c); + static void _setAddress(uint8_t newAddress, I2c& i2c); +}; diff --git a/library/stm32f072xb/motor/brushed-motor/brushed-motor-init.hpp b/library/stm32f072xb/motor/brushed-motor/brushed-motor-init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71f82e9db10d7c67dce49751643cf29b6f490c2d --- /dev/null +++ b/library/stm32f072xb/motor/brushed-motor/brushed-motor-init.hpp @@ -0,0 +1,19 @@ +#ifndef BRUSHED_MOTOR_INIT_H +#define BRUSHED_MOTOR_INIT_H + +#include <stm32f0xx.h> +#include <gpio-afr.hpp> +#include <gpio-pin.hpp> +#include "output-compare-channel.hpp" + +using namespace stm32f072xb; + +struct BrushedMotorInit { + TIM_TypeDef* timer; + const OutputCompareChannel OCC; + const GpioPin pwmPin; + AlternateFunction alternateFunction; + const GpioPin directionPin; +}; + +#endif diff --git a/library/stm32f072xb/motor/brushed-motor/brushed-motor.hpp b/library/stm32f072xb/motor/brushed-motor/brushed-motor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bff3a0a19fc5c96229fbfa327dcc726c572ce3de --- /dev/null +++ b/library/stm32f072xb/motor/brushed-motor/brushed-motor.hpp @@ -0,0 +1,44 @@ +#ifndef BRUSHED_MOTOR_H +#define BRUSHED_MOTOR_H + +#include <stdlib.h> + +#include <alternate-gpio-init.hpp> +#include <alternate-gpio.hpp> +#include <output-gpio-init.hpp> +#include <output-gpio.hpp> + +#include "brushed-motor-init.hpp" +#include "motor-direction.hpp" +#include "pwm-timer.hpp" + +using namespace stm32f072xb; + +template <Port pwmPort, Port directionPort> +class BrushedMotor { + private: + PwmTimer _timer; + AlternateGpio<pwmPort> _pwmOutputPin; + OutputGpio<directionPort> _directionPin; + + public: + BrushedMotor(BrushedMotorInit& motorInit, const uint16_t pwmResolution = 128, const uint16_t pwmFrequency = 1000) + : _timer(motorInit.timer, motorInit.OCC, pwmResolution, pwmFrequency), + _pwmOutputPin({motorInit.pwmPin, Pupdr::NONE, motorInit.alternateFunction}), + _directionPin({motorInit.directionPin, Pupdr::NONE}) {} + + void setSpeed(int16_t speed) { + _timer.setDutyCycle(abs(speed)); + setDirection(speed >= 0 ? MotorDirection::CLOCK_WISE : MotorDirection::COUNTER_CLOCK_WISE); + } + + void setDirection(MotorDirection direction) { + if (direction == MotorDirection::CLOCK_WISE) { + _directionPin.setPin(FlagStatus::SET); + } else { + _directionPin.setPin(FlagStatus::RESET); + } + } +}; + +#endif diff --git a/library/stm32f072xb/motor/motor-direction.hpp b/library/stm32f072xb/motor/motor-direction.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d7c26431480ca45c03e4887dcb77fb7ade703047 --- /dev/null +++ b/library/stm32f072xb/motor/motor-direction.hpp @@ -0,0 +1,6 @@ +#ifndef MOTOR_DIRECTION_H +#define MOTOR_DIRECTION_H + +enum class MotorDirection { CLOCK_WISE, COUNTER_CLOCK_WISE }; + +#endif diff --git a/library/stm32f072xb/motor/servo/servo-init.hpp b/library/stm32f072xb/motor/servo/servo-init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..11682077c026485441fc1ef28b378429a6d88c22 --- /dev/null +++ b/library/stm32f072xb/motor/servo/servo-init.hpp @@ -0,0 +1,18 @@ +#ifndef SERVO_INIT_H +#define SERVO_INIT_H + +#include <stm32f0xx.h> +#include <gpio-afr.hpp> +#include <gpio-pin.hpp> +#include "output-compare-channel.hpp" + +using namespace stm32f072xb; + +struct ServoInit { + TIM_TypeDef* timer; + const OutputCompareChannel OCC; + const GpioPin pwmPin; + AlternateFunction alternateFunction; +}; + +#endif diff --git a/library/stm32f072xb/motor/servo/servo.hpp b/library/stm32f072xb/motor/servo/servo.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8f30b653de78ca050bf57971ef8c3c39e5d5ab3f --- /dev/null +++ b/library/stm32f072xb/motor/servo/servo.hpp @@ -0,0 +1,82 @@ +#ifndef SERVO_H +#define SERVO_H + +#include <alternate-gpio.hpp> +#include <gpio-port.hpp> + +#include "pwm-timer.hpp" +#include "servo-init.hpp" + +using namespace stm32f072xb; + +// usage sample +/*! + \code {.cpp} + ServoInit servoInit = {TIM2, OutputCompareChannel::OCC2, GpioPin::P1, AlternateFunction::AF2}; + // For the HS-422 + Servo<Port::A> servo(servoInit, 90, -90, 90, 450, 2300); + servo.setAngle(0); + \endcode +*/ +/** + * We found the maximum and minimum frequency range in microseconds for the following servos. + * This is the template for displaying the informations: Servo Name : minimum pulse width-maximum pulse width + * HS-422 : 450-2300 + * HS-485HB : 900-2100 + * MG-996R : 544-2400 + **/ +template <Port pwmPort> +class Servo { + private: + static const uint16_t PWM_RESOLUTION = 20000; + static const uint16_t PWM_FREQUENCY = 50; + uint16_t _zeroDegreeDutyCycle; + int16_t _currentAngle; + int16_t _minAngle; + int16_t _maxAngle; + float _scaler; + + PwmTimer _timer; + AlternateGpio<pwmPort> _pwmOutputPin; + + public: + Servo(ServoInit& servoInit, + uint16_t amplitude, + int16_t minAngle, + int16_t maxAngle, + uint16_t pulseWidthMIN = 900, + uint16_t pulseWidthMAX = 2100) + : _zeroDegreeDutyCycle((pulseWidthMIN + pulseWidthMAX) / 2), + _currentAngle(minAngle), + _minAngle(minAngle), + _maxAngle(maxAngle), + _scaler((float)(_zeroDegreeDutyCycle - pulseWidthMIN) / (float)amplitude), + _timer(servoInit.timer, servoInit.OCC, PWM_RESOLUTION, PWM_FREQUENCY, _zeroDegreeDutyCycle), + _pwmOutputPin({servoInit.pwmPin, Pupdr::NONE, servoInit.alternateFunction}) { + setAngle(_currentAngle); + } + + void setAngle(int16_t angle) { + if (angle > _maxAngle) { + angle = _maxAngle; + } + if (angle < _minAngle) { + angle = _minAngle; + } + uint16_t dutyCycle = _zeroDegreeDutyCycle + _scaler * angle; + _timer.setDutyCycle(dutyCycle); + } + + void incrementAngle() { + if (_currentAngle < _maxAngle) { + setAngle(++_currentAngle); + } + } + void decrementAngle() { + if (_currentAngle > _minAngle) { + setAngle(--_currentAngle); + } + } +}; + +#endif diff --git a/library/stm32f072xb/motor/stepper/microstep-resolution.hpp b/library/stm32f072xb/motor/stepper/microstep-resolution.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d645925de0d5db2b40b91c2c7f1ff9c743dc5cb3 --- /dev/null +++ b/library/stm32f072xb/motor/stepper/microstep-resolution.hpp @@ -0,0 +1,8 @@ +#ifndef MICROSTEP_RESOLUTION_H +#define MICROSTEP_RESOLUTION_H + +namespace stm32f072xb { + +enum class MicrostepResolution { FULL_STEP, HALF_STEP, QUARTER_STEP, EIGHTH_STEP, SIXTEENTH_STEP, THIRTY_SECOND_STEP }; +} +#endif diff --git a/library/stm32f072xb/motor/stepper/stepper-init.hpp b/library/stm32f072xb/motor/stepper/stepper-init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ddc14ebf497629c264052ad93390a0b10eb5ac76 --- /dev/null +++ b/library/stm32f072xb/motor/stepper/stepper-init.hpp @@ -0,0 +1,20 @@ +#ifndef STEPPER_INIT_H +#define STEPPEr_INIT_H + +#include <stm32f0xx.h> +#include <gpio-afr.hpp> +#include <gpio-pin.hpp> +#include "output-compare-channel.hpp" + +using namespace stm32f072xb; + +struct StepperInit { + TIM_TypeDef* timer; + const OutputCompareChannel OCC; + const GpioPin pwmPin; + AlternateFunction alternateFunction; + const GpioPin directionPin; + const GpioPin disablePort; +}; + +#endif diff --git a/library/stm32f072xb/motor/stepper/stepper.hpp b/library/stm32f072xb/motor/stepper/stepper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c386c799348034c35f2332f103b272b791e91e5 --- /dev/null +++ b/library/stm32f072xb/motor/stepper/stepper.hpp @@ -0,0 +1,48 @@ +#ifndef STEPPER_H +#define STEPPER_H + +#include <alternate-gpio.hpp> +#include <output-gpio.hpp> + +#include "microstep-resolution.hpp" +#include "motor-direction.hpp" +#include "pwm-timer.hpp" +#include "stepper-init.hpp" + +using namespace stm32f072xb; + +template <Port pwmPort, Port directionPort, Port disablePort> +class Stepper { + public: + static const uint16_t PWM_RESOLUTION = 1000; + static const uint16_t PWM_FREQUENCY = 1000; + + public: + Stepper(StepperInit stepperInit) + : _timer(stepperInit.timer, stepperInit.OCC, PWM_RESOLUTION, PWM_FREQUENCY), + _pwmOutputPin({stepperInit.pwmPin, Pupdr::NONE, stepperInit.alternateFunction}), + _directionPin({stepperInit.directionPin, Pupdr::NONE}), + _disablePort({stepperInit.disablePort, Pupdr::NONE}) { + _timer.setDutyCyclePercentage(50); + } + + void setDirection(MotorDirection direction) { + if (direction == MotorDirection::CLOCK_WISE) { + _directionPin.setPin(FlagStatus::SET); + } else { + _directionPin.setPin(FlagStatus::RESET); + } + } + + void start() { _disablePort.setPin(FlagStatus::RESET); } + + void stop() { _disablePort.setPin(FlagStatus::SET); } + + private: + PwmTimer _timer; + AlternateGpio<pwmPort> _pwmOutputPin; + OutputGpio<directionPort> _directionPin; + OutputGpio<disablePort> _disablePort; +}; + +#endif diff --git a/library/stm32f072xb/motor/vex-motor/vex-motor-init.hpp b/library/stm32f072xb/motor/vex-motor/vex-motor-init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6433455d5e9dcb27a33927786d2aff9e1192db90 --- /dev/null +++ b/library/stm32f072xb/motor/vex-motor/vex-motor-init.hpp @@ -0,0 +1,13 @@ +#ifndef VEX_MOTOR_INIT_H +#define VEX_MOTOR_INIT_H + +#include <stm32f0xx.h> +#include <brushed-motor-init.hpp> + +using namespace stm32f072xb; + +struct VexMotorInit : public BrushedMotorInit { + I2C_TypeDef* i2c; +}; + +#endif diff --git a/library/stm32f072xb/motor/vex-motor/vex-motor.hpp b/library/stm32f072xb/motor/vex-motor/vex-motor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9a9627be44fa88f51d76b151b9cbd4523ea03ebc --- /dev/null +++ b/library/stm32f072xb/motor/vex-motor/vex-motor.hpp @@ -0,0 +1,46 @@ +#ifndef VEX_MOTOR_H +#define VEX_MOTOR_H + +#include "brushed-motor.hpp" +#include "vex-encoder.hpp" + +using namespace stm32f072xb; + +/** + * @brief TU DOIS LE METTRE SUR LES MEMES PWM PORT ET DIRECTION PORT POUR TOUS TES VEX MOTORS + * + * @tparam pwmPort + * @tparam directionPort + */ +template <Port pwmPort, Port directionPort> +class VexMotor : public BrushedMotor<pwmPort, directionPort>, public VexEncoder { + public: + VexMotor(BrushedMotorInit& motorInit, VexEncoderInit& encoderInit) + : BrushedMotor<pwmPort, directionPort>(motorInit, 255), VexEncoder(encoderInit) {} + + void setSpeed(int16_t speed) { + if (isAllowedToMove(speed)) { + BrushedMotor<pwmPort, directionPort>::setSpeed(speed); + } else { + BrushedMotor<pwmPort, directionPort>::setSpeed(0); + } + } + + private: + bool isAllowedToMove(int16_t speed) { + const float minPosition = 0.05f; + bool isAllow = true; + float absolutePosition = abs(this->getPosition()); + + // Prevent going foward if we are already at the maximum forward + if (speed >= 0 && absolutePosition >= this->getMaxPosition()) { + isAllow = false; + } else if (speed <= 0 && absolutePosition < minPosition) { + isAllow = false; + } + + return isAllow; + } +}; + +#endif diff --git a/library/stm32f072xb/pwm-timer/output-compare-channel.hpp b/library/stm32f072xb/pwm-timer/output-compare-channel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e0dfe5ee507270be70c465522a82bcf82649a4b3 --- /dev/null +++ b/library/stm32f072xb/pwm-timer/output-compare-channel.hpp @@ -0,0 +1,6 @@ +#ifndef OUTPUT_COMPARE_CHANNEL_H +#define OUTPUT_COMPARE_CHANNEL_H + +enum class OutputCompareChannel { OCC1, OCC2, OCC3, OCC4 }; + +#endif diff --git a/library/stm32f072xb/pwm-timer/pwm-timer.cpp b/library/stm32f072xb/pwm-timer/pwm-timer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed5ed139f20bc4210111d9c7698ae8c4cda77167 --- /dev/null +++ b/library/stm32f072xb/pwm-timer/pwm-timer.cpp @@ -0,0 +1,102 @@ +#include "pwm-timer.hpp" + +PwmTimer::PwmTimer(TIM_TypeDef* timer, + OutputCompareChannel OCC, + uint16_t pwmResolution, + uint16_t pwmFrequency, + uint16_t initalDutyCycle) + : _timer(timer), _OCC(OCC), _pwmResolution(pwmResolution), _pwmFrequency(pwmFrequency) { + enableTimer(); + + CLEAR_BIT(timer->CR1, TIM_CR1_DIR); // upcounting mode + CLEAR_BIT(timer->CR1, TIM_CR1_ARPE); // enable auto-reload + + setDutyCycle(initalDutyCycle); // Reset PWM + setupOutputChannel(); // Configure Output Channel + + WRITE_REG(timer->ARR, pwmResolution); // PWM resolution + + // TODO - Variable for clock speed + uint16_t prescaler = 8000000 / (pwmResolution * pwmFrequency); + WRITE_REG(timer->PSC, prescaler); // Prescaler for timer counter + SET_BIT(_timer->BDTR, TIM_BDTR_MOE); + SET_BIT(timer->CR1, TIM_CR1_CEN); // Enable counter +} + +PwmTimer::PwmTimer(TIM_TypeDef* timer, OutputCompareChannel OCC, uint16_t pwmResolution, uint16_t pwmFrequency) + : PwmTimer(timer, OCC, pwmResolution, pwmFrequency, 0) {} + +void PwmTimer::setDutyCycle(uint16_t compareValue) { + compareValue = compareValue < _pwmResolution ? compareValue : _pwmResolution; + switch (_OCC) { + case OutputCompareChannel::OCC1: + _timer->CCR1 = compareValue; + break; + case OutputCompareChannel::OCC2: + _timer->CCR2 = compareValue; + break; + case OutputCompareChannel::OCC3: + _timer->CCR3 = compareValue; + break; + case OutputCompareChannel::OCC4: + _timer->CCR4 = compareValue; + break; + } +} + +void PwmTimer::setDutyCyclePercentage(uint8_t dutyCyclePercentage) { + dutyCyclePercentage = dutyCyclePercentage <= 100 ? dutyCyclePercentage : 100; + uint16_t captureCompareValue = _pwmResolution * dutyCyclePercentage / 100; + this->setDutyCycle(captureCompareValue); +} + +void PwmTimer::enableTimer() { + if (_timer == TIM1) { + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM1EN); + } else if (_timer == TIM2) { + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM2EN); + } else if (_timer == TIM3) { + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN); + } else if (_timer == TIM14) { + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM14EN); + } else if (_timer == TIM15) { + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM15EN); + } else if (_timer == TIM16) { + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM16EN); + } else if (_timer == TIM17) { + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM17EN); + } +} + +void PwmTimer::setupOutputChannel() { + switch (_OCC) { + case OutputCompareChannel::OCC1: + CLEAR_BIT(_timer->CCER, TIM_CCER_CC1E); // CCxS bits are writable only when the channel is off + CLEAR_BIT(_timer->CCMR1, TIM_CCMR1_CC1S); // set chanel as output + SET_BIT(_timer->CCER, TIM_CCER_CC1E); // Channel is active high + SET_BIT(_timer->CCMR1, TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); // enable pwm mode 1 + CLEAR_BIT(_timer->CCER, TIM_CCER_CC1P); // enable output signal polarity of channel + break; + case OutputCompareChannel::OCC2: + CLEAR_BIT(_timer->CCER, TIM_CCER_CC2E); + CLEAR_BIT(_timer->CCMR1, TIM_CCMR1_CC2S); + SET_BIT(_timer->CCER, TIM_CCER_CC2E); + SET_BIT(_timer->CCMR1, TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1); + CLEAR_BIT(_timer->CCER, TIM_CCER_CC2P); + break; + case OutputCompareChannel::OCC3: + CLEAR_BIT(_timer->CCER, TIM_CCER_CC3E); + CLEAR_BIT(_timer->CCMR2, TIM_CCMR2_CC3S); + SET_BIT(_timer->CCER, TIM_CCER_CC3E); + SET_BIT(_timer->CCMR2, TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1); + CLEAR_BIT(_timer->CCER, TIM_CCER_CC3P); + break; + case OutputCompareChannel::OCC4: + CLEAR_BIT(_timer->CCER, TIM_CCER_CC4E); + CLEAR_BIT(_timer->CCMR2, TIM_CCMR2_CC4S); + SET_BIT(_timer->CCER, TIM_CCER_CC4E); + SET_BIT(_timer->CCMR2, TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1); + CLEAR_BIT(_timer->CCER, TIM_CCER_CC4P); + break; + } +} diff --git a/library/stm32f072xb/pwm-timer/pwm-timer.hpp b/library/stm32f072xb/pwm-timer/pwm-timer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d4faab766891ebabb1897bf2036fd443910b788a --- /dev/null +++ b/library/stm32f072xb/pwm-timer/pwm-timer.hpp @@ -0,0 +1,29 @@ +#ifndef PWM_TIMER_H +#define PWM_TIMER_H + +#include <stm32f0xx.h> +#include "output-compare-channel.hpp" + +class PwmTimer { + private: + TIM_TypeDef* _timer; + OutputCompareChannel _OCC; + uint16_t _pwmResolution; + uint16_t _pwmFrequency; + + public: + PwmTimer(TIM_TypeDef* timer, OutputCompareChannel OCC, uint16_t pwmResolution, uint16_t pwmFrequency); + PwmTimer(TIM_TypeDef* timer, + OutputCompareChannel OCC, + uint16_t pwmResolution, + uint16_t pwmFrequency, + uint16_t initalDutyCycle); + void setDutyCycle(uint16_t compareValue); + void setDutyCyclePercentage(uint8_t dutyCyclePercentage); + + private: + void enableTimer(); + void setupOutputChannel(); +}; + +#endif