diff --git a/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp b/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp deleted file mode 100644 index 09c7f252e..000000000 --- a/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-05-08. */ - -#if LT_RTL8710B - -#include "Wire.h" - -#include - -extern "C" { -extern int i2c_write_timeout(i2c_t *obj, int address, char *data, int length, int stop, int timeout_ms); -} - -#ifdef PIN_WIRE0_SDA -// Wire object associated to I2C0 interface. -TwoWire Wire(PIN_WIRE0_SDA, PIN_WIRE0_SCL); -#endif - -#if defined(PIN_WIRE0_SDA) && defined(PIN_WIRE1_SDA) -// Wire object associated to I2C1 interface. -TwoWire Wire1(PIN_WIRE1_SDA, PIN_WIRE1_SCL); -#endif - -#if !defined(PIN_WIRE0_SDA) && defined(PIN_WIRE1_SDA) -// Wire object associated to I2C1 interface. The board doesn't support I2C0. -TwoWire Wire(PIN_WIRE1_SDA, PIN_WIRE1_SCL); -#endif - -TwoWire::TwoWire() { - _timeout = 50; -} - -TwoWire::TwoWire(int8_t sda, int8_t scl) { - _timeout = 50; - _sda = sda; - _scl = scl; -} - -TwoWire::~TwoWire() {} - -bool TwoWire::setPins(int8_t sda, int8_t scl) { - // return true when changing pins on initialized I2C - if (_inSetPins) - return true; - // check if pins are provided - if (sda == -1 || scl == -1) - return false; - // set private pins - _sda = sda; - _scl = scl; - sda = pinInfo(sda)->gpio; - scl = pinInfo(scl)->gpio; - - // check if pins are valid - if ((sda == PA_4 || sda == PA_19 || sda == PA_30) && (scl == PA_1 || scl == PA_22 || scl == PA_29)) { - // I2C index 0 - _idx = 0; - } else if ((sda == PA_27 || sda == PA_2 || sda == PA_23) && (scl == PA_28 || scl == PA_3 || scl == PA_18)) { - // I2C index 1 - _idx = 1; - } else { - return false; - } - - // restart I2C if changing pins - // this will never be called from begin() - if (_i2c) { - _inSetPins = true; - end(); - begin(); - _inSetPins = false; - } - return true; -} - -bool TwoWire::begin(int8_t sda, int8_t scl, uint32_t frequency) { - if (_i2c) - return true; - // set private i2c pins - if (!setPins(sda, scl)) - return false; - // use default frequency - if (!frequency) - frequency = WIRE_DEFAULT_FREQ; - - _i2c = new i2c_t; - i2c_init(_i2c, (PinName)pinInfo(_sda)->gpio, (PinName)pinInfo(_scl)->gpio); - i2c_frequency(_i2c, frequency); - _freq = frequency; - return true; -} - -bool TwoWire::begin(uint8_t address, int8_t sda, int8_t scl, uint32_t frequency) { - if (_i2c) - return true; - // init master bus first, return if failed (wrong pins) - if (!begin(sda, scl, frequency)) - return false; - - i2c_slave_address(_i2c, _idx, address, 0xff); - i2c_slave_mode(_i2c, true); - return true; -} - -bool TwoWire::end() { - i2c_reset(_i2c); - delete _i2c; - _i2c = NULL; - return true; -} - -bool TwoWire::setClock(uint32_t freq) { - if (_i2c) { - i2c_frequency(_i2c, freq); - } - _freq = freq; - return true; -} - -void TwoWire::beginTransmission(uint8_t address) { - _txAddr = address; - _txBuf.clear(); -} - -// Errors: -// 0 : Success -// 1 : Data too long -// 2 : NACK on transmit of address -// 3 : NACK on transmit of data -// 4 : Other error -uint8_t TwoWire::endTransmission(bool stopBit) { - if (!_i2c || !_txAddr) - return 4; - char *buf = (char *)malloc(_txBuf.available()); - uint8_t i = 0; - while (_txBuf.available()) { - buf[i++] = _txBuf.read_char(); - } - int len = i2c_write_timeout(_i2c, _txAddr, buf, i, stopBit, _timeout); - free(buf); - _txAddr = 0; - if (len == -1) - return 2; // slave not available (if tx length == 0) - if (len != i) - return 3; // less bytes written - return 0; -} - -size_t TwoWire::requestFrom(uint8_t address, size_t len, bool stopBit) { - if (!len) - return 0; - - if (len > SERIAL_BUFFER_SIZE) - len = SERIAL_BUFFER_SIZE; - - _rxBuf.clear(); - - char *buf = (char *)malloc(_txBuf.available()); - i2c_read(_i2c, address, buf, len, stopBit); - uint8_t i = 0; - while (len) { - _rxBuf.store_char(buf[i++]); - len--; - } - free(buf); - return len; -} - -size_t TwoWire::write(uint8_t data) { - if (!_txAddr || _txBuf.isFull()) - return 0; - _txBuf.store_char(data); - return 1; -} - -size_t TwoWire::write(const uint8_t *data, size_t len) { - for (size_t i = 0; i < len; i++) { - if (!write(data[i])) - return i; - } - return len; -} - -int TwoWire::available() { - return _rxBuf.available(); -} - -int TwoWire::read() { - return _rxBuf.read_char(); -} - -int TwoWire::peek() { - return _rxBuf.peek(); -} - -void TwoWire::flush() {} - -#endif diff --git a/cores/realtek-amb/arduino/libraries/Wire/Wire.h b/cores/realtek-amb/arduino/libraries/Wire/Wire.h deleted file mode 100644 index bc48214c3..000000000 --- a/cores/realtek-amb/arduino/libraries/Wire/Wire.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-05-08. */ - -#pragma once - -#include -#include -#include - -#if !defined(PIN_WIRE0_SDA) && defined(PIN_WIRE1_SDA) -#define Wire1 Wire -#endif - -#define WIRE_HAS_END 1 -#define WIRE_DEFAULT_FREQ 100000 - -struct i2c_s; -typedef struct i2c_s i2c_t; - -using arduino::RingBuffer; - -class TwoWire : public HardwareI2C { - private: - i2c_t *_i2c = NULL; - uint8_t _idx = 0; - - RingBuffer _rxBuf; - RingBuffer _txBuf; - uint8_t _txAddr = 0; - bool _inSetPins = false; - - public: - TwoWire(); - TwoWire(int8_t sda, int8_t scl); - ~TwoWire(); - - bool setPins(int8_t sda, int8_t scl); - - bool begin(int8_t sda, int8_t scl, uint32_t frequency = 0); - bool begin(uint8_t address, int8_t sda, int8_t scl, uint32_t frequency = 0); - bool end(); - - bool setClock(uint32_t freq); - - void beginTransmission(uint8_t address); - uint8_t endTransmission(bool stopBit); - - size_t requestFrom(uint8_t address, size_t len, bool stopBit); - size_t write(uint8_t data); - size_t write(const uint8_t *data, size_t len); - - int available(); - int read(); - int peek(); - void flush(); - - using HardwareI2C::begin; - using HardwareI2C::endTransmission; - using HardwareI2C::requestFrom; - using HardwareI2C::write; - using Print::write; -}; - -#ifdef PIN_WIRE0_SDA -extern TwoWire Wire; -#endif -#ifdef PIN_WIRE1_SDA -extern TwoWire Wire1; -#endif diff --git a/cores/realtek-ambz/arduino/libraries/Wire/Wire.cpp b/cores/realtek-ambz/arduino/libraries/Wire/Wire.cpp new file mode 100644 index 000000000..a672d4bb0 --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/Wire/Wire.cpp @@ -0,0 +1,245 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-21. */ + +#include "WirePrivate.h" + +bool TwoWire::begin(pin_size_t sda, pin_size_t scl, uint32_t frequency) { + return this->begin(0x00, sda, scl, frequency); +} + +bool TwoWire::begin(uint8_t address, pin_size_t sda, pin_size_t scl, uint32_t frequency) { + if (!this->setPinsPrivate(sda, scl)) + return false; + + LT_DM(I2C, "Begin: sda=%d, scl=%d, port=%d", this->sda, this->scl, this->port); + + if (!this->data) { + this->data = new WireData(); + this->rxBuf = &this->data->buf; + this->txBuf = new RingBuffer(); + this->data->i2c = I2C_DEV_TABLE[this->port].I2Cx; + + switch (this->port) { + case 0: + RCC_PeriphClockCmd(APBPeriph_I2C0, APBPeriph_I2C0_CLOCK, ENABLE); + break; + case 1: + RCC_PeriphClockCmd(APBPeriph_I2C1, APBPeriph_I2C1_CLOCK, ENABLE); + break; + } + } + + if (frequency == 0) + frequency = WIRE_DEFAULT_FREQ; + + Pinmux_Config(this->sda, PINMUX_FUNCTION_I2C); + Pinmux_Config(this->scl, PINMUX_FUNCTION_I2C); + PAD_PullCtrl(this->sda, GPIO_PuPd_UP); + PAD_PullCtrl(this->scl, GPIO_PuPd_UP); + + I2C_InitTypeDef *init = &this->data->init; + I2C_StructInit(init); + init->I2CIdx = this->port; + init->I2CMaster = address == 0x00 ? I2C_MASTER_MODE : I2C_SLAVE_MODE; + init->I2CAckAddr = address; + this->address = address; + + if (this->frequency != frequency) + this->setClock(frequency); + + this->txBuf->clear(); + + return true; +} + +bool TwoWire::setClock(uint32_t frequency) { + if (!this->data) + return false; + + I2C_InitTypeDef *init = &this->data->init; + I2C_TypeDef *i2c = this->data->i2c; + uint32_t freqKhz = frequency / 1000; + + I2C_Cmd(i2c, DISABLE); + + if (freqKhz <= 100) + init->I2CSpdMod = I2C_SS_MODE; + else if (freqKhz <= 400) + init->I2CSpdMod = I2C_FS_MODE; + else + init->I2CSpdMod = I2C_HS_MODE; + init->I2CClk = freqKhz; + + I2C_Init(i2c, init); + I2C_Cmd(i2c, ENABLE); + + this->frequency = frequency; + return true; +} + +bool TwoWire::end() { + if (!this->data) + return true; + I2C_TypeDef *i2c = this->data->i2c; + + I2C_Cmd(i2c, DISABLE); + + delete this->data; + delete this->txBuf; + this->data = nullptr; + this->rxBuf = nullptr; + this->txBuf = nullptr; + this->frequency = 0; + return true; +} + +static bool isFlagTimeout(I2C_TypeDef *i2c, uint32_t timeout, uint32_t flag) { + unsigned long start = millis(); + while ((I2C_CheckFlagState(i2c, flag)) == 0) { + DelayUs(2); + if (millis() - start > timeout) { + // reset I2C to clear trap state + DelayUs(100); + I2C_Cmd(i2c, DISABLE); + I2C_Cmd(i2c, ENABLE); + return true; + } + } + return false; +} + +static bool isNoAck(I2C_TypeDef *i2c) { + if (I2C_GetRawINT(i2c) & BIT_IC_RAW_INTR_STAT_TX_ABRT) { + // received NACK + I2C_ClearAllINT(i2c); + // reset I2C to clear trap state + DelayUs(100); + I2C_Cmd(i2c, DISABLE); + I2C_Cmd(i2c, ENABLE); + return true; + } + return false; +} + +uint8_t TwoWire::endTransmission(bool sendStop) { + if (!this->txBuf || this->address != 0x00) + return 4; + I2C_InitTypeDef *init = &this->data->init; + I2C_TypeDef *i2c = this->data->i2c; + RingBuffer *buf = this->txBuf; + + if (init->I2CAckAddr != this->txAddress) { + init->I2CAckAddr = this->txAddress; + I2C_Cmd(i2c, DISABLE); + I2C_Init(i2c, init); + I2C_Cmd(i2c, ENABLE); + } + + // TODO + sendStop = true; + + uint8_t err = 0; + bool scanOnly = false; + if (buf->available() == 0) { + uint32_t data = this->txAddress << 1; + if (sendStop) + data |= BIT_CTRL_IC_DATA_CMD_STOP; + else + data |= BIT_CTRL_IC_DATA_CMD_RESTART; + i2c->IC_DATA_CMD = data | BIT_CTRL_IC_DATA_CMD_NULLDATA; + + long waitTime; + if (this->frequency < 5000) + waitTime = 20; + else if (this->frequency < 10000) + waitTime = 10; + else + waitTime = 5; + delay(waitTime); + + scanOnly = true; + goto check; + } + + int bytesLeft; + while (bytesLeft = buf->available()) { + // wait for TX FIFO to be not full + if (isFlagTimeout(i2c, timeout, BIT_IC_STATUS_TFNF)) { + LT_EM(I2C, "Timeout @ 0x%02x (TX FIFO full)", address); + return 5; + } + + uint32_t data = buf->read_char(); + if (bytesLeft == 1) { + if (sendStop) + data |= BIT_CTRL_IC_DATA_CMD_STOP; + else + data |= BIT_CTRL_IC_DATA_CMD_RESTART; + } + i2c->IC_DATA_CMD = data; + } + +check: + // wait for TX FIFO to be empty + if (isFlagTimeout(i2c, timeout, BIT_IC_STATUS_TFE)) { + if (!scanOnly) + LT_EM(I2C, "Timeout @ 0x%02x (TX FIFO not empty)", address); + return 5; + } + // check if transmission succeeded + if (sendStop && isNoAck(i2c)) { + if (!scanOnly) + LT_EM(I2C, "No ACK @ 0x%02x", address); + return scanOnly ? 2 : 3; + } + return 0; +} + +size_t TwoWire::requestFrom(uint16_t address, size_t len, bool sendStop) { + if (!this->rxBuf || this->address != 0x00) + return 0; + I2C_InitTypeDef *init = &this->data->init; + I2C_TypeDef *i2c = this->data->i2c; + RingBuffer *buf = this->rxBuf; + + if (init->I2CAckAddr != address) { + init->I2CAckAddr = address; + I2C_Cmd(i2c, DISABLE); + I2C_Init(i2c, init); + I2C_Cmd(i2c, ENABLE); + } + + // TODO + sendStop = true; + + int bytesLeft = len; + buf->clear(); + while (bytesLeft && buf->availableForStore()) { + uint32_t data = BIT_CTRL_IC_DATA_CMD_CMD; + if (bytesLeft == 1) { + if (sendStop) + data |= BIT_CTRL_IC_DATA_CMD_STOP; + else + data |= BIT_CTRL_IC_DATA_CMD_RESTART; + } + i2c->IC_DATA_CMD = data; + + // check if transmission succeeded + if (isNoAck(i2c)) { + LT_EM(I2C, "No ACK @ 0x%02x", address); + goto end; + } + + // wait for RX FIFO to be not empty + if (isFlagTimeout(i2c, timeout, BIT_IC_STATUS_RFNE)) { + LT_EM(I2C, "Timeout @ 0x%02x (RX FIFO empty)", address); + goto end; + } + + buf->store_char((uint8_t)i2c->IC_DATA_CMD); + + bytesLeft--; + } + +end: + return len - bytesLeft; +} diff --git a/cores/realtek-ambz/arduino/libraries/Wire/WirePrivate.h b/cores/realtek-ambz/arduino/libraries/Wire/WirePrivate.h new file mode 100644 index 000000000..392f27cc5 --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/Wire/WirePrivate.h @@ -0,0 +1,12 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-21. */ + +#pragma once + +#include +#include + +struct WireData { + I2C_InitTypeDef init; + I2C_TypeDef *i2c; + RingBuffer buf; +}; diff --git a/cores/realtek-ambz/arduino/src/lt_defs.h b/cores/realtek-ambz/arduino/src/lt_defs.h index e631ff802..f37e5bf88 100644 --- a/cores/realtek-ambz/arduino/src/lt_defs.h +++ b/cores/realtek-ambz/arduino/src/lt_defs.h @@ -4,4 +4,5 @@ #define LT_ARD_HAS_SERIAL 1 #define LT_ARD_HAS_SOFTSERIAL 1 +#define LT_ARD_HAS_WIRE 1 #define LT_ARD_MD5_MBEDTLS 1 diff --git a/docs/status/supported.md b/docs/status/supported.md index bcc15a256..7764c1e73 100644 --- a/docs/status/supported.md +++ b/docs/status/supported.md @@ -36,30 +36,30 @@ The following list corresponds to UF2 OTA format family names, and is also [avai If you notice a feature that you've tested, which works (or not) and doesn't match this table, feel free to submit an issue on GitHub. -  | `BK7231T` | `BK7231N` | `RTL8710B` | `RTL8720C` | `BK7231Q` --------------------------|-----------|-----------|------------|------------|---------- -Stability | 5/5 | 5/5 | 4/5 | 2/5 | 1/5 -LibreTiny Core | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ -Wiring Core | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ -**PERIPHERALS** (Core) | | | | | -UART I/O | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ -Flash I/O | ✔️ | ✔️ | ✔️ | ❓ | ❓ -Deep sleep | ❓ | ✔️ | ❌ | ❌ | ❓ -Watchdog timer | ✔️ | ✔️ | ✔️ | ❓ | ❓ -**PERIPHERALS** (Wiring) | | | | | -Digital I/O | ✔️ | ✔️ | ✔️ | ❓ | ❓ -PWM | ✔️ | ✔️ | ✔️ | ❓ | ❓ -Interrupts | ✔️ | ✔️ | ✔️ | ❓ | ❓ -Analog input (ADC) | ✔️ | ✔️ | ✔️ | ❓ | ❓ -`Wire` (I²C) | ❌ | ❌ | ❗ | ❌ | ❌ -`SPI` | ❌ | ❌ | ❌ | ❌ | ❌ -`Serial` | ✔️ | ✔️ | ✔️ | ✔️ | ❓ -`SoftwareSerial` | ❌ | ❌ | ✔️ | ❌ | ❌ -**NETWORKING** | | | | | -Wi-Fi STA/AP/Mixed | ✔️ | ✔️ | ✔️ | ❓ | ❌ -Wi-Fi Events | ✔️ | ✔️ | ✔️ | ❓ | ❌ -OTA updates | ✔️ | ✔️ | ✔️ | ❌ | ❌ -MDNS | ✔️ | ✔️ | ✔️ | ❓ | ❓ +  | `BK7231T` | `BK7231N` | `RTL8710B` | `RTL8720C` | `BK7231Q` | `BK7251` +-------------------------|-----------|-----------|------------|------------|-----------|--------- +Stability | 5/5 | 5/5 | 3/5 | 1/5 | 0/5 | 1/5 +LibreTiny Core | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ +Wiring Core | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ +**PERIPHERALS** (Core) | | | | | | +UART I/O | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ +Flash I/O | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +Deep sleep | ❓ | ✔️ | ❌ | ❌ | ❓ | ❓ +Watchdog timer | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +**PERIPHERALS** (Wiring) | | | | | | +Digital I/O | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +PWM | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +Interrupts | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +Analog input (ADC) | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ +`Wire` (I²C) | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ +`SPI` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ +`Serial` | ✔️ | ✔️ | ✔️ | ✔️ | ❓ | ❓ +`SoftwareSerial` | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ +**NETWORKING** | | | | | | +Wi-Fi STA/AP/Mixed | ✔️ | ✔️ | ✔️ | ❓ | ❌ | ❓ +Wi-Fi Events | ✔️ | ✔️ | ✔️ | ❓ | ❌ | ❓ +OTA updates | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ +MDNS | ✔️ | ✔️ | ✔️ | ❓ | ❓ | ❓ Symbols: