From 9daf0f13528ed19f536bc5483e71a2d2b491866a Mon Sep 17 00:00:00 2001 From: Emilio Corigliano <emilio.corigliano@skywarder.eu> Date: Sat, 18 Mar 2023 14:18:08 +0000 Subject: [PATCH] [I2C] Added high level methods, modified pin modes, deleted copy/move constructors and operators and bugfix in SyncI2C - Added high level methods to read multiple bytes and write a single register - Deleted copy/move constructors and operators - Fixed SyncedI2C::readRegister method - Changed pin modes from ALTERNATE_OD to ALTERNATE_OD_PULL_UP - Modified tests in order to try also the new methods - Uniformed passing of const reference in low-level and high-level drivers --- src/shared/drivers/i2c/I2C.cpp | 54 +++++++++++--- src/shared/drivers/i2c/I2C.h | 102 ++++++++++++++++++++++++--- src/shared/drivers/i2c/I2CDriver.cpp | 12 ++-- src/shared/drivers/i2c/I2CDriver.h | 10 ++- src/tests/drivers/i2c/test-i2c.cpp | 33 ++++++--- 5 files changed, 175 insertions(+), 36 deletions(-) diff --git a/src/shared/drivers/i2c/I2C.cpp b/src/shared/drivers/i2c/I2C.cpp index 5242318f4..a58a3049d 100644 --- a/src/shared/drivers/i2c/I2C.cpp +++ b/src/shared/drivers/i2c/I2C.cpp @@ -25,33 +25,52 @@ namespace Boardcore { -I2C::I2C(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda) +I2C::I2C(I2C_TypeDef *i2c, const miosix::GpioPin &scl, + const miosix::GpioPin &sda) : i2c(i2c, scl, sda) { } bool I2C::read(const I2CDriver::I2CSlaveConfig &slaveConfig, void *buffer, - size_t nBytes) + const size_t &nBytes) { i2c.flushBus(); return i2c.read(slaveConfig, buffer, nBytes); } bool I2C::write(const I2CDriver::I2CSlaveConfig &slaveConfig, - const void *buffer, size_t nBytes) + const void *buffer, const size_t &nBytes) { i2c.flushBus(); return i2c.write(slaveConfig, buffer, nBytes); } bool I2C::readRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, - const uint8_t registerAddress, uint8_t ®isterContent) + const uint8_t ®isterAddress, uint8_t ®isterContent) { i2c.flushBus(); return i2c.write(slaveConfig, ®isterAddress, 1, false) && i2c.read(slaveConfig, ®isterContent, 1); } +bool I2C::writeRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, + const uint8_t ®isterContent) +{ + const uint8_t reg[2] = {registerAddress, registerContent}; + i2c.flushBus(); + return i2c.write(slaveConfig, reg, 2); +} + +bool I2C::readFromRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, void *buffer, + const size_t &nBytes) +{ + i2c.flushBus(); + return i2c.write(slaveConfig, ®isterAddress, 1, false) && + i2c.read(slaveConfig, buffer, nBytes); +} + bool I2C::probe(const I2CDriver::I2CSlaveConfig &slaveConfig) { i2c.flushBus(); @@ -60,33 +79,50 @@ bool I2C::probe(const I2CDriver::I2CSlaveConfig &slaveConfig) uint16_t I2C::getLastError() { return i2c.getLastError(); } -SyncedI2C::SyncedI2C(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda) +SyncedI2C::SyncedI2C(I2C_TypeDef *i2c, const miosix::GpioPin &scl, + const miosix::GpioPin &sda) : I2C(i2c, scl, sda) { } bool SyncedI2C::read(const I2CDriver::I2CSlaveConfig &slaveConfig, void *buffer, - size_t nBytes) + const size_t &nBytes) { miosix::Lock<miosix::FastMutex> lock(mutex); return I2C::read(slaveConfig, buffer, nBytes); } bool SyncedI2C::write(const I2CDriver::I2CSlaveConfig &slaveConfig, - const void *buffer, size_t nBytes) + const void *buffer, const size_t &nBytes) { miosix::Lock<miosix::FastMutex> lock(mutex); return I2C::write(slaveConfig, buffer, nBytes); } bool SyncedI2C::readRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, - const uint8_t registerAddress, - uint8_t registerContent) + const uint8_t ®isterAddress, + uint8_t ®isterContent) { miosix::Lock<miosix::FastMutex> lock(mutex); return I2C::readRegister(slaveConfig, registerAddress, registerContent); } +bool SyncedI2C::writeRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, + const uint8_t ®isterContent) +{ + miosix::Lock<miosix::FastMutex> lock(mutex); + return I2C::writeRegister(slaveConfig, registerAddress, registerContent); +} + +bool SyncedI2C::readFromRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, void *buffer, + const size_t &nBytes) +{ + miosix::Lock<miosix::FastMutex> lock(mutex); + return I2C::readFromRegister(slaveConfig, registerAddress, buffer, nBytes); +} + bool SyncedI2C::probe(const I2CDriver::I2CSlaveConfig &slaveConfig) { miosix::Lock<miosix::FastMutex> lock(mutex); diff --git a/src/shared/drivers/i2c/I2C.h b/src/shared/drivers/i2c/I2C.h index 071925e8e..42835840e 100644 --- a/src/shared/drivers/i2c/I2C.h +++ b/src/shared/drivers/i2c/I2C.h @@ -49,7 +49,14 @@ public: * @param scl Serial clock GpioPin of the relative I2C peripheral. * @param sda Serial data GpioPin of the relative I2C peripheral. */ - I2C(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda); + I2C(I2C_TypeDef *i2c, const miosix::GpioPin &scl, + const miosix::GpioPin &sda); + + ///< Delete copy/move constructors/operators. + I2C(const I2C &) = delete; + I2C &operator=(const I2C &) = delete; + I2C(I2C &&) = delete; + I2C &operator=(I2C &&) = delete; /** * @brief Non blocking read operation to read nBytes. @@ -64,7 +71,7 @@ public: * @returns True if the read is successful, false otherwise. */ [[nodiscard]] bool read(const I2CDriver::I2CSlaveConfig &slaveConfig, - void *buffer, size_t nBytes); + void *buffer, const size_t &nBytes); /** * @brief Non blocking write operation to write nBytes. @@ -79,7 +86,7 @@ public: * @returns True if the write is successful, false otherwise. */ [[nodiscard]] bool write(const I2CDriver::I2CSlaveConfig &slaveConfig, - const void *buffer, size_t nBytes); + const void *buffer, const size_t &nBytes); /** * @brief Non blocking operation to read a 1-byte register from a slave. @@ -95,7 +102,40 @@ public: */ [[nodiscard]] bool readRegister( const I2CDriver::I2CSlaveConfig &slaveConfig, - const uint8_t registerAddress, uint8_t ®isterContent); + const uint8_t ®isterAddress, uint8_t ®isterContent); + + /** + * @brief Non blocking operation to write a 1-byte register from a slave. + * + * This method, if necessary, flushes the bus before the write operation is + * performed. In case of an error during the communication, this method + * returns false immediately. + * @warning Check always if the operation succeeded or not! + * @param slaveConfig The configuration struct of the slave device. + * @param registerAddress Byte that represents the address of the register. + * @param registerContent The content to write on the register. + * @returns True if the write is successful, false otherwise. + */ + [[nodiscard]] bool writeRegister( + const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, const uint8_t ®isterContent); + + /** + * @brief Non blocking operation to read n-bytes from register from a slave. + * + * This method, if necessary, flushes the bus before the read operation is + * performed. In case of an error during the communication, this method + * returns false immediately. + * @warning Check always if the operation succeeded or not! + * @param slaveConfig The configuration struct of the slave device. + * @param registerAddress Byte that represents the address of the register. + * @param buffer Data buffer where to store the data read. + * @param nBytes Number of bytes to read. + * @returns True if the write is successful, false otherwise. + */ + [[nodiscard]] bool readFromRegister( + const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, void *buffer, const size_t &nBytes); /** * @brief Non blocking operation to check if a slave is available. @@ -110,7 +150,7 @@ public: * @brief Returns the last errors happened in the communication. * * For checking if a specific error occurred in the last transaction you can - * do `if(getLastError() & Errors::<error-to-check>)`. Do not use `==` to + * do `if(getLastError() &Errors::<error-to-check>)`. Do not use `==` to * check for errors because there could be more errors at once. To check if * no errors occurred use `if(getLastError() == Errors::NO_ERROR)` or simply * `if(!getLastError())` @@ -138,7 +178,14 @@ public: * @param scl Serial clock GpioPin of the relative I2C peripheral. * @param sda Serial data GpioPin of the relative I2C peripheral. */ - SyncedI2C(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda); + SyncedI2C(I2C_TypeDef *i2c, const miosix::GpioPin &scl, + const miosix::GpioPin &sda); + + ///< Delete copy/move constructors/operators. + SyncedI2C(const SyncedI2C &) = delete; + SyncedI2C &operator=(const SyncedI2C &) = delete; + SyncedI2C(SyncedI2C &&) = delete; + SyncedI2C &operator=(SyncedI2C &&) = delete; /** * @brief Read operation to read nBytes. @@ -153,7 +200,7 @@ public: * @returns True if the read is successful, false otherwise. */ [[nodiscard]] bool read(const I2CDriver::I2CSlaveConfig &slaveConfig, - void *buffer, size_t nBytes); + void *buffer, const size_t &nBytes); /** * @brief Write operation to write nBytes. @@ -168,7 +215,7 @@ public: * @returns True if the write is successful, false otherwise. */ [[nodiscard]] bool write(const I2CDriver::I2CSlaveConfig &slaveConfig, - const void *buffer, size_t nBytes); + const void *buffer, const size_t &nBytes); /** * @brief Read a one-byte register from the device. @@ -180,11 +227,44 @@ public: * @param slaveConfig The configuration struct of the slave device. * @param registerAddress Byte that represents the address of the register. * @param registerContent Where to store the content of the register. - * @returns True if the write is successful, false otherwise. + * @returns True if the read is successful, false otherwise. */ [[nodiscard]] bool readRegister( const I2CDriver::I2CSlaveConfig &slaveConfig, - const uint8_t registerAddress, uint8_t registerContent); + const uint8_t ®isterAddress, uint8_t ®isterContent); + + /** + * @brief Write a one-byte register from the device. + * + * This method could have to wait that no other thread is trying to do some + * operation on the bus. In case of an error during the communication, this + * method returns false immediately. + * @warning Check always if the operation succeeded or not! + * @param slaveConfig The configuration struct of the slave device. + * @param registerAddress Byte that represents the address of the register. + * @param registerContent The content to write on the register. + * @returns True if the write is successful, false otherwise. + */ + [[nodiscard]] bool writeRegister( + const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, const uint8_t ®isterContent); + + /** + * @brief Read n-bytes from register from a slave. + * + * This method could have to wait that no other thread is trying to do some + * operation on the bus. In case of an error during the communication, this + * method returns false immediately. + * @warning Check always if the operation succeeded or not! + * @param slaveConfig The configuration struct of the slave device. + * @param registerAddress Byte that represents the address of the register. + * @param buffer Data buffer where to store the data read. + * @param nBytes Number of bytes to read. + * @returns True if the write is successful, false otherwise. + */ + [[nodiscard]] bool readFromRegister( + const I2CDriver::I2CSlaveConfig &slaveConfig, + const uint8_t ®isterAddress, void *buffer, const size_t &nBytes); /** * @brief Check if a slave is available. @@ -201,7 +281,7 @@ public: * @brief Returns the last errors happened in the communication. * * For checking if a specific error occurred in the last transaction you can - * do `if(getLastError() & Errors::<error-to-check>)`. Do not use `==` to + * do `if(getLastError() &Errors::<error-to-check>)`. Do not use `==` to * check for errors because there could be more errors at once. To check if * no errors occurred use `if(getLastError() == Errors::NO_ERROR)` or simply * `if(!getLastError())` diff --git a/src/shared/drivers/i2c/I2CDriver.cpp b/src/shared/drivers/i2c/I2CDriver.cpp index 99a879920..53a938783 100644 --- a/src/shared/drivers/i2c/I2CDriver.cpp +++ b/src/shared/drivers/i2c/I2CDriver.cpp @@ -278,8 +278,8 @@ I2CDriver::I2CDriver(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda) // 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); + scl.mode(miosix::Mode::ALTERNATE_OD_PULL_UP); + sda.mode(miosix::Mode::ALTERNATE_OD_PULL_UP); } // Checking that this particular I2C port hasn't been already instantiated @@ -382,7 +382,7 @@ void I2CDriver::setupPeripheral(const I2CSlaveConfig &slaveConfig) } bool I2CDriver::read(const I2CSlaveConfig &slaveConfig, void *buffer, - size_t nBytes) + const size_t &nBytes) { // Setting up the read transaction transaction.operation = Operation::READ; @@ -403,7 +403,7 @@ bool I2CDriver::read(const I2CSlaveConfig &slaveConfig, void *buffer, }; bool I2CDriver::write(const I2CSlaveConfig &slaveConfig, const void *buffer, - size_t nBytes, bool generateStop) + const size_t &nBytes, bool generateStop) { // Setting up the write transaction transaction.operation = Operation::WRITE; @@ -520,7 +520,7 @@ void I2CDriver::flushBus() // 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); + scl.mode(miosix::Mode::OPEN_DRAIN_PULL_UP); } for (size_t c = 0; c < I2CConsts::N_SCL_BITBANG; c++) @@ -535,7 +535,7 @@ void I2CDriver::flushBus() miosix::FastInterruptDisableLock dLock; // We set again the scl pin to the correct Alternate function - scl.mode(miosix::Mode::ALTERNATE_OD); + scl.mode(miosix::Mode::ALTERNATE_OD_PULL_UP); scl.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION); } diff --git a/src/shared/drivers/i2c/I2CDriver.h b/src/shared/drivers/i2c/I2CDriver.h index d1301e336..cf205d404 100644 --- a/src/shared/drivers/i2c/I2CDriver.h +++ b/src/shared/drivers/i2c/I2CDriver.h @@ -121,6 +121,12 @@ public: */ I2CDriver(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda); + ///< Delete copy/move constructors/operators. + I2CDriver(const I2CDriver &) = delete; + I2CDriver &operator=(const I2CDriver &) = delete; + I2CDriver(I2CDriver &&) = delete; + I2CDriver &operator=(I2CDriver &&) = delete; + /** * @brief Disables the peripheral, the interrupts in the NVIC and the * peripheral's clock. @@ -138,7 +144,7 @@ public: * @return True if the read is successful, false otherwise. */ [[nodiscard]] bool read(const I2CSlaveConfig &slaveConfig, void *buffer, - size_t nBytes); + const size_t &nBytes); /** * @brief Write operation to write nBytes. In case of an error during the @@ -152,7 +158,7 @@ public: * @return True if the write is successful, false otherwise. */ [[nodiscard]] bool write(const I2CSlaveConfig &slaveConfig, - const void *buffer, size_t nBytes, + const void *buffer, const size_t &nBytes, bool generateStop = true); /** diff --git a/src/tests/drivers/i2c/test-i2c.cpp b/src/tests/drivers/i2c/test-i2c.cpp index ee9ba2587..38d1554ef 100644 --- a/src/tests/drivers/i2c/test-i2c.cpp +++ b/src/tests/drivers/i2c/test-i2c.cpp @@ -67,8 +67,6 @@ typedef miosix::Gpio<GPIOH_BASE, 8> i3scl2; * one sensor (in order to provoke a locked state). */ -uint8_t buffer = 0; - typedef struct { // cppcheck-suppress unusedStructMember @@ -91,16 +89,21 @@ I2CDriver::I2CSlaveConfig BME280Config{BME280.addressSensor, I2CDriver::I2CSlaveConfig OLEDConfig{ OLED.addressSensor, I2CDriver::Addressing::BIT7, I2CDriver::Speed::FAST}; -bool i2cDriver(I2C &i2c, I2CSensor sensor, +bool i2cDriver(SyncedI2C &i2c, I2CSensor sensor, I2CDriver::I2CSlaveConfig sensorConfig) { - buffer = 0; + uint8_t whoamiContent = 0; + const size_t nRead = 300; + uint8_t buffer[nRead] = {0}; // reset the sensor and then read the whoami if (!(i2c.probe(sensorConfig) && - i2c.write(sensorConfig, sensor.softReset, 2) && - i2c.readRegister(sensorConfig, sensor.whoamiRegister, buffer) && - buffer == sensor.whoamiContent)) + i2c.writeRegister(sensorConfig, sensor.softReset[0], + sensor.softReset[1]) && + i2c.readRegister(sensorConfig, sensor.whoamiRegister, + whoamiContent) && + i2c.readFromRegister(sensorConfig, sensor.whoamiRegister, buffer, + nRead))) { uint16_t lastError{i2c.getLastError()}; if (!(lastError & I2CDriver::Errors::AF)) @@ -110,12 +113,26 @@ bool i2cDriver(I2C &i2c, I2CSensor sensor, return false; } + if (whoamiContent != 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]); + return false; + } + return true; } int main() { - int nRepeat = 50; + int nRepeat = 10; // thread that uses 100% CPU std::thread t( -- GitLab