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, &reg, bufferSize);
+// }
+
+void VexEncoder::_accessRegister(uint8_t reg, uint8_t address, I2c& i2c) {
+	i2c.blockingWrite(address, &reg, 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