diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4307d22ebb9b8f788937234da5cbce6fb0a40d14..72516192f67d941830b02456351511511052db84 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -241,11 +241,17 @@ sbs_target(test-xbee-snd stm32f429zi_stm32f4discovery)
add_executable(test-usart src/tests/drivers/usart/test-usart.cpp)
sbs_target(test-usart stm32f407vg_stm32f4discovery)
-# add_executable(test-i2c-driver src/tests/drivers/i2c/test-i2c-driver.cpp)
-# sbs_target(test-i2c-driver stm32f429zi_stm32f4discovery)
+add_executable(test-i2c-driver-f4 src/tests/drivers/i2c/test-i2c-driver.cpp)
+sbs_target(test-i2c-driver-f4 stm32f429zi_stm32f4discovery)
-# add_executable(test-i2c src/tests/drivers/i2c/test-i2c.cpp)
-# sbs_target(test-i2c stm32f429zi_stm32f4discovery)
+add_executable(test-i2c-f4 src/tests/drivers/i2c/test-i2c.cpp)
+sbs_target(test-i2c-f4 stm32f429zi_stm32f4discovery)
+
+add_executable(test-i2c-driver-f7 src/tests/drivers/i2c/test-i2c-driver.cpp)
+sbs_target(test-i2c-driver-f7 stm32f767zi_nucleo)
+
+add_executable(test-i2c-f7 src/tests/drivers/i2c/test-i2c.cpp)
+sbs_target(test-i2c-f7 stm32f767zi_nucleo)
add_executable(test-internal-temp src/tests/drivers/test-internal-temp.cpp)
sbs_target(test-internal-temp stm32f407vg_stm32f4discovery)
diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake
index 2ddcb8dd831cae9f1db139f84484e882f42374ff..a3d1da7a95ea1553aeecedb6b9d4ca498c3838ef 100644
--- a/cmake/boardcore.cmake
+++ b/cmake/boardcore.cmake
@@ -57,7 +57,8 @@ foreach(OPT_BOARD ${BOARDS})
${SBS_BASE}/src/shared/drivers/runcam/Runcam.cpp
${SBS_BASE}/src/shared/drivers/spi/SPITransaction.cpp
${SBS_BASE}/src/shared/drivers/usart/USART.cpp
- ${SBS_BASE}/src/shared/drivers/i2c/I2CDriver.cpp
+ ${SBS_BASE}/src/shared/drivers/i2c/I2CDriver-f4.cpp
+ ${SBS_BASE}/src/shared/drivers/i2c/I2CDriver-f7.cpp
${SBS_BASE}/src/shared/drivers/i2c/I2C.cpp
# Events
diff --git a/src/shared/drivers/i2c/I2CDriver.cpp b/src/shared/drivers/i2c/I2CDriver-f4.cpp
similarity index 98%
rename from src/shared/drivers/i2c/I2CDriver.cpp
rename to src/shared/drivers/i2c/I2CDriver-f4.cpp
index 53a938783f83578ad2d4a70a63dcbcdd14e01163..79288dc17fddace034b1afe2a28e0dd0a9d2591c 100644
--- a/src/shared/drivers/i2c/I2CDriver.cpp
+++ b/src/shared/drivers/i2c/I2CDriver-f4.cpp
@@ -20,13 +20,15 @@
* THE SOFTWARE.
*/
-#include "I2CDriver.h"
+#ifdef _ARCH_CORTEXM4_STM32F4
#include <assert.h>
#include <kernel/scheduler/scheduler.h>
#include <utils/ClockUtils.h>
#include <utils/Debug.h>
+#include "I2CDriver.h"
+
namespace I2CConsts
{
static Boardcore::I2CDriver *ports[N_I2C_PORTS] =
@@ -359,7 +361,7 @@ void I2CDriver::setupPeripheral(const I2CSlaveConfig &slaveConfig)
i2c->CCR =
I2CConsts::f * 5; // Setting the CCR bits (implicit Standard mode)
}
- else
+ else if (slaveConfig.speed == Speed::FAST)
{
// [WARNING] Hardcoded to use DUTY = 0
i2c->CCR = I2C_CCR_FS | // Selecting Fast mode
@@ -370,6 +372,10 @@ void I2CDriver::setupPeripheral(const I2CSlaveConfig &slaveConfig)
// I2C_CCR_DUTY | // Selecting duty cycle of 9 - 16
// f * 2 / 5; // Setting the CCR bits (f * 10 / 25)
}
+ else
+ {
+ D(assert(false && "speed not supported!"));
+ }
// Configuring the TRISE
i2c->TRISE = (I2CConsts::f & I2C_CR2_FREQ) + 1;
@@ -397,8 +403,7 @@ bool I2CDriver::read(const I2CSlaveConfig &slaveConfig, void *buffer,
i2c->CR1 =
((nBytes <= 1) ? (i2c->CR1 & ~I2C_CR1_ACK) : (i2c->CR1 | I2C_CR1_ACK));
- // Sending doOperation when the channel isn't busy (LSB set to signal this
- // is a read)
+ // Sending doOperation when the channel isn't busy
return doOperation(slaveConfig);
};
@@ -703,4 +708,6 @@ void I2CDriver::IRQhandleErrInterrupt()
IRQwakeUpWaitingThread();
}
-} // namespace Boardcore
\ No newline at end of file
+} // namespace Boardcore
+
+#endif // _ARCH_CORTEXM4_STM32F4
\ No newline at end of file
diff --git a/src/shared/drivers/i2c/I2CDriver-f7.cpp b/src/shared/drivers/i2c/I2CDriver-f7.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..606e3835ad37592b200dbc0bfa43f5ea9e414775
--- /dev/null
+++ b/src/shared/drivers/i2c/I2CDriver-f7.cpp
@@ -0,0 +1,720 @@
+/* Copyright (c) 2022 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.
+ */
+
+#ifdef _ARCH_CORTEXM7_STM32F7
+
+#include <assert.h>
+#include <kernel/scheduler/scheduler.h>
+#include <utils/ClockUtils.h>
+#include <utils/Debug.h>
+
+#include "I2CDriver.h"
+
+namespace I2CConsts
+{
+static Boardcore::I2CDriver *ports[N_I2C_PORTS] =
+ {}; ///< Pointer to serial port classes to
+ ///< let interrupts access the classes
+static const int MAX_N_POLLING =
+ 2000; ///< Maximum number of cycles for polling
+static const int N_SCL_BITBANG =
+ 16; ///< Number of clocks created for slave locked bus recovery
+static const uint8_t I2C_PIN_ALTERNATE_FUNCTION =
+ 4; ///< Alternate Function number of the I2C peripheral pins
+static uint8_t f; ///< APB peripheral clock frequency
+} // namespace I2CConsts
+
+#ifdef I2C1
+/**
+ * I2C1 event interrupt
+ */
+void __attribute__((naked)) I2C1_EV_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15I2C1HandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C1 event interrupt actual implementation
+ */
+void __attribute__((used)) I2C1HandlerImpl()
+{
+ auto *port = I2CConsts::ports[0];
+ if (port)
+ {
+ port->IRQhandleInterrupt();
+ }
+}
+
+/**
+ * I2C1 error interrupt
+ */
+void __attribute__((naked)) I2C1_ER_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z18I2C1errHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C1 error interrupt actual implementation
+ */
+void __attribute__((used)) I2C1errHandlerImpl()
+{
+ auto *port = I2CConsts::ports[0];
+ if (port)
+ {
+ port->IRQhandleErrInterrupt();
+ }
+}
+#endif
+
+#ifdef I2C2
+/**
+ * I2C2 event interrupt
+ */
+void __attribute__((naked)) I2C2_EV_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15I2C2HandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C2 event interrupt actual implementation
+ */
+void __attribute__((used)) I2C2HandlerImpl()
+{
+ auto *port = I2CConsts::ports[1];
+ if (port)
+ {
+ port->IRQhandleInterrupt();
+ }
+}
+
+/**
+ * I2C2 error interrupt
+ */
+void __attribute__((naked)) I2C2_ER_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z18I2C2errHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C2 error interrupt actual implementation
+ */
+void __attribute__((used)) I2C2errHandlerImpl()
+{
+ auto *port = I2CConsts::ports[1];
+ if (port)
+ {
+ port->IRQhandleErrInterrupt();
+ }
+}
+#endif
+
+#ifdef I2C3
+/**
+ * I2C3 event interrupt
+ */
+void __attribute__((naked)) I2C3_EV_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15I2C3HandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C3 event interrupt actual implementation
+ */
+void __attribute__((used)) I2C3HandlerImpl()
+{
+ auto *port = I2CConsts::ports[2];
+ if (port)
+ {
+ port->IRQhandleInterrupt();
+ }
+}
+
+/**
+ * I2C3 error interrupt
+ */
+void __attribute__((naked)) I2C3_ER_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z18I2C3errHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C3 error interrupt actual implementation
+ */
+void __attribute__((used)) I2C3errHandlerImpl()
+{
+ auto *port = I2CConsts::ports[2];
+ if (port)
+ {
+ port->IRQhandleErrInterrupt();
+ }
+}
+#endif
+
+#ifdef I2C4
+/**
+ * I2C4 event interrupt
+ */
+void __attribute__((naked)) I2C4_EV_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15I2C4HandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C4 event interrupt actual implementation
+ */
+void __attribute__((used)) I2C4HandlerImpl()
+{
+ auto *port = I2CConsts::ports[3];
+ if (port)
+ {
+ port->IRQhandleInterrupt();
+ }
+}
+
+/**
+ * I2C4 error interrupt
+ */
+void __attribute__((naked)) I2C4_ER_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z18I2C4errHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C4 error interrupt actual implementation
+ */
+void __attribute__((used)) I2C4errHandlerImpl()
+{
+ auto *port = I2CConsts::ports[3];
+ if (port)
+ {
+ port->IRQhandleErrInterrupt();
+ }
+}
+#endif
+
+namespace Boardcore
+{
+
+I2CDriver::I2CDriver(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda)
+ : i2c(i2c), scl(scl), sda(sda), transaction()
+{
+ // Setting the id and irqn of the right i2c peripheral
+ switch (reinterpret_cast<uint32_t>(i2c))
+ {
+#ifdef I2C1
+ case I2C1_BASE:
+ this->id = 1;
+ irqnEv = I2C1_EV_IRQn;
+ irqnErr = I2C1_ER_IRQn;
+ break;
+#endif
+#ifdef I2C2
+ case I2C2_BASE:
+ this->id = 2;
+ irqnEv = I2C2_EV_IRQn;
+ irqnErr = I2C2_ER_IRQn;
+ break;
+#endif
+#ifdef I2C3
+ case I2C3_BASE:
+ this->id = 3;
+ irqnEv = I2C3_EV_IRQn;
+ irqnErr = I2C3_ER_IRQn;
+ break;
+#endif
+#ifdef I2C4
+ case I2C4_BASE:
+ this->id = 4;
+ irqnEv = I2C4_EV_IRQn;
+ irqnErr = I2C4_ER_IRQn;
+ break;
+#endif
+ default:
+ // Checking that the peripheral is present in this architecture
+ D(assert(false &&
+ "I2C peripheral not present in this architecture"));
+ break;
+ }
+
+ {
+ miosix::FastInterruptDisableLock dLock;
+
+ // Initializing the alternate function and mode of the pins so we won't
+ // forget the open-drain mode, avoiding eventual short-circuits between
+ // master and slaves when they both drive the same bus on two different
+ // logical values.
+ scl.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
+ sda.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
+ scl.mode(miosix::Mode::ALTERNATE_OD);
+ sda.mode(miosix::Mode::ALTERNATE_OD);
+ }
+
+ // Checking that this particular I2C port hasn't been already instantiated
+ D(assert(id > 0));
+ D(assert(I2CConsts::ports[id - 1] == nullptr));
+
+ // Enabling the peripheral's clock
+ ClockUtils::enablePeripheralClock(i2c);
+
+ init();
+
+ // Add to the array of i2c peripherals so that the interrupts can see it
+ I2CConsts::ports[id - 1] = this;
+
+ // Enabling the interrupts (Ev and Err) in the NVIC
+ NVIC_SetPriority(irqnEv, 15);
+ NVIC_ClearPendingIRQ(irqnEv);
+ NVIC_EnableIRQ(irqnEv);
+ NVIC_SetPriority(irqnErr, 15);
+ NVIC_ClearPendingIRQ(irqnErr);
+ NVIC_EnableIRQ(irqnErr);
+}
+
+I2CDriver::~I2CDriver()
+{
+ // Removing the relative i2c port from the array
+ I2CConsts::ports[id - 1] = nullptr;
+
+ // Disabling the interrupts (Ev and Err) in the NVIC
+ NVIC_DisableIRQ(irqnEv);
+ NVIC_DisableIRQ(irqnErr);
+
+ // Disabling the peripheral
+ i2c->CR1 &= ~I2C_CR1_PE;
+
+ // Disabling the peripheral on the bus
+ ClockUtils::disablePeripheralClock(i2c);
+}
+
+void I2CDriver::init()
+{
+ // Resetting the I2C peripheral before setting the registers (resetting PE
+ // bit resets the peripheral)
+ i2c->CR1 = 0;
+
+ // Retrieving the frequency of the APB relative to the I2C peripheral
+ // [MHz] (I2C peripherals are always connected to APB1, Low speed bus). In
+ // fact by default the I2C peripheral is clocked by the APB1 bus; anyway HSI
+ // and SYSCLK can be chosen.
+ I2CConsts::f =
+ ClockUtils::getAPBPeripheralsClock(ClockUtils::APB::APB1) / 1000000;
+
+ // I2CCLK < (t_low - t_filters) / 4
+ // I2CCLK < t_high
+ // I2CCLK < 4/3 * t_SCL
+}
+
+bool I2CDriver::read(const I2CSlaveConfig &slaveConfig, void *buffer,
+ size_t nBytes)
+{
+ // Setting up the read transaction
+ transaction.operation = Operation::READ;
+ transaction.buffWrite = nullptr;
+ transaction.buffRead = static_cast<uint8_t *>(buffer);
+ transaction.nBytes = nBytes;
+ transaction.nBytesDone = 0;
+ transaction.generateStop = true;
+
+ // enabling only the RX interrupts
+ i2c->CR1 &= ~I2C_CR1_TXIE;
+ i2c->CR1 |= I2C_CR1_RXIE;
+
+ // Sending doOperation when the channel isn't busy
+ return doOperation(slaveConfig);
+};
+
+bool I2CDriver::write(const I2CSlaveConfig &slaveConfig, const void *buffer,
+ size_t nBytes, bool generateStop)
+{
+ // Setting up the write transaction
+ transaction.operation = Operation::WRITE;
+ transaction.buffWrite = static_cast<const uint8_t *>(buffer);
+ transaction.buffRead = nullptr;
+ transaction.nBytes = nBytes;
+ transaction.nBytesDone = 0;
+ transaction.generateStop = generateStop;
+
+ // enabling only the TX interrupts
+ i2c->CR1 &= ~I2C_CR1_RXIE;
+ i2c->CR1 |= I2C_CR1_TXIE;
+
+ // Sending doOperation when the channel isn't busy
+ return doOperation(slaveConfig);
+};
+
+bool I2CDriver::doOperation(const I2CSlaveConfig &slaveConfig)
+{
+ // Not yet supported
+ D(assert(slaveConfig.addressing == Addressing::BIT7 &&
+ "Only 7-bit addressing supported!"));
+
+ // If already detected a locked state return directly without loosing time
+ if (lastError & Errors::BUS_LOCKED)
+ {
+ reStarting = false;
+ return false;
+ }
+
+ // If starting a new transaction (so STOP bit sent in previous transaction),
+ // wait for the bus to be clear
+ if (!reStarting)
+ {
+ // Waiting for the bus to be clear
+ uint32_t i{0};
+ for (; (i < I2CConsts::MAX_N_POLLING) && (i2c->ISR & I2C_ISR_BUSY); ++i)
+ ;
+
+ // Locked state detected after N polling cycles
+ if (i == I2CConsts::MAX_N_POLLING)
+ {
+ lastError = Errors::BUS_LOCKED;
+ LOG_ERR(logger, fmt::format("I2C{} bus locked state detected", id));
+ return false;
+ }
+
+ // Setting up the peripheral when the bus is clear in order to
+ // communicate in the mode wanted by the slave device
+ setupPeripheral(slaveConfig);
+ }
+
+ reStarting = false;
+
+ // Setting up transaction
+ setupTransaction();
+
+ // Starting the transaction with the START bit
+ // From the wait till the end of transaction it will all be executed in the
+ // event Interrupt Service Routine
+ {
+ miosix::FastInterruptDisableLock dLock;
+
+ // Sending the start condition
+ i2c->CR2 |= I2C_CR2_START;
+
+ // Making the thread wait for the operation completion. The next steps
+ // will be performed in the ISR while the thread stays in waiting state.
+ // The only way the thread will be waken up are the completion of the
+ // operation or an error during the transaction.
+ return IRQwaitForOperationCompletion(dLock);
+ }
+}
+
+void I2CDriver::setupPeripheral(const I2CSlaveConfig &slaveConfig)
+{
+ // Disabling the I2C peripheral before setting the registers
+ i2c->CR1 &= ~I2C_CR1_PE;
+
+ // setting the SCLH and SCLL bits in I2C_TIMINGR register to generate
+ // correct timings for each speed modes
+ if (slaveConfig.speed == Speed::STANDARD)
+ {
+ // PRESC = 0xb
+ // SCLL = 0x13
+ // SCLH = 0xf
+ // SDADEL = 0x2
+ // SCLDEL = 0x4
+ i2c->TIMINGR = (0xb << I2C_TIMINGR_PRESC_Pos) | // PRESC
+ (0x13 << I2C_TIMINGR_SCLL_Pos) | // SCLL
+ (0xf << I2C_TIMINGR_SCLH_Pos) | // SCLH
+ (0x2 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
+ (0x4 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
+ }
+ else if (slaveConfig.speed == Speed::FAST)
+ {
+ // PRESC = 0x5
+ // SCLL = 0x9
+ // SCLH = 0x3
+ // SDADEL = 0x3
+ // SCLDEL = 0x3
+ i2c->TIMINGR = (0x5 << I2C_TIMINGR_PRESC_Pos) | // PRESC
+ (0x9 << I2C_TIMINGR_SCLL_Pos) | // SCLL
+ (0x3 << I2C_TIMINGR_SCLH_Pos) | // SCLH
+ (0x3 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
+ (0x3 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
+ }
+ else if (slaveConfig.speed == Speed::FAST_PLUS)
+ {
+ // PRESC = 0x5
+ // SCLL = 0x3
+ // SCLH = 0x1
+ // SDADEL = 0x0
+ // SCLDEL = 0x1
+ i2c->TIMINGR = (0x5 << I2C_TIMINGR_PRESC_Pos) | // PRESC
+ (0x3 << I2C_TIMINGR_SCLL_Pos) | // SCLL
+ (0x1 << I2C_TIMINGR_SCLH_Pos) | // SCLH
+ (0x0 << I2C_TIMINGR_SDADEL_Pos) | // SDADEL
+ (0x1 << I2C_TIMINGR_SCLDEL_Pos); // SCLDEL
+ }
+ else
+ {
+ D(assert(false && "speed not supported"));
+ }
+
+ // setting addressing mode, read or write mode and slave address
+ i2c->CR2 =
+ (slaveConfig.addressing == Addressing::BIT10 ? I2C_CR2_ADD10 : 0) |
+ (slaveConfig.slaveAddress << (I2C_CR2_SADD_Pos + 1));
+
+ // Finally enabling the peripheral
+ i2c->CR1 |= I2C_CR1_PE;
+}
+
+void I2CDriver::setupTransaction()
+{
+ // Setting the direction of the transaction
+ if (transaction.operation == Operation::READ)
+ {
+ i2c->CR2 |= I2C_CR2_RD_WRN;
+ }
+ else
+ {
+ i2c->CR2 &= ~I2C_CR2_RD_WRN;
+ }
+
+ // setting automatic stop generation if we won't generate a reStart
+ if (transaction.generateStop)
+ {
+ i2c->CR2 |= I2C_CR2_AUTOEND;
+ }
+ else
+ {
+ i2c->CR2 &= ~I2C_CR2_AUTOEND;
+ }
+
+ // setting registers for the remaining bytes
+ setupReload();
+}
+
+void I2CDriver::setupReload()
+{
+ if (transaction.nBytes - transaction.nBytesDone <= 0xffu)
+ {
+ i2c->CR2 &= ~I2C_CR2_RELOAD;
+ i2c->CR2 |= (transaction.nBytes - transaction.nBytesDone)
+ << I2C_CR2_NBYTES_Pos;
+ }
+ else
+ {
+ i2c->CR2 |=
+ I2C_CR2_RELOAD | // There must be a reload
+ 0xff << I2C_CR2_NBYTES_Pos; // maximum bytes that can be sent
+ }
+}
+
+void I2CDriver::flushBus()
+{
+ // If there isn't any locked state return immediately
+ if (!(lastError & Errors::BUS_LOCKED))
+ {
+ return;
+ }
+
+ // Set the period of the bit-banged clock (Default to standard mode)
+ uint8_t toggleDelay = 5;
+
+ {
+ miosix::FastInterruptDisableLock dLock;
+
+ // Recovery from the locked state due to a stuck Slave.
+ // We bit-bang 16 clocks on the scl line in order to restore pending
+ // packets of the slaves.
+ scl.mode(miosix::Mode::OPEN_DRAIN);
+ }
+
+ for (size_t c = 0; c < I2CConsts::N_SCL_BITBANG; c++)
+ {
+ scl.low();
+ miosix::delayUs(toggleDelay);
+ scl.high();
+ miosix::delayUs(toggleDelay);
+ }
+
+ {
+ miosix::FastInterruptDisableLock dLock;
+
+ // We set again the scl pin to the correct Alternate function
+ scl.mode(miosix::Mode::ALTERNATE_OD);
+ scl.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
+ }
+
+ // Re-initializing the peripheral in order to avoid inconsistent state
+ init();
+
+ // Assuming the locked state is solved. If it is not the case, only when
+ // it will be the case it will be detected again
+ lastError = Errors::NO_ERROR;
+
+ LOG_WARN(logger, fmt::format("I2C{} Bus flushed", id));
+}
+
+uint16_t I2CDriver::getLastError() { return lastError; }
+
+inline bool I2CDriver::IRQwaitForOperationCompletion(
+ miosix::FastInterruptDisableLock &dLock)
+{
+ // Saving the current thread in order to be waken up by interrupts
+ waiting = miosix::Thread::IRQgetCurrentThread();
+
+ // enabling interrupts for errors
+ i2c->CR1 |= I2C_CR1_ERRIE | // interrupt for errors
+ I2C_CR1_NACKIE | // interrupt for NACKs
+ I2C_CR1_TCIE | // interrupt for TC and TCR
+ I2C_CR1_STOPIE; // interrupt for STOP detected
+
+ // flag thread as waiting, enable interrupts in I2C peripheral and yield
+ // till an interrupt doesn't wake up the thread
+ while (waiting)
+ {
+ waiting->IRQwait();
+ miosix::FastInterruptEnableLock eLock(dLock);
+ waiting->yield();
+ }
+
+ // If error occurred, parse it to notify the error(s); otherwise reset
+ // lastError parameter
+ if (error)
+ {
+ lastError |= ((error & I2C_ISR_OVR) ? Errors::OVR : 0) |
+ ((error & I2C_ISR_BERR) ? Errors::BERR : 0) |
+ ((error & I2C_ISR_ARLO) ? Errors::ARLO : 0);
+ error = 0;
+
+ return false;
+ }
+
+ lastError = Errors::NO_ERROR;
+ return true;
+}
+
+inline void I2CDriver::IRQwakeUpWaitingThread()
+{
+ // invalidating the buffer pointers (avoiding to keep a pointer to an old
+ // memory location)
+ transaction.buffRead = nullptr;
+ transaction.buffWrite = nullptr;
+
+ // disabling interrupts for errors
+ i2c->CR1 &= ~(I2C_CR1_ERRIE | // interrupt for errors
+ I2C_CR1_NACKIE | // interrupt for NACKs
+ I2C_CR1_TCIE | // interrupt for TC and TCR
+ I2C_CR1_STOPIE); // interrupt for STOP detected
+
+ if (waiting)
+ {
+ waiting->IRQwakeup();
+
+ if (waiting->IRQgetPriority() >
+ miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
+ {
+ miosix::Scheduler::IRQfindNextThread();
+ }
+
+ waiting = nullptr;
+ }
+}
+
+void I2CDriver::IRQhandleInterrupt()
+{
+ // Transfer complete reload: setting registers for the remaining bytes
+ if (i2c->ISR & I2C_ISR_TCR)
+ {
+ setupReload();
+ }
+
+ // If NACK reception, return error
+ if (i2c->ISR & I2C_ISR_NACKF)
+ {
+ // "reserved" bit in the ISR register, so we don't collide with
+ // other fields
+ error = 1 << 14;
+ lastError |= Errors::AF;
+ i2c->ICR |= I2C_ICR_NACKCF;
+ IRQwakeUpWaitingThread();
+ return;
+ }
+
+ if (transaction.nBytesDone < transaction.nBytes)
+ {
+ // Performing the read/write
+ if (i2c->ISR & I2C_ISR_RXNE)
+ {
+ // READ
+ transaction.buffRead[transaction.nBytesDone] = i2c->RXDR;
+ transaction.nBytesDone++;
+ }
+ else if (i2c->ISR & I2C_ISR_TXIS)
+ {
+ // WRITE
+ i2c->TXDR = transaction.buffWrite[transaction.nBytesDone];
+ transaction.nBytesDone++;
+ }
+ }
+ else
+ {
+ // Sending STOP condition and wake up thread
+ if (!transaction.generateStop)
+ {
+ reStarting = true;
+
+ // waking up the waiting thread
+ IRQwakeUpWaitingThread();
+ }
+ }
+
+ if (i2c->ISR & I2C_ISR_STOPF)
+ {
+ // waking up the waiting thread after stop condition generated
+ IRQwakeUpWaitingThread();
+ }
+}
+
+void I2CDriver::IRQhandleErrInterrupt()
+{
+ error = i2c->ISR | (1 << 24);
+
+ // Clearing all the errors in the register
+ i2c->ICR |= I2C_ICR_ADDRCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF |
+ I2C_ICR_OVRCF | I2C_ICR_STOPCF;
+
+ // Waking up the waiting thread
+ IRQwakeUpWaitingThread();
+}
+
+} // namespace Boardcore
+
+#endif // _ARCH_CORTEXM7_STM32F7
\ No newline at end of file
diff --git a/src/shared/drivers/i2c/I2CDriver.h b/src/shared/drivers/i2c/I2CDriver.h
index cf205d404a5383da87419678700e2dc8d421f5a8..9d639636f496188ab786134e8db81909acef545f 100644
--- a/src/shared/drivers/i2c/I2CDriver.h
+++ b/src/shared/drivers/i2c/I2CDriver.h
@@ -62,10 +62,12 @@ public:
READ = 1
};
+ // [TODO] limit speed possibilities at compile time with ifdefs?
enum Speed : uint8_t
{
- STANDARD = 0,
- FAST = 1
+ STANDARD = 0,
+ FAST = 1,
+ FAST_PLUS = 2
};
enum Addressing : uint8_t
@@ -239,6 +241,12 @@ private:
*/
void setupPeripheral(const I2CSlaveConfig &slaveConfig);
+#ifdef _ARCH_CORTEXM7_STM32F7
+ inline void setupTransaction();
+
+ inline void setupReload();
+#endif // _ARCH_CORTEXM7_STM32F7
+
/**
* @brief Method to perform a read or write operation.
*
@@ -283,7 +291,7 @@ private:
miosix::GpioPin sda; ///< GpioPin of the serial data pin
uint16_t lastError = NO_ERROR; ///< Flag for the last error occurred
- uint16_t error = 0; ///< Flag that tells if an error occurred
+ uint32_t error = 0; ///< Flag that tells if an error occurred
bool reStarting = false; ///< Flag true if not generated a STOP condition
miosix::Thread *waiting{}; ///< Pointer to the waiting for event thread
I2CTransaction transaction; ///< Struct storing the transaction info
diff --git a/src/tests/drivers/i2c/test-i2c-driver.cpp b/src/tests/drivers/i2c/test-i2c-driver.cpp
index 80f3e52953f4dd850b535c197d7b9b848d7fa898..ce9d8ceebe2d091c282167427e8e191441e02bab 100644
--- a/src/tests/drivers/i2c/test-i2c-driver.cpp
+++ b/src/tests/drivers/i2c/test-i2c-driver.cpp
@@ -84,25 +84,32 @@ I2CSensor OLED{0b0111100, 0xD0, 0x43, {}};
I2CDriver::I2CSlaveConfig BMP180Config{BMP180.addressSensor,
I2CDriver::Addressing::BIT7,
I2CDriver::Speed::STANDARD};
+
I2CDriver::I2CSlaveConfig BME280Config{BME280.addressSensor,
I2CDriver::Addressing::BIT7,
I2CDriver::Speed::STANDARD};
-I2CDriver::I2CSlaveConfig OLEDConfig{
+
+I2CDriver::I2CSlaveConfig OLEDConfig_F{
OLED.addressSensor, I2CDriver::Addressing::BIT7, I2CDriver::Speed::FAST};
+#ifdef _ARCH_CORTEXM7_STM32F7
+I2CDriver::I2CSlaveConfig OLEDConfig_FP{OLED.addressSensor,
+ I2CDriver::Addressing::BIT7,
+ I2CDriver::Speed::FAST_PLUS};
+#endif // _ARCH_CORTEXM7_STM32F7
+
bool i2cDriver(I2CDriver &i2c, I2CSensor sensor,
I2CDriver::I2CSlaveConfig sensorConfig)
{
uint8_t buffer[10] = {0};
- const uint8_t nRead = 10;
+ const uint8_t nRead = 1;
i2c.flushBus();
// reset the sensor and then read the whoami
if (!(i2c.write(sensorConfig, sensor.softReset, 2) &&
i2c.write(sensorConfig, &sensor.whoamiRegister, 1, false) &&
- i2c.read(sensorConfig, buffer, nRead) &&
- (buffer[0] == sensor.whoamiContent)))
+ i2c.read(sensorConfig, buffer, nRead)))
{
uint16_t lastError{i2c.getLastError()};
if (!(lastError &
@@ -113,6 +120,13 @@ bool i2cDriver(I2CDriver &i2c, I2CSensor sensor,
return false;
}
+ if (buffer[0] != sensor.whoamiContent)
+ {
+ printf("whoami expected %d, received %d\n", sensor.whoamiContent,
+ buffer[0]);
+ return false;
+ }
+
return true;
}
@@ -138,8 +152,11 @@ int main()
for (int i = 0; i < nRepeat; i++)
{
- statusOLED &= i2cDriver(i2c, OLED, OLEDConfig);
statusBMP &= i2cDriver(i2c, BMP180, BMP180Config);
+ statusOLED &= i2cDriver(i2c, OLED, OLEDConfig_F);
+#ifdef _ARCH_CORTEXM7_STM32F7
+ statusOLED &= i2cDriver(i2c, OLED, OLEDConfig_FP);
+#endif // _ARCH_CORTEXM7_STM32F7
}
printf("OLED:%d\tBMP:%d\n", statusOLED, statusBMP);
diff --git a/src/tests/drivers/i2c/test-i2c.cpp b/src/tests/drivers/i2c/test-i2c.cpp
index 38d1554ef8b2f5a9fff7dbdc2c47f376584b08cd..ac292392e681fbcf4b81a18f4f81553544240805 100644
--- a/src/tests/drivers/i2c/test-i2c.cpp
+++ b/src/tests/drivers/i2c/test-i2c.cpp
@@ -83,13 +83,21 @@ I2CSensor OLED{0b0111100, 0xD0, 0x43, {}};
I2CDriver::I2CSlaveConfig BMP180Config{BMP180.addressSensor,
I2CDriver::Addressing::BIT7,
I2CDriver::Speed::STANDARD};
+
I2CDriver::I2CSlaveConfig BME280Config{BME280.addressSensor,
I2CDriver::Addressing::BIT7,
I2CDriver::Speed::STANDARD};
-I2CDriver::I2CSlaveConfig OLEDConfig{
+
+I2CDriver::I2CSlaveConfig OLEDConfig_F{
OLED.addressSensor, I2CDriver::Addressing::BIT7, I2CDriver::Speed::FAST};
-bool i2cDriver(SyncedI2C &i2c, I2CSensor sensor,
+#ifdef _ARCH_CORTEXM7_STM32F7
+I2CDriver::I2CSlaveConfig OLEDConfig_FP{OLED.addressSensor,
+ I2CDriver::Addressing::BIT7,
+ I2CDriver::Speed::FAST_PLUS};
+#endif // _ARCH_CORTEXM7_STM32F7
+
+bool i2cDriver(I2C &i2c, I2CSensor sensor,
I2CDriver::I2CSlaveConfig sensorConfig)
{
uint8_t whoamiContent = 0;
@@ -98,32 +106,22 @@ bool i2cDriver(SyncedI2C &i2c, I2CSensor sensor,
// reset the sensor and then read the whoami
if (!(i2c.probe(sensorConfig) &&
- i2c.writeRegister(sensorConfig, sensor.softReset[0],
- sensor.softReset[1]) &&
- i2c.readRegister(sensorConfig, sensor.whoamiRegister,
- whoamiContent) &&
- i2c.readFromRegister(sensorConfig, sensor.whoamiRegister, buffer,
- nRead)))
+ i2c.write(sensorConfig, sensor.softReset, 2) &&
+ i2c.readRegister(sensorConfig, sensor.whoamiRegister, buffer)))
{
uint16_t lastError{i2c.getLastError()};
- if (!(lastError & I2CDriver::Errors::AF))
+ if (!(lastError &
+ (I2CDriver::Errors::AF | I2CDriver::Errors::BUS_LOCKED)))
{
printf("LastError: %d\n", lastError);
}
return false;
}
- if (whoamiContent != sensor.whoamiContent)
+ if (buffer != sensor.whoamiContent)
{
printf("whoami expected %d, received %d\n", sensor.whoamiContent,
- whoamiContent);
- return false;
- }
-
- if (buffer[0] != sensor.whoamiContent)
- {
- printf("buffer[0] expected %d, received %d\n", sensor.whoamiContent,
- buffer[0]);
+ buffer);
return false;
}
@@ -152,8 +150,11 @@ int main()
for (int i = 0; i < nRepeat; i++)
{
- statusOLED &= i2cDriver(i2c, OLED, OLEDConfig);
statusBMP &= i2cDriver(i2c, BMP180, BMP180Config);
+ statusOLED &= i2cDriver(i2c, OLED, OLEDConfig_F);
+#ifdef _ARCH_CORTEXM7_STM32F7
+ statusOLED &= i2cDriver(i2c, OLED, OLEDConfig_FP);
+#endif // _ARCH_CORTEXM7_STM32F7
}
printf("OLED:%d\tBMP:%d\n", statusOLED, statusBMP);