diff --git a/library/stm32f072xb/dma/abstract-dma/abstract-dma.cpp b/library/stm32f072xb/dma/abstract-dma/abstract-dma.cpp
index c5bb305fa9399a168b9ebc023816446e48fe5981..a3766d05cb04fce1f8b01eec0391122d4f20665a 100644
--- a/library/stm32f072xb/dma/abstract-dma/abstract-dma.cpp
+++ b/library/stm32f072xb/dma/abstract-dma/abstract-dma.cpp
@@ -47,6 +47,12 @@ void AbstractDma::setPeripheral(DmaPeripheral peripheral) {
 		case DmaPeripheral::USART2_TX:
 			_dmaChannel->CPAR = (uintptr_t)&USART2->TDR;
 			break;
+		case DmaPeripheral::I2C1_RX:
+			_dmaChannel->CPAR = (uintptr_t)&I2C1->RXDR;
+			break;
+		case DmaPeripheral::I2C1_TX:
+			_dmaChannel->CPAR = (uintptr_t)&I2C1->TXDR;
+			break;
 	}
 }
 
diff --git a/library/stm32f072xb/dma/abstract-dma/abstract-dma.hpp b/library/stm32f072xb/dma/abstract-dma/abstract-dma.hpp
index c6987a3e5050ecf893d27aee32775eb70a8f72c1..fac2e6227276193944a671df8bef034876eeae9e 100644
--- a/library/stm32f072xb/dma/abstract-dma/abstract-dma.hpp
+++ b/library/stm32f072xb/dma/abstract-dma/abstract-dma.hpp
@@ -13,19 +13,20 @@ class AbstractDma {
 	void start();
 	void stop();
 
+	void setMemory(volatile const uint8_t* buffer);
+	void setNumberOfDataTransfer(uint16_t numberOfDataTransfer);
+
    protected:
 	void setupRegister();
 
    private:
-	void setPeripheral(DmaPeripheral peripheral);
-	void setMemory(volatile const uint8_t* buffer);
-	void setNumberOfDataTransfer(uint16_t numberOfDataTransfer);
-	void setDirection(DmaDirection direction);
-	void setCircularMode(DmaCircularMode circularMode);
-	void setMemoryIncrement(DmaMemoryIncrement memoryIncrement);
-	void setMemorySize(DmaMemorySize memorySize);
 	void setPeripheralIncrement(DmaPeripheralIncrement peripheralIncrement);
 	void setPeripheralSize(DmaPeripheralSize peripheralSize);
+	void setMemoryIncrement(DmaMemoryIncrement memoryIncrement);
+	void setMemorySize(DmaMemorySize memorySize);
+	void setCircularMode(DmaCircularMode circularMode);
+	void setDirection(DmaDirection direction);
+	void setPeripheral(DmaPeripheral peripheral);
 
 	DMA_Channel_TypeDef* _dmaChannel;
 	AbstractDmaInit& _dmaInit;
diff --git a/library/stm32f072xb/dma/dma-register/dma-peripheral.hpp b/library/stm32f072xb/dma/dma-register/dma-peripheral.hpp
index 059067de285866b79d4cdf7055fddb6773b4bb60..b7318343205aa3b5e1d718093220e43a2548f2bf 100644
--- a/library/stm32f072xb/dma/dma-register/dma-peripheral.hpp
+++ b/library/stm32f072xb/dma/dma-register/dma-peripheral.hpp
@@ -2,6 +2,6 @@
 #define DMA_PERIPHERAL_H
 
 namespace stm32f072xb {
-enum class DmaPeripheral { USART2_RX, USART2_TX };
+enum class DmaPeripheral { USART2_RX, USART2_TX, I2C1_RX, I2C1_TX };
 }
 #endif
diff --git a/library/stm32f072xb/dma/dma.cpp b/library/stm32f072xb/dma/dma.cpp
index 8c8b5ede33d4725f6ea6dfb6a664f4ebda99a1b5..11f76ce185304a0cdb2d56e2c9f8dead0b9002fd 100644
--- a/library/stm32f072xb/dma/dma.cpp
+++ b/library/stm32f072xb/dma/dma.cpp
@@ -2,6 +2,16 @@
 
 using namespace stm32f072xb;
 
+Dma<DmaChannel::CHANNEL2, DmaPeripheral::I2C1_TX>::Dma(AbstractDmaInit& dmaInit, I2c& i2c)
+    : AbstractDma(DMA1_Channel2, dmaInit) {
+	i2c.enableDmaTransmission();
+}
+
+Dma<DmaChannel::CHANNEL3, DmaPeripheral::I2C1_RX>::Dma(AbstractDmaInit& dmaInit, I2c& i2c)
+    : AbstractDma(DMA1_Channel3, dmaInit) {
+	i2c.enableDmaReception();
+}
+
 Dma<DmaChannel::CHANNEL4, DmaPeripheral::USART2_TX>::Dma(AbstractDmaInit& dmaInit,
                                                          Uart<UartRegister::UART2, Port::A>& uart)
     : AbstractDma(DMA1_Channel4, dmaInit) {
diff --git a/library/stm32f072xb/dma/dma.hpp b/library/stm32f072xb/dma/dma.hpp
index 42ab955155c16dd4b0668d0ba1bcb4bc2b2b2792..86f01707f2dec90dc6734da74429d01ae4d844ea 100644
--- a/library/stm32f072xb/dma/dma.hpp
+++ b/library/stm32f072xb/dma/dma.hpp
@@ -4,6 +4,7 @@
 
 #include "abstract-dma.hpp"
 #include "dma-register.hpp"
+#include "i2c.hpp"
 #include "stm32f072xb.h"
 #include "uart.hpp"
 
@@ -12,6 +13,18 @@ namespace stm32f072xb {
 template <DmaChannel T, DmaPeripheral P>
 class Dma {};
 
+template <>
+class Dma<DmaChannel::CHANNEL2, DmaPeripheral::I2C1_TX> : public AbstractDma {
+   public:
+	Dma(AbstractDmaInit& dmaInit, I2c& i2c);
+};
+
+template <>
+class Dma<DmaChannel::CHANNEL3, DmaPeripheral::I2C1_RX> : public AbstractDma {
+   public:
+	Dma(AbstractDmaInit& dmaInit, I2c& i2c);
+};
+
 template <>
 class Dma<DmaChannel::CHANNEL4, DmaPeripheral::USART2_TX> : public AbstractDma {
    public:
diff --git a/library/stm32f072xb/gpio/i2c-gpio/i2c-gpio.hpp b/library/stm32f072xb/gpio/i2c-gpio/i2c-gpio.hpp
index 61e9d52f186d2ff7b876d0e44bd9030367177e92..bf7ee0d7a9976abf9f13411f40969d968a00554c 100644
--- a/library/stm32f072xb/gpio/i2c-gpio/i2c-gpio.hpp
+++ b/library/stm32f072xb/gpio/i2c-gpio/i2c-gpio.hpp
@@ -18,6 +18,6 @@ class I2cGpio : public AlternateGpio<T> {
 	}
 };
 
-};  // namespace stm32f072xb
+}  // namespace stm32f072xb
 
 #endif
diff --git a/library/stm32f072xb/i2c/i2c.cpp b/library/stm32f072xb/i2c/i2c.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b02f4a4554e2e86f243f10c482a5c717d4572949
--- /dev/null
+++ b/library/stm32f072xb/i2c/i2c.cpp
@@ -0,0 +1,172 @@
+#include "i2c.hpp"
+
+using namespace stm32f072xb;
+
+I2c::I2c(I2C_TypeDef* type) : _type(type) {
+	setupI2c();
+	setupGpio();
+	softwareReset();
+}
+
+void I2c::configureI2cClock() {
+	// Clear TIMINGR
+	_type->TIMINGR = 0;
+
+	// I2CCLK is at 8MHz
+	SET_BIT(_type->TIMINGR, (0x1 << I2C_TIMINGR_PRESC_Pos));
+	SET_BIT(_type->TIMINGR, (0x13 << I2C_TIMINGR_SCLL_Pos));
+	SET_BIT(_type->TIMINGR, (0xF << I2C_TIMINGR_SCLH_Pos));
+	SET_BIT(_type->TIMINGR, (0x4 << I2C_TIMINGR_SCLDEL_Pos));
+	SET_BIT(_type->TIMINGR, (0x2 << I2C_TIMINGR_SDADEL_Pos));
+}
+
+void I2c::setupGpio() {
+	if (_type == I2C1) {
+		// Setup GPIO - Open Drain
+		I2cGpioInit sdaInit = {GpioPin::P9, Pupdr::PULL_UP, AlternateFunction::AF1};
+		I2cGpio<Port::B> sda(sdaInit);
+		I2cGpioInit sclInit = {GpioPin::P8, Pupdr::PULL_UP, AlternateFunction::AF1};
+		I2cGpio<Port::B> scl(sclInit);
+	} else if (_type == I2C2) {
+		// Setup GPIO - Open Drain
+		I2cGpioInit sdaInit = {GpioPin::P11, Pupdr::PULL_UP, AlternateFunction::AF1};
+		I2cGpio<Port::B> sda(sdaInit);
+		I2cGpioInit sclInit = {GpioPin::P10, Pupdr::PULL_UP, AlternateFunction::AF1};
+		I2cGpio<Port::B> scl(sclInit);
+	}
+}
+
+void I2c::enableI2cClock() {
+	if (_type == I2C1) {
+		// Set HSI (8MHz) as I2C1 clock (Only available for I2C1)
+		CLEAR_BIT(RCC->CFGR3, RCC_CFGR3_I2C1SW);
+
+		// Enable I2C peripheral clock
+		SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);
+	} else if (_type == I2C2) {
+		// Enable I2C peripheral clock
+		SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C2EN);
+	}
+}
+
+void I2c::setupI2c() {
+	// Chose clock source and enable clock
+	enableI2cClock();
+
+	// Make sure I2C is disabled
+	CLEAR_BIT(_type->CR1, I2C_CR1_PE);
+
+	// Setup timing 8MHz clock
+	configureI2cClock();
+
+	// Setup 7 bit addressing
+	CLEAR_BIT(_type->CR2, I2C_CR2_ADD10);
+
+	// I2C automatically sends stop bit after nBytes have been transmitted
+	// I2C automatically sends NACKF before stop bit after nBytes have been received in master receive mode
+	SET_BIT(_type->CR2, I2C_CR2_AUTOEND);
+
+	// Enable clock stretching (This needs to be enabled as we are in master mode)
+	CLEAR_BIT(_type->CR1, I2C_CR1_NOSTRETCH);
+
+	// Enable analog noise filter
+	CLEAR_BIT(_type->CR1, I2C_CR1_ANFOFF);
+
+	// Enable I2C peripheral
+	SET_BIT(_type->CR1, I2C_CR1_PE);
+}
+
+void I2c::setupTransmission(uint8_t slaveAddress, uint8_t bufferSize) {
+	// Clear Start bit to allow for changes
+	CLEAR_BIT(_type->CR2, I2C_CR2_START);
+
+	// Setup SADD and NBYTES
+	CLEAR_BIT(_type->CR2, I2C_CR2_NBYTES);
+	SET_BIT(_type->CR2, bufferSize << I2C_CR2_NBYTES_Pos);
+	CLEAR_BIT(_type->CR2, I2C_CR2_SADD);
+	SET_BIT(_type->CR2, slaveAddress << I2C_CR2_SADD_Pos);
+}
+
+void I2c::blockingWrite(uint8_t slaveAddress, uint8_t* bytes, uint8_t bufferSize) {
+	// Send message until it is accepted by the slave
+	do {
+		// Clear NACK ISR bit
+		SET_BIT(_type->ICR, I2C_ICR_NACKCF);
+
+		// Set TXE bit to flush previous content of TXDR
+		SET_BIT(_type->ISR, I2C_ISR_TXE);
+
+		// Setup slave address and nBytes to be sent
+		setupTransmission(slaveAddress, bufferSize);
+
+		// Clear RD_WRN bit to indicate a write
+		CLEAR_BIT(_type->CR2, I2C_CR2_RD_WRN);
+
+		for (int i = 0; i < bufferSize; i++) {
+			_type->TXDR = bytes[i];
+
+			if (i == 0)
+				SET_BIT(_type->CR2, I2C_CR2_START);
+
+			// TODO - Replace polling with DMA
+			while ((_type->ISR & I2C_ISR_TXE) != I2C_ISR_TXE) {
+				// If we receive a NACK flush TXDR and stop sending bytes
+				if ((_type->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF) {
+					i = bufferSize;
+					SET_BIT(_type->ISR, I2C_ISR_TXE);
+				}
+			}
+		}
+	} while ((_type->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF);
+}
+
+void I2c::blockingRead(uint8_t slaveAddress, uint8_t* bytes, uint8_t bufferSize) {
+	// TODO - make sure All bytes are received
+
+	// Clear NACK ISR bit
+	SET_BIT(_type->ICR, I2C_ICR_NACKCF);
+
+	setupTransmission(slaveAddress, bufferSize);
+
+	// Clear RD_WRN bit to indicate a write
+	SET_BIT(_type->CR2, I2C_CR2_RD_WRN);
+
+	// Send Start condition to begin receiving data
+	SET_BIT(_type->CR2, I2C_CR2_START);
+
+	for (int i = 0; i < bufferSize; i++) {
+		while ((_type->ISR & I2C_ISR_RXNE) != I2C_ISR_RXNE)
+			;
+		bytes[i] = _type->RXDR;
+	}
+}
+
+void I2c::write(uint8_t slaveAddress, uint8_t bufferSize) {
+	setupTransmission(slaveAddress, bufferSize);
+	// Clear RD_WRN bit to indicate a write
+	CLEAR_BIT(_type->CR2, I2C_CR2_RD_WRN);
+	// Send Start condition to begin receiving data
+	SET_BIT(_type->CR2, I2C_CR2_START);
+}
+
+void I2c::read(uint8_t slaveAddress, uint8_t bufferSize) {
+	setupTransmission(slaveAddress, bufferSize);
+	// Clear RD_WRN bit to indicate a write
+	SET_BIT(_type->CR2, I2C_CR2_RD_WRN);
+	// Send Start condition to begin receiving data
+	SET_BIT(_type->CR2, I2C_CR2_START);
+}
+
+void I2c::softwareReset() {
+	CLEAR_BIT(I2C1->CR1, I2C_CR1_PE);
+	READ_BIT(I2C1->CR1, I2C_CR1_PE);
+	SET_BIT(I2C1->CR1, I2C_CR1_PE);
+}
+
+void I2c::enableDmaReception() {
+	SET_BIT(_type->CR1, I2C_CR1_RXDMAEN);
+}
+
+void I2c::enableDmaTransmission() {
+	SET_BIT(_type->CR1, I2C_CR1_TXDMAEN);
+}
diff --git a/library/stm32f072xb/i2c/i2c.hpp b/library/stm32f072xb/i2c/i2c.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..26be0b2026fd94691e41177b825effdb633e5977
--- /dev/null
+++ b/library/stm32f072xb/i2c/i2c.hpp
@@ -0,0 +1,58 @@
+#ifndef I2C_H
+#define I2C_H
+
+#include "i2c-gpio.hpp"
+// https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
+
+// The default address for an encoder that's just been turrned on.
+#define I2CENCODER_DEFAULT_ADDRESS 0x60
+
+// Registers for important data
+// See : https://www.vexforum.com/t/vex-i2c-integrated-motor-encoder-device-spec/20600
+
+// Write to this register to change the addresse of the vex encoder
+// By default the adress is 0x60
+#define I2CENCODER_ADDRESS_REGISTER 0x4D
+
+// Read these registers to get data from the encoder
+#define I2CENCODER_POSITION_REGISTER 0x40
+#define I2CENCODER_VELOCITY_REGISTER 0x44
+
+// Write to this register to zero the encoder
+#define I2CENCODER_ZERO_REGISTER 0x4A
+
+// Using address 0x00 write to this register by sending bytes 0xCA and 0x03
+#define I2CENCODER_RESET_ALL_DEVICES 0x4E
+
+// Write to these registers to Terminate/Unterminate encoder. Only usefull when daisy chaining encoders
+#define I2CENCODER_UNTERMINATE_REGISTER 0x4B
+#define I2CENCODER_TERMINATE_REGISTER 0x4C
+
+namespace stm32f072xb {
+
+class I2c {
+   private:
+	I2C_TypeDef* _type;
+
+   public:
+	I2c(I2C_TypeDef* type);
+	void blockingWrite(uint8_t slaveAddress, uint8_t* bytes, uint8_t bufferSize);
+	void blockingRead(uint8_t slaveAddress, uint8_t* bytes, uint8_t bufferSize);
+	void write(uint8_t slaveAddress, uint8_t bufferSize);
+	void read(uint8_t slaveAddress, uint8_t bufferSize);
+	void softwareReset();
+
+	void enableDmaReception();
+	void enableDmaTransmission();
+
+   private:
+	void setupI2c();
+	void configureI2cClock();
+	void setupGpio();
+	void enableI2cClock();
+	void setupTransmission(uint8_t slaveAddress, uint8_t nBytes);
+};
+
+}  // namespace stm32f072xb
+
+#endif
diff --git a/library/stm32f072xb/meson.build b/library/stm32f072xb/meson.build
index c06c6d389f60cee4c379f4ad22ef626ffa6c04d2..a37e21c6e7bb6537f7e50841c251133cf99bf736 100644
--- a/library/stm32f072xb/meson.build
+++ b/library/stm32f072xb/meson.build
@@ -17,6 +17,9 @@ stm32f072xb_modules += ['uart']
 stm32f072xb_modules += ['uart/abstract-uart']
 stm32f072xb_modules += ['uart/uart-register']
 
+stm32f072xb_modules += ['i2c']
+
+
 stm32f072xb_modules += ['dma']
 stm32f072xb_modules += ['dma/abstract-dma']
 stm32f072xb_modules += ['dma/dma-register']
diff --git a/samples/meson.build b/samples/meson.build
index 0e4afd3297adb0efa66bf265f17ac9bcb8454d3d..424affeef8c3b368c6db45088fcdc3c51e572716 100644
--- a/samples/meson.build
+++ b/samples/meson.build
@@ -8,6 +8,7 @@ samples += ['interruption']
 samples += ['pwm']
 samples += ['extended-interrupt']
 samples += ['dma-uart']
+samples += ['dma-i2c']
 samples += ['timeout-system-clock']
 samples += ['timeout-callback']
 samples += ['uart']
diff --git a/samples/stm32f072xb/dma-i2c/main.cpp b/samples/stm32f072xb/dma-i2c/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ea7338a5e1ea1b36480c77d1dd06897471ddb52
--- /dev/null
+++ b/samples/stm32f072xb/dma-i2c/main.cpp
@@ -0,0 +1,11 @@
+#include <dma.hpp>
+#include <gpio.hpp>
+#include <i2c-gpio.hpp>
+#include <i2c.hpp>
+
+using namespace stm32f072xb;
+
+int main() {
+	while (true) {
+	}
+}