diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index f7fc1f768..f1955f58a 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -17,7 +17,7 @@ jobs: python-version: '3.10' - name: Install docs dependencies - run: pip install -U ltchiptool "boardgen>=0.11.0" + run: pip install -U ltchiptool "boardgen>=0.11.1" - name: Generate docs and static JSON files run: | diff --git a/boards/_base/ic/rtl8710bn.json b/boards/_base/ic/rtl8710bn.json index 0a3d6e73c..605cf93fb 100644 --- a/boards/_base/ic/rtl8710bn.json +++ b/boards/_base/ic/rtl8710bn.json @@ -52,42 +52,42 @@ "C_NAME": "PA_6", "GPIO": "PA06", "IRQ": null, - "SPI": "FCS", + "FLASH": "^FCS", "SD": "D2" }, "19": { "C_NAME": "PA_7", "GPIO": "PA07", "IRQ": null, - "SPI": "FD1", + "FLASH": "FD1", "SD": "D3" }, "20": { "C_NAME": "PA_8", "GPIO": "PA08", "IRQ": null, - "SPI": "FD2", + "FLASH": "FD2", "SD": "CMD" }, "21": { "C_NAME": "PA_9", "GPIO": "PA09", "IRQ": null, - "SPI": "FD0", + "FLASH": "FD0", "SD": "CLK" }, "22": { "C_NAME": "PA_10", "GPIO": "PA10", "IRQ": null, - "SPI": "FSCK", + "FLASH": "FSCK", "SD": "D0" }, "23": { "C_NAME": "PA_11", "GPIO": "PA11", "IRQ": null, - "SPI": "FD3", + "FLASH": "FD3", "SD": "D1" }, "27": { diff --git a/boards/variants/cb3se.h b/boards/variants/cb3se.h index 7a1f53e33..92285ed82 100644 --- a/boards/variants/cb3se.h +++ b/boards/variants/cb3se.h @@ -12,6 +12,17 @@ #define NUM_ANALOG_OUTPUTS 6 // PWM & DAC outputs #define PINS_GPIO_MAX 26 // Last usable GPIO number +// SPI Interfaces +// -------------- +#define PIN_SPI0_CS 15u // GPIO15 +#define PIN_SPI0_MISO 17u // GPIO17 +#define PIN_SPI0_MOSI 16u // GPIO16 +#define PIN_SPI0_SCK 14u // GPIO14 +#define PINS_SPI0_CS (pin_size_t[]){15u} +#define PINS_SPI0_MISO (pin_size_t[]){17u} +#define PINS_SPI0_MOSI (pin_size_t[]){16u} +#define PINS_SPI0_SCK (pin_size_t[]){14u} + // Wire Interfaces // --------------- #define PIN_WIRE2_SCL 0u // GPIO0 @@ -72,8 +83,10 @@ // ----------------- #define HAS_SERIAL1 1 #define HAS_SERIAL2 1 +#define HAS_SPI0 1 #define HAS_WIRE2 1 #define SERIAL_INTERFACES_COUNT 2 +#define SPI_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 1 // Arduino pin names diff --git a/boards/variants/cbu.h b/boards/variants/cbu.h index 3714d7fd5..2cca37c83 100644 --- a/boards/variants/cbu.h +++ b/boards/variants/cbu.h @@ -12,6 +12,17 @@ #define NUM_ANALOG_OUTPUTS 6 // PWM & DAC outputs #define PINS_GPIO_MAX 28 // Last usable GPIO number +// SPI Interfaces +// -------------- +#define PIN_SPI0_CS 15u // GPIO15 +#define PIN_SPI0_MISO 17u // GPIO17 +#define PIN_SPI0_MOSI 16u // GPIO16 +#define PIN_SPI0_SCK 14u // GPIO14 +#define PINS_SPI0_CS (pin_size_t[]){15u} +#define PINS_SPI0_MISO (pin_size_t[]){17u} +#define PINS_SPI0_MOSI (pin_size_t[]){16u} +#define PINS_SPI0_SCK (pin_size_t[]){14u} + // Wire Interfaces // --------------- #define PIN_WIRE1_SCL 20u // GPIO20 @@ -79,9 +90,11 @@ // ----------------- #define HAS_SERIAL1 1 #define HAS_SERIAL2 1 +#define HAS_SPI0 1 #define HAS_WIRE1 1 #define HAS_WIRE2 1 #define SERIAL_INTERFACES_COUNT 2 +#define SPI_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 2 // Arduino pin names diff --git a/boards/variants/generic-bk7231n-qfn32-tuya.h b/boards/variants/generic-bk7231n-qfn32-tuya.h index 45ecbbcf4..8f2e4545a 100644 --- a/boards/variants/generic-bk7231n-qfn32-tuya.h +++ b/boards/variants/generic-bk7231n-qfn32-tuya.h @@ -12,6 +12,17 @@ #define NUM_ANALOG_OUTPUTS 6 // PWM & DAC outputs #define PINS_GPIO_MAX 28 // Last usable GPIO number +// SPI Interfaces +// -------------- +#define PIN_SPI0_CS 15u // GPIO15 +#define PIN_SPI0_MISO 17u // GPIO17 +#define PIN_SPI0_MOSI 16u // GPIO16 +#define PIN_SPI0_SCK 14u // GPIO14 +#define PINS_SPI0_CS (pin_size_t[]){15u} +#define PINS_SPI0_MISO (pin_size_t[]){17u} +#define PINS_SPI0_MOSI (pin_size_t[]){16u} +#define PINS_SPI0_SCK (pin_size_t[]){14u} + // Wire Interfaces // --------------- #define PIN_WIRE1_SCL 20u // GPIO20 @@ -79,9 +90,11 @@ // ----------------- #define HAS_SERIAL1 1 #define HAS_SERIAL2 1 +#define HAS_SPI0 1 #define HAS_WIRE1 1 #define HAS_WIRE2 1 #define SERIAL_INTERFACES_COUNT 2 +#define SPI_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 2 // Arduino pin names diff --git a/boards/variants/generic-bk7231t-qfn32-tuya.h b/boards/variants/generic-bk7231t-qfn32-tuya.h index d79dd755a..13376cccd 100644 --- a/boards/variants/generic-bk7231t-qfn32-tuya.h +++ b/boards/variants/generic-bk7231t-qfn32-tuya.h @@ -12,6 +12,17 @@ #define NUM_ANALOG_OUTPUTS 6 // PWM & DAC outputs #define PINS_GPIO_MAX 28 // Last usable GPIO number +// SPI Interfaces +// -------------- +#define PIN_SPI0_CS 15u // GPIO15 +#define PIN_SPI0_MISO 17u // GPIO17 +#define PIN_SPI0_MOSI 16u // GPIO16 +#define PIN_SPI0_SCK 14u // GPIO14 +#define PINS_SPI0_CS (pin_size_t[]){15u} +#define PINS_SPI0_MISO (pin_size_t[]){17u} +#define PINS_SPI0_MOSI (pin_size_t[]){16u} +#define PINS_SPI0_SCK (pin_size_t[]){14u} + // Wire Interfaces // --------------- #define PIN_WIRE1_SCL 20u // GPIO20 @@ -79,9 +90,11 @@ // ----------------- #define HAS_SERIAL1 1 #define HAS_SERIAL2 1 +#define HAS_SPI0 1 #define HAS_WIRE1 1 #define HAS_WIRE2 1 #define SERIAL_INTERFACES_COUNT 2 +#define SPI_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 2 // Arduino pin names diff --git a/boards/variants/generic-bk7252.h b/boards/variants/generic-bk7252.h index 20c0b7ee3..ea0488334 100644 --- a/boards/variants/generic-bk7252.h +++ b/boards/variants/generic-bk7252.h @@ -12,6 +12,17 @@ #define NUM_ANALOG_OUTPUTS 4 // PWM & DAC outputs #define PINS_GPIO_MAX 39 // Last usable GPIO number +// SPI Interfaces +// -------------- +#define PIN_SPI0_CS 15u // GPIO15 +#define PIN_SPI0_MISO 17u // GPIO17 +#define PIN_SPI0_MOSI 16u // GPIO16 +#define PIN_SPI0_SCK 14u // GPIO14 +#define PINS_SPI0_CS (pin_size_t[]){15u} +#define PINS_SPI0_MISO (pin_size_t[]){17u} +#define PINS_SPI0_MOSI (pin_size_t[]){16u} +#define PINS_SPI0_SCK (pin_size_t[]){14u} + // Wire Interfaces // --------------- #define PIN_WIRE1_SCL 20u // GPIO20 @@ -108,9 +119,11 @@ // ----------------- #define HAS_SERIAL1 1 #define HAS_SERIAL2 1 +#define HAS_SPI0 1 #define HAS_WIRE1 1 #define HAS_WIRE2 1 #define SERIAL_INTERFACES_COUNT 2 +#define SPI_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 2 // Arduino pin names diff --git a/boards/variants/generic-rtl8710bn-2mb-468k.c b/boards/variants/generic-rtl8710bn-2mb-468k.c index 10a9b9883..b1b1787fb 100644 --- a/boards/variants/generic-rtl8710bn-2mb-468k.c +++ b/boards/variants/generic-rtl8710bn-2mb-468k.c @@ -12,18 +12,18 @@ PinInfo lt_arduino_pin_info_list[PINS_COUNT] = { {PA_0, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D1: PA05, PWM4, WAKE1 {PA_5, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, - // D2: PA06, FCS, SD_D2 - {PA_6, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + // D2: PA06, ^FCS, SD_D2 + {PA_6, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D3: PA07, FD1, SD_D3 - {PA_7, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_7, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D4: PA08, FD2, SD_CMD - {PA_8, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_8, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D5: PA09, FD0, SD_CLK - {PA_9, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_9, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D6: PA10, FSCK, SD_D0 - {PA_10, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_10, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D7: PA11, FD3, SD_D1 - {PA_11, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_11, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D8: PA12, PWM3 {PA_12, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D9: PA14, PWM0, SWCLK diff --git a/boards/variants/generic-rtl8710bn-2mb-468k.h b/boards/variants/generic-rtl8710bn-2mb-468k.h index 03075cc19..4e8cbd748 100644 --- a/boards/variants/generic-rtl8710bn-2mb-468k.h +++ b/boards/variants/generic-rtl8710bn-2mb-468k.h @@ -66,12 +66,6 @@ #define PIN_CS0 19u // PA_19 #define PIN_CS1 19u // PA_19 #define PIN_CTS0 19u // PA_19 -#define PIN_FCS 6u // PA_6 -#define PIN_FD0 9u // PA_9 -#define PIN_FD1 7u // PA_7 -#define PIN_FD2 8u // PA_8 -#define PIN_FD3 11u // PA_11 -#define PIN_FSCK 10u // PA_10 #define PIN_MISO0 22u // PA_22 #define PIN_MISO1 22u // PA_22 #define PIN_MOSI0 23u // PA_23 diff --git a/boards/variants/generic-rtl8710bn-2mb-788k.c b/boards/variants/generic-rtl8710bn-2mb-788k.c index a19fd6c44..aef320e8f 100644 --- a/boards/variants/generic-rtl8710bn-2mb-788k.c +++ b/boards/variants/generic-rtl8710bn-2mb-788k.c @@ -12,18 +12,18 @@ PinInfo lt_arduino_pin_info_list[PINS_COUNT] = { {PA_0, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D1: PA05, PWM4, WAKE1 {PA_5, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, - // D2: PA06, FCS, SD_D2 - {PA_6, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + // D2: PA06, ^FCS, SD_D2 + {PA_6, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D3: PA07, FD1, SD_D3 - {PA_7, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_7, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D4: PA08, FD2, SD_CMD - {PA_8, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_8, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D5: PA09, FD0, SD_CLK - {PA_9, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_9, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D6: PA10, FSCK, SD_D0 - {PA_10, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_10, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D7: PA11, FD3, SD_D1 - {PA_11, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_11, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D8: PA12, PWM3 {PA_12, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D9: PA14, PWM0, SWCLK diff --git a/boards/variants/generic-rtl8710bn-2mb-788k.h b/boards/variants/generic-rtl8710bn-2mb-788k.h index a61de78b1..be91a8a41 100644 --- a/boards/variants/generic-rtl8710bn-2mb-788k.h +++ b/boards/variants/generic-rtl8710bn-2mb-788k.h @@ -66,12 +66,6 @@ #define PIN_CS0 19u // PA_19 #define PIN_CS1 19u // PA_19 #define PIN_CTS0 19u // PA_19 -#define PIN_FCS 6u // PA_6 -#define PIN_FD0 9u // PA_9 -#define PIN_FD1 7u // PA_7 -#define PIN_FD2 8u // PA_8 -#define PIN_FD3 11u // PA_11 -#define PIN_FSCK 10u // PA_10 #define PIN_MISO0 22u // PA_22 #define PIN_MISO1 22u // PA_22 #define PIN_MOSI0 23u // PA_23 diff --git a/boards/variants/generic-rtl8710bx-4mb-980k.c b/boards/variants/generic-rtl8710bx-4mb-980k.c index b2f0fce83..5f1f54041 100644 --- a/boards/variants/generic-rtl8710bx-4mb-980k.c +++ b/boards/variants/generic-rtl8710bx-4mb-980k.c @@ -12,18 +12,18 @@ PinInfo lt_arduino_pin_info_list[PINS_COUNT] = { {PA_0, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D1: PA05, PWM4, WAKE1 {PA_5, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, - // D2: PA06, FCS, SD_D2 - {PA_6, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + // D2: PA06, ^FCS, SD_D2 + {PA_6, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D3: PA07, FD1, SD_D3 - {PA_7, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_7, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D4: PA08, FD2, SD_CMD - {PA_8, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_8, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D5: PA09, FD0, SD_CLK - {PA_9, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_9, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D6: PA10, FSCK, SD_D0 - {PA_10, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_10, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D7: PA11, FD3, SD_D1 - {PA_11, PIN_GPIO | PIN_IRQ | PIN_SPI, PIN_NONE, 0}, + {PA_11, PIN_GPIO | PIN_IRQ, PIN_NONE, 0}, // D8: PA12, PWM3 {PA_12, PIN_GPIO | PIN_IRQ | PIN_PWM, PIN_NONE, 0}, // D9: PA14, PWM0, SWCLK diff --git a/boards/variants/generic-rtl8710bx-4mb-980k.h b/boards/variants/generic-rtl8710bx-4mb-980k.h index ffbef9404..45b9b6eb9 100644 --- a/boards/variants/generic-rtl8710bx-4mb-980k.h +++ b/boards/variants/generic-rtl8710bx-4mb-980k.h @@ -65,12 +65,6 @@ #define PIN_CS0 19u // PA_19 #define PIN_CS1 19u // PA_19 #define PIN_CTS0 19u // PA_19 -#define PIN_FCS 6u // PA_6 -#define PIN_FD0 9u // PA_9 -#define PIN_FD1 7u // PA_7 -#define PIN_FD2 8u // PA_8 -#define PIN_FD3 11u // PA_11 -#define PIN_FSCK 10u // PA_10 #define PIN_MISO0 22u // PA_22 #define PIN_MISO1 22u // PA_22 #define PIN_MOSI0 23u // PA_23 diff --git a/builder/utils/cores.py b/builder/utils/cores.py index 9e4018466..18bbf8042 100644 --- a/builder/utils/cores.py +++ b/builder/utils/cores.py @@ -51,8 +51,8 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool: "+", "+", "+", - "+", - "+", + "+", + "+", ], includes=[ # prepend the paths before SDK directories diff --git a/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp b/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp new file mode 100644 index 000000000..1a33d9d1d --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp @@ -0,0 +1,198 @@ +/* Copyright (c) Kuba Szczodrzyński 2024-03-15. */ + +#include "SPIPrivate.h" + +#define SPI_PERI_CLK_26M (26 * 1000 * 1000) +#define SPI_PERI_CLK_DCO (80 * 1000 * 1000) + +bool SPIClass::beginPrivate() { + if (!this->data) + return false; + uint32_t param; + + REG_SPI0->ctrl = 0; + + intc_service_change_handler(IRQ_SPI, (FUNCPTR)SPIClass::isrHandlerStatic); + // bk_spi_configure() + REG_SPI0->BIT_WIDTH = 0; + REG_SPI0->MSTEN = true; + // disable hardware CS - issues with per-byte data transfer otherwise +#if LT_BK7231N + REG_SPI0->WIRE3_EN = true; +#else + REG_SPI0->NSSMD = 3; +#endif + // spi_init_msten(0) + REG_SPI0->TX_FIFO_INT_LEVEL = 0; + REG_SPI0->TX_FIFO_INT_LEVEL = 0; + REG_SPI0->TX_UDF_INT_EN = true; + REG_SPI0->RX_OVF_INT_EN = true; + // spi_active(1) + REG_SPI0->SPIEN = true; + // spi_icu_configuration(1) + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + param = IRQ_SPI_BIT; + icu_ctrl(CMD_ICU_INT_ENABLE, ¶m); + // spi_gpio_configuration() + param = GFUNC_MODE_SPI; + gpio_ctrl(CMD_GPIO_ENABLE_SECOND, ¶m); + + return true; +} + +bool SPIClass::endPrivate() { + if (!this->data) + return true; + uint32_t param; + + param = IRQ_SPI_BIT; + icu_ctrl(CMD_ICU_INT_DISABLE, ¶m); + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + + REG_SPI0->ctrl = 0; + + return true; +} + +void SPIClass::setFrequency(uint32_t frequency) { + if (!this->data) + return; + uint32_t param; + uint32_t maxFrequency = 30000000; + uint32_t sourceClk; + uint32_t clockDiv; + + REG_SPI0->SPIEN = false; + this->settings._clock = frequency; + + if (frequency == 26000000 || frequency == 13000000 || frequency == 6500000 || frequency <= 4333000) { +#if CFG_XTAL_FREQUENCE + sourceClk = CFG_XTAL_FREQUENCE; +#else + sourceClk = SPI_PERI_CLK_26M; +#endif + param = PCLK_POSI_SPI; + icu_ctrl(CMD_CONF_PCLK_26M, ¶m); + } else { + if (frequency > maxFrequency) { + LT_WM(SPI, "Clock freq too high! %lu > %lu", frequency, maxFrequency); + frequency = maxFrequency; + } + sourceClk = SPI_PERI_CLK_DCO; + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + param = PCLK_POSI_SPI; + icu_ctrl(CMD_CONF_PCLK_DCO, ¶m); + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + } + + if (frequency == 26000000 || frequency == 13000000 || frequency == 6500000) { + clockDiv = sourceClk / frequency - 1; + } else { + clockDiv = ((sourceClk >> 1) / frequency); + if (clockDiv < 2) + clockDiv = 2; + else if (clockDiv >= 255) + clockDiv = 255; + } + + REG_SPI0->SPI_CKR = clockDiv; + REG_SPI0->SPIEN = true; +} + +void SPIClass::setBitOrder(uint8_t bitOrder) { + if (!this->data) + return; + this->settings._bitOrder = bitOrder; +#if LT_BK7231N + REG_SPI0->LSB_FIRST = bitOrder == SPI_LSBFIRST; +#endif +} + +void SPIClass::setDataMode(uint8_t dataMode) { + if (!this->data) + return; + this->settings._dataMode = dataMode; + REG_SPI0->CKPOL = (dataMode >> 1) & 0b1; + REG_SPI0->CKPHA = (dataMode >> 0) & 0b1; +} + +void SPIClass::commitTransaction() { + if (this->txLen != 0) { + REG_SPI0->TX_FIFO_CLR = true; + REG_SPI0->TX_TRANS_LEN = this->txLen * 8; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->TX_EN = true; + while (this->txLen--) { + while (REG_SPI0->TX_FIFO_WR_READY == false) {} + uint32_t data = *this->txBuf++; + // LT_I("<- TX: %02x", data); + REG_SPI0->SPI_DAT = data; + } + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->TX_EN = false; + } + if (this->rxLen != 0) { + // REG_SPI0->RX_FIFO_CLR = true; + // REG_SPI0->RX_TRANS_LEN = this->rxLen * 8; + // REG_SPI0->RX_FIFO_INT_EN = false; + // REG_SPI0->RX_FINISH_INT_EN = false; + // REG_SPI0->RX_EN = true; + // while (this->rxLen--) { + // while (REG_SPI0->RX_FIFO_RD_READY == false) {} + // uint32_t data = REG_SPI0->SPI_DAT; + // // LT_I("-> RX: %02x", data); + // *this->rxBuf++ = data; + // } + // REG_SPI0->RX_EN = false; + // REG_SPI0->TX_FIFO_INT_EN = false; + // REG_SPI0->RX_FIFO_INT_EN = false; + } +} + +void SPIClass::isrHandlerStatic(void *param) { + SPI.isrHandler(param); +} + +void SPIClass::isrHandler(void *param) { + if (REG_SPI0->RX_FIFO_INT) { + LT_I("RX_FIFO_INT, rxLen=%d", this->rxLen); + while (this->rxLen != 0 && REG_SPI0->RX_FIFO_RD_READY == true) { + uint8_t data = REG_SPI0->SPI_DAT; + LT_I("RX data in ISR #1 %02x", data); + *this->rxBuf++ = data; + this->rxLen--; + } + if (this->rxLen == 0) { + REG_SPI0->RX_EN = false; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->RX_FIFO_INT_EN = false; + } + REG_SPI0->RX_FIFO_INT = true; + } + if (REG_SPI0->RX_FINISH_INT) { + LT_I("RX_FINISH_INT, rxLen=%d", this->rxLen); + while (this->rxLen != 0 && REG_SPI0->RX_FIFO_RD_READY == true) { + uint8_t data = REG_SPI0->SPI_DAT; + LT_I("RX data in ISR #2 %02x", data); + *this->rxBuf++ = data; + this->rxLen--; + } + REG_SPI0->RX_EN = false; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->RX_FIFO_INT_EN = false; + REG_SPI0->RX_FINISH_INT = true; + } + + if (REG_SPI0->TX_UDF_INT) { + LT_W("TX underflow"); + REG_SPI0->TX_UDF_INT = true; + } + if (REG_SPI0->RX_OVF_INT) { + LT_W("RX overflow"); + REG_SPI0->RX_OVF_INT = true; + } +} diff --git a/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h b/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h new file mode 100644 index 000000000..96aa8c5ef --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h @@ -0,0 +1,143 @@ +/* Copyright (c) Kuba Szczodrzyński 2024-03-15. */ + +#pragma once + +#include +#include + +struct SPIData {}; + +// Register structures based on: +// - https://github.com/bekencorp/armino/blob/main/middleware/soc/bk7231n/soc/spi_struct.h +// - https://wiki.bekencorp.com/pages/viewpage.action?pageId=23692278 +// - BDK beken378/driver/spi/*.h + +typedef volatile struct { + union { + struct { + uint32_t TX_FIFO_INT_LEVEL : 2; //!< TX FIFO interrupt generation condition (1/16/32/48) [RW] (0x0[0:1]) + uint32_t RX_FIFO_INT_LEVEL : 2; //!< RX FIFO interrupt generation condition (1/16/32/48) [RW] (0x0[2:3]) + uint32_t TX_UDF_INT_EN : 1; //!< TX FIFO underflow interrupt enable [RW] (0x0[4]) + uint32_t RX_OVF_INT_EN : 1; //!< RX FIFO overflow interrupt enable [RW] (0x0[5]) + uint32_t TX_FIFO_INT_EN : 1; //!< TX FIFO interrupt enable [RW] (0x0[6]) + uint32_t RX_FIFO_INT_EN : 1; //!< RX FIFO interrupt enable [RW] (0x0[7]) + uint32_t SPI_CKR : 8; //!< Prescaler factor [RW] (0x0[8:15]) +#if LT_BK7231N || LT_BK7271 + uint32_t SLV_RELEASE_INT_EN : 1; //!< Slave release interrupt enable (four-wire slave mode) [RW] (0x0[16]) + uint32_t WIRE3_EN : 1; //!< Three-wire mode (no CSN signal) [RW] (0x0[17]) +#else + uint32_t NSSMD : 2; //!< (0x0[16:17]) +#endif + uint32_t BIT_WIDTH : 1; //!< Data bit width (0: 8-bit, 1: 16-bit) [RW] (0x0[18]) +#if LT_BK7231N || LT_BK7271 + uint32_t LSB_FIRST : 1; //!< Data frame format (0: MSB-first, 1: LSB-first) [RW] (0x0[19]) +#else + uint32_t _reserved0 : 1; //!< (0x0[19]) +#endif + uint32_t CKPOL : 1; //!< SPI clock polarity [RW] (0x0[20]) + uint32_t CKPHA : 1; //!< SPI clock phase [RW] (0x0[21]) + uint32_t MSTEN : 1; //!< Master mode enable [RW] (0x0[22]) + uint32_t SPIEN : 1; //!< SPI enable [RW] (0x0[23]) +#if LT_BK7231N || LT_BK7271 + uint32_t BYTE_INTLVAL : 8; //!< SCK interval between data units (master) [RW] (0x0[24:31]) +#else + uint32_t _reserved1 : 8; //!< (0x0[24:31]) +#endif + }; + + uint32_t ctrl; + }; + +#if LT_BK7231N || LT_BK7271 + union { + struct { + uint32_t TX_EN : 1; //!< TX enable [RW] (0x1[0]) + uint32_t RX_EN : 1; //!< RX enable [RW] (0x1[1]) + uint32_t TX_FINISH_INT_EN : 1; //!< TX_TRANS_LEN completed interrupt enabled [RW] (0x1[2]) + uint32_t RX_FINISH_INT_EN : 1; //!< RX_TRANS_LEN completed interrupt enable [RW] (0x1[3]) + uint32_t _reserved2 : 4; //!< (0x1[4:7]) + uint32_t TX_TRANS_LEN : 12; //!< TX data length (bits), 0: unlimited [RW] (0x1[8:19]) + uint32_t RX_TRANS_LEN : 12; //!< RX data length (bits), 0: unlimited [RW] (0x1[20:31]) + }; + + uint32_t config; + }; +#endif + +#if LT_BK7231N || LT_BK7271 + union { + struct { + uint32_t _reserved3 : 1; //!< (0x2[0]) + uint32_t TX_FIFO_WR_READY : 1; //!< TX FIFO write allowed [R] (0x2[1]) + uint32_t RX_FIFO_RD_READY : 1; //!< RX FIFO read allowed [R] (0x2[2]) + uint32_t _reserved4 : 1; //!< (0x2[3]) + uint32_t _reserved5 : 4; //!< (0x2[4:7]) + uint32_t TX_FIFO_INT : 1; //!< TX FIFO interrupt (1: clear) [RW] (0x2[8]) + uint32_t RX_FIFO_INT : 1; //!< RX FIFO interrupt (1: clear) [RW] (0x2[9]) + uint32_t SLV_RELEASE_INT : 1; //!< Slave release interrupt (1: clear) [RW] (0x2[10]) + uint32_t TX_UDF_INT : 1; //!< TX FIFO underflow interrupt (1: clear) [RW] (0x2[11]) + uint32_t RX_OVF_INT : 1; //!< RX FIFO overflow interrupt (1: clear) [RW] (0x2[12]) + uint32_t TX_FINISH_INT : 1; //!< TX_TRANS_LEN completed interrupt [R] (0x2[13]) + uint32_t RX_FINISH_INT : 1; //!< RX_TRANS_LEN completed interrupt [R] (0x2[14]) + uint32_t _reserved6 : 1; //!< (0x2[15]) + uint32_t TX_FIFO_CLR : 1; //!< TX FIFO clear enable [W] (0x2[16]) + uint32_t RX_FIFO_CLR : 1; //!< RX FIFO clear enable [W] (0x2[17]) + uint32_t _reserved7 : 14; //!< (0x2[18:31]) + }; + + uint32_t status; + }; +#else + union { + struct { + uint32_t TX_FIFO_EMPTY : 1; //!< (0x1[0]) + uint32_t TX_FIFO_FULL : 1; //!< (0x1[1]) + uint32_t RX_FIFO_EMPTY : 1; //!< (0x1[2]) + uint32_t RX_FIFO_FULL : 1; //!< (0x1[3]) + uint32_t _reserved8 : 4; //!< (0x2[4:7]) + uint32_t TX_FIFO_INT : 1; //!< TX FIFO interrupt (1: clear) [RW] (0x2[8]) + uint32_t RX_FIFO_INT : 1; //!< RX FIFO interrupt (1: clear) [RW] (0x2[9]) + uint32_t MODF : 1; //!< (0x1[10]) + uint32_t TX_UDF_INT : 1; //!< TX FIFO underflow interrupt (1: clear) [RW] (0x2[11]) + uint32_t RX_OVF_INT : 1; //!< RX FIFO overflow interrupt (1: clear) [RW] (0x2[12]) + uint32_t _reserved9 : 1; //!< (0x1[13]) + uint32_t SLVSEL : 1; //!< (0x1[14]) + uint32_t SPIBUSY : 1; //!< (0x1[15]) + uint32_t _reserved10 : 16; //!< (0x1[16:31]) + }; + + uint32_t status; + }; +#endif + + union { + struct { + uint32_t SPI_DAT : 16; //!< FIFO read/write operation [RW] (0x2[0:15]) + uint32_t _reserved11 : 16; //!< (0x2[16:31]) + }; + + uint32_t data; + }; + +#if !(LT_BK7231N || LT_BK7271) + union { + struct { + uint32_t _reserved12 : 1; //!< (0x3[0]) + uint32_t S_CS_UP_INT_EN : 1; //!< (0x3[1]) + uint32_t _reserved13 : 2; //!< (0x3[2:3]) + uint32_t S_CS_UP_INT_STATUS : 1; //!< (0x3[4]) + uint32_t _reserved14 : 27; //!< (0x3[5:31]) + }; + + uint32_t slave_ctrl; + }; +#endif +} spi_hw_t; + +#if LT_BK7271 +#define REG_SPI0 ((spi_hw_t *)0x00802500) +#define REG_SPI1 ((spi_hw_t *)0x00802540) +#define REG_SPI2 ((spi_hw_t *)0x00802580) +#else +#define REG_SPI0 ((spi_hw_t *)0x00802700) +#endif diff --git a/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp b/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp index 0aafcb940..9e0b1f561 100644 --- a/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp +++ b/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp @@ -1,15 +1,10 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-23. */ -#include "SerialPrivate.h" +#if LT_ARD_HAS_SERIAL || DOXYGEN -#if LT_HW_UART1 -SerialClass Serial1(UART1_PORT); -#endif -#if LT_HW_UART2 -SerialClass Serial2(UART2_PORT); -#endif +#include "SerialPrivate.h" -static void callback(int port, void *param) { +static void callback(int port, RingBuffer *buf) { int ch; while ((ch = uart_read_byte(port)) != -1) { #if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) && PIN_SERIAL1_RX != PIN_INVALID @@ -17,18 +12,18 @@ static void callback(int port, void *param) { if (port == UART1_PORT) SerialClass::adrParse(ch); #endif - pBUF->store_char(ch); + buf->store_char(ch); } } -void SerialClass::begin(unsigned long baudrate, uint16_t config) { - if (!this->data) { - this->data = new SerialData(); - this->buf = &BUF; - } +void SerialClass::beginPrivate(unsigned long baudrate, uint16_t config) { + if (!this->data) + return; + this->data->buf = this->rxBuf; - if (this->baudrate != baudrate || this->config != config) - this->configure(baudrate, config); + if (this->rx != PIN_INVALID) { + uart_rx_callback_set(this->port - 1, (uart_callback)callback, this->rxBuf); + } } void SerialClass::configure(unsigned long baudrate, uint16_t config) { @@ -47,23 +42,22 @@ void SerialClass::configure(unsigned long baudrate, uint16_t config) { .flow_control = FLOW_CTRL_DISABLED, }; - if (port == 1) + if (this->port == 1) uart1_init(); - else if (port == 2) + else if (this->port == 2) uart2_init(); - uart_hw_set_change(port, &cfg); - uart_rx_callback_set(port, callback, &BUF); + uart_hw_set_change(this->port - 1, &cfg); this->baudrate = baudrate; this->config = config; } -void SerialClass::end() { +void SerialClass::endPrivate() { if (!this->data) return; - uart_rx_callback_set(port, NULL, NULL); - switch (port) { + uart_rx_callback_set(this->port - 1, nullptr, nullptr); + switch (this->port) { case 1: uart1_exit(); break; @@ -71,11 +65,6 @@ void SerialClass::end() { uart2_exit(); break; } - - delete DATA; - this->data = NULL; - this->buf = NULL; - this->baudrate = 0; } void SerialClass::flush() { @@ -87,6 +76,8 @@ void SerialClass::flush() { size_t SerialClass::write(uint8_t c) { if (!this->data) return 0; - bk_send_byte(port, c); + bk_send_byte(this->port - 1, c); return 1; } + +#endif diff --git a/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h b/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h index c481237c0..fb134cc12 100644 --- a/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h +++ b/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h @@ -2,13 +2,8 @@ #pragma once -#include -#include +#include -typedef struct { - RingBuffer buf; -} SerialData; - -#define DATA ((SerialData *)data) -#define BUF (DATA->buf) -#define pBUF ((RingBuffer *)param) +struct SerialData { + RingBuffer *buf; +}; diff --git a/cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h b/cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h index 38ec80c00..0ff36c0da 100644 --- a/cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h +++ b/cores/beken-72xx/arduino/libraries/WiFi/WiFiPrivate.h @@ -2,8 +2,8 @@ #pragma once +#include #include -#include extern "C" { diff --git a/cores/beken-72xx/arduino/libraries/Wire/Wire.cpp b/cores/beken-72xx/arduino/libraries/Wire/Wire.cpp new file mode 100644 index 000000000..ab9600b81 --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/Wire/Wire.cpp @@ -0,0 +1,539 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-12-08. */ + +#include "WirePrivate.h" + +#define I2C_DIVID_CLK(div) (I2C1_DEFAULT_CLK / (((div + 1) * 3) - 6)) +#define I2C_BUSY_RETRIES 5 + +enum I2CISRMode { + I2C_ISR_IDLE = 0, + I2C_ISR_MASTER_WRITE = 1, + I2C_ISR_MASTER_READ = 2, + I2C_ISR_SLAVE_WRITE = 3, + I2C_ISR_SLAVE_READ = 4, +}; + +static I2CISRMode i2c1Mode = I2C_ISR_IDLE; +static bool i2c1SendStop = false; +static bool i2c1GotAck = false; +static RingBuffer *i2c1TxBuf = nullptr; +static RingBuffer *i2c1RxBuf = nullptr; +static int32_t i2c1RxLength = 0; + +static I2CISRMode i2c2Mode = I2C_ISR_IDLE; +static bool i2c2SendStop = false; +static bool i2c2GotAck = false; +static RingBuffer *i2c2TxBuf = nullptr; +static RingBuffer *i2c2RxBuf = nullptr; +static int32_t i2c2RxLength = 0; + +static void i2c1Handler(); +static void i2c2Handler(); + +#define REG_I2C2_READ_ALL(x) \ + x.config = REG_I2C2->config; \ + x.status = REG_I2C2->status; + +#define REG_I2C2_WRITE_ALL(x) \ + REG_I2C2->status = x.status; \ + REG_I2C2->config = x.config; + +bool TwoWire::beginPrivate(uint8_t address, uint32_t frequency) { + if (!this->data) + return false; + this->data->buf = this->rxBuf; + + if (this->port == 1 && address != 0x00) + // I2C1 supports master mode only + return false; + + LT_BUILD_CHECK(sizeof(*REG_I2C1) == 8); + LT_BUILD_CHECK(sizeof(*REG_I2C2) == 12); + + uint32_t reg, param; + switch (this->port) { + case 1: + // i2c1_hardware_init() + REG_I2C1->config = 0; + intc_service_register(IRQ_I2C1, PRI_IRQ_I2C1, i2c1Handler); + // i2c1_power_up() + param = PWD_I2C1_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + param = PCLK_POSI_I2C1; + icu_ctrl(CMD_CONF_PCLK_26M, ¶m); + // i2c1_enable_interrupt() + param = IRQ_I2C1_BIT; + icu_ctrl(CMD_ICU_INT_ENABLE, ¶m); + // i2c1_gpio_config() + param = GFUNC_MODE_I2C1; + gpio_ctrl(CMD_GPIO_ENABLE_SECOND, ¶m); + break; + case 2: + // i2c2_hardware_init() + REG_I2C2->config = 0; + REG_I2C2->status = 0; + intc_service_register(IRQ_I2C2, PRI_IRQ_I2C2, i2c2Handler); + // i2c2_set_idle_cr(0x3) + REG_I2C2->IDLE_CR = 3; + // i2c2_set_scl_cr(0x4) + REG_I2C2->SCL_CR = 4; + // i2c2_set_smbus_cs(0x3) + REG_I2C2->SMBCS = 0b11; + // i2c2_set_timeout_en(1) + REG_I2C2->SMBTOE = true; + // i2c2_set_free_detect(1) + REG_I2C2->SMBFTE = true; + // i2c2_set_salve_en(0) + REG_I2C2->INH = true; + // i2c2_power_up() + param = PCLK_POSI_I2C2; + icu_ctrl(CMD_CONF_PCLK_26M, ¶m); + param = PWD_I2C2_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + // i2c1_enable_interrupt() + param = IRQ_I2C2_BIT; + icu_ctrl(CMD_ICU_INT_ENABLE, ¶m); + // i2c2_gpio_config() + param = GFUNC_MODE_I2C2; + gpio_ctrl(CMD_GPIO_ENABLE_SECOND, ¶m); + break; + } + + // TODO support I2C2 and slave mode + this->address = address; + + return true; +} + +/* if (REG_I2C1->BUSY) { + if (this->data->busyCount >= I2C_BUSY_RETRIES) { + LT_FM(I2C, "GPIO didn't help!"); + return false; + } else { + LT_EM( + I2C, + "Busy in begin()! GPIO taking over (%u/" STRINGIFY_MACRO(I2C_BUSY_RETRIES) ")", + ++this->data->busyCount + ); + this->endPrivate(); + delay(200); + pinMode(this->sda, OUTPUT); + pinMode(this->scl, OUTPUT); + digitalWrite(this->sda, HIGH); + digitalWrite(this->scl, HIGH); + delay(300); + return this->beginPrivate(address, frequency); + } +} */ + +bool TwoWire::setClock(uint32_t frequency) { + if (!this->data) + return false; + + uint32_t maxFrequency = 400000; + if (frequency > maxFrequency) { + LT_WM(I2C, "Clock freq too high! %lu < %lu", frequency, maxFrequency); + frequency = maxFrequency; + } + + uint32_t div = I2C_CLK_DIVID(frequency); + if (div > I2C1_FREQ_DIV_MASK) { + uint32_t minFrequency = I2C_DIVID_CLK(1023); + LT_WM(I2C, "Clock freq too low! %lu < %lu", frequency, minFrequency); + div = I2C1_FREQ_DIV_MASK; + } + + uint32_t reg; + switch (this->port) { + case 1: + REG_I2C1->FREQ_DIV = div; + break; + case 2: + REG_I2C2->FREQ_DIV = div; + break; + } + + this->frequency = frequency; + return true; +} + +bool TwoWire::endPrivate() { + if (!this->data) + return true; + + uint32_t reg, param; + switch (this->port) { + case 1: + // i2c1_set_ensmb(0) + REG_I2C1->ENSMB = false; + // i2c1_disable_interrupt() + param = IRQ_I2C1_BIT; + icu_ctrl(CMD_ICU_INT_DISABLE, ¶m); + // i2c1_power_down() + param = PWD_I2C1_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + // i2c1_exit() + REG_I2C1->config = 0; + break; + case 2: + // i2c2_set_ensmb(0) + REG_I2C2->ENSMB = false; + // i2c2_disable_interrupt() + param = IRQ_I2C2_BIT; + icu_ctrl(CMD_ICU_INT_DISABLE, ¶m); + // i2c2_power_down() + param = PWD_I2C2_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + // i2c2_exit() + REG_I2C2->config = 0; + REG_I2C2->status = 0; + break; + } + + return true; +} + +/* LT_DM( + I2C, + "ISR IN : mode=%d, tx=%d/rx=%d, ENSMB=%d, STA=%d, STO=%d, ACKTX=%d, TXMODE=%d, ACKRX=%d, ACKRQ=%d", + i2c1Mode, + i2c1TxBuf ? i2c1TxBuf->available() : -1, + i2c1RxLength, + REG_I2C1->ENSMB, + REG_I2C1->STA, + REG_I2C1->STO, + REG_I2C1->ACKTX, + REG_I2C1->TXMODE, + REG_I2C1->ACKRX, + REG_I2C1->ACKRQ +); */ + +static void i2c1Handler() { + if (!REG_I2C1->SI) { + LT_WM(I2C, "I2C1 interrupt not triggered"); + return; + } + + if (REG_I2C1->TXMODE) { + // check if ACK received + i2c1GotAck = REG_I2C1->ACKRX; + // end the transmission if NACK + if (!i2c1GotAck) + goto stop; + } + + switch (i2c1Mode) { + case I2C_ISR_MASTER_WRITE: + // end the transmission if no data left to send + if (i2c1TxBuf == nullptr || i2c1TxBuf->available() == 0) + goto stop; + // otherwise write the next byte + REG_I2C1->SMB_DAT = i2c1TxBuf->read_char(); + break; + + case I2C_ISR_MASTER_READ: + // disable TX mode unconditionally + REG_I2C1->TXMODE = false; + // failsafe for no buffer + if (i2c1RxBuf == nullptr) + i2c1RxLength = 0; + // quit the first ISR call, wait for next one with data + if (REG_I2C1->STA) { + // send ACK if *any* data is requested + REG_I2C1->ACKTX = i2c1RxLength > 0 ? true : false; + goto end; + } else { + // send ACK if *more* data is requested + REG_I2C1->ACKTX = i2c1RxLength > 1 ? true : false; + } + // receive data + if (i2c1RxLength > 0) { + i2c1RxBuf->store_char(REG_I2C1->SMB_DAT); + i2c1RxLength--; + } + // end the transmission if no data left to receive + if (i2c1RxLength <= 0) + goto stop; + break; + } + goto end; + +stop: + // both methods will stop firing the ISR: + if (i2c1SendStop) { + // - send STOP condition if requested + // REG_I2C1->TXMODE = true; + REG_I2C1->STO = true; + } else { + // - otherwise disable I2C entirely + REG_I2C1->ENSMB = false; + } + // finish the transmission + i2c1Mode = I2C_ISR_IDLE; + +end: + REG_I2C1->STA = false; + REG_I2C1->SI = false; +} + +static void i2c2Handler() { + if (!REG_I2C2->SI) { + LT_WM(I2C, "I2C2 interrupt not triggered"); + return; + } + + i2c2_hw_t REG_I2C; + REG_I2C2_READ_ALL(REG_I2C); + + /* LT_DM( + I2C, + "ISR IN : mode=%d, tx=%d/rx=%d, ENSMB=%d, STA=%d, STO=%d, ACK=%d, TXMODE=%d, ACKRQ=%d", + i2c2Mode, + i2c2TxBuf ? i2c2TxBuf->available() : -1, + i2c2RxLength, + REG_I2C.ENSMB, + REG_I2C.STA, + REG_I2C.STO, + REG_I2C.ACK, + REG_I2C.TXMODE, + REG_I2C.ACKRQ + ); */ + + if (REG_I2C.TXMODE) { + // check if ACK received + i2c2GotAck = REG_I2C.ACK; + // end the transmission if NACK + if (!i2c2GotAck) + goto stop; + } + + switch (i2c2Mode) { + case I2C_ISR_MASTER_WRITE: + // end the transmission if no data left to send + if (i2c2TxBuf == nullptr || i2c2TxBuf->available() == 0) + goto stop; + // otherwise write the next byte + REG_I2C2->SMB_DAT = i2c2TxBuf->read_char(); + break; + + case I2C_ISR_MASTER_READ: + // failsafe for no buffer + if (i2c2RxBuf == nullptr) + i2c2RxLength = 0; + // quit the first ISR call, wait for next one with data + if (REG_I2C.STA) { + // send ACK if *any* data is requested + REG_I2C.ACK = i2c2RxLength > 0 ? true : false; + goto end; + } else { + // send ACK if *more* data is requested + REG_I2C.ACK = i2c2RxLength > 1 ? true : false; + } + // receive data + if (i2c2RxLength > 0) { + i2c2RxBuf->store_char(REG_I2C2->SMB_DAT); + i2c2RxLength--; + } + // end the transmission if no data left to receive + if (i2c2RxLength <= 0) + goto stop; + break; + } + goto end; + +stop: + // both methods will stop firing the ISR: + if (i2c2SendStop) { + // - send STOP condition if requested + REG_I2C.STO = true; + } else { + // - otherwise disable I2C entirely + // REG_I2C2->ENSMB = false; + } + // finish the transmission + i2c2Mode = I2C_ISR_IDLE; + +end: + REG_I2C.STA = false; + REG_I2C.SI = false; + REG_I2C2_WRITE_ALL(REG_I2C); + + /* LT_DM( + I2C, + "ISR OUT: mode=%d, tx=%d/rx=%d, ENSMB=%d, STA=%d, STO=%d, ACK=%d, TXMODE=%d, ACKRQ=%d", + i2c2Mode, + i2c2TxBuf ? i2c2TxBuf->available() : -1, + i2c2RxLength, + REG_I2C.ENSMB, + REG_I2C.STA, + REG_I2C.STO, + REG_I2C.ACK, + REG_I2C.TXMODE, + REG_I2C.ACKRQ + ); */ +} + +TwoWireResult TwoWire::endTransmission(bool sendStop) { + if (!this->txBuf || this->txAddress == 0x00) + return TWOWIRE_ERROR; + RingBuffer *buf = this->txBuf; + + i2c2_hw_t REG_I2C; + int dataLength; + I2CISRMode *i2cModePtr; + bool *i2cGotAckPtr; + + portDISABLE_INTERRUPTS(); + switch (this->port) { + case 1: + // disable STOP condition + REG_I2C1->STO = false; + // set slave address + REG_I2C1->SMB_DAT = (this->txAddress << 1) | 0; + // enable START condition + REG_I2C1->STA = true; + // set work state for ISR + dataLength = buf->available(); + i2c1Mode = I2C_ISR_MASTER_WRITE; + i2c1SendStop = sendStop; + i2c1TxBuf = buf; + i2cModePtr = &i2c1Mode; + i2cGotAckPtr = &i2c1GotAck; + // start the transmission and I2C peripheral + REG_I2C1->TXMODE = true; + REG_I2C1->ENSMB = true; + break; + case 2: + REG_I2C2_READ_ALL(REG_I2C); + // disable STOP condition + REG_I2C.STO = false; + // set slave address + REG_I2C2->SMB_DAT = (this->txAddress << 1) | 0; + // enable START condition + REG_I2C.STA = true; + // set work state for ISR + dataLength = buf->available(); + i2c2Mode = I2C_ISR_MASTER_WRITE; + i2c2SendStop = sendStop; + i2c2TxBuf = buf; + i2cModePtr = &i2c2Mode; + i2cGotAckPtr = &i2c2GotAck; + // start the transmission and I2C peripheral + REG_I2C.ENSMB = true; + REG_I2C2_WRITE_ALL(REG_I2C); + break; + default: + portENABLE_INTERRUPTS(); + return TWOWIRE_ERROR; + } + portENABLE_INTERRUPTS(); + + // wait for transmission to finish + unsigned long start = millis(); + while (*i2cModePtr != I2C_ISR_IDLE) { + ps_delay(1000); + if (millis() - start > timeout) { + LT_EM(I2C, "Timeout @ 0x%02x (TX not done)", this->txAddress); + return TWOWIRE_TIMEOUT; + } + } + + // stop the transmission (wait a bit for STOP condition) + delayMicroseconds(500); + switch (this->port) { + case 1: + REG_I2C1->TXMODE = false; + REG_I2C1->ENSMB = false; + break; + case 2: + REG_I2C2->ENSMB = false; + break; + } + + // check if ACK received + if (!*i2cGotAckPtr) { + if (buf->available() == dataLength) + return TWOWIRE_NACK_ADDR; + return TWOWIRE_NACK_DATA; + } + return TWOWIRE_SUCCESS; +} + +size_t TwoWire::requestFrom(uint16_t address, size_t len, bool sendStop) { + if (!this->rxBuf || this->address != 0x00) + return 0; + RingBuffer *buf = this->rxBuf; + + I2CISRMode *i2cModePtr; + int32_t *i2cRxLengthPtr; + + portDISABLE_INTERRUPTS(); + switch (this->port) { + case 1: + // disable STOP condition + REG_I2C1->STO = false; + // set slave address + REG_I2C1->SMB_DAT = (this->txAddress << 1) | 1; + // enable START condition + REG_I2C1->STA = true; + // set work state for ISR + i2c1Mode = I2C_ISR_MASTER_READ; + i2c1SendStop = sendStop; + i2c1RxBuf = buf; + i2c1RxLength = len; + i2cModePtr = &i2c1Mode; + i2cRxLengthPtr = &i2c1RxLength; + // start the transmission and I2C peripheral + REG_I2C1->TXMODE = true; + REG_I2C1->ENSMB = true; + break; + case 2: + // disable STOP condition + REG_I2C2->STO = false; + // set slave address + REG_I2C2->SMB_DAT = (this->txAddress << 1) | 1; + // enable START condition + REG_I2C2->STA = true; + // set work state for ISR + i2c2Mode = I2C_ISR_MASTER_READ; + i2c2SendStop = sendStop; + i2c2RxBuf = buf; + i2c2RxLength = len; + i2cModePtr = &i2c2Mode; + i2cRxLengthPtr = &i2c2RxLength; + // start the transmission and I2C peripheral + REG_I2C2->TXMODE = true; + REG_I2C2->ENSMB = true; + break; + default: + portENABLE_INTERRUPTS(); + return 0; + } + portENABLE_INTERRUPTS(); + + // wait for transmission to finish + unsigned long start = millis(); + while (*i2cModePtr != I2C_ISR_IDLE) { + ps_delay(1000); + if (millis() - start > timeout) { + LT_EM(I2C, "Timeout @ 0x%02x (RX not done)", this->txAddress); + goto end; + } + } + + // stop the transmission (wait a bit for STOP condition) + delayMicroseconds(500); + switch (this->port) { + case 1: + REG_I2C1->TXMODE = false; + REG_I2C1->ENSMB = false; + break; + case 2: + REG_I2C2->TXMODE = false; + REG_I2C2->ENSMB = false; + break; + } + +end: + return len - *i2cRxLengthPtr; +} diff --git a/cores/beken-72xx/arduino/libraries/Wire/WirePrivate.h b/cores/beken-72xx/arduino/libraries/Wire/WirePrivate.h new file mode 100644 index 000000000..c3694376a --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/Wire/WirePrivate.h @@ -0,0 +1,97 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-12-08. */ + +#pragma once + +#include +#include + +struct WireData { + RingBuffer *buf; +}; + +// Register structures based on: +// - https://github.com/bekencorp/armino/blob/main/middleware/soc/bk7231n/soc/i2c_struct.h +// - https://wiki.bekencorp.com/pages/viewpage.action?pageId=23692262 + +typedef volatile struct { + union { + struct { + uint32_t ENSMB : 1; //!< I2C interface enabled [RW] (0x0[0]) + uint32_t STA : 1; //!< Start/rep.start generation [RW] (0x0[1]) + uint32_t STO : 1; //!< Stop bit generation [RW] (0x0[2]) + uint32_t ACKTX : 1; //!< ACK TX generation [RW] (0x0[3]) + uint32_t TXMODE : 1; //!< Transmitter mode [RW] (0x0[4]) + uint32_t _reserved0 : 1; //!< (0x0[5]) + uint32_t FREQ_DIV : 10; //!< Clock frequency divider [RW] (0x0[6:15]) + uint32_t SI : 1; //!< I2C RX/TX completed [RW] (0x0[16]) + uint32_t ACKRX : 1; //!< ACK RX detection [R] (0x0[17]) + uint32_t ACKRQ : 1; //!< ACK response request (RX mode) [R] (0x0[18]) + uint32_t BUSY : 1; //!< Bus busy [R] (0x0[19]) + uint32_t _reserved1 : 12; //!< (0x0[20:31]) + }; + + uint32_t config; + }; + + union { + struct { + uint32_t SMB_DAT : 8; //!< Data register [RW] (0x1[0:7]) + uint32_t _reserved2 : 24; //!< (0x1[8:31]) + }; + + uint32_t data; + }; +} i2c1_hw_t; + +typedef volatile struct { + union { + struct { + uint32_t IDLE_CR : 3; //!< The bus idle detection threshold [RW] (0x0[0:2]) + uint32_t SCL_CR : 3; //!< SCL low level timeout threshold [RW] (0x0[3:5]) + uint32_t FREQ_DIV : 10; //!< Clock frequency divider [RW] (0x0[6:15]) + uint32_t SLV_ADDR : 10; //!< Slave address [RW] (0x0[16:25]) + uint32_t SMBCS : 2; //!< Interface clock source selection [RW] (0x0[26:27]) + uint32_t SMBTOE : 1; //!< SCL low level timeout detection enable [RW] (0x0[28]) + uint32_t SMBFTE : 1; //!< Bus idle detection enabled [RW] (0x0[29]) + uint32_t INH : 1; //!< Slave mode (address matching) disabled [RW] (0x0[30]) + uint32_t ENSMB : 1; //!< I2C interface enabled [RW] (0x0[31]) + }; + + uint32_t config; + }; + + union { + struct { + uint32_t SI : 1; //!< I2C RX/TX completed [RW] (0x1[0]) + uint32_t SCL_TMOT : 1; //!< SCL low level timeout [RW] (0x1[1]) + uint32_t _reserved0 : 1; //!< (0x1[2]) + uint32_t ARBLOST : 1; //!< Multi-master arbitration lost [RW] (0x1[3]) + uint32_t RXFIFO_EMPTY : 1; //!< Receive FIFO empty [R] (0x1[4]) + uint32_t TXFIFO_FULL : 1; //!< Send FIFO full [R] (0x1[5]) + uint32_t INT_MODE : 2; //!< FIFO threshold configuration [RW] (0x1[6:7]) + uint32_t ACK : 1; //!< ACK generation/detection [RW] (0x1[8]) + uint32_t STO : 1; //!< Stop bit generation/detection [RW] (0x1[9]) + uint32_t STA : 1; //!< Start/rep.start generation (master mode) [RW] (0x1[10]) + uint32_t ADDR_MATCH : 1; //!< Slave address matched (slave mode) [R] (0x1[11]) + uint32_t ACKRQ : 1; //!< ACK response request (RX mode) [R] (0x1[12]) + uint32_t TXMODE : 1; //!< Transmitter mode [R] (0x1[13]) + uint32_t MASTER : 1; //!< Master/slave mode [R] (0x1[14]) + uint32_t BUSY : 1; //!< Bus busy [R] (0x1[15]) + uint32_t _reserved1 : 16; //!< (0x1[16:31]) + }; + + uint32_t status; + }; + + union { + struct { + uint32_t SMB_DAT : 8; //!< Data register [RW] (0x2[0:7]) + uint32_t _reserved2 : 24; //!< (0x2[8:31]) + }; + + uint32_t data; + }; +} i2c2_hw_t; + +#define REG_I2C1 ((i2c1_hw_t *)I2C1_BASE_ADDR) +#define REG_I2C2 ((i2c2_hw_t *)I2C2_BASE_ADDR) diff --git a/cores/beken-72xx/arduino/src/lt_defs.h b/cores/beken-72xx/arduino/src/lt_defs.h index 3acbe6415..cd52f5fa2 100644 --- a/cores/beken-72xx/arduino/src/lt_defs.h +++ b/cores/beken-72xx/arduino/src/lt_defs.h @@ -4,4 +4,6 @@ #define LT_ARD_HAS_WIFI 1 #define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_HAS_SPI 1 +#define LT_ARD_HAS_WIRE 1 #define LT_ARD_MD5_HOSTAPD 1 diff --git a/cores/beken-72xx/arduino/src/main.cpp b/cores/beken-72xx/arduino/src/main.cpp index baf3ff7f5..a0b56aa11 100644 --- a/cores/beken-72xx/arduino/src/main.cpp +++ b/cores/beken-72xx/arduino/src/main.cpp @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-19. */ -#include +#include extern "C" { diff --git a/cores/beken-72xx/arduino/src/wiring.c b/cores/beken-72xx/arduino/src/wiring.c index cc37e37ce..b7f5ffb10 100644 --- a/cores/beken-72xx/arduino/src/wiring.c +++ b/cores/beken-72xx/arduino/src/wiring.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-19. */ -#include "wiring_private.h" +#include #if LT_BK7231Q #undef LT_MICROS_HIGH_RES diff --git a/cores/beken-72xx/arduino/src/wiring_analog.c b/cores/beken-72xx/arduino/src/wiring_analog.c index 110f33bbf..e7c82a3f0 100644 --- a/cores/beken-72xx/arduino/src/wiring_analog.c +++ b/cores/beken-72xx/arduino/src/wiring_analog.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-20. */ -#include "wiring_private.h" +#include static GPIO_INDEX pwmToGpio[] = { GPIO6, // PWM0 diff --git a/cores/beken-72xx/arduino/src/wiring_data.h b/cores/beken-72xx/arduino/src/wiring_data.h index 461eb6220..0ec412c06 100644 --- a/cores/beken-72xx/arduino/src/wiring_data.h +++ b/cores/beken-72xx/arduino/src/wiring_data.h @@ -2,8 +2,7 @@ #pragma once -#include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/cores/beken-72xx/arduino/src/wiring_digital.c b/cores/beken-72xx/arduino/src/wiring_digital.c index ab7dcffa0..5890b462b 100644 --- a/cores/beken-72xx/arduino/src/wiring_digital.c +++ b/cores/beken-72xx/arduino/src/wiring_digital.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-20. */ -#include "wiring_private.h" +#include void pinMode(pin_size_t pinNumber, PinMode pinMode) { pinCheckGetData(pinNumber, PIN_GPIO, ); diff --git a/cores/beken-72xx/arduino/src/wiring_irq.c b/cores/beken-72xx/arduino/src/wiring_irq.c index 884bc03f2..8e5c81a76 100644 --- a/cores/beken-72xx/arduino/src/wiring_irq.c +++ b/cores/beken-72xx/arduino/src/wiring_irq.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-07-31. */ -#include "wiring_private.h" +#include static void irqHandler(unsigned char gpio) { PinInfo *pin = pinByGpio(gpio); diff --git a/cores/beken-72xx/base/sdk_extern.h b/cores/beken-72xx/base/sdk_extern.h index 4cbccb07b..f9c9491a2 100644 --- a/cores/beken-72xx/base/sdk_extern.h +++ b/cores/beken-72xx/base/sdk_extern.h @@ -20,6 +20,8 @@ void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config); int uart_rx_callback_set(int uport, uart_callback callback, void *param); void sctrl_enter_rtos_deep_sleep(PS_DEEP_CTRL_PARAM *deep_param); void ps_delay(volatile UINT16 times); +void intc_service_change_handler(UINT8 int_num, FUNCPTR isr); +UINT32 icu_ctrl(UINT32 cmd, void *param); #ifdef __cplusplus } // extern "C" diff --git a/cores/beken-72xx/base/sdk_private.h b/cores/beken-72xx/base/sdk_private.h index 05e783238..6cbec2e04 100644 --- a/cores/beken-72xx/base/sdk_private.h +++ b/cores/beken-72xx/base/sdk_private.h @@ -14,11 +14,17 @@ extern "C" { #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include diff --git a/cores/common/arduino/libraries/api/SPI/SPI.cpp b/cores/common/arduino/libraries/api/SPI/SPI.cpp new file mode 100644 index 000000000..d8e5c0c2a --- /dev/null +++ b/cores/common/arduino/libraries/api/SPI/SPI.cpp @@ -0,0 +1,278 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#if LT_ARD_HAS_SPI || DOXYGEN + +#include + +SPIClass::~SPIClass() {} + +int SPIClass::getPortByPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi) { + if (sck == PIN_INVALID || miso == PIN_INVALID || mosi == PIN_INVALID) + return -1; +#if defined(PINS_SPI0_SCK) && defined(PINS_SPI0_MISO) && defined(PINS_SPI0_MOSI) + if (ltArrayContains(PINS_SPI0_SCK, sck) && ltArrayContains(PINS_SPI0_MISO, miso) && + ltArrayContains(PINS_SPI0_MOSI, mosi)) + return 0; +#endif +#if defined(PINS_SPI1_SCK) && defined(PINS_SPI1_MISO) && defined(PINS_SPI1_MOSI) + if (ltArrayContains(PINS_SPI1_SCK, sck) && ltArrayContains(PINS_SPI1_MISO, miso) && + ltArrayContains(PINS_SPI1_MOSI, mosi)) + return 1; +#endif +#if defined(PINS_SPI2_SCK) && defined(PINS_SPI2_MISO) && defined(PINS_SPI2_MOSI) + if (ltArrayContains(PINS_SPI2_SCK, sck) && ltArrayContains(PINS_SPI2_MISO, miso) && + ltArrayContains(PINS_SPI2_MOSI, mosi)) + return 2; +#endif + return -1; +} + +bool SPIClass::setPinsPrivate(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { + // default to already set pins + if (sck == PIN_INVALID) + sck = this->sck; + if (miso == PIN_INVALID) + miso = this->miso; + if (mosi == PIN_INVALID) + mosi = this->mosi; + + if (cs != PIN_INVALID) + this->cs = cs; + + if (sck == PIN_INVALID && miso == PIN_INVALID && mosi == PIN_INVALID) { + // set pins by port number +#if defined(PINS_SPI0_SCK) && defined(PINS_SPI0_MISO) && defined(PINS_SPI0_MOSI) + if (this->port == 0) { + this->sck = PINS_SPI0_SCK[0]; + this->miso = PINS_SPI0_MISO[0]; + this->mosi = PINS_SPI0_MOSI[0]; + return true; + } +#endif +#if defined(PINS_SPI1_SCK) && defined(PINS_SPI1_MISO) && defined(PINS_SPI1_MOSI) + if (this->port == 1) { + this->sck = PINS_SPI1_SCK[0]; + this->miso = PINS_SPI1_MISO[0]; + this->mosi = PINS_SPI1_MOSI[0]; + return true; + } +#endif +#if defined(PINS_SPI2_SCK) && defined(PINS_SPI2_MISO) && defined(PINS_SPI2_MOSI) + if (this->port == 2) { + this->sck = PINS_SPI2_SCK[0]; + this->miso = PINS_SPI2_MISO[0]; + this->mosi = PINS_SPI2_MOSI[0]; + return true; + } +#endif + } + + // set port number by specified pins + int port = this->getPortByPins(sck, miso, mosi); + if (port == -1) + // no such port + return false; + if (this->fixedPort && port != this->port) + // allow only same port for fixed port instances + return false; + this->port = port; + this->sck = sck; + this->miso = miso; + this->mosi = mosi; + return true; +} + +bool SPIClass::begin(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { + if (!this->setPinsPrivate(sck, miso, mosi, cs)) + return false; + return this->begin(); +} + +bool SPIClass::begin() { + if (!this->setPinsPrivate(PIN_INVALID, PIN_INVALID, PIN_INVALID)) + return false; + + LT_DM(SPI, "Begin: sck=%d, miso=%d, mosi=%d, port=%d", this->sck, this->miso, this->mosi, this->port); + + if (!this->data) { + this->data = new SPIData(); + } + + if (!this->beginPrivate()) + return false; + this->setFrequency(settings._clock); + this->setBitOrder(settings._bitOrder); + this->setDataMode(settings._dataMode); + + return true; +} + +bool SPIClass::end() { + if (!this->endPrivate()) + return false; + + delete this->data; + this->data = nullptr; + return true; +} + +bool SPIClass::setPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { + if (!this->data) + return this->setPinsPrivate(sck, miso, mosi, cs); + return this->begin(sck, miso, mosi, cs); +} + +__attribute__((weak)) void SPIClass::beginTransaction(SPISettings settings) { + auto oldSettings = this->settings; + + if (settings._clock != oldSettings._clock) + this->setFrequency(settings._clock); + if (settings._bitOrder != oldSettings._bitOrder) + this->setBitOrder(settings._bitOrder); + if (settings._dataMode != oldSettings._dataMode) + this->setDataMode(settings._dataMode); + + // this->inTransaction = true; + if (useCs && this->cs != PIN_INVALID) { + digitalWrite(useCs, LOW); + } +} + +__attribute__((weak)) void SPIClass::endTransaction() { + // this->inTransaction = false; + if (useCs && this->cs != PIN_INVALID) { + digitalWrite(useCs, HIGH); + } +} + +__attribute__((weak)) void SPIClass::setHwCs(bool useCs) { + this->useCs = useCs; + if (this->cs != PIN_INVALID) { + if (useCs) + pinMode(this->cs, OUTPUT); + else + pinModeRemove(this->cs, PIN_GPIO); + } +} + +__attribute__((weak)) void SPIClass::setClockDivider(uint32_t clockDiv) { + this->setFrequency(LT.getCpuFreq() / clockDiv); +} + +__attribute__((weak)) uint32_t SPIClass::getClockDivider() { + return LT.getCpuFreq() / this->settings._clock; +} + +__attribute__((weak)) void SPIClass::setBitOrder(uint8_t bitOrder) { + this->settings._bitOrder = bitOrder; +} + +__attribute__((weak)) uint8_t SPIClass::transfer(uint8_t data) { + this->txBuf = &data; + this->txLen = 1; + this->rxBuf = &data; + this->rxLen = 1; + this->commitTransaction(); + return data; +} + +__attribute__((weak)) uint16_t SPIClass::transfer16(uint16_t data) { + ByteUint16 data16; + data16.val = data; + if (this->settings._bitOrder == LSBFIRST) { + data16.lsb = this->transfer(data16.lsb); + data16.msb = this->transfer(data16.msb); + } else { + data16.msb = this->transfer(data16.msb); + data16.lsb = this->transfer(data16.lsb); + } + return data16.val; +} + +__attribute__((weak)) uint32_t SPIClass::transfer32(uint32_t data) { + ByteUint32 data32; + data32.val = data; + if (this->settings._bitOrder == LSBFIRST) { + data32.lsb = this->transfer(data32.lsb); + data32.byte1 = this->transfer(data32.byte1); + data32.byte2 = this->transfer(data32.byte2); + data32.msb = this->transfer(data32.msb); + } else { + data32.msb = this->transfer(data32.msb); + data32.byte2 = this->transfer(data32.byte2); + data32.byte1 = this->transfer(data32.byte1); + data32.lsb = this->transfer(data32.lsb); + } + return data32.val; +} + +__attribute__((weak)) void SPIClass::write(uint8_t data) { + this->txBuf = &data; + this->txLen = 1; + this->rxLen = 0; + this->commitTransaction(); +} + +__attribute__((weak)) void SPIClass::write16(uint16_t data) { + ByteUint16 data16; + data16.val = data; + if (this->settings._bitOrder == LSBFIRST) { + this->write(data16.lsb); + this->write(data16.msb); + } else { + this->write(data16.msb); + this->write(data16.lsb); + } +} + +__attribute__((weak)) void SPIClass::write32(uint32_t data) { + ByteUint32 data32; + data32.val = data; + if (this->settings._bitOrder == LSBFIRST) { + this->write(data32.lsb); + this->write(data32.byte1); + this->write(data32.byte2); + this->write(data32.msb); + } else { + this->write(data32.msb); + this->write(data32.byte2); + this->write(data32.byte1); + this->write(data32.lsb); + } +} + +__attribute__((weak)) void SPIClass::transferBytes(const uint8_t *data, uint8_t *out, uint32_t len) { + while (len--) { + *(out++) = this->transfer(*(data++)); + } +} + +__attribute__((weak)) void SPIClass::writeBytes(const uint8_t *data, uint32_t len) { + while (len--) { + this->write(*(data++)); + } +} + +// __attribute__((weak)) void SPIClass::transferBits(uint32_t data, uint32_t *out, uint8_t bits) {} +// __attribute__((weak)) void SPIClass::writePixels(const void *data, uint32_t len) {} +// __attribute__((weak)) void SPIClass::writePattern(const uint8_t *data, uint8_t len, uint32_t repeat) {} + +#if LT_HW_SPI0 +SPIClass SPI(0); +#elif LT_HW_SPI1 +SPIClass SPI(1); +#elif LT_HW_SPI2 +SPIClass SPI(2); +#endif + +#if LT_HW_SPI0 +SPIClass SPI0(PORT_FIXED_BIT | 0); +#endif +#if LT_HW_SPI1 +SPIClass SPI1(PORT_FIXED_BIT | 1); +#endif +#if LT_HW_SPI2 +SPIClass SPI2(PORT_FIXED_BIT | 2); +#endif + +#endif diff --git a/cores/common/arduino/libraries/api/SPI/SPI.h b/cores/common/arduino/libraries/api/SPI/SPI.h new file mode 100644 index 000000000..4ceaebf83 --- /dev/null +++ b/cores/common/arduino/libraries/api/SPI/SPI.h @@ -0,0 +1,128 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#pragma once + +#if !LT_ARD_HAS_SPI +#error "SPI library not implemented for this chip family" +#endif + +#include + +typedef struct SPIData SPIData; + +#define SPI_MODE0 0 //!< CPOL=0, CPHA=0 +#define SPI_MODE1 1 //!< CPOL=0, CPHA=1 +#define SPI_MODE2 2 //!< CPOL=1, CPHA=0 +#define SPI_MODE3 3 //!< CPOL=1, CPHA=1 + +#define SPI_LSBFIRST 0 +#define SPI_MSBFIRST 1 + +#define SPI_HAS_TRANSACTION + +class SPISettings { + public: + SPISettings() {} + + SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) + : _clock(clock), _bitOrder(bitOrder), _dataMode(dataMode) {} + + uint32_t _clock{1000000}; + uint8_t _bitOrder{SPI_MSBFIRST}; + uint8_t _dataMode{SPI_MODE0}; +}; + +class SPIClass { + private: + uint8_t port{0}; //!< port number, family-specific + bool fixedPort{false}; //!< whether port is not changeable + pin_size_t sck{PIN_INVALID}; //!< SCK pin number of this instance + pin_size_t miso{PIN_INVALID}; //!< MISO pin number of this instance + pin_size_t mosi{PIN_INVALID}; //!< MOSI pin number of this instance + pin_size_t cs{PIN_INVALID}; //!< CS pin number of this instance + + private: + SPIData *data{nullptr}; //!< family-specific, created in begin(), destroyed in end() + SPISettings settings{}; //!< current SPI settings + bool inTransaction{false}; //!< whether transaction is in progress + bool useCs{false}; //!< whether to toggle CS automatically + + uint8_t *txBuf{nullptr}; //!< TX data buffer (read head) + uint32_t txLen{0}; //!< TX data length (remaining) + uint8_t *rxBuf{nullptr}; //!< RX data buffer (write head) + uint32_t rxLen{0}; //!< RX data length (remaining) + + int getPortByPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi); + bool setPinsPrivate(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); + + private: /* family core */ + bool beginPrivate(); + bool endPrivate(); + + public: /* family core */ + void setFrequency(uint32_t frequency); + void setDataMode(uint8_t dataMode); + + void commitTransaction(); + void isrHandler(void *param); + static void isrHandlerStatic(void *param); + + public: /* common core */ + SPIClass(uint32_t port) : port(port) { + this->fixedPort = bool(port & PORT_FIXED_BIT); + } + + SPIClass(pin_size_t sck, pin_size_t miso, pin_size_t mosi) : sck(sck), miso(miso), mosi(mosi) {} + + ~SPIClass(); + + bool begin(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); + bool begin(); + bool end(); + + bool setPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); + + void beginTransaction(SPISettings settings); + void endTransaction(); + void setHwCs(bool useCs); + + void setClockDivider(uint32_t clockDiv); + uint32_t getClockDivider(); + void setBitOrder(uint8_t bitOrder); + + inline void transfer(void *data, uint32_t len) { + this->transferBytes((const uint8_t *)data, (uint8_t *)data, len); + } + + uint8_t transfer(uint8_t data); + uint16_t transfer16(uint16_t data); + uint32_t transfer32(uint32_t data); + void write(uint8_t data); + void write16(uint16_t data); + void write32(uint32_t data); + + void transferBytes(const uint8_t *data, uint8_t *out, uint32_t len); + void writeBytes(const uint8_t *data, uint32_t len); + + void transferBits(uint32_t data, uint32_t *out, uint8_t bits); + void writePixels(const void *data, uint32_t len); + void writePattern(const uint8_t *data, uint8_t len, uint32_t repeat); + + int8_t pinSS() { + return (int8_t)this->cs; + } +}; + +#if LT_HW_SPI0 || LT_HW_SPI1 || LT_HW_SPI2 +extern SPIClass SPI; +#endif + +#if LT_HW_SPI0 +extern SPIClass SPI0; +#endif +#if LT_HW_SPI1 +extern SPIClass SPI1; +#endif +#if LT_HW_SPI2 +extern SPIClass SPI2; +#endif diff --git a/cores/common/arduino/libraries/api/Serial/Serial.cpp b/cores/common/arduino/libraries/api/Serial/Serial.cpp index d9a3cb46d..05fb34e5e 100644 --- a/cores/common/arduino/libraries/api/Serial/Serial.cpp +++ b/cores/common/arduino/libraries/api/Serial/Serial.cpp @@ -1,14 +1,8 @@ /* Copyright (c) Kuba Szczodrzyński 2023-05-23. */ -#include "Serial.h" - -SerialClass::SerialClass(uint32_t port, pin_size_t rx, pin_size_t tx) { - this->port = port; - this->rx = rx; - this->tx = tx; - this->buf = NULL; - this->data = NULL; -} +#if LT_ARD_HAS_SERIAL || DOXYGEN + +#include #if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) static uint8_t adrState = 0; @@ -23,14 +17,46 @@ void SerialClass::adrParse(uint8_t c) { } #endif +void SerialClass::begin(unsigned long baudrate, uint16_t config) { + if (!this->data) { + this->data = new SerialData(); + this->rxBuf = new RingBuffer(); + } + + this->beginPrivate(baudrate, config); + // if (this->baudrate != baudrate || this->config != config) + this->configure(baudrate, config); +} + +void SerialClass::end() { + this->endPrivate(); + + delete this->data; + this->data = nullptr; + this->rxBuf = nullptr; + this->baudrate = 0; +} + int SerialClass::available() { - return this->buf ? this->buf->available() : 0; + return this->rxBuf ? this->rxBuf->available() : 0; } int SerialClass::peek() { - return this->buf ? this->buf->peek() : -1; + return this->rxBuf ? this->rxBuf->peek() : -1; } int SerialClass::read() { - return this->buf ? this->buf->read_char() : -1; + return this->rxBuf ? this->rxBuf->read_char() : -1; } + +#if LT_HW_UART0 +SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX); +#endif +#if LT_HW_UART1 +SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX); +#endif +#if LT_HW_UART2 +SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX); +#endif + +#endif diff --git a/cores/common/arduino/libraries/api/Serial/Serial.h b/cores/common/arduino/libraries/api/Serial/Serial.h index 1b80b6809..a743fe2c8 100644 --- a/cores/common/arduino/libraries/api/Serial/Serial.h +++ b/cores/common/arduino/libraries/api/Serial/Serial.h @@ -2,48 +2,54 @@ #pragma once +#if !LT_ARD_HAS_SERIAL +#error "Serial library not implemented for this chip family" +#endif + #include -#include -#include -using namespace arduino; +typedef struct SerialData SerialData; class SerialClass : public HardwareSerial { private: - uint32_t port; - pin_size_t rx; - pin_size_t tx; - - public: - void *data; + uint32_t port; //!< port number, family-specific + pin_size_t rx; //!< RX pin number of this instance + pin_size_t tx; //!< TX pin number of this instance private: - RingBuffer *buf; - uint32_t baudrate; - uint16_t config; + SerialData *data{nullptr}; //!< family-specific, created in begin(), destroyed in end() + RingBuffer *rxBuf{nullptr}; //!< RX data buffer, created in begin(), destroyed in end() + uint32_t baudrate{0}; //!< currently set baudrate + uint16_t config{0}; //!< currently set configuration - public: - SerialClass(uint32_t port, pin_size_t rx = PIN_INVALID, pin_size_t tx = PIN_INVALID); + private: /* family core */ + void beginPrivate(unsigned long baudrate, uint16_t config); + void endPrivate(); - inline void begin(unsigned long baudrate) { - begin(baudrate, SERIAL_8N1); - } + public: /* family core */ + void configure(unsigned long baudrate, uint16_t config); + void flush(); + size_t write(uint8_t c); - inline void configure(unsigned long baudrate) { - configure(baudrate, SERIAL_8N1); - } + public: /* common core */ + SerialClass(uint32_t port, pin_size_t rx = PIN_INVALID, pin_size_t tx = PIN_INVALID) : port(port), rx(rx), tx(tx) {} void begin(unsigned long baudrate, uint16_t config); - void configure(unsigned long baudrate, uint16_t config); void end(); int available(); int peek(); int read(); - void flush(); - size_t write(uint8_t c); + + inline void begin(unsigned long baudrate) { + begin(baudrate, SERIAL_8N1); + } + + inline void configure(unsigned long baudrate) { + configure(baudrate, SERIAL_8N1); + } operator bool() { - return !!buf; + return !!this->rxBuf; } public: @@ -54,4 +60,16 @@ class SerialClass : public HardwareSerial { using Print::write; }; -#define HAS_SERIAL_CLASS 1 +#if LT_HW_UART0 +extern SerialClass Serial0; +#endif +#if LT_HW_UART1 +extern SerialClass Serial1; +#endif +#if LT_HW_UART2 +extern SerialClass Serial2; +#endif + +#define SerialN(x) Serial##x +#define SerialM(x) SerialN(x) +#define Serial SerialM(LT_UART_DEFAULT_SERIAL) diff --git a/cores/common/arduino/libraries/api/Wire/Wire.cpp b/cores/common/arduino/libraries/api/Wire/Wire.cpp new file mode 100644 index 000000000..273ae0410 --- /dev/null +++ b/cores/common/arduino/libraries/api/Wire/Wire.cpp @@ -0,0 +1,196 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-21. */ + +#if LT_ARD_HAS_WIRE || DOXYGEN + +#include + +TwoWire::~TwoWire() {} + +int TwoWire::getPortByPins(pin_size_t sda, pin_size_t scl) { + if (sda == PIN_INVALID || scl == PIN_INVALID) + return -1; +#if defined(PINS_WIRE0_SDA) && defined(PINS_WIRE0_SCL) + if (ltArrayContains(PINS_WIRE0_SDA, sda) && ltArrayContains(PINS_WIRE0_SCL, scl)) + return 0; +#endif +#if defined(PINS_WIRE1_SDA) && defined(PINS_WIRE1_SCL) + if (ltArrayContains(PINS_WIRE1_SDA, sda) && ltArrayContains(PINS_WIRE1_SCL, scl)) + return 1; +#endif +#if defined(PINS_WIRE2_SDA) && defined(PINS_WIRE2_SCL) + if (ltArrayContains(PINS_WIRE2_SDA, sda) && ltArrayContains(PINS_WIRE2_SCL, scl)) + return 2; +#endif + return -1; +} + +bool TwoWire::setPinsPrivate(pin_size_t sda, pin_size_t scl) { + // default to already set pins + if (sda == PIN_INVALID) + sda = this->sda; + if (scl == PIN_INVALID) + scl = this->scl; + + // clear current transmission + if (this->txBuf) + this->txBuf->clear(); + this->txAddress = 0; + + if (sda == PIN_INVALID && scl == PIN_INVALID) { + // set pins by port number +#if defined(PINS_WIRE0_SDA) && defined(PINS_WIRE0_SCL) + if (this->port == 0) { + this->sda = PINS_WIRE0_SDA[0]; + this->scl = PINS_WIRE0_SCL[0]; + return true; + } +#endif +#if defined(PINS_WIRE1_SDA) && defined(PINS_WIRE1_SCL) + if (this->port == 1) { + this->sda = PINS_WIRE1_SDA[0]; + this->scl = PINS_WIRE1_SCL[0]; + return true; + } +#endif +#if defined(PINS_WIRE2_SDA) && defined(PINS_WIRE2_SCL) + if (this->port == 2) { + this->sda = PINS_WIRE2_SDA[0]; + this->scl = PINS_WIRE2_SCL[0]; + return true; + } +#endif + } + + // set port number by specified pins + int port = this->getPortByPins(sda, scl); + if (port == -1) + // no such port + return false; + if (this->fixedPort && port != this->port) + // allow only same port for fixed port instances + return false; + this->port = port; + this->sda = sda; + this->scl = scl; + return true; +} + +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; + + if (address != 0x00) { + LT_EM(I2C, "Slave mode is not supported yet!"); + 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 = new RingBuffer(); + this->txBuf = new RingBuffer(); + } + + if (frequency == 0) + frequency = this->frequency; + if (frequency == 0) + frequency = WIRE_DEFAULT_FREQ; + + if (!this->beginPrivate(address, frequency)) + return false; + if (!this->setClock(frequency)) + return false; + + this->txBuf->clear(); + + return true; +} + +bool TwoWire::end() { + if (!this->endPrivate()) + return false; + + delete this->data; + delete this->txBuf; + this->data = nullptr; + this->rxBuf = nullptr; + this->txBuf = nullptr; + this->frequency = 0; + return true; +} + +bool TwoWire::setPins(pin_size_t sda, pin_size_t scl) { + if (!this->data) + return this->setPinsPrivate(sda, scl); + if (this->address != 0x00) + return this->begin(this->address, sda, scl, this->frequency); + else + return this->begin(sda, scl, this->frequency); +} + +void TwoWire::beginTransmission(uint16_t address) { + if (!this->txBuf || this->address != 0x00) + // cannot begin if not started in master mode + return; + this->txBuf->clear(); + this->txAddress = address & 0x7F; +} + +size_t TwoWire::write(uint8_t data) { + if (!this->txBuf || this->txBuf->isFull()) + return 0; + this->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 (!this->write(data[i])) { + return i; + } + } + return len; +} + +void TwoWire::flush() { + if (!this->txBuf) + return; + this->txBuf->clear(); +} + +int TwoWire::available() { + return this->rxBuf ? this->rxBuf->available() : 0; +} + +int TwoWire::peek() { + return this->rxBuf ? this->rxBuf->peek() : -1; +} + +int TwoWire::read() { + return this->rxBuf ? this->rxBuf->read_char() : -1; +} + +#if LT_HW_I2C0 +TwoWire Wire(0); +#elif LT_HW_I2C1 +TwoWire Wire(1); +#elif LT_HW_I2C2 +TwoWire Wire(2); +#endif + +#if LT_HW_I2C0 +TwoWire Wire0(PORT_FIXED_BIT | 0); +#endif +#if LT_HW_I2C1 +TwoWire Wire1(PORT_FIXED_BIT | 1); +#endif +#if LT_HW_I2C2 +TwoWire Wire2(PORT_FIXED_BIT | 2); +#endif + +#endif diff --git a/cores/common/arduino/libraries/api/Wire/Wire.h b/cores/common/arduino/libraries/api/Wire/Wire.h new file mode 100644 index 000000000..afc281b9f --- /dev/null +++ b/cores/common/arduino/libraries/api/Wire/Wire.h @@ -0,0 +1,195 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-21. */ + +#pragma once + +#if !LT_ARD_HAS_WIRE +#error "Wire library not implemented for this chip family" +#endif + +#include + +typedef struct WireData WireData; + +// WIRE_HAS_BUFFER_SIZE means Wire has setBufferSize() +#undef WIRE_HAS_BUFFER_SIZE +// WIRE_HAS_END means Wire has end() +#define WIRE_HAS_END 1 +#define WIRE_DEFAULT_FREQ 100000 + +enum TwoWireResult { + TWOWIRE_SUCCESS = 0, + TWOWIRE_TX_OVERFLOW = 1, + TWOWIRE_NACK_ADDR = 2, + TWOWIRE_NACK_DATA = 3, + TWOWIRE_ERROR = 4, + TWOWIRE_TIMEOUT = 5, +}; + +class TwoWire : public Stream { + private: + int port{-1}; //!< port number, family-specific + bool fixedPort{false}; //!< whether port is not changeable + pin_size_t sda{PIN_INVALID}; //!< SDA pin number of this instance + pin_size_t scl{PIN_INVALID}; //!< SCL pin number of this instance + + private: + WireData *data{nullptr}; //!< family-specific, created in begin(), destroyed in end() + RingBuffer *rxBuf{nullptr}; //!< RX data buffer, assigned in begin(), removed in end() + RingBuffer *txBuf{nullptr}; //!< TX data buffer, created in beginTransmission(), destroyed in endTransmission() + uint32_t frequency{0}; //!< currently set clock frequency + uint32_t timeout{50}; //!< I2C ACK timeout + uint16_t address{0}; //!< slave address of this instance, or 0 if master + uint16_t txAddress{0}; //!< target device address of current transmission + + void (*onRequestCallback)(void){nullptr}; + void (*onReceiveCallback)(int){nullptr}; + + int getPortByPins(pin_size_t sda, pin_size_t scl); + bool setPinsPrivate(pin_size_t sda, pin_size_t scl); + + private: /* family core */ + bool beginPrivate(uint8_t address, uint32_t frequency = 0); + bool endPrivate(); + + public: /* family core */ + bool setClock(uint32_t frequency); + /* (master write) */ + TwoWireResult endTransmission(bool sendStop = true); + /* (master read) */ + size_t requestFrom(uint16_t address, size_t len, bool sendStop); + + public: /* common core */ + TwoWire(uint32_t port) : port(port) { + this->fixedPort = bool(port & PORT_FIXED_BIT); + } + + TwoWire(pin_size_t sda, pin_size_t scl) : sda(sda), scl(scl) {} + + TwoWire() {} + + ~TwoWire(); + + bool begin(pin_size_t sda, pin_size_t scl, uint32_t frequency = 0); + bool begin(uint8_t address, pin_size_t sda, pin_size_t scl, uint32_t frequency = 0); + bool end(); + + bool setPins(pin_size_t sda, pin_size_t scl); + void beginTransmission(uint16_t address); + size_t write(uint8_t data); + size_t write(const uint8_t *data, size_t len); + void flush(); + int available(); + int read(); + int peek(); + + inline uint16_t getTimeOut() { + return this->timeout; + } + + inline void setTimeOut(uint16_t millis) { + this->timeout = millis; + } + + inline uint32_t getClock() { + return this->frequency; + } + + inline void onReceive(void (*callback)(int)) { + this->onReceiveCallback = callback; + } + + inline void onRequest(void (*callback)(void)) { + this->onRequestCallback = callback; + } + + public: + inline bool begin() { + return this->begin(PIN_INVALID, PIN_INVALID, static_cast(0)); + } + + inline bool begin(uint8_t address) { + return this->begin(address, PIN_INVALID, PIN_INVALID, 0); + } + + inline bool begin(int address) { + return this->begin(address, PIN_INVALID, PIN_INVALID, 0); + } + + public: + inline void beginTransmission(uint8_t address) { + return this->beginTransmission(static_cast(address)); + } + + inline void beginTransmission(int address) { + return this->beginTransmission(static_cast(address)); + } + + public: + inline uint8_t requestFrom(uint16_t address, uint8_t len, bool sendStop) { + return this->requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); + } + + inline uint8_t requestFrom(uint16_t address, uint8_t len, uint8_t sendStop) { + return this->requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); + } + + inline uint8_t requestFrom(uint16_t address, uint8_t len) { + return this->requestFrom(static_cast(address), static_cast(len), true); + } + + public: + inline size_t requestFrom(uint8_t address, size_t len, bool sendStop) { + return this->requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); + } + + inline uint8_t requestFrom(uint8_t address, uint8_t len, uint8_t sendStop) { + return this->requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); + } + + inline uint8_t requestFrom(uint8_t address, uint8_t len) { + return this->requestFrom(static_cast(address), static_cast(len), true); + } + + inline uint8_t requestFrom(int address, int len, int sendStop) { + return this->requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); + } + + inline uint8_t requestFrom(int address, int len) { + return this->requestFrom(static_cast(address), static_cast(len), true); + } + + public: + inline size_t write(const char *s) { + return this->write((uint8_t *)s, strlen(s)); + } + + inline size_t write(unsigned long n) { + return this->write((uint8_t)n); + } + + inline size_t write(long n) { + return this->write((uint8_t)n); + } + + inline size_t write(unsigned int n) { + return this->write((uint8_t)n); + } + + inline size_t write(int n) { + return this->write((uint8_t)n); + } +}; + +#if LT_HW_I2C0 || LT_HW_I2C1 || LT_HW_I2C2 +extern TwoWire Wire; +#endif + +#if LT_HW_I2C0 +extern TwoWire Wire0; +#endif +#if LT_HW_I2C1 +extern TwoWire Wire1; +#endif +#if LT_HW_I2C2 +extern TwoWire Wire2; +#endif diff --git a/cores/common/arduino/src/Arduino.h b/cores/common/arduino/src/Arduino.h index 4688e28ff..bdb7234d9 100644 --- a/cores/common/arduino/src/Arduino.h +++ b/cores/common/arduino/src/Arduino.h @@ -24,8 +24,10 @@ using std::min; // Arduino Core and LT class #include #ifdef __cplusplus -#include +#include +#include using namespace arduino; +#include #endif // Include family-specific code @@ -42,20 +44,4 @@ using namespace arduino; // Define available serial ports #if defined(__cplusplus) && LT_ARD_HAS_SERIAL #include - -#if HAS_SERIAL_CLASS -#if LT_HW_UART0 -extern SerialClass Serial0; -#endif -#if LT_HW_UART1 -extern SerialClass Serial1; -#endif -#if LT_HW_UART2 -extern SerialClass Serial2; -#endif -#endif - -#define SerialN(x) Serial##x -#define SerialM(x) SerialN(x) -#define Serial SerialM(LT_UART_DEFAULT_SERIAL) #endif diff --git a/cores/common/arduino/src/ArduinoPrivate.h b/cores/common/arduino/src/ArduinoPrivate.h new file mode 100644 index 000000000..c46738cb7 --- /dev/null +++ b/cores/common/arduino/src/ArduinoPrivate.h @@ -0,0 +1,6 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#pragma once + +#include +#include diff --git a/cores/common/arduino/src/compat/pins_arduino.h b/cores/common/arduino/src/compat/pins_arduino.h new file mode 100644 index 000000000..c7ca2551e --- /dev/null +++ b/cores/common/arduino/src/compat/pins_arduino.h @@ -0,0 +1,5 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#pragma once + +#include LT_VARIANT_H diff --git a/cores/common/arduino/src/wiring/wiring_custom.c b/cores/common/arduino/src/wiring/wiring_custom.c index 31bd58265..2c80bde00 100644 --- a/cores/common/arduino/src/wiring/wiring_custom.c +++ b/cores/common/arduino/src/wiring/wiring_custom.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-20. */ -#include "wiring_private.h" +#include #if LT_HAS_FREERTOS #include diff --git a/cores/common/arduino/src/wiring/wiring_custom.h b/cores/common/arduino/src/wiring/wiring_custom.h index 7be9b6b83..d6c061f9c 100644 --- a/cores/common/arduino/src/wiring/wiring_custom.h +++ b/cores/common/arduino/src/wiring/wiring_custom.h @@ -23,6 +23,8 @@ extern "C" { #define PIN_MODE_ALL 0xFFFFFFFF +#define PORT_FIXED_BIT (1 << 16) + typedef struct PinData_s PinData; typedef struct { diff --git a/cores/common/arduino/src/wiring/wiring_math.cpp b/cores/common/arduino/src/wiring/wiring_math.cpp index e48753114..57d125253 100644 --- a/cores/common/arduino/src/wiring/wiring_math.cpp +++ b/cores/common/arduino/src/wiring/wiring_math.cpp @@ -29,7 +29,7 @@ long random(long howbig) { return 0; } - return rand() % howbig; + return abs(rand()) % howbig; } long random(long howsmall, long howbig) { diff --git a/cores/common/arduino/src/wiring/wiring_private.h b/cores/common/arduino/src/wiring/wiring_private.h index 5d0b9c358..40d3a6d94 100644 --- a/cores/common/arduino/src/wiring/wiring_private.h +++ b/cores/common/arduino/src/wiring/wiring_private.h @@ -62,3 +62,51 @@ inline void pinDisable(PinInfo *pin, uint32_t mask) { #ifdef __cplusplus } // extern "C" #endif + +// C++ utilities +#ifdef __cplusplus + +template +static bool ltArrayContains(C &&c, T e) { + for (auto x : c) { + if (x == e) + return true; + } + return false; +}; + +#endif // __cplusplus + +// Additional custom structures + +typedef union { + uint16_t val; + + struct { +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t lsb; + uint8_t msb; +#else + uint8_t msb; + uint8_t lsb; +#endif + }; +} ByteUint16; + +typedef union { + uint32_t val; + + struct { +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t lsb; + uint8_t byte1; + uint8_t byte2; + uint8_t msb; +#else + uint8_t msb; + uint8_t byte2; + uint8_t byte1; + uint8_t lsb; +#endif + }; +} ByteUint32; diff --git a/cores/common/base/api/lt_utils.h b/cores/common/base/api/lt_utils.h index ab942bb48..bef817434 100644 --- a/cores/common/base/api/lt_utils.h +++ b/cores/common/base/api/lt_utils.h @@ -18,6 +18,13 @@ _a < _b ? _a : _b; \ }) +#define LT_MEM32(addr) (*((volatile uint32_t *)(addr))) + +// from https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/ +// (include/linux/kernel.h) +#define LT_BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) +#define LT_BUILD_CHECK(condition) ((void)sizeof(char[1 - 2 * !(condition)])) + /** * @brief Generate random bytes using rand(). * diff --git a/cores/common/base/lt_config.h b/cores/common/base/lt_config.h index 75d861101..de9b79a72 100644 --- a/cores/common/base/lt_config.h +++ b/cores/common/base/lt_config.h @@ -130,3 +130,11 @@ #ifndef LT_DEBUG_LWIP_ASSERT #define LT_DEBUG_LWIP_ASSERT 0 #endif + +#ifndef LT_DEBUG_I2C +#define LT_DEBUG_I2C 1 +#endif + +#ifndef LT_DEBUG_SPI +#define LT_DEBUG_SPI 1 +#endif diff --git a/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp b/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp index 1176126a5..a3676fef1 100644 --- a/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -2,8 +2,8 @@ #if LT_RTL8710B +#include #include -#include #define TIMER_MAX 3 #define OBJ ((gtimer_t *)this->param) diff --git a/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h b/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h index 99745a259..63193c74e 100644 --- a/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h +++ b/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h @@ -2,8 +2,8 @@ #pragma once +#include #include -#include extern "C" { 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-amb/arduino/src/api/lt_cpu.c b/cores/realtek-amb/arduino/src/api/lt_cpu.c index bfc1f49a6..f867ef53a 100644 --- a/cores/realtek-amb/arduino/src/api/lt_cpu.c +++ b/cores/realtek-amb/arduino/src/api/lt_cpu.c @@ -1,7 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2023-03-10. */ -#include -#include +#include uint32_t lt_cpu_get_cycle_count() { return microsecondsToClockCycles(micros()); diff --git a/cores/realtek-amb/arduino/src/lt_defs.h b/cores/realtek-amb/arduino/src/lt_defs.h index 948ea5612..eac211873 100644 --- a/cores/realtek-amb/arduino/src/lt_defs.h +++ b/cores/realtek-amb/arduino/src/lt_defs.h @@ -2,7 +2,6 @@ #error "Don't include this file directly" -#define LT_ARD_HAS_WIFI 1 -#define LT_ARD_HAS_SOFTSERIAL 1 +#define LT_ARD_HAS_WIFI 1 #define ARDUINO_AMEBA diff --git a/cores/realtek-amb/arduino/src/main.cpp b/cores/realtek-amb/arduino/src/main.cpp index 4fcc21ff6..3b9e38b2a 100644 --- a/cores/realtek-amb/arduino/src/main.cpp +++ b/cores/realtek-amb/arduino/src/main.cpp @@ -1,7 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-19. */ -#include -#include +#include extern "C" { diff --git a/cores/realtek-amb/arduino/src/wiring.c b/cores/realtek-amb/arduino/src/wiring.c index bb0fc6bfa..070901d65 100644 --- a/cores/realtek-amb/arduino/src/wiring.c +++ b/cores/realtek-amb/arduino/src/wiring.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-04-23. */ -#include "wiring_private.h" +#include #ifndef portNVIC_SYSTICK_CURRENT_VALUE_REG #define portNVIC_SYSTICK_CURRENT_VALUE_REG (*((volatile uint32_t *)0xe000e018)) diff --git a/cores/realtek-amb/arduino/src/wiring_analog.c b/cores/realtek-amb/arduino/src/wiring_analog.c index 68b7f8307..02bfbf0b5 100644 --- a/cores/realtek-amb/arduino/src/wiring_analog.c +++ b/cores/realtek-amb/arduino/src/wiring_analog.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-06-20. */ -#include "wiring_private.h" +#include /* ADC */ static analogin_t adc1; diff --git a/cores/realtek-amb/arduino/src/wiring_data.h b/cores/realtek-amb/arduino/src/wiring_data.h index 1f0038f57..704cee45a 100644 --- a/cores/realtek-amb/arduino/src/wiring_data.h +++ b/cores/realtek-amb/arduino/src/wiring_data.h @@ -2,8 +2,7 @@ #pragma once -#include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/cores/realtek-amb/arduino/src/wiring_digital.c b/cores/realtek-amb/arduino/src/wiring_digital.c index 1eb2f9c6c..4175df658 100644 --- a/cores/realtek-amb/arduino/src/wiring_digital.c +++ b/cores/realtek-amb/arduino/src/wiring_digital.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-04-23. */ -#include "wiring_private.h" +#include void pinMode(pin_size_t pinNumber, PinMode pinMode) { pinCheckGetData(pinNumber, PIN_GPIO, ); diff --git a/cores/realtek-amb/arduino/src/wiring_irq.c b/cores/realtek-amb/arduino/src/wiring_irq.c index f387fccca..11b70a0b4 100644 --- a/cores/realtek-amb/arduino/src/wiring_irq.c +++ b/cores/realtek-amb/arduino/src/wiring_irq.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba Szczodrzyński 2022-04-23. */ -#include "wiring_private.h" +#include static void gpioIrqHandler(uint32_t id, gpio_irq_event event) { // id is pin data diff --git a/cores/realtek-amb/arduino/src/wiring_pulse.c b/cores/realtek-amb/arduino/src/wiring_pulse.c index 9fcd794da..532021f2c 100644 --- a/cores/realtek-amb/arduino/src/wiring_pulse.c +++ b/cores/realtek-amb/arduino/src/wiring_pulse.c @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "wiring_private.h" +#include /* Measures the length (in microseconds) of a pulse on the pin; state is HIGH * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds diff --git a/cores/realtek-ambz/arduino/libraries/SPI/SPI.cpp b/cores/realtek-ambz/arduino/libraries/SPI/SPI.cpp new file mode 100644 index 000000000..a7558e0dd --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/SPI/SPI.cpp @@ -0,0 +1,99 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#include "SPIPrivate.h" + +bool SPIClass::beginPrivate() { + if (!this->data) + return false; + + this->data->spi = SPI_DEV_TABLE[this->port].SPIx; + this->data->irq = (IRQn)SPI_DEV_TABLE[this->port].IrqNum; + + switch (this->port) { + case 0: + RCC_PeriphClockCmd(APBPeriph_SPI0, APBPeriph_SPI0_CLOCK, ENABLE); + break; + case 1: + RCC_PeriphClockCmd(APBPeriph_SPI1, APBPeriph_SPI1_CLOCK, ENABLE); + break; + } + + Pinmux_Config(this->sck, PINMUX_FUNCTION_SPIM); + Pinmux_Config(this->miso, PINMUX_FUNCTION_SPIM); + Pinmux_Config(this->mosi, PINMUX_FUNCTION_SPIM); + + SSI_InitTypeDef *init = &this->data->init; + SPI_TypeDef *spi = this->data->spi; + IRQn irq = this->data->irq; + SSI_StructInit(init); + init->SPI_Role = SSI_MASTER; + + SSI_Cmd(spi, DISABLE); + SSI_Init(spi, init); + SSI_Cmd(spi, ENABLE); + + // VECTOR_IrqUnRegister(irq); + // VECTOR_IrqRegister((IRQ_FUN)callback, irq, (u32)this->data, 10); + // VECTOR_IrqEn(irq, 10); + + return true; +} + +bool SPIClass::endPrivate() { + if (!this->data) + return true; + SPI_TypeDef *spi = this->data->spi; + IRQn irq = this->data->irq; + + SSI_INTConfig(spi, BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM, DISABLE); + VECTOR_IrqDis(irq); + VECTOR_IrqUnRegister(irq); + SSI_Cmd(spi, DISABLE); + + return true; +} + +void SPIClass::setFrequency(uint32_t frequency) { + if (!this->data) + return; + uint32_t maxFrequency = CPU_ClkGet(false) >> 2; + if (frequency > maxFrequency) { + LT_WM(SPI, "Clock freq too high! %lu > %lu", frequency, maxFrequency); + SSI_SetBaud(this->data->spi, maxFrequency, maxFrequency << 1); + } else { + + SSI_SetBaud(this->data->spi, frequency, maxFrequency << 1); + } + this->settings._clock = frequency; +} + +void SPIClass::setClockDivider(uint32_t clockDiv) { + if (!this->data) + return; + if (clockDiv < 4) { + LT_EM(SPI, "Clock div too low! %lu < 4", clockDiv); + return; + } + SSI_SetBaudDiv(this->data->spi, clockDiv << 1); + this->settings._clock = CPU_ClkGet(false) / clockDiv; +} + +void SPIClass::setDataMode(uint8_t dataMode) { + if (!this->data) + return; + this->settings._dataMode = dataMode; + SSI_SetSclkPolarity(this->data->spi, (dataMode >> 1) & 0b1); + SSI_SetSclkPhase(this->data->spi, (dataMode >> 0) & 0b1); +} + +uint8_t SPIClass::transfer(uint8_t data) { + while ((SPI1_DEV->SR & BIT_SR_TFNF) == 0) {} + SPI1_DEV->DR[0] = data; + while ((SPI1_DEV->SR & BIT_SR_RFNE) == 0) {} + return SPI1_DEV->DR[0]; +} + +void SPIClass::write(uint8_t data) { + while ((SPI1_DEV->SR & BIT_SR_TFNF) == 0) {} + SPI1_DEV->DR[0] = data; +} diff --git a/cores/realtek-ambz/arduino/libraries/SPI/SPIPrivate.h b/cores/realtek-ambz/arduino/libraries/SPI/SPIPrivate.h new file mode 100644 index 000000000..f65c59e47 --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/SPI/SPIPrivate.h @@ -0,0 +1,12 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ + +#pragma once + +#include +#include + +struct SPIData { + SSI_InitTypeDef init; + SPI_TypeDef *spi; + IRQn irq; +}; diff --git a/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp b/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp index 6f5d8e310..2559d3084 100644 --- a/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp +++ b/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp @@ -1,22 +1,11 @@ /* Copyright (c) Kuba Szczodrzyński 2022-07-03. */ -#include "SerialPrivate.h" +#if LT_ARD_HAS_SERIAL || DOXYGEN -#if LT_HW_UART0 -SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX); -#endif -#if LT_HW_UART1 -SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX); -#endif -#if LT_HW_UART2 -SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX); -#endif - -static UART_TypeDef *PORT_UART[3] = {UART0_DEV, UART1_DEV, UART2_DEV}; -static const IRQn PORT_IRQ[3] = {UART0_IRQ, UART1_IRQ, UART_LOG_IRQ}; +#include "SerialPrivate.h" -static uint32_t callback(void *param) { - UART_TypeDef *uart = pdUART; +static uint32_t callback(SerialData *data) { + UART_TypeDef *uart = data->uart; uint32_t intcr = uart->DLH_INTCR; uart->DLH_INTCR = 0; @@ -29,53 +18,53 @@ static uint32_t callback(void *param) { if (uart == UART2_DEV) SerialClass::adrParse(c); #endif - pdBUF.store_char(c); + data->buf->store_char(c); } uart->DLH_INTCR = intcr; return 0; } -void SerialClass::begin(unsigned long baudrate, uint16_t config) { - if (!this->data) { - this->data = new SerialData(); - this->buf = &BUF; - DATA->uart = PORT_UART[this->port]; - DATA->irq = PORT_IRQ[this->port]; - - switch ((uint32_t)DATA->uart) { - case UART0_REG_BASE: - RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); - break; - case UART1_REG_BASE: - RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, ENABLE); - break; - } - - if (this->tx != PIN_INVALID) { - Pinmux_Config(this->tx, PINMUX_FUNCTION_UART); - } - if (this->rx != PIN_INVALID) { - Pinmux_Config(this->rx, PINMUX_FUNCTION_UART); - PAD_PullCtrl(this->rx, GPIO_PuPd_UP); - } +void SerialClass::beginPrivate(unsigned long baudrate, uint16_t config) { + if (!this->data) + return; + this->data->buf = this->rxBuf; + this->data->uart = UART_DEV_TABLE[this->port].UARTx; + this->data->irq = (IRQn)UART_DEV_TABLE[this->port].IrqNum; + + switch (this->port) { + case 0: + RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); + break; + case 1: + RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, ENABLE); + break; } - if (this->baudrate != baudrate || this->config != config) - this->configure(baudrate, config); + if (this->tx != PIN_INVALID) { + Pinmux_Config(this->tx, PINMUX_FUNCTION_UART); + } + if (this->rx != PIN_INVALID) { + Pinmux_Config(this->rx, PINMUX_FUNCTION_UART); + PAD_PullCtrl(this->rx, GPIO_PuPd_UP); + } if (this->rx != PIN_INVALID) { - VECTOR_IrqUnRegister(DATA->irq); - VECTOR_IrqRegister(callback, DATA->irq, (uint32_t)this->data, 10); - VECTOR_IrqEn(DATA->irq, 10); - UART_RxCmd(UART, ENABLE); - UART_INTConfig(UART, RUART_IER_ERBI, ENABLE); + UART_TypeDef *uart = this->data->uart; + IRQn irq = this->data->irq; + + VECTOR_IrqUnRegister(irq); + VECTOR_IrqRegister((IRQ_FUN)callback, irq, (uint32_t)this->data, 10); + VECTOR_IrqEn(irq, 10); + UART_RxCmd(uart, ENABLE); + UART_INTConfig(uart, RUART_IER_ERBI, ENABLE); } } void SerialClass::configure(unsigned long baudrate, uint16_t config) { if (!this->data) return; + UART_TypeDef *uart = this->data->uart; // RUART_WLS_7BITS / RUART_WLS_8BITS uint8_t dataWidth = (config & SERIAL_DATA_MASK) == SERIAL_DATA_8; @@ -92,44 +81,43 @@ void SerialClass::configure(unsigned long baudrate, uint16_t config) { cfg.Parity = parity; cfg.ParityType = parityType; cfg.StopBit = stopBits; - UART_Init(UART, &cfg); - UART_SetBaud(UART, baudrate); + UART_Init(uart, &cfg); + UART_SetBaud(uart, baudrate); this->baudrate = baudrate; this->config = config; } -void SerialClass::end() { +void SerialClass::endPrivate() { if (!this->data) return; + UART_TypeDef *uart = this->data->uart; + IRQn irq = this->data->irq; - UART_INTConfig(UART, RUART_IER_ERBI, DISABLE); - UART_RxCmd(UART, DISABLE); - VECTOR_IrqDis(DATA->irq); - VECTOR_IrqUnRegister(DATA->irq); - UART_DeInit(UART); + UART_INTConfig(uart, RUART_IER_ERBI, DISABLE); + UART_RxCmd(uart, DISABLE); + VECTOR_IrqDis(irq); + VECTOR_IrqUnRegister(irq); + UART_DeInit(uart); - if (UART == UART2_DEV) { + if (uart == UART2_DEV) { // restore command line mode DIAG_UartReInit((IRQ_FUN)UartLogIrqHandle); } - - delete DATA; - this->data = NULL; - this->buf = NULL; - this->baudrate = 0; } void SerialClass::flush() { if (!this->data) return; - UART_WaitBusy(UART, 10); + UART_WaitBusy(this->data->uart, 10); } size_t SerialClass::write(uint8_t c) { if (!this->data) return 0; - while (UART_Writable(UART) == 0) {} - UART_CharPut(UART, c); + while (UART_Writable(this->data->uart) == 0) {} + UART_CharPut(this->data->uart, c); return 1; } + +#endif diff --git a/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h b/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h index 315113531..665a007d3 100644 --- a/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h +++ b/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h @@ -2,18 +2,11 @@ #pragma once -#include -#include +#include +#include -typedef struct { +struct SerialData { + RingBuffer *buf; UART_TypeDef *uart; IRQn irq; - RingBuffer buf; -} SerialData; - -#define DATA ((SerialData *)data) -#define pDATA ((SerialData *)param) -#define BUF (DATA->buf) -#define pdBUF (pDATA->buf) -#define UART (DATA->uart) -#define pdUART (pDATA->uart) +}; 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..07347f92e --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/Wire/Wire.cpp @@ -0,0 +1,218 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-09-21. */ + +#include "WirePrivate.h" + +bool TwoWire::beginPrivate(uint8_t address, uint32_t frequency) { + if (!this->data) + return false; + this->data->buf = this->rxBuf; + 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; + } + + 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; + + 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::endPrivate() { + if (!this->data) + return true; + I2C_TypeDef *i2c = this->data->i2c; + + I2C_Cmd(i2c, DISABLE); + 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; +} + +TwoWireResult TwoWire::endTransmission(bool sendStop) { + if (!this->txBuf || this->txAddress == 0x00) + return TWOWIRE_ERROR; + 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, this->timeout, BIT_IC_STATUS_TFNF)) { + LT_EM(I2C, "Timeout @ 0x%02x (TX FIFO full)", this->txAddress); + return TWOWIRE_TIMEOUT; + } + + 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, this->timeout, BIT_IC_STATUS_TFE)) { + if (!scanOnly) + LT_EM(I2C, "Timeout @ 0x%02x (TX FIFO not empty)", this->txAddress); + return TWOWIRE_TIMEOUT; + } + // check if transmission succeeded + if (sendStop && isNoAck(i2c)) { + if (!scanOnly) + LT_EM(I2C, "No ACK @ 0x%02x", address); + return scanOnly ? TWOWIRE_NACK_ADDR : TWOWIRE_NACK_DATA; + } + return TWOWIRE_SUCCESS; +} + +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..9763974f9 --- /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 { + RingBuffer *buf; + I2C_TypeDef *i2c; + I2C_InitTypeDef init; +}; diff --git a/cores/realtek-ambz/arduino/src/lt_defs.h b/cores/realtek-ambz/arduino/src/lt_defs.h index a3ea2d6ff..674284a0c 100644 --- a/cores/realtek-ambz/arduino/src/lt_defs.h +++ b/cores/realtek-ambz/arduino/src/lt_defs.h @@ -2,5 +2,8 @@ #error "Don't include this file directly" -#define LT_ARD_HAS_SERIAL 1 -#define LT_ARD_MD5_MBEDTLS 1 +#define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_HAS_SOFTSERIAL 1 +#define LT_ARD_HAS_SPI 1 +#define LT_ARD_HAS_WIRE 1 +#define LT_ARD_MD5_MBEDTLS 1 diff --git a/cores/realtek-ambz/base/sdk_extern.h b/cores/realtek-ambz/base/sdk_extern.h index 131f623f3..a4a508117 100644 --- a/cores/realtek-ambz/base/sdk_extern.h +++ b/cores/realtek-ambz/base/sdk_extern.h @@ -11,6 +11,7 @@ extern "C" { int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70! +void SSI_SetBaudDiv(SPI_TypeDef *spi_dev, uint32_t ClockDivider); #ifdef __cplusplus } // extern "C" diff --git a/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp b/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp index 9fb18d66c..8da97b617 100644 --- a/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp +++ b/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp @@ -1,21 +1,13 @@ /* Copyright (c) Kuba Szczodrzyński 2023-05-24. */ -#include "SerialPrivate.h" +#if LT_ARD_HAS_SERIAL || DOXYGEN -#if LT_HW_UART0 -SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX); -#endif -#if LT_HW_UART1 -SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX); -#endif -#if LT_HW_UART2 -SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX); -#endif +#include "SerialPrivate.h" -static void callback(uint32_t param, uint32_t event) { +static void callback(SerialData *data, uint32_t event) { if (event != RxIrq) return; - hal_uart_adapter_t *uart = pdUART; + hal_uart_adapter_t *uart = data->uart; uint8_t c; while (hal_uart_rgetc(uart, (char *)&c)) { @@ -24,34 +16,31 @@ static void callback(uint32_t param, uint32_t event) { if (uart->base_addr == UART2) SerialClass::adrParse(c); #endif - pdBUF.store_char(c); + data->buf->store_char(c); } } -void SerialClass::begin(unsigned long baudrate, uint16_t config) { - if (!this->data) { - this->data = new SerialData(); - this->buf = &BUF; - - if (this->port == 2) { - DATA->uart = &log_uart; - } else { - UART = new hal_uart_adapter_t(); - // TODO handle PIN_INVALID - hal_uart_init(UART, this->tx, this->rx, NULL); - } - - if (this->rx != PIN_INVALID) { - hal_uart_enter_critical(); - hal_uart_rxind_hook(UART, callback, (uint32_t)this->data, RxIrq); - UART->base_addr->ier_b.erbi = 1; - UART->base_addr->ier_b.etbei = 0; - hal_uart_exit_critical(); - } +void SerialClass::beginPrivate(unsigned long baudrate, uint16_t config) { + if (!this->data) + return; + this->data->buf = this->rxBuf; + + hal_uart_adapter_t *uart; + if (this->port == 2) { + this->data->uart = uart = &log_uart; + } else { + this->data->uart = uart = new hal_uart_adapter_t(); + // TODO handle PIN_INVALID + hal_uart_init(uart, this->tx, this->rx, nullptr); } - if (this->baudrate != baudrate || this->config != config) - this->configure(baudrate, config); + if (this->rx != PIN_INVALID) { + hal_uart_enter_critical(); + hal_uart_rxind_hook(uart, (uart_irq_callback_t)callback, (uint32_t)this->data, RxIrq); + uart->base_addr->ier_b.erbi = 1; + uart->base_addr->ier_b.etbei = 0; + hal_uart_exit_critical(); + } } void SerialClass::configure(unsigned long baudrate, uint16_t config) { @@ -62,41 +51,39 @@ void SerialClass::configure(unsigned long baudrate, uint16_t config) { uint8_t parity = (config & SERIAL_PARITY_MASK) ^ 0b11; uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2 ? 2 : 1; - hal_uart_set_baudrate(UART, baudrate); - hal_uart_set_format(UART, dataWidth, parity, stopBits); + hal_uart_set_baudrate(this->data->uart, baudrate); + hal_uart_set_format(this->data->uart, dataWidth, parity, stopBits); this->baudrate = baudrate; this->config = config; } -void SerialClass::end() { +void SerialClass::endPrivate() { if (!this->data) return; + hal_uart_adapter_t *uart = this->data->uart; if (this->port == 2) { - UART->base_addr->ier_b.erbi = 0; - hal_uart_rxind_hook(UART, NULL, 0, RxIrq); + uart->base_addr->ier_b.erbi = 0; + hal_uart_rxind_hook(uart, nullptr, 0, RxIrq); } else { - hal_uart_deinit(UART); - delete UART; + hal_uart_deinit(uart); + delete this->data->uart; } - - delete DATA; - this->data = NULL; - this->buf = NULL; - this->baudrate = 0; } void SerialClass::flush() { if (!this->data) return; - while (UART->base_addr->tflvr_b.tx_fifo_lv != 0) {} + while (this->data->uart->base_addr->tflvr_b.tx_fifo_lv != 0) {} } size_t SerialClass::write(uint8_t c) { if (!this->data) return 0; - while (!hal_uart_writeable(UART)) {} - hal_uart_putc(UART, c); + while (!hal_uart_writeable(this->data->uart)) {} + hal_uart_putc(this->data->uart, c); return 1; } + +#endif diff --git a/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h b/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h index 6fb1ec947..ef6f03fb8 100644 --- a/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h +++ b/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h @@ -2,17 +2,10 @@ #pragma once -#include -#include +#include +#include -typedef struct { +struct SerialData { + RingBuffer *buf; hal_uart_adapter_t *uart; - RingBuffer buf; -} SerialData; - -#define DATA ((SerialData *)data) -#define pDATA ((SerialData *)param) -#define BUF (DATA->buf) -#define pdBUF (pDATA->buf) -#define UART (DATA->uart) -#define pdUART (pDATA->uart) +}; diff --git a/cores/realtek-ambz2/arduino/src/lt_defs.h b/cores/realtek-ambz2/arduino/src/lt_defs.h index f6cceb071..7904e9901 100644 --- a/cores/realtek-ambz2/arduino/src/lt_defs.h +++ b/cores/realtek-ambz2/arduino/src/lt_defs.h @@ -2,8 +2,6 @@ #error "Don't include this file directly" -#define LT_ARD_HAS_SERIAL 1 -#define LT_ARD_HAS_SOFTSERIAL 0 -#define LT_ARD_HAS_WIFI 1 -#define LT_ARD_HAS_WIRE 0 -#define LT_ARD_MD5_MBEDTLS 1 +#define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_HAS_WIFI 1 +#define LT_ARD_MD5_MBEDTLS 1 diff --git a/docs/dev/config.md b/docs/dev/config.md index bc668db2b..7c1c756c9 100644 --- a/docs/dev/config.md +++ b/docs/dev/config.md @@ -84,6 +84,8 @@ To see debug messages from i.e. OTA, loglevel must also be changed. - `LT_DEBUG_MDNS` (0) - mDNS client library - `LT_DEBUG_LWIP` (0) - enables `LWIP_DEBUG`, provides `LWIP_PLATFORM_DIAG`; per-module options (i.e. `TCP_DEBUG`) are off by default and need to be enabled separately - `LT_DEBUG_LWIP_ASSERT` (0) - enables assertions within lwIP (doesn't need `LT_DEBUG_LWIP`) +- `LT_DEBUG_I2C` (1) - I²C warnings and errors (`Wire` library) +- `LT_DEBUG_SPI` (1) - SPI warnings and errors (`SPI` library) !!! tip Enabling `LT_DEBUG_ALL` doesn't mean that *every* debugging message will be printed. If loglevel is i.e. `WARN`, debug messages won't be visible anyway. 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: