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) { + } +}