diff --git a/README.md b/README.md index 23dbf4aeb9..6d93882896 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ○ -○ +✅ ○ ✅ ✅ @@ -692,93 +692,94 @@ you specific needs. ADS816x AMS5915 APA102 +AT24MAC402 SPI Flash -BME280 +BME280 BMP085 BNO055 CAT24AA DRV832X DS1302 -DS1631 +DS1631 DS18B20 EA-DOG Encoder Input Encoder Input BitBang Encoder Output BitBang -FT245 +FT245 FT6x06 Gpio Sampler HCLAx HD44780 HMC58x -HMC6343 +HMC6343 HX711 I2C-EEPROM ILI9341 IS31FL3733 ITG3200 -L3GD20 +L3GD20 LAN8720A LAWICEL LIS302DL LIS3DSH LIS3MDL -LM75 +LM75 LP503x LSM303A LSM6DS33 LSM6DSO LTC2984 -MAX31855 +MAX31855 MAX6966 MAX7219 MCP23x17 MCP2515 MCP7941x -MCP990X +MCP990X MMC5603 MS5611 MS5837 NOKIA5110 NRF24 -TFT-DISPLAY +TFT-DISPLAY PAT9125EL PCA8574 PCA9535 PCA9548A PCA9685 -SH1106 +SH1106 SIEMENS-S65 SIEMENS-S75 SK6812 SK9822 SSD1306 -ST7586S +ST7586S ST7789 STTS22H STUSB4500 SX1276 TCS3414 -TCS3472 +TCS3472 TLC594x TMP102 TMP12x TMP175 TOUCH2046 -VL53L0 +VL53L0 VL6180 WS2812 diff --git a/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/main.cpp b/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/main.cpp new file mode 100644 index 0000000000..d4413f455f --- /dev/null +++ b/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/main.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include + +using namespace Board; +using namespace modm::literals; + +// On board AT24MAC402 EEPROM driver example +int main() +{ + Board::initialize(); + + MODM_LOG_INFO << "AT24MAC402 Test" << modm::endl; + + /* Defined in board support package: + * using Sda = GpioA3; + * using Scl = GpioA4; + * using I2c = I2cMaster0; + */ + I2c::connect(); + I2c::initialize(); + + // address 0x57 = 1 0 1 0 A0 A1 A2 + // with A0 = A1 = A2 = 1 connected to 3.3V + modm::At24Mac402 eeprom{0x57}; + + MODM_LOG_INFO << "EEPROM detected: " << RF_CALL_BLOCKING(eeprom.ping()) << "\n\n"; + + std::array buffer{}; + + // Read pre-programmed MAC address + bool readSuccess = RF_CALL_BLOCKING(eeprom.readMac(buffer)); + if (readSuccess) + { + MODM_LOG_INFO.printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n\n", + buffer[0], buffer[1], buffer[2], + buffer[3], buffer[4], buffer[5]); + + } + else + { + MODM_LOG_INFO << "Reading MAC address from EEPROM failed!\n"; + } + + // Write 4 data bytes to address 0x80 + /*constexpr std::array data = {0xAA, 0xBB, 0xCC, 0xDD}; + const bool writeSuccess = RF_CALL_BLOCKING(eeprom.write(0x80, data.data(), 4)); + MODM_LOG_INFO << "write successful: " << writeSuccess << "\n\n";*/ + + // Read 4 data bytes from address 0x80 + // retry read until device responds after finishing previous write + readSuccess = false; + while (!readSuccess) + { + readSuccess = RF_CALL_BLOCKING(eeprom.read(0x80, buffer.data(), buffer.size())); + } + MODM_LOG_INFO.printf("data: 0x%02x 0x%02x 0x%02x 0x%02x\n\n", + buffer[0], buffer[1], buffer[2], buffer[3]); + + while (true) + { + Led0::toggle(); + Led1::toggle(); + modm::delay(500ms); + } + + return 0; +} diff --git a/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/project.xml b/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/project.xml new file mode 100644 index 0000000000..5f7c10e243 --- /dev/null +++ b/examples/samv71_xplained_ultra/i2c-eeprom/at24mac402/project.xml @@ -0,0 +1,11 @@ + + modm:samv71-xplained-ultra + + + + + modm:build:scons + modm:driver:at24mac402 + modm:platform:i2c:0 + + diff --git a/examples/samv71_xplained_ultra/i2c-eeprom/generic/main.cpp b/examples/samv71_xplained_ultra/i2c-eeprom/generic/main.cpp new file mode 100644 index 0000000000..01f890df21 --- /dev/null +++ b/examples/samv71_xplained_ultra/i2c-eeprom/generic/main.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include + +using namespace Board; +using namespace modm::literals; + +// Generic I2C EEPROM driver example using on-board 2 kBit AT24MAC402 EEPROM +// For extended AT24MAC402 functionality see specific example and documentation. +int main() +{ + Board::initialize(); + + MODM_LOG_INFO << "I2C EEPROM Test" << modm::endl; + + /* Defined in board support package: + * using Sda = GpioA3; + * using Scl = GpioA4; + * using I2c = I2cMaster0; + */ + I2c::connect(); + I2c::initialize(); + + // address 0x57 = 1 0 1 0 A0 A1 A2 + // with A0 = A1 = A2 = 1 connected to 3.3V + // 8 bit address width + constexpr auto AddressWidth = 1; // use 2 for 16 bit address EEPROMs + modm::I2cEeprom eeprom{0x57}; + + MODM_LOG_INFO << "EEPROM detected: " << RF_CALL_BLOCKING(eeprom.ping()) << "\n\n"; + + // Write 4 data bytes to address 0x80 + /*constexpr std::array data = {0xAA, 0xBB, 0xCC, 0xDD}; + const bool writeSuccess = RF_CALL_BLOCKING(eeprom.write(0x80, data.data(), 4)); + MODM_LOG_INFO << "write successful: " << writeSuccess << "\n\n";*/ + + // Read 4 data bytes from address 0x80 + std::array buffer{}; + bool readSuccess = false; + // retry read until device responds after finishing previous write + while (!readSuccess) + { + readSuccess = RF_CALL_BLOCKING(eeprom.read(0x80, buffer.data(), buffer.size())); + } + MODM_LOG_INFO << "read successful: " << readSuccess << "\n"; + MODM_LOG_INFO.printf("data: 0x%02x 0x%02x 0x%02x 0x%02x\n\n", buffer[0], buffer[1], buffer[2], buffer[3]); + + while (true) + { + Led0::toggle(); + Led1::toggle(); + modm::delay(500ms); + } + + return 0; +} diff --git a/examples/samv71_xplained_ultra/i2c-eeprom/generic/project.xml b/examples/samv71_xplained_ultra/i2c-eeprom/generic/project.xml new file mode 100644 index 0000000000..f01217b264 --- /dev/null +++ b/examples/samv71_xplained_ultra/i2c-eeprom/generic/project.xml @@ -0,0 +1,11 @@ + + modm:samv71-xplained-ultra + + + + + modm:build:scons + modm:driver:i2c.eeprom + modm:platform:i2c:0 + + diff --git a/src/modm/board/samv71_xplained_ultra/board.hpp b/src/modm/board/samv71_xplained_ultra/board.hpp index 4b4ae300a4..bb5afc0f8b 100644 --- a/src/modm/board/samv71_xplained_ultra/board.hpp +++ b/src/modm/board/samv71_xplained_ultra/board.hpp @@ -31,6 +31,7 @@ struct SystemClock static constexpr uint32_t Mck = Frequency / 2; // 150 MHz max. static constexpr uint32_t Usart1 = Mck; static constexpr uint32_t Spi0 = Mck; + static constexpr uint32_t Twihs0 = Mck; // static constexpr uint32_t Usb = 48_MHz; static bool inline @@ -62,6 +63,10 @@ struct Debug using UartRx = GpioA21; }; +using I2c = I2cMaster0; +using Sda = GpioA3; +using Scl = GpioA4; + using LoggerDevice = modm::IODeviceWrapper; inline void diff --git a/src/modm/board/samv71_xplained_ultra/module.lb b/src/modm/board/samv71_xplained_ultra/module.lb index 1b08b54603..8dcdd3672c 100644 --- a/src/modm/board/samv71_xplained_ultra/module.lb +++ b/src/modm/board/samv71_xplained_ultra/module.lb @@ -19,7 +19,7 @@ def prepare(module, options): if not options[":target"].partname == "samv71q21b-aab": return False - module.depends(":debug", ":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usart:1") #, ":platform:usb") + module.depends(":debug", ":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usart:1", ":platform:i2c:0") #, ":platform:usb") return True def build(env): diff --git a/src/modm/driver/storage/at24mac402.hpp b/src/modm/driver/storage/at24mac402.hpp new file mode 100644 index 0000000000..cb97d9ec86 --- /dev/null +++ b/src/modm/driver/storage/at24mac402.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_AT24MAC402_HPP +#define MODM_AT24MAC402_HPP + +#include +#include + +namespace modm +{ + +/** + * AT24MAC402 I2C EEPROM + * + * 2 kBit EEPROM with pre-programmed 48 bit MAC address, 128 bit unique id and + * 8 bit address pointer. + * The device address can be configured from 0x50 to 0x57 with 3 address input + * lines. + * + * @ingroup modm_driver_at24mac402 + * @author Christopher Durand + */ +template +class At24Mac402 : protected I2cEeprom +{ + using Base = I2cEeprom; + static constexpr uint8_t UuidAddress{0x80}; + static constexpr uint8_t MacAddress{0x9A}; +public: + using Base::attachConfigurationHandler; + + /// @param address i2c data base address (0b1010xxx) + At24Mac402(uint8_t address = 0x50); + + /** + * Set I2C data base address (0b1010xxx). + * The upper 4 bits will always be forced to 0b1010. + */ + void + setAddress(uint8_t address); + + // Read 48 bit pre-programmed MAC + modm::ResumableResult + readMac(std::span data); + + // Read 128 bit pre-programmed unique id + modm::ResumableResult + readUniqueId(std::span data); + + // start documentation inherited + modm::ResumableResult + ping(); + + modm::ResumableResult + writeByte(uint32_t address, uint8_t data); + + modm::ResumableResult + write(uint32_t address, const uint8_t* data, std::size_t length); + + template + modm::ResumableResult + write(uint32_t address, const T& data); + + modm::ResumableResult + readByte(uint32_t address, uint8_t& data); + + modm::ResumableResult + read(uint32_t address, uint8_t* data, std::size_t length); + + template + modm::ResumableResult + read(uint32_t address, T& data); + // end documentation inherited +private: + uint8_t address_{}; +}; + +} // namespace modm + +#include "at24mac402_impl.hpp" + +#endif // MODM_AT24MAC402_HPP diff --git a/src/modm/driver/storage/at24mac402.lb b/src/modm/driver/storage/at24mac402.lb new file mode 100644 index 0000000000..66375d4f7c --- /dev/null +++ b/src/modm/driver/storage/at24mac402.lb @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Christopher Durand +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:at24mac402" + module.description = """\ +# AT24MAC404 I²C EEPROM + +2 kBit I²C EEPROM with pre-programmed read-only 48 bit MAC address and 128 bit +unique id. +""" + +def prepare(module, options): + module.depends(":architecture:i2c.device", ":driver:i2c.eeprom") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/storage" + env.copy("at24mac402.hpp") + env.copy("at24mac402_impl.hpp") diff --git a/src/modm/driver/storage/at24mac402_impl.hpp b/src/modm/driver/storage/at24mac402_impl.hpp new file mode 100644 index 0000000000..3f084236e2 --- /dev/null +++ b/src/modm/driver/storage/at24mac402_impl.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_AT24MAC402_HPP +# error "Don't include this file directly, use 'at24mac402_impl.hpp' instead!" +#endif + +// ---------------------------------------------------------------------------- + +template +modm::At24Mac402::At24Mac402(uint8_t address) +{ + setAddress(address); +} + +template +void +modm::At24Mac402::setAddress(uint8_t address) +{ + address_ = (address & 0b111) | 0b1010'000; + Base::setAddress(address_); +} + +template +modm::ResumableResult +modm::At24Mac402::readMac(std::span data) +{ + Base::setAddress(address_ | 0b1'000); + return Base::read(MacAddress, &data[0], 6); +} + +template +modm::ResumableResult +modm::At24Mac402::readUniqueId(std::span data) +{ + Base::setAddress(address_ | 0b1'000); + return Base::read(UuidAddress, &data[0], 16); +} + +template +modm::ResumableResult +modm::At24Mac402::ping() +{ + Base::setAddress(address_); + return Base::ping(); +} + +template +modm::ResumableResult +modm::At24Mac402::writeByte(uint32_t address, uint8_t data) +{ + Base::setAddress(address_); + return Base::writeByte(address, data); +} + +template +modm::ResumableResult +modm::At24Mac402::write(uint32_t address, const uint8_t* data, std::size_t length) +{ + Base::setAddress(address_); + return Base::write(address, data, length); +} + +template +template +modm::ResumableResult +modm::At24Mac402::write(uint32_t address, const T& data) +{ + Base::setAddress(address_); + return Base::write(address, data); +} + +template +modm::ResumableResult +modm::At24Mac402::readByte(uint32_t address, uint8_t& data) +{ + Base::setAddress(address_); + return Base::readByte(address, data); +} + +template +modm::ResumableResult +modm::At24Mac402::read(uint32_t address, uint8_t* data, std::size_t length) +{ + Base::setAddress(address_); + return Base::read(address, data, length); +} + +template +template +modm::ResumableResult +modm::At24Mac402::read(uint32_t address, T& data) +{ + Base::setAddress(address_); + return read(address, reinterpret_cast(&data), sizeof(T)); +} diff --git a/src/modm/driver/storage/i2c_eeprom.cpp b/src/modm/driver/storage/i2c_eeprom.cpp deleted file mode 100644 index c5fdfa7414..0000000000 --- a/src/modm/driver/storage/i2c_eeprom.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2009, Martin Rosekeit - * Copyright (c) 2009-2012, Fabian Greif - * Copyright (c) 2011, Georgi Grinshpun - * Copyright (c) 2012, Sascha Schade - * Copyright (c) 2012-2015, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include "i2c_eeprom.hpp" - -// ---------------------------------------------------------------------------- -modm::i2cEeprom::DataTransmissionAdapter::DataTransmissionAdapter(uint8_t address) : - I2cWriteReadTransaction(address), writeAddress(false) -{} - -bool -modm::i2cEeprom::DataTransmissionAdapter::configureWrite(uint16_t address, const uint8_t *buffer, std::size_t size) -{ - if (I2cWriteReadTransaction::configureWrite(buffer, size)) - { - addressBuffer[0] = address >> 8; - addressBuffer[1] = address; - writeAddress = true; - return true; - } - return false; -} - -bool -modm::i2cEeprom::DataTransmissionAdapter::configureRead(uint16_t address, uint8_t *buffer, std::size_t size) -{ - addressBuffer[0] = address >> 8; - addressBuffer[1] = address; - writeAddress = false; - return I2cWriteReadTransaction::configureWriteRead(addressBuffer, 2, buffer, size); -} - -modm::I2cTransaction::Writing -modm::i2cEeprom::DataTransmissionAdapter::writing() -{ - if (writeAddress) - { - writeAddress = false; - return Writing(addressBuffer, 2, OperationAfterWrite::Write); - } - return I2cWriteReadTransaction::writing(); -} diff --git a/src/modm/driver/storage/i2c_eeprom.hpp b/src/modm/driver/storage/i2c_eeprom.hpp index 228b3fc061..a488ea9536 100644 --- a/src/modm/driver/storage/i2c_eeprom.hpp +++ b/src/modm/driver/storage/i2c_eeprom.hpp @@ -3,6 +3,7 @@ * Copyright (c) 2009-2012, Fabian Greif * Copyright (c) 2011, Georgi Grinshpun * Copyright (c) 2012-2016, Niklas Hauser + * Copyright (c) 2021, Christopher Durand * * This file is part of the modm project. * @@ -22,30 +23,35 @@ namespace modm { /// @cond -struct i2cEeprom +namespace i2c_eeprom::detail { - class DataTransmissionAdapter : public modm::I2cWriteReadTransaction - { - public: - DataTransmissionAdapter(uint8_t address); - bool - configureWrite(uint16_t address, const uint8_t *buffer, std::size_t size); +template +class DataTransmissionAdapter : public I2cWriteReadTransaction +{ +public: + static_assert(AddressBytes == 1 || AddressBytes == 2, "Only 8 or 16 bit addresses are supported"); + + DataTransmissionAdapter(uint8_t address); - bool - configureRead(uint16_t address, uint8_t *buffer, std::size_t size); + bool + configureWrite(uint16_t address, const uint8_t *buffer, std::size_t size); - inline uint8_t getAddress() const { - return this->address >> 1; - } + bool + configureRead(uint16_t address, uint8_t *buffer, std::size_t size); - protected: - virtual Writing - writing() override; + inline uint8_t getAddress() const { + return this->address >> 1; + } + +protected: + Writing + writing() override; + + uint8_t addressBuffer[AddressBytes]; + bool writeAddress; +}; - uint8_t addressBuffer[2]; - bool writeAddress; - }; }; /// @endcond @@ -60,8 +66,8 @@ struct i2cEeprom * @author Fabian Greif * @author Niklas Hauser */ -template -class I2cEeprom : public modm::I2cDevice< I2cMaster, 1, i2cEeprom::DataTransmissionAdapter > +template +class I2cEeprom : public I2cDevice> { public: I2cEeprom(uint8_t address = 0x50); diff --git a/src/modm/driver/storage/i2c_eeprom.lb b/src/modm/driver/storage/i2c_eeprom.lb index 8e6e36699a..f8f9a1b005 100644 --- a/src/modm/driver/storage/i2c_eeprom.lb +++ b/src/modm/driver/storage/i2c_eeprom.lb @@ -17,7 +17,8 @@ def init(module): # I2C Eeprom Compatible with the 24C256 (ST) and 24FC1025 (Microchip) family and other -I2C eeprom with an 16-bit address pointer. +I2C eeprom with an 16-bit address pointer as well as devices with an 8-bit +address pointer. Base address for most 24xxyyyy eeproms is 0x50. """ @@ -28,5 +29,4 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/driver/storage" env.copy("i2c_eeprom.hpp") - env.copy("i2c_eeprom.cpp") env.copy("i2c_eeprom_impl.hpp") diff --git a/src/modm/driver/storage/i2c_eeprom_impl.hpp b/src/modm/driver/storage/i2c_eeprom_impl.hpp index 798783b8e4..8ac0d77400 100644 --- a/src/modm/driver/storage/i2c_eeprom_impl.hpp +++ b/src/modm/driver/storage/i2c_eeprom_impl.hpp @@ -4,6 +4,7 @@ * Copyright (c) 2011, Georgi Grinshpun * Copyright (c) 2012, Sascha Schade * Copyright (c) 2012-2016, Niklas Hauser + * Copyright (c) 2021, Christopher Durand * * This file is part of the modm project. * @@ -18,21 +19,25 @@ #endif // ---------------------------------------------------------------------------- -template -modm::I2cEeprom::I2cEeprom(uint8_t address) : - I2cDevice(address) +template +modm::I2cEeprom::I2cEeprom(uint8_t address) : + I2cDevice>{address} { } // MARK: - write operations -template +template modm::ResumableResult -modm::I2cEeprom::write(uint32_t address, const uint8_t *data, std::size_t length) +modm::I2cEeprom::write(uint32_t address, const uint8_t *data, std::size_t length) { RF_BEGIN(); - if (address & 0x10000) this->setAddress(this->transaction.getAddress() | 0b100); - else this->setAddress(this->transaction.getAddress() & ~0b100); + if constexpr (AddressBytes > 1) { + if (address & 0x10000) this->setAddress(this->transaction.getAddress() | 0b100); + else this->setAddress(this->transaction.getAddress() & ~0b100); + } else { + this->setAddress(this->transaction.getAddress()); + } RF_WAIT_UNTIL( this->transaction.configureWrite(address, data, length) and this->startTransaction() ); @@ -42,14 +47,18 @@ modm::I2cEeprom::write(uint32_t address, const uint8_t *data, std::si } // MARK: - read operations -template +template modm::ResumableResult -modm::I2cEeprom::read(uint32_t address, uint8_t *data, std::size_t length) +modm::I2cEeprom::read(uint32_t address, uint8_t *data, std::size_t length) { RF_BEGIN(); - if (address & 0x10000) this->setAddress(this->transaction.getAddress() | 0b100); - else this->setAddress(this->transaction.getAddress() & ~0b100); + if constexpr (AddressBytes > 1) { + if (address & 0x10000) this->setAddress(this->transaction.getAddress() | 0b100); + else this->setAddress(this->transaction.getAddress() & ~0b100); + } else { + this->setAddress(this->transaction.getAddress()); + } RF_WAIT_UNTIL( this->transaction.configureRead(address, data, length) and this->startTransaction() ); @@ -57,3 +66,57 @@ modm::I2cEeprom::read(uint32_t address, uint8_t *data, std::size_t le RF_END_RETURN( this->wasTransactionSuccessful() ); } + +namespace modm::i2c_eeprom::detail +{ + +template +DataTransmissionAdapter::DataTransmissionAdapter(uint8_t address) : + I2cWriteReadTransaction(address), writeAddress(false) +{} + +template +bool +DataTransmissionAdapter::configureWrite(uint16_t address, const uint8_t *buffer, std::size_t size) +{ + if (I2cWriteReadTransaction::configureWrite(buffer, size)) + { + if constexpr (AddressBytes == 1) { + addressBuffer[0] = address; + } else { + addressBuffer[0] = address >> 8; + addressBuffer[1] = address; + } + writeAddress = true; + return true; + } + return false; +} + +template +bool +DataTransmissionAdapter::configureRead(uint16_t address, uint8_t *buffer, std::size_t size) +{ + if constexpr (AddressBytes == 1) { + addressBuffer[0] = address; + } else { + addressBuffer[0] = address >> 8; + addressBuffer[1] = address; + } + writeAddress = false; + return I2cWriteReadTransaction::configureWriteRead(addressBuffer, AddressBytes, buffer, size); +} + +template +I2cTransaction::Writing +DataTransmissionAdapter::writing() +{ + if (writeAddress) + { + writeAddress = false; + return Writing(addressBuffer, AddressBytes, OperationAfterWrite::Write); + } + return I2cWriteReadTransaction::writing(); +} + +} diff --git a/src/modm/platform/i2c/sam_x7x/i2c_master.cpp.in b/src/modm/platform/i2c/sam_x7x/i2c_master.cpp.in new file mode 100644 index 0000000000..ef36040818 --- /dev/null +++ b/src/modm/platform/i2c/sam_x7x/i2c_master.cpp.in @@ -0,0 +1,376 @@ +// coding: utf-8 +/* + * Copyright (c) 2023, Christopher Durand + * Copyright (c) 2017, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "i2c_master_{{ id }}.hpp" +#include +#include +#include +#include +#include + +namespace +{ + static modm::I2c::Operation nextOperation; + + // transaction queue management + struct ConfiguredTransaction + { + ConfiguredTransaction() + : transaction(nullptr), configuration(nullptr) {} + + ConfiguredTransaction(modm::I2cTransaction* transaction, modm::I2c::ConfigurationHandler configuration) + : transaction(transaction), configuration(configuration) {} + + modm::I2cTransaction* transaction; + modm::I2c::ConfigurationHandler configuration; + }; + + static modm::BoundedQueue queue; + static modm::I2c::ConfigurationHandler configuration{nullptr}; + + static modm::I2cTransaction* transaction{}; + static modm::I2cMaster::Error error{modm::I2cMaster::Error::NoError}; + + static modm::I2cTransaction::Starting starting{0, modm::I2c::OperationAfterStart::Stop}; + static modm::I2cTransaction::Writing writing{nullptr, 0, modm::I2c::OperationAfterWrite::Stop}; + static modm::I2cTransaction::Reading reading{nullptr, 0, modm::I2c::OperationAfterRead::Stop}; + + static constexpr int MaxRetries{3}; + static int arbitrationRetryCounter{MaxRetries}; + + enum class PreviousOperation + { + Idle = 0, + Write = 1, + Read = 2 + }; + + static inline void + callStarting(PreviousOperation previous); + + static inline void + callWriteOperation(PreviousOperation previous) + { + TWIHS{{ id }}->TWIHS_MMR = TWIHS_MMR_DADR(starting.address >> 1); + + // The write transfer is started automatically when TWIHS_THR is written. + // In case of a repeated start TWIHS_CR_START must be set before writing + // the register. + if (previous != PreviousOperation::Idle) { + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_START; + } + + // Write first data byte + if ((writing.length > 0) and (writing.buffer != nullptr)) { + TWIHS{{ id }}->TWIHS_THR = *writing.buffer++; + --writing.length; + } else { + // This case should never occcur. + // Transactions with zero data length are unsupported by hardware. + // A write must be performed to restore valid peripheral state. + writing.length = 0; + TWIHS{{ id }}->TWIHS_THR = 0; + } + + if (writing.length == 0) { + if (nextOperation == modm::I2c::Operation::Restart) { + callStarting(PreviousOperation::Write); + } else if (nextOperation == modm::I2c::Operation::Write) { + writing = transaction->writing(); + nextOperation = static_cast(writing.next); + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_TXRDY; + } else { + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_STOP; + } + } else { + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_TXRDY; + } + + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_TXCOMP; + } + + static inline void + callReadOperation(PreviousOperation previous) + { + // In case of a restart after a read, the last byte from the previous + // operation has not been read yet and thus may not be cleared. + // The next read operation must be pre-programmed before the last byte + // of the previous one has been read. + // Reading that byte will trigger transmission of the repeated + // start condition. + if (previous != PreviousOperation::Read) { + (void) TWIHS{{ id }}->TWIHS_RHR; + } + + TWIHS{{ id }}->TWIHS_MMR = TWIHS_MMR_DADR(starting.address >> 1) | TWIHS_MMR_MREAD; + if (reading.length <= 1) { + // For a one byte read START and STOP must be set simultaneously + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP; + } else { + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_START; + } + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_RXRDY; + } + + static inline void + callStarting(PreviousOperation previous) + { + starting = transaction->starting(); + switch (starting.next) + { + case modm::I2c::OperationAfterStart::Read: + reading = transaction->reading(); + nextOperation = static_cast(reading.next); + + callReadOperation(previous); + break; + + case modm::I2c::OperationAfterStart::Write: + writing = transaction->writing(); + nextOperation = static_cast(writing.next); + + callWriteOperation(previous); + break; + + case modm::I2c::OperationAfterStart::Stop: + // It is impossible to do a transaction with a stop immediatately + // after the address has been ACK'ed. Writing the data output register is + // the only way to start a write transaction and TWIHS_CR_STOP will + // only output a stop condition after the first byte has been sent if an + // ACK has been received from the device. + + // Use a dummy zero byte buffer, there is no way not to send the data :( + static constexpr uint8_t zero{0}; + writing.length = 1; + writing.buffer = &zero; + reading.length = 0; + writing = transaction->writing(); + nextOperation = modm::I2c::Operation::Stop; + + callWriteOperation(previous); + break; + } + + error = modm::I2cMaster::Error::NoError; + } + + static inline void + callNextTransaction() + { + if (queue.isNotEmpty()) + { + ConfiguredTransaction next = queue.get(); + queue.pop(); + // configure the peripheral if necessary + if (next.configuration and (configuration != next.configuration)) { + configuration = next.configuration; + configuration(); + } + + arbitrationRetryCounter = MaxRetries; + ::transaction = next.transaction; + callStarting(PreviousOperation::Idle); + } + } +} + +// ---------------------------------------------------------------------------- +MODM_ISR(TWIHS{{ id }}) +{ + const uint32_t status = TWIHS{{ id }}->TWIHS_SR; + + // tx ready interrupt enabled, writing operation in progress + if (TWIHS{{ id }}->TWIHS_IMR & TWIHS_IMR_TXRDY) { + if (status & TWIHS_SR_TXRDY) { + if ((writing.length > 0) and (writing.buffer != nullptr)) { + TWIHS{{ id }}->TWIHS_THR = *writing.buffer++; + --writing.length; + } + + if (writing.length == 0) { + if (nextOperation == modm::I2c::Operation::Restart) { + // disable tx ready interrupt + TWIHS{{ id }}->TWIHS_IDR = TWIHS_IDR_TXRDY; + callStarting(PreviousOperation::Write); + } else if (nextOperation == modm::I2c::Operation::Write) { + // continue writing with new data buffer + // do not disable tx ready interrupt + writing = transaction->writing(); + nextOperation = static_cast(writing.next); + } else { + // disable tx ready interrupt + TWIHS{{ id }}->TWIHS_IDR = TWIHS_IDR_TXRDY; + // send stop after write, enable completion interrupt + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_STOP; + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_TXCOMP; + } + } + } + // rx interrupt enabled, reading operation in progress + } else if (TWIHS{{ id }}->TWIHS_IMR & TWIHS_IMR_RXRDY) { + if (status & TWIHS_SR_RXRDY) { + if (reading.length <= 1) { + TWIHS{{ id }}->TWIHS_IDR = TWIHS_IDR_RXRDY; + // To end the current reading operation TWIHS_CR_STOP + // or TWIHS_CR_START must be programmed before the data of + // the last byte is read. Otherwise reading of the last byte + // would automatically start another read. + // + // Remember buffer pointer and length + // In case of a restart with a new reading operation + // reading.buffer will point to the next buffer. + uint8_t* const buffer = reading.buffer; + const auto length = reading.length; + + if (nextOperation == modm::I2c::Operation::Restart) { + callStarting(PreviousOperation::Read); + } else { + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_STOP; + } + // must be read after setting TWIHS_CR_STOP or TWIHS_CR_START + const uint8_t data = TWIHS{{ id }}->TWIHS_RHR; + if (buffer != nullptr && length != 0) { + *buffer = data; + } + } else { + const uint8_t data = TWIHS{{ id }}->TWIHS_RHR; + if (reading.buffer != nullptr) { + *reading.buffer++ = data; + } + --reading.length; + } + } + } + + // if transfer completed (after stop condition) + if (status & TWIHS_SR_TXCOMP) { + // disable interrupts + TWIHS{{ id }}->TWIHS_IDR = TWIHS_IDR_TXCOMP | TWIHS_IDR_RXRDY | TWIHS_IDR_TXRDY; + if (status & TWIHS_SR_NACK) { // NACK received + error = modm::I2cMaster::Error::AddressNack; + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_LOCKCLR; + if (transaction) { + transaction->detaching(modm::I2c::DetachCause::ErrorCondition); + transaction = nullptr; + } + } else if (status & TWIHS_SR_ARBLST) { // arbitration lost + if (arbitrationRetryCounter > 0) { + --arbitrationRetryCounter; + // TODO: is it safe with the transaction interface to call starting() again? + callStarting(PreviousOperation::Idle); + } else { // retries exceeded + error = modm::I2cMaster::Error::ArbitrationLost; + if (transaction) { + transaction->detaching(modm::I2c::DetachCause::ErrorCondition); + transaction = nullptr; + } + } + } else { // valid ACK from device + if (transaction) { + transaction->detaching(modm::I2c::DetachCause::NormalStop); + transaction = nullptr; + } + } + callNextTransaction(); + } +} + +// ---------------------------------------------------------------------------- + +void +modm::platform::I2cMaster{{ id }}::initializeWithClockConfig(uint32_t cwgrRegister) +{ + ClockGen::enable(); + + // disable all interrupts in case this function is called while the peripheral is operating + TWIHS{{ id }}->TWIHS_IDR = ~0ul; + + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_SWRST; + TWIHS{{ id }}->TWIHS_CWGR = cwgrRegister; + + // Enable master mode + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_MSDIS; + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_SVDIS; + TWIHS{{ id }}->TWIHS_CR = TWIHS_CR_MSEN; + + // Enable arbitration lost interrupt + TWIHS{{ id }}->TWIHS_IER = TWIHS_IER_ARBLST; + + // TODO: make priority configurable? + // 10 is also used in the STM32 extended driver + NVIC_SetPriority(TWIHS{{ id }}_IRQn, 10); + NVIC_EnableIRQ(TWIHS{{ id }}_IRQn); +} + +void +modm::platform::I2cMaster{{ id }}::reset() +{ + modm::atomic::Lock lock; + // TODO: reset peripheral? + reading.length = 0; + writing.length = 0; + error = Error::SoftwareReset; + if (transaction) transaction->detaching(DetachCause::ErrorCondition); + transaction = nullptr; + // remove all queued transactions + while (queue.isNotEmpty()) + { + ConfiguredTransaction next = queue.get(); + if (next.transaction) { next.transaction->detaching(DetachCause::ErrorCondition); } + queue.pop(); + } +} + +bool +modm::platform::I2cMaster{{ id }}::start(I2cTransaction *transaction, ConfigurationHandler handler) +{ + modm::atomic::Lock lock; + // if we have a place in the queue and the transaction object is valid + if (queue.isNotFull() && transaction) + { + // if the transaction object wants to attach to the queue + if (transaction->attaching()) + { + // if no current transaction is taking place + if (!modm::accessor::asVolatile(::transaction)) + { + // configure the peripheral if necessary + if (handler and (configuration != handler)) { + configuration = handler; + configuration(); + } + + arbitrationRetryCounter = MaxRetries; + ::transaction = transaction; + callStarting(PreviousOperation::Idle); + } + else + { + // queue the transaction for later execution + queue.push(ConfiguredTransaction(transaction, configuration)); + } + return true; + } + else { + transaction->detaching(modm::I2c::DetachCause::FailedToAttach); + } + } + return false; +} + +modm::I2cMaster::Error +modm::platform::I2cMaster{{ id }}::getErrorState() +{ + return error; +} diff --git a/src/modm/platform/i2c/sam_x7x/i2c_master.hpp.in b/src/modm/platform/i2c/sam_x7x/i2c_master.hpp.in new file mode 100644 index 0000000000..2fd036b0a7 --- /dev/null +++ b/src/modm/platform/i2c/sam_x7x/i2c_master.hpp.in @@ -0,0 +1,180 @@ +// coding: utf-8 +/* + * Copyright (c) 2023, Christopher Durand + * Copyright (c) 2017, Sascha Schade + * Copyright (c) 2017-2018, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_I2C_{{ id }}_HPP +#define MODM_STM32_I2C_{{ id }}_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace modm +{ + +namespace platform +{ + +/** + * I2C master implementation for TWIHS{{ id }} peripheral. + * + * @ingroup modm_platform_i2c modm_platform_i2c_{{id}} + */ +class I2cMaster{{ id }} : public ::modm::I2cMaster +{ +public: + static constexpr size_t TransactionBufferSize = {{ options["buffer.transaction"] }}; + +private: + // calculate required bits to represent value + static constexpr int + bits(uint32_t value) + { + return 32 - std::countl_zero(value); + } + + template + static constexpr bool + checkBaudrate() + { + if (ckdiv > 7) { + return false; + } + + const float tHigh = ((chdiv * (1 << ckdiv)) + 3) / float(clock); + const float tLow = ((cldiv * (1 << ckdiv)) + 3) / float(clock); + const float freq = 1.f / (tHigh + tLow); + const float error = std::fabs(1.f - freq / baudrate); + + return error < pct2f(tolerance); + } + + template + static consteval std::optional + calculateTimings() + { + constexpr auto clock = SystemClock::Twihs{{ id }}; + + // hold time = (HOLD + 3) * (1 / peripheral clock) + // => HOLD = hold time * (peripheral clock) - 3 + constexpr float holdTime = 300.e-9f; + constexpr float holdIdeal = holdTime * clock - 3.f; + constexpr uint8_t hold = static_cast(std::clamp(std::ceil(holdIdeal), 0.f, 63.f)); + + constexpr bool fastMode = baudrate > 125'000; + // Baudrate threshold above which the low time is fixed to the minimum value of + // 1.3us in fast mode or 4.7us in standard mode. + constexpr auto minLowTime = fastMode ? 1.3e-6 : 4.7e-6; + constexpr auto minLowTimeLimit = 1.f / (2*minLowTime); + + // t_high = ((CHDIV * 2^CKDIV) + 3) * (1 / peripheral clock) + // t_low = ((CLDIV * 2^CKDIV) + 3) * (1 / peripheral clock) + if constexpr (baudrate > minLowTimeLimit) { + // calculate ideal low and high prescaler values (formula from ASF vendor HAL) + constexpr auto cldiv = uint32_t(std::round((minLowTime * clock) - 3)); + constexpr auto tHigh = 1.f / ((baudrate + (baudrate - minLowTimeLimit)) * 2.f); + constexpr auto chdiv = uint32_t(std::round((tHigh * clock) - 3)); + + // use 2^N pre-divider if max. prescaler exceeds 8 bits + constexpr auto ckdiv = std::max(0, std::max(bits(cldiv), bits(chdiv)) - 8); + constexpr uint32_t cldivScaled = std::round(float(cldiv) / (1 << ckdiv)); + constexpr uint32_t chdivScaled = std::round(float(chdiv) / (1 << ckdiv)); + + if (!checkBaudrate()) + return std::nullopt; + + return TWIHS_CWGR_HOLD(hold) | TWIHS_CWGR_CLDIV(cldivScaled) | + TWIHS_CWGR_CHDIV(chdivScaled) | TWIHS_CWGR_CKDIV(ckdiv); + } else { + constexpr auto div = uint32_t(std::round(clock / (baudrate * 2.f) - 3)); + + // use 2^N pre-divider if max. prescaler exceeds 8 bits + constexpr auto ckdiv = std::max(0, bits(div) - 8); + constexpr uint32_t divScaled = std::round(float(div) / (1 << ckdiv)); + + if (!checkBaudrate()) + return std::nullopt; + + return TWIHS_CWGR_HOLD(hold) | TWIHS_CWGR_CLDIV(divScaled) | + TWIHS_CWGR_CHDIV(divScaled) | TWIHS_CWGR_CKDIV(ckdiv); + } + } + +public: + template + static void + connect(PullUps pullups = PullUps::External, ResetDevices reset = ResetDevices::Standard) + { + using Scl = GetPin_t; + using Sda = GetPin_t; + + using Peripheral = Peripherals::Twihs<{{ id | int }}>; + + using SclConnector = typename Scl::template Connector>; + using SdaConnector = typename Sda::template Connector>; + + const InputType input = + (pullups == PullUps::Internal) ? InputType::PullUp : InputType::Floating; + + Scl::configure(input); + Sda::configure(input); + if (reset != ResetDevices::NoReset) resetDevices(uint32_t(reset)); + SclConnector::connect(); + SdaConnector::connect(); + } + + /** + * Set up the I2C module for master operation. + * + * @param rate + * `Standard` (100 kHz) or `Fast` (400 kHz) + */ + template + static void + initialize() + { + static_assert(baudrate <= 400'000, "Baudrate must not exceed 400 kHz for I2C fast mode"); + constexpr std::optional registerValue = calculateTimings(); + static_assert(bool(registerValue), "Could not find a valid clock configuration for the requested" + " baudrate and tolerance"); + + initializeWithClockConfig(registerValue.value()); + } + + // start documentation inherited + static bool + start(I2cTransaction* transaction, ConfigurationHandler handler = nullptr); + + static Error + getErrorState(); + + static void + reset(); + // end documentation inherited + +private: + static void + initializeWithClockConfig(uint32_t cwgrRegister); +}; + + +} // namespace platform + +} // namespace modm + +#endif // MODM_STM32_I2C_{{ id }}_HPP diff --git a/src/modm/platform/i2c/sam_x7x/module.lb b/src/modm/platform/i2c/sam_x7x/module.lb new file mode 100644 index 0000000000..8c572e21e1 --- /dev/null +++ b/src/modm/platform/i2c/sam_x7x/module.lb @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Christopher DUrand +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +class Instance(Module): + def __init__(self, instance): + self.instance = instance + + def init(self, module): + module.name = str(self.instance) + module.description = "Instance {}".format(self.instance) + + def prepare(self, module, options): + module.depends(":platform:i2c") + module.add_option( + NumericOption( + name="buffer.transaction", + description="Length of transaction queue (in transactions)", + minimum=1, + maximum="64Ki-2", + default=8)) + + return True + + def build(self, env): + device = env[":target"] + driver = device.get_driver("twihs") + + properties = device.properties + properties["target"] = target = device.identifier + properties["id"] = self.instance + + env.substitutions = properties + env.outbasepath = "modm/src/modm/platform/i2c" + + env.template("i2c_master.cpp.in", "i2c_master_{}.cpp".format(self.instance)) + env.template("i2c_master.hpp.in", "i2c_master_{}.hpp".format(self.instance)) + +def init(module): + module.name = ":platform:i2c" + module.description = "Inter-Integrated Circuit (I²C)" + +def prepare(module, options): + device = options[":target"] + if not device.has_driver("twihs:sam*"): + return False + + module.depends( + ":architecture:accessor", + ":architecture:atomic", + ":architecture:clock", + ":architecture:i2c", + ":architecture:interrupt", + ":cmsis:device", + ":container", + ":platform:gpio") + + for instance in listify(device.get_driver("twihs")["instance"]): + module.add_submodule(Instance(int(instance))) + + return True + +def build(env): + pass diff --git a/test/modm/platform/i2c/samx7x/i2c_platform_test.cpp b/test/modm/platform/i2c/samx7x/i2c_platform_test.cpp new file mode 100644 index 0000000000..57c397cb98 --- /dev/null +++ b/test/modm/platform/i2c/samx7x/i2c_platform_test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "i2c_platform_test.hpp" + +#include +#include +#include + +using namespace modm::platform; +using namespace Board; + +namespace +{ + modm::At24Mac402 eeprom{0x57}; +} + +void +I2cPlatformTest::setUp() +{ + I2c::connect(); + I2c::initialize(); +} + +void +I2cPlatformTest::testPing() +{ + // ping at wrong address + for (uint8_t address = 0x50; address <= 0x56; ++address) { + eeprom.setAddress(address); + const bool response = RF_CALL_BLOCKING(eeprom.ping()); + TEST_ASSERT_FALSE(response); + } + // set correct address 0x57 + eeprom.setAddress(0x57); + // ping at correct address + for (int i = 0; i < 20; ++i) { + const bool response = RF_CALL_BLOCKING(eeprom.ping()); + TEST_ASSERT_TRUE(response); + } +} + +void +I2cPlatformTest::testDataRead() +{ + std::array buffer{}; + + // read pre-programmed MAC address + + // read at wrong address + eeprom.setAddress(0x55); + bool readSuccess = RF_CALL_BLOCKING(eeprom.readMac(buffer)); + TEST_ASSERT_FALSE(readSuccess); + + // read at correct address + eeprom.setAddress(0x57); + readSuccess = RF_CALL_BLOCKING(eeprom.readMac(buffer)); + TEST_ASSERT_TRUE(readSuccess); + + TEST_ASSERT_EQUALS(buffer[0], 0xfc); + TEST_ASSERT_EQUALS(buffer[1], 0xc2); + TEST_ASSERT_EQUALS(buffer[2], 0x3d); +} diff --git a/test/modm/platform/i2c/samx7x/i2c_platform_test.hpp b/test/modm/platform/i2c/samx7x/i2c_platform_test.hpp new file mode 100644 index 0000000000..5c622d1517 --- /dev/null +++ b/test/modm/platform/i2c/samx7x/i2c_platform_test.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +/// @ingroup modm_test_test_platform_i2c +class I2cPlatformTest : public unittest::TestSuite +{ +public: + void + setUp() override; + + void + testPing(); + + void + testDataRead(); +}; diff --git a/test/modm/platform/i2c/samx7x/module.lb b/test/modm/platform/i2c/samx7x/module.lb new file mode 100644 index 0000000000..f7da155f2b --- /dev/null +++ b/test/modm/platform/i2c/samx7x/module.lb @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Christopher Durand +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +def init(module): + module.name = ":test:platform:i2c" + module.description = "Tests for SAMx7x I2C" + +def prepare(module, options): + target = options[":target"] + + identifier = target.identifier + if identifier.platform != "sam" or identifier.family != "e7x/s7x/v7x": + return False + + module.depends(":platform:i2c:0") + module.depends(":driver:at24mac402") + return True + +def build(env): + if not env.has_module(":board:samv71-xplained-ultra"): + env.log.warn("The test requires an AT24MAC402 EEPROM to be connected to specific pins." + "Only the SAMV71 Xplained Ultra board is supported for now.") + return + + env.outbasepath = "modm-test/src/modm-test/platform/i2c_test" + env.copy("i2c_platform_test.hpp") + env.copy("i2c_platform_test.cpp")