[lightning-ln882h] Add support for Lightning LN882H family (#312)

* fix mbedtls bad pointer in function call (prototype mismatch)

* fix issue with weak families functions implemented in static library, it will never be linked. fixed by redefining prototypes inside families

* [ln882x] add support for lightning ln882x & ln882h families

* add i2c (wire) support

* add analog (adc) support

* add watchdog support

* [ln882x] changed default uart 0/1 pins; added board wl2s

* [ln882x] fix IRQ & ADC pins

* [ln882x] boards cosmetic

* [ln882x] wifi sta use otp mac addr by default; re-enabled wifi powersave mode

* [ln882x] clang-format clean code

* [ln882x] clang-format clean code

* Update families.json

* Apply suggestions from code review

* [ln882x] reformat json board files

* [ln882x] os_queue cleanup

* [ln882x] removed Beken auto-download command

* [ln882x] removed personal script file

* [ln882x] removed unusefull pi section in debugging.md

* [ln882x] removed Arduino.h and changed private I2C definition

* [ln882x] updated README.md

* [ln882x] changed pin naming scheme to PA/PB

* [ln882x] clean code

* [ln882x] clean code

* [ln882x] add ota image verification

* Update push-dev.yml

* [ln882x] fix boards ADC missing inputs]

* [ln882x] removed reg_xxx fixup files and use include guards instead

* [ln882x] cleanup code

* [ln882x] cleanup code

* [ln882x] fix lt_init weak functions linking

* [ln882x] revert lt_api.h modification, fixed with previous commit

* [ln882x] setup UF2 firmware for flasher with partitions

* [ln882x] update README.md

* [ln882x] include ln_wifi.h and ln_serial.h to avoid including bad headers on case insensitive systems

* [ln882x] Replace RingBuffer by SerialRingBuffer

* [ln882x] clang-format

* [ln882x] update README.md

* Apply suggestions from code review

* Reformat board JSON files

* Add mkdocs link redirect

* Update ltchiptool to v4.12.0

---------

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>
This commit is contained in:
lamauny
2025-03-25 17:26:53 +01:00
committed by GitHub
parent 6083cca72e
commit 69e7e2debe
73 changed files with 4856 additions and 28 deletions

View File

@@ -3,6 +3,7 @@ on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
jobs:
lint-clang:
name: Run Clang lint

View File

@@ -15,10 +15,11 @@
![RTL8710BN](https://img.shields.io/badge/-rtl8710bn-blue)
![BK7231](https://img.shields.io/badge/-bk7231-blue)
![LN882H](https://img.shields.io/badge/-ln882h-blue)
</div>
PlatformIO development platform for BK7231 and RTL8710 IoT chips.
PlatformIO development platform for BK7231 and RTL8710 and LN882H IoT chips.
The main goal of this project is to provide a usable build environment for IoT developers. While also providing vendor SDKs as PlatformIO cores,
the project focuses on developing working Arduino-compatible cores for supported families. The cores are inspired by Espressif's official core for ESP32,

View File

@@ -19,6 +19,7 @@
* [Realtek AmebaZ](docs/platform/realtek-ambz/README.md)
* [Debugging](docs/platform/realtek-ambz/debugging.md)
* [Exception decoder](docs/platform/realtek-ambz/exception-decoder.md)
* [Lightning LN882x](docs/platform/lightning-ln882x/README.md)
* [🔧 LT Configuration](docs/dev/config.md)
* 🧑 Programmer's manual
* [⚠️ Migration guide](docs/dev/migration_v1.0.0.md)

View File

@@ -0,0 +1,209 @@
{
"pcb": {
"ic": {
"7": {
"IO": "I",
"CTRL": "CEN"
},
"8": {
"C_NAME": "PA_0",
"GPIO": "PA00",
"IRQ": null,
"ADC": 2,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"9": {
"C_NAME": "PA_1",
"GPIO": "PA01",
"IRQ": null,
"ADC": 3,
"SWD": "DIO",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"10": {
"C_NAME": "PA_2",
"GPIO": "PA02",
"IRQ": null,
"UART": "0_TX",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"11": {
"C_NAME": "PA_3",
"GPIO": "PA03",
"IRQ": null,
"UART": "0_RX",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"12": {
"C_NAME": "PA_4",
"GPIO": "PA04",
"ADC": 4,
"SWD": "CLK",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"13": {
"C_NAME": "PA_5",
"GPIO": "PA05",
"IRQ": null,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"14": {
"C_NAME": "PA_6",
"GPIO": "PA06",
"IRQ": null,
"SD": "D2",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"15": {
"C_NAME": "PA_7",
"GPIO": "PA07",
"IRQ": null,
"SD": "D3",
"I2S": "0_RX",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"16": {
"C_NAME": "PA_8",
"GPIO": "PA08",
"CTRL": "BOOT0",
"SD": "CMD",
"I2S": "0_WS",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"17": {
"C_NAME": "PA_9",
"GPIO": "PA09",
"CTRL": "BOOT1",
"SD": "CLK",
"I2S": "0_SCLK",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"21": {
"C_NAME": "PA_10",
"GPIO": "PA10",
"SD": "D0",
"I2S": "0_TX",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"22": {
"C_NAME": "PA_11",
"GPIO": "PA11",
"SD": "D1",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"23": {
"C_NAME": "PA_12",
"GPIO": "PA12",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"24": {
"C_NAME": "PB_3",
"GPIO": "PB03",
"GPIONUM": 19,
"ADC": 5,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"25": {
"C_NAME": "PB_4",
"GPIO": "PB04",
"GPIONUM": 20,
"ADC": 6,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"26": {
"C_NAME": "PB_5",
"GPIO": "PB05",
"GPIONUM": 21,
"ADC": 7,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"27": {
"C_NAME": "PB_6",
"GPIO": "PB06",
"GPIONUM": 22,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"28": {
"C_NAME": "PB_7",
"GPIO": "PB07",
"GPIONUM": 23,
"I2C": [
"0_SCL",
"0_SDA"
]
},
"29": {
"C_NAME": "PB_8",
"GPIO": "PB08",
"GPIONUM": 24,
"UART": "1_RX",
"I2C": [
"0_SCL",
"0_SDA"
]
},
"30": {
"C_NAME": "PB_9",
"GPIO": "PB09",
"GPIONUM": 25,
"IRQ": null,
"UART": "1_TX",
"I2C": [
"0_SCL",
"0_SDA"
]
}
}
}
}

View File

@@ -0,0 +1,31 @@
{
"build": {
"family": "LN882H",
"ldscript": "ln882hki_bsp.ld",
"bootfile": "boot_ln882h.bin"
},
"flash": {
"boot": "0x000000+0x06000",
"part_tab": "0x006000+0x01000",
"app": "0x007000+0x12C000",
"ota": "0x133000+0xAA000",
"nvds": "0x1DD000+0x03000",
"kv": "0x1E0000+0x04000",
"kvs": "0x1E4000+0x08000",
"user": "0x1EC000+0x14000"
},
"upload": {
"flash_size": 2097152,
"maximum_size": 1228800
},
"connectivity": [
"ble"
],
"doc": {
"params": {
"extra": {
"Bluetooth": "BLE v5.1"
}
}
}
}

View File

@@ -0,0 +1,43 @@
{
"build": {
"f_cpu": "160000000L",
"prefix": "arm-none-eabi-"
},
"flash": {},
"connectivity": [
"wifi"
],
"debug": {
"protocol": "openocd",
"protocols": [
"openocd"
],
"openocd_config": "ln882x.cfg",
"gdb_init": [
"mem 0x00000000 0x10000000 ro"
]
},
"upload": {
"maximum_ram_size": 302080,
"require_upload_port": true,
"speed": 115200,
"protocol": "uart",
"protocols": [
"uart"
]
},
"doc": {
"params": {
"manufacturer": "Lightning",
"series": "LN882X",
"voltage": "3.0V - 3.6V",
"extra": {
"Wi-Fi": "802.11 b/g/n"
}
},
"links": {
"Info & flashing guide": "../../docs/platform/lightning-ln882x/README.md",
"Debugging": "../../docs/platform/lightning-ln882x/debugging.md"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"pcb": {
"test_pads": {
"TBOOT": "ln-02.back.boot.anchor"
},
"back": [
{
"id": "boot",
"name": "label_line_up",
"pos": "12,12.1",
"vars": {
"DIR": "right",
"W": 2.5,
"H": 0
}
},
{
"name": "test_pad_1mm",
"pos": "11,12"
}
]
}
}

View File

@@ -0,0 +1,67 @@
{
"pcb": {
"templates": [
"tuya2",
"pcb-black",
"rf-type1"
],
"scale": 10.5,
"pinout_hidden": "I2S,TRIG,WAKE,CTS,RTS,SD,SPI,I2C",
"pinout": {
"1": {
"PWR": 3.3
},
"2": {
"IC": 22,
"ARD": "D0"
},
"3": {
"GND": null
},
"4": {
"IC": 24,
"ARD": [
"D1",
"A0"
]
},
"5": {
"IC": 11,
"ARD": "D2"
},
"6": {
"IC": 29,
"ARD": "D3"
},
"7": {
"IC": 10,
"ARD": "D4"
},
"8": {
"IC": 30,
"ARD": "D5"
},
"9": {
"IC": 9,
"ARD": [
"D6",
"A1"
]
},
"10": {
"IC": 7
},
"11": {
"IC": 8,
"ARD": [
"D7",
"A2"
]
},
"TBOOT": {
"IC": 17,
"ARD": "D8"
}
}
}
}

View File

@@ -0,0 +1,98 @@
{
"pcb": {
"test_pads": {
"TA9": "wl2s.back.a9.anchor",
"TGND": "wl2s.back.gnd.anchor",
"TRX1": "wl2s.back.rx1.anchor",
"TTX1": "wl2s.back.tx1.anchor",
"TSCLK": "wl2s.back.sclk.anchor",
"TSWD": "wl2s.back.swd.anchor"
},
"back": [
{
"id": "a9",
"name": "label_line_up",
"pos": "2.8,11.8",
"vars": {
"DIR": "left",
"W": 2.5,
"H": 0
}
},
{
"name": "test_pad_1mm",
"pos": "3,12"
},
{
"id": "gnd",
"name": "label_line_up",
"pos": "0.8,8.3",
"vars": {
"DIR": "left",
"W": 0.5,
"H": 0
}
},
{
"name": "test_pad_1mm",
"pos": "1,8.5"
},
{
"id": "rx1",
"name": "label_line_up",
"pos": "3,5.8",
"vars": {
"DIR": "left",
"W": 2.6,
"H": 0
}
},
{
"name": "test_pad_1mm",
"pos": "3.2,6"
},
{
"id": "tx1",
"name": "label_line_up",
"pos": "5,5.8",
"vars": {
"DIR": "left",
"W": 4.5,
"H": 2
}
},
{
"name": "test_pad_1mm",
"pos": "5.2,6"
},
{
"id": "swd",
"name": "label_line_up",
"pos": "10.2,11.1",
"vars": {
"DIR": "right",
"W": 3.0,
"H": 1
}
},
{
"name": "test_pad_1mm",
"pos": "10,11"
},
{
"id": "sclk",
"name": "label_line_up",
"pos": "5.2,12.2",
"vars": {
"DIR": "right",
"W": 8.0,
"H": 0
}
},
{
"name": "test_pad_1mm",
"pos": "5,12"
}
]
}
}

View File

@@ -0,0 +1,86 @@
{
"pcb": {
"templates": [
"tuya2",
"pcb-blue-light",
"rf-type1"
],
"scale": 10.5,
"pinout_hidden": "I2S,TRIG,WAKE,CTS,RTS,SD,SPI,I2C",
"pinout": {
"1": {
"PWR": 3.3
},
"2": {
"IC": 15,
"ARD": "D0"
},
"3": {
"GND": null
},
"4": {
"IC": 23,
"ARD": "D1"
},
"5": {
"IC": 11,
"ARD": "D2"
},
"6": {
"IC": 21,
"ARD": "D3"
},
"7": {
"IC": 10,
"ARD": "D4"
},
"8": {
"IC": 8,
"ARD": [
"D5",
"A0"
]
},
"9": {
"IC": 24,
"ARD": [
"D6",
"A1"
]
},
"10": {
"IC": 7
},
"11": {
"IC": 22,
"ARD": "D7"
},
"TA9": {
"IC": 17,
"ARD": "D8"
},
"TGND": {
"GND": null
},
"TRX1": {
"IC": 29,
"ARD": "D9"
},
"TTX1": {
"IC": 30,
"ARD": "D10"
},
"TSCLK": {
"IC": 13,
"ARD": "D11"
},
"TSWD": {
"IC": 9,
"ARD": [
"D12",
"A2"
]
}
}
}
}

22
boards/ln-02.json Normal file
View File

@@ -0,0 +1,22 @@
{
"_base": [
"lightning-ln882x",
"lightning-ln882hki",
"ic/ln882hk",
"pcb/ln-02",
"pcb/ln-02-test"
],
"build": {
"mcu": "ln882hk",
"variant": "ln-02"
},
"name": "LN-02 Wi-Fi/BLE Module",
"url": "https://docs.libretiny.eu/boards/ln-02/",
"vendor": "Unknown",
"doc": {
"fccid": ""
},
"pcb": {
"symbol": "LN-02"
}
}

42
boards/variants/ln-02.c Normal file
View File

@@ -0,0 +1,42 @@
/* This file was auto-generated from ln-02.json using boardgen */
#include <Arduino.h>
#ifdef LT_VARIANT_INCLUDE
#include LT_VARIANT_INCLUDE
#endif
// clang-format off
PinInfo lt_arduino_pin_info_list[PINS_COUNT] = {
// D0: PA11, SD_D1, I2C0_SCL, I2C0_SDA
{PA_11, PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D1: PB03, ADC5, I2C0_SCL, I2C0_SDA
{PB_3, PIN_I2C | PIN_ADC | PIN_GPIO, PIN_NONE, 0},
// D2: PA03, UART0_RX, I2C0_SCL, I2C0_SDA
{PA_3, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D3: PB08, UART1_RX, I2C0_SCL, I2C0_SDA
{PB_8, PIN_UART | PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D4: PA02, UART0_TX, I2C0_SCL, I2C0_SDA
{PA_2, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D5: PB09, UART1_TX, I2C0_SCL, I2C0_SDA
{PB_9, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D6: PA01, ADC3, SWDIO, I2C0_SCL, I2C0_SDA
{PA_1, PIN_SWD | PIN_I2C | PIN_ADC | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D7: PA00, ADC2, I2C0_SCL, I2C0_SDA
{PA_0, PIN_I2C | PIN_ADC | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D8: PA09, BOOT1, SD_CLK, I2S0_SCLK, I2C0_SCL, I2C0_SDA
{PA_9, PIN_I2S | PIN_I2C | PIN_GPIO, PIN_NONE, 0},
};
PinInfo *lt_arduino_pin_gpio_map[] = {
[0] = &(lt_arduino_pin_info_list[7]), // PA_0 (D7)
[1] = &(lt_arduino_pin_info_list[6]), // PA_1 (D6)
[2] = &(lt_arduino_pin_info_list[4]), // PA_2 (D4)
[3] = &(lt_arduino_pin_info_list[2]), // PA_3 (D2)
[9] = &(lt_arduino_pin_info_list[8]), // PA_9 (D8)
[11] = &(lt_arduino_pin_info_list[0]), // PA_11 (D0)
[19] = &(lt_arduino_pin_info_list[1]), // PB_3 (D1)
[24] = &(lt_arduino_pin_info_list[3]), // PB_8 (D3)
[25] = &(lt_arduino_pin_info_list[5]), // PB_9 (D5)
};
// clang-format on

106
boards/variants/ln-02.h Normal file
View File

@@ -0,0 +1,106 @@
/* This file was auto-generated from ln-02.json using boardgen */
#pragma once
// clang-format off
// Pins
// ----
#define PINS_COUNT 9 // Total GPIO count
#define NUM_DIGITAL_PINS 9 // Digital inputs/outputs
#define NUM_ANALOG_INPUTS 3 // ADC inputs
#define NUM_ANALOG_OUTPUTS 0 // PWM & DAC outputs
#define PINS_GPIO_MAX 25 // Last usable GPIO number
// Wire Interfaces
// ---------------
#define PIN_WIRE0_SCL_0 11u // PA_11
#define PIN_WIRE0_SCL_1 19u // PB_3
#define PIN_WIRE0_SCL_2 3u // PA_3
#define PIN_WIRE0_SCL_3 24u // PB_8
#define PIN_WIRE0_SCL_4 2u // PA_2
#define PIN_WIRE0_SCL_5 25u // PB_9
#define PIN_WIRE0_SCL_6 1u // PA_1
#define PIN_WIRE0_SCL_7 0u // PA_0
#define PIN_WIRE0_SCL_8 9u // PA_9
#define PIN_WIRE0_SDA_0 11u // PA_11
#define PIN_WIRE0_SDA_1 19u // PB_3
#define PIN_WIRE0_SDA_2 3u // PA_3
#define PIN_WIRE0_SDA_3 24u // PB_8
#define PIN_WIRE0_SDA_4 2u // PA_2
#define PIN_WIRE0_SDA_5 25u // PB_9
#define PIN_WIRE0_SDA_6 1u // PA_1
#define PIN_WIRE0_SDA_7 0u // PA_0
#define PIN_WIRE0_SDA_8 9u // PA_9
#define PINS_WIRE0_SCL (pin_size_t[]){11u, 19u, 3u, 24u, 2u, 25u, 1u, 0u, 9u}
#define PINS_WIRE0_SDA (pin_size_t[]){11u, 19u, 3u, 24u, 2u, 25u, 1u, 0u, 9u}
// Serial ports
// ------------
#define PIN_SERIAL0_RX 3u // PA_3
#define PIN_SERIAL0_TX 2u // PA_2
#define PIN_SERIAL1_RX 24u // PB_8
#define PIN_SERIAL1_TX 25u // PB_9
#define PINS_SERIAL0_RX (pin_size_t[]){3u}
#define PINS_SERIAL0_TX (pin_size_t[]){2u}
#define PINS_SERIAL1_RX (pin_size_t[]){24u}
#define PINS_SERIAL1_TX (pin_size_t[]){25u}
// Pin function macros
// -------------------
#define PIN_ADC2 0u // PA_0
#define PIN_ADC3 1u // PA_1
#define PIN_ADC5 19u // PB_3
#define PIN_PA00 0u // PA_0
#define PIN_PA01 1u // PA_1
#define PIN_PA02 2u // PA_2
#define PIN_PA03 3u // PA_3
#define PIN_PA09 9u // PA_9
#define PIN_PA11 11u // PA_11
#define PIN_PB03 19u // PB_3
#define PIN_PB08 24u // PB_8
#define PIN_PB09 25u // PB_9
#define PIN_RX0 3u // PA_3
#define PIN_RX1 24u // PB_8
#define PIN_SCL0 9u // PA_9
#define PIN_SDA0 9u // PA_9
#define PIN_TX0 2u // PA_2
#define PIN_TX1 25u // PB_9
// Port availability
// -----------------
#define HAS_SERIAL0 1
#define HAS_SERIAL1 1
#define HAS_WIRE0 1
#define SERIAL_INTERFACES_COUNT 2
#define WIRE_INTERFACES_COUNT 1
// Arduino pin names
// -----------------
#define PIN_D0 11u // PA_11
#define PIN_D1 19u // PB_3
#define PIN_D2 3u // PA_3
#define PIN_D3 24u // PB_8
#define PIN_D4 2u // PA_2
#define PIN_D5 25u // PB_9
#define PIN_D6 1u // PA_1
#define PIN_D7 0u // PA_0
#define PIN_D8 9u // PA_9
#define PIN_A0 19u // PB_3
#define PIN_A1 1u // PA_1
#define PIN_A2 0u // PA_0
// Static pin names
// ----------------
static const unsigned char A0 = PIN_A0;
static const unsigned char A1 = PIN_A1;
static const unsigned char A2 = PIN_A2;
static const unsigned char D0 = PIN_D0;
static const unsigned char D1 = PIN_D1;
static const unsigned char D2 = PIN_D2;
static const unsigned char D3 = PIN_D3;
static const unsigned char D4 = PIN_D4;
static const unsigned char D5 = PIN_D5;
static const unsigned char D6 = PIN_D6;
static const unsigned char D7 = PIN_D7;
static const unsigned char D8 = PIN_D8;

54
boards/variants/wl2s.c Normal file
View File

@@ -0,0 +1,54 @@
/* This file was auto-generated from wl2s.json using boardgen */
#include <Arduino.h>
#ifdef LT_VARIANT_INCLUDE
#include LT_VARIANT_INCLUDE
#endif
// clang-format off
PinInfo lt_arduino_pin_info_list[PINS_COUNT] = {
// D0: PA07, SD_D3, I2S0_RX, I2C0_SCL, I2C0_SDA
{PA_7, PIN_I2S | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D1: PA12, I2C0_SCL, I2C0_SDA
{PA_12, PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D2: PA03, UART0_RX, I2C0_SCL, I2C0_SDA
{PA_3, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D3: PA10, SD_D0, I2S0_TX, I2C0_SCL, I2C0_SDA
{PA_10, PIN_I2S | PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D4: PA02, UART0_TX, I2C0_SCL, I2C0_SDA
{PA_2, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D5: PA00, ADC2, I2C0_SCL, I2C0_SDA
{PA_0, PIN_I2C | PIN_ADC | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D6: PB03, ADC5, I2C0_SCL, I2C0_SDA
{PB_3, PIN_I2C | PIN_ADC | PIN_GPIO, PIN_NONE, 0},
// D7: PA11, SD_D1, I2C0_SCL, I2C0_SDA
{PA_11, PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D8: PA09, BOOT1, SD_CLK, I2S0_SCLK, I2C0_SCL, I2C0_SDA
{PA_9, PIN_I2S | PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D9: PB08, UART1_RX, I2C0_SCL, I2C0_SDA
{PB_8, PIN_UART | PIN_I2C | PIN_GPIO, PIN_NONE, 0},
// D10: PB09, UART1_TX, I2C0_SCL, I2C0_SDA
{PB_9, PIN_UART | PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D11: PA05, I2C0_SCL, I2C0_SDA
{PA_5, PIN_I2C | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
// D12: PA01, ADC3, SWDIO, I2C0_SCL, I2C0_SDA
{PA_1, PIN_SWD | PIN_I2C | PIN_ADC | PIN_IRQ | PIN_GPIO, PIN_NONE, 0},
};
PinInfo *lt_arduino_pin_gpio_map[] = {
[0] = &(lt_arduino_pin_info_list[5]), // PA_0 (D5)
[1] = &(lt_arduino_pin_info_list[12]), // PA_1 (D12)
[2] = &(lt_arduino_pin_info_list[4]), // PA_2 (D4)
[3] = &(lt_arduino_pin_info_list[2]), // PA_3 (D2)
[5] = &(lt_arduino_pin_info_list[11]), // PA_5 (D11)
[7] = &(lt_arduino_pin_info_list[0]), // PA_7 (D0)
[9] = &(lt_arduino_pin_info_list[8]), // PA_9 (D8)
[10] = &(lt_arduino_pin_info_list[3]), // PA_10 (D3)
[11] = &(lt_arduino_pin_info_list[7]), // PA_11 (D7)
[12] = &(lt_arduino_pin_info_list[1]), // PA_12 (D1)
[19] = &(lt_arduino_pin_info_list[6]), // PB_3 (D6)
[24] = &(lt_arduino_pin_info_list[9]), // PB_8 (D9)
[25] = &(lt_arduino_pin_info_list[10]), // PB_9 (D10)
};
// clang-format on

126
boards/variants/wl2s.h Normal file
View File

@@ -0,0 +1,126 @@
/* This file was auto-generated from wl2s.json using boardgen */
#pragma once
// clang-format off
// Pins
// ----
#define PINS_COUNT 13 // Total GPIO count
#define NUM_DIGITAL_PINS 13 // Digital inputs/outputs
#define NUM_ANALOG_INPUTS 3 // ADC inputs
#define NUM_ANALOG_OUTPUTS 0 // PWM & DAC outputs
#define PINS_GPIO_MAX 25 // Last usable GPIO number
// Wire Interfaces
// ---------------
#define PIN_WIRE0_SCL_0 7u // PA_7
#define PIN_WIRE0_SCL_1 12u // PA_12
#define PIN_WIRE0_SCL_2 3u // PA_3
#define PIN_WIRE0_SCL_3 10u // PA_10
#define PIN_WIRE0_SCL_4 2u // PA_2
#define PIN_WIRE0_SCL_5 0u // PA_0
#define PIN_WIRE0_SCL_6 19u // PB_3
#define PIN_WIRE0_SCL_7 11u // PA_11
#define PIN_WIRE0_SCL_8 9u // PA_9
#define PIN_WIRE0_SCL_9 24u // PB_8
#define PIN_WIRE0_SCL_10 25u // PB_9
#define PIN_WIRE0_SCL_11 5u // PA_5
#define PIN_WIRE0_SCL_12 1u // PA_1
#define PIN_WIRE0_SDA_0 7u // PA_7
#define PIN_WIRE0_SDA_1 12u // PA_12
#define PIN_WIRE0_SDA_2 3u // PA_3
#define PIN_WIRE0_SDA_3 10u // PA_10
#define PIN_WIRE0_SDA_4 2u // PA_2
#define PIN_WIRE0_SDA_5 0u // PA_0
#define PIN_WIRE0_SDA_6 19u // PB_3
#define PIN_WIRE0_SDA_7 11u // PA_11
#define PIN_WIRE0_SDA_8 9u // PA_9
#define PIN_WIRE0_SDA_9 24u // PB_8
#define PIN_WIRE0_SDA_10 25u // PB_9
#define PIN_WIRE0_SDA_11 5u // PA_5
#define PIN_WIRE0_SDA_12 1u // PA_1
#define PINS_WIRE0_SCL (pin_size_t[]){7u, 12u, 3u, 10u, 2u, 0u, 19u, 11u, 9u, 24u, 25u, 5u, 1u}
#define PINS_WIRE0_SDA (pin_size_t[]){7u, 12u, 3u, 10u, 2u, 0u, 19u, 11u, 9u, 24u, 25u, 5u, 1u}
// Serial ports
// ------------
#define PIN_SERIAL0_RX 3u // PA_3
#define PIN_SERIAL0_TX 2u // PA_2
#define PIN_SERIAL1_RX 24u // PB_8
#define PIN_SERIAL1_TX 25u // PB_9
#define PINS_SERIAL0_RX (pin_size_t[]){3u}
#define PINS_SERIAL0_TX (pin_size_t[]){2u}
#define PINS_SERIAL1_RX (pin_size_t[]){24u}
#define PINS_SERIAL1_TX (pin_size_t[]){25u}
// Pin function macros
// -------------------
#define PIN_ADC2 0u // PA_0
#define PIN_ADC3 1u // PA_1
#define PIN_ADC5 19u // PB_3
#define PIN_PA00 0u // PA_0
#define PIN_PA01 1u // PA_1
#define PIN_PA02 2u // PA_2
#define PIN_PA03 3u // PA_3
#define PIN_PA05 5u // PA_5
#define PIN_PA07 7u // PA_7
#define PIN_PA09 9u // PA_9
#define PIN_PA10 10u // PA_10
#define PIN_PA11 11u // PA_11
#define PIN_PA12 12u // PA_12
#define PIN_PB03 19u // PB_3
#define PIN_PB08 24u // PB_8
#define PIN_PB09 25u // PB_9
#define PIN_RX0 3u // PA_3
#define PIN_RX1 24u // PB_8
#define PIN_SCL0 1u // PA_1
#define PIN_SDA0 1u // PA_1
#define PIN_TX0 2u // PA_2
#define PIN_TX1 25u // PB_9
// Port availability
// -----------------
#define HAS_SERIAL0 1
#define HAS_SERIAL1 1
#define HAS_WIRE0 1
#define SERIAL_INTERFACES_COUNT 2
#define WIRE_INTERFACES_COUNT 1
// Arduino pin names
// -----------------
#define PIN_D0 7u // PA_7
#define PIN_D1 12u // PA_12
#define PIN_D2 3u // PA_3
#define PIN_D3 10u // PA_10
#define PIN_D4 2u // PA_2
#define PIN_D5 0u // PA_0
#define PIN_D6 19u // PB_3
#define PIN_D7 11u // PA_11
#define PIN_D8 9u // PA_9
#define PIN_D9 24u // PB_8
#define PIN_D10 25u // PB_9
#define PIN_D11 5u // PA_5
#define PIN_D12 1u // PA_1
#define PIN_A0 0u // PA_0
#define PIN_A1 19u // PB_3
#define PIN_A2 1u // PA_1
// Static pin names
// ----------------
static const unsigned char A0 = PIN_A0;
static const unsigned char A1 = PIN_A1;
static const unsigned char A2 = PIN_A2;
static const unsigned char D0 = PIN_D0;
static const unsigned char D1 = PIN_D1;
static const unsigned char D2 = PIN_D2;
static const unsigned char D3 = PIN_D3;
static const unsigned char D4 = PIN_D4;
static const unsigned char D5 = PIN_D5;
static const unsigned char D6 = PIN_D6;
static const unsigned char D7 = PIN_D7;
static const unsigned char D8 = PIN_D8;
static const unsigned char D9 = PIN_D9;
static const unsigned char D10 = PIN_D10;
static const unsigned char D11 = PIN_D11;
static const unsigned char D12 = PIN_D12;

22
boards/wl2s.json Normal file
View File

@@ -0,0 +1,22 @@
{
"_base": [
"lightning-ln882x",
"lightning-ln882hki",
"ic/ln882hk",
"pcb/wl2s",
"pcb/wl2s-test"
],
"build": {
"mcu": "ln882hk",
"variant": "wl2s"
},
"name": "WL2S Wi-Fi/BLE Module",
"url": "https://docs.libretiny.eu/boards/wl2s/",
"vendor": "Unknown",
"doc": {
"fccid": ""
},
"pcb": {
"symbol": "WL2S"
}
}

View File

@@ -0,0 +1,263 @@
# Copyright (c) Etienne Le Cousin 2024-02-08.
from os.path import join
from shutil import copyfile
from platformio.platform.board import PlatformBoardConfig
from SCons.Script import DefaultEnvironment, Environment
env: Environment = DefaultEnvironment()
board: PlatformBoardConfig = env.BoardConfig()
queue = env.AddLibraryQueue("lightning-ln882h")
env.ConfigureFamily()
# Flags
queue.AppendPublic(
CCFLAGS=[
"-mcpu=cortex-m4",
"-mthumb",
"-mabi=aapcs",
"-mfloat-abi=hard",
"-mfpu=fpv4-sp-d16",
"-fno-short-enums",
"-Wfatal-errors",
"-ffunction-sections",
"-fdata-sections",
"-fno-strict-aliasing",
"-MD",
"-MP",
],
CFLAGS=[
"-Wpointer-arith",
"-Wno-write-strings",
"-Wno-maybe-uninitialized",
],
CXXFLAGS=[],
CPPDEFINES=[
# other options
"ARM_MATH_CM4",
"LN882H",
("PLATFORM_LN882H", "1"),
("MBEDTLS_CONFIG_FILE", r"\"mbedtls_config.h\""),
],
ASFLAGS=[
"-mcpu=cortex-m4",
"-mthumb",
"-mabi=aapcs",
"-mfloat-abi=hard",
"-mfpu=fpv4-sp-d16",
"-g",
"-c",
],
LINKFLAGS=[
"-mcpu=cortex-m4",
"-mthumb",
"-mabi=aapcs",
"-mfloat-abi=hard",
"-mfpu=fpv4-sp-d16",
"--specs=nano.specs",
"-Wl,--gc-sections",
"-Wl,--whole-archive",
"-lcore_ln882h",
"-Wl,--no-whole-archive",
# stdio wrappers (base/port/printf.c)
"-Wl,-wrap,ln_printf",
],
)
queue.AppendPrivate(
CFLAGS=[
"-Wno-implicit-function-declaration",
"-Wno-incompatible-pointer-types",
"-Wno-int-conversion",
"-Wno-pointer-sign",
"-Wfatal-errors",
"-ffunction-sections",
"-fdata-sections",
"-fno-strict-aliasing",
"-std=gnu99",
"-MD",
"-MP",
],
)
# Boot files - from SDK project/wifi_mcu_basic_example
"""
queue.AddLibrary(
name="ln882h_boot",
base_dir="$SDK_DIR/project/wifi_mcu_basic_example",
srcs=[
"+<startup/startup_ln882h_gcc.c>",
],
)
"""
# Sources
queue.AddLibrary(
name="ln882h_sdk",
base_dir="$SDK_DIR",
srcs=[
# MCU
"+<mcu/ln882h/system_ln882h.c>"
# Hal
"+<mcu/driver_ln882h/hal/*.c>",
# FreeRTOS
"+<components/kernel/FreeRTOS_Adapter/*.c>",
# Serial
"+<components/serial/serial.c>",
# Misc
"+<components/fs/nvds/ln_nvds.c>",
"+<components/fs/kv/kv/ln_kv.c>",
"+<components/fs/kv/kv_port/ln_kv_port.c>",
"+<components/fota/ota_agent/ota_image.c>",
"+<components/utils/fifo/fifobuf.c>",
"+<components/utils/debug/log.c>",
"+<components/utils/debug/CmBacktrace/cm_backtrace.c>",
"+<components/utils/system_parameter.c>",
"+<components/utils/power_mgmt/ln_pm.c>",
"+<components/utils/runtime/runtime.c>",
"+<components/utils/reboot_trace/reboot_trace.c>",
"+<components/tencent/qcloud_iot_c_sdk/external_libs/cJSON/cJSON.c>",
],
includes=[
# MCU
"+<mcu/CMSIS_5.3.0>",
"+<mcu/ln882h>",
# Hal
"+<mcu/driver_ln882h>",
"+<mcu/driver_ln882h/reg>",
"+<mcu/driver_ln882h/hal>",
# Components
"+<components>",
# FreeRTOS
"+<components/kernel>",
"+<components/kernel/FreeRTOS/Source/portable/GCC/ARM_CM4F>",
"+<components/kernel/FreeRTOS_Adapter>",
"+<components/kernel/osal>",
# Misc
"+<components/fs/nvds>",
"+<components/fs/kv/kv>",
"+<components/fs/kv/kv_port>",
"+<components/fs/partition_mgr>",
"+<components/fota/ota_agent>",
"+<components/serial>",
"+<components/utils/debug/CmBacktrace>",
"+<components/utils/power_mgmt>",
# "+<components/utils/runtime>",
"+<components/utils/reboot_trace>",
"+<components/tencent/qcloud_iot_c_sdk/external_libs/cJSON>",
],
options=dict(
CPPDEFINES=["LN882H_SDK"],
CFLAGS=["-w"],
),
)
queue.AddLibrary(
name="ln882h_net",
base_dir="$SDK_DIR",
srcs=[
# WiFi
"+<components/wifi/wifi_manager/wifi_manager.c>",
"+<components/wifi/wifi_lib_import/wifi_port.c>",
# DHCPD
"+<components/net/dhcpd/*.c>",
# Utils
"+<components/utils/ln_misc.c>",
"+<components/utils/ln_sha1.c>",
"+<components/utils/ln_aes.c>",
"+<components/utils/ln_psk_calc.c>",
],
includes=[
"+<components/wifi/wifi_manager>",
"+<components/wifi/wifi_lib_import>",
"+<components/wifi/wifi_lib_export>",
"+<components/net/dhcpd>",
],
options=dict(
CPPDEFINES=["LN882H_SDK"],
CFLAGS=["-w"],
),
)
# Sources - FreeRTOS
env.Replace(FREERTOS_PORT=env["FAMILY_NAME"], FREERTOS_PORT_DEFINE="LIGHTNING_LN882H")
queue.AddExternalLibrary("freertos")
queue.AddExternalLibrary("freertos-port")
# Sources - lwIP
queue.AddExternalLibrary("lwip", port="ln882h")
# Sources - mbedTLS
queue.AddLibrary(
name="ln882h_mbedtls",
base_dir="$SDK_DIR",
srcs=[
# mbedTLS from SDK
"+<components/net/mbedtls/library/*.c>",
# replace this with a port specific
"-<components/net/mbedtls/library/net_sockets.c>",
"-<components/net/mbedtls/library/platform.c>",
"-<components/net/mbedtls/library/threading_alt.c>",
"-<components/net/mbedtls/library/timing.c>",
# ln port
"+<components/net/mbedtls/port_ln/library/*.c>",
],
includes=[
"+<components/net/mbedtls/include>",
"+<components/net/mbedtls/port_ln/include>",
],
options=dict(
CPPDEFINES=["LN882H_SDK"],
CFLAGS=["-w"],
),
)
# Libs & linker config
queue.AppendPublic(
LIBPATH=[
# fmt: off
join("$SDK_DIR", "lib", "gcclib"),
# fmt: on
],
LIBS=[
"arm_cortexM4lf_math",
"ln882h_wifi",
"m",
"c",
"nosys",
"gcc",
],
)
# Copy Bootloader
bootfile = board.get("build.bootfile")
copyfile(
join(env.subst("$MISC_DIR"), bootfile), join(env.subst("$BUILD_DIR"), "boot.bin")
)
# Generate linker scripts with correct flash offsets
env.GenerateLinkerScript(board, board.get("build.ldscript"))
# Build all libraries
queue.BuildLibraries()
# Main firmware outputs and actions
# fmt: off
image_ota = "${BUILD_DIR}/image_ota.${FLASH_OTA_OFFSET}.bin"
image_part_tab = "${BUILD_DIR}/image_part_tab.${FLASH_PART_TAB_OFFSET}.bin"
image_boot = "${BUILD_DIR}/image_boot.${FLASH_BOOT_OFFSET}.bin"
image_app = "${BUILD_DIR}/image_app.${FLASH_APP_OFFSET}.bin"
# fmt: on
env.Replace(
# linker command
LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""',
# UF2OTA input list
UF2OTA=[
# ota binary image for device
f"{image_ota}=device:ota",
# binary image for flasher
f"{image_boot}=flasher:boot",
f"{image_part_tab}=flasher:part_tab",
f"{image_app}=flasher:app",
],
)

View File

@@ -18,7 +18,7 @@ platform: PlatformBase = env.PioPlatform()
board: PlatformBoardConfig = env.BoardConfig()
python_deps = {
"ltchiptool": ">=4.5.1,<5.0",
"ltchiptool": ">=4.12.0,<5.0",
}
env.SConscript("python-venv.py", exports="env")
env.ConfigurePythonVenv()

View File

@@ -173,7 +173,7 @@ int MbedTLSClient::connect(
#ifdef MBEDTLS_SSL_ALPN
if (_alpnProtocols) {
ret = mbedtls_ssl_conf_alpn_protocols(&_sslCfg, _alpnProtocols);
ret = mbedtls_ssl_conf_alpn_protocols(_sslCfg, _alpnProtocols);
LT_RET_NZ(ret);
}
#endif

View File

@@ -17,6 +17,7 @@ typedef enum {
F_BK7231N = 0x7B3EF230, // Beken 7231N
F_BK7251 = 0x6A82CC42, // Beken 7251/7252
F_BL60X = 0xDE1270B7, // Boufallo 602
F_LN882H = 0xA38090A8, // Lightning LN882H
} lt_cpu_family_t;
typedef enum {
@@ -44,4 +45,8 @@ typedef enum {
BL2028N = BK7231N,
BK7231S = BK7231T,
BK7231U = BK7231T,
// Lightning LN882x
LN882HF = CPU_MODEL(F_LN882H, 0x00), // TODO / QFN24
LN882HK = CPU_MODEL(F_LN882H, 0x00), // TODO / QFN32
LN882HC = CPU_MODEL(F_LN882H, 0x00), // TODO / QFN40
} lt_cpu_model_t;

View File

@@ -0,0 +1,99 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#include "SerialPrivate.h"
extern Serial_t *serial_handles[SER_PORT_NUM];
#if LT_HW_UART0
SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX);
static void callback_uart0(void) {
SerialData *data = (SerialData *)Serial0.data;
char ch;
while (serial_read(serial_handles[0], &ch, 1)) {
data->buf.store_char(ch);
}
}
#else
#define callback_uart0 NULL
#endif
#if LT_HW_UART1
SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX);
static void callback_uart1(void) {
SerialData *data = (SerialData *)Serial1.data;
char ch;
while (serial_read(serial_handles[1], &ch, 1)) {
data->buf.store_char(ch);
}
}
#else
#define callback_uart1 NULL
#endif
#if LT_HW_UART2
SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX);
static void callback_uart2(void) {
SerialData *data = (SerialData *)Serial2.data;
char ch;
while (serial_read(serial_handles[2], &ch, 1)) {
data->buf.store_char(ch);
}
}
#else
#define callback_uart2 NULL
#endif
// clang-format off
static const serial_rx_callbcak serial_rx_callbacks[SER_PORT_NUM] = {
callback_uart0,
callback_uart1,
callback_uart2
};
// clang-format on
void SerialClass::begin(unsigned long baudrate, uint16_t config) {
if (!this->data) {
this->data = new SerialData();
this->buf = &BUF;
}
if (this->baudrate != baudrate || this->config != config)
this->configure(baudrate, config);
}
void SerialClass::configure(unsigned long baudrate, uint16_t config) {
if (!this->data)
return;
serial_init(serial_handles[port], (serial_port_id_t)port, baudrate, serial_rx_callbacks[port]);
this->baudrate = baudrate;
this->config = config;
}
void SerialClass::end() {
if (!this->data)
return;
serial_deinit(serial_handles[port]);
delete (SerialData *)this->data;
delete this->buf;
this->data = NULL;
this->buf = NULL;
this->baudrate = 0;
}
void SerialClass::flush() {
if (!this->data)
return;
serial_flush(serial_handles[port]);
}
size_t SerialClass::write(uint8_t c) {
if (!this->data)
return 0;
serial_putchar(serial_handles[port], c);
return 1;
}

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
#include <Arduino.h>
#include <sdk_private.h>
typedef struct {
SerialRingBuffer buf;
void (*callback)(void);
} SerialData;
#define DATA ((SerialData *)this->data)
#define BUF (DATA->buf)

View File

@@ -0,0 +1,59 @@
/* Copyright (c) Etienne Le Cousin 2024-02-18. */
#include "WiFiPrivate.h"
WiFiClass::WiFiClass() {
data = (WiFiData *)calloc(1, sizeof(WiFiData));
DATA->scanSem = xSemaphoreCreateBinary();
}
WiFiClass::~WiFiClass() {
vSemaphoreDelete(DATA->scanSem);
free(data);
data = NULL;
}
WiFiAuthMode securityTypeToAuthMode(uint8_t type) {
switch (type) {
case LN_WIFI_AUTH_OPEN:
return WIFI_AUTH_OPEN;
case LN_WIFI_AUTH_WEP:
return WIFI_AUTH_WEP;
case LN_WIFI_AUTH_WPA_PSK:
return WIFI_AUTH_WPA_PSK;
case LN_WIFI_AUTH_WPA2_PSK:
return WIFI_AUTH_WPA2_PSK;
case LN_WIFI_AUTH_WPA_WPA2_PSK:
return WIFI_AUTH_WPA_WPA2_PSK;
case LN_WIFI_AUTH_WPA2_ENTERPRISE:
return WIFI_AUTH_WPA2_ENTERPRISE;
case LN_WIFI_AUTH_WPA3_SAE:
return WIFI_AUTH_WPA3_PSK;
case LN_WIFI_AUTH_WPA2_PSK_WPA3_SAE:
return WIFI_AUTH_WPA2_WPA3_PSK;
}
return WIFI_AUTH_INVALID;
}
uint8_t authModeToSecurityType(WiFiAuthMode auth) {
switch (auth) {
case WIFI_AUTH_OPEN:
return LN_WIFI_AUTH_OPEN;
case WIFI_AUTH_WEP:
return LN_WIFI_AUTH_WEP;
case WIFI_AUTH_WPA_PSK:
return LN_WIFI_AUTH_WPA_PSK;
case WIFI_AUTH_WPA2_PSK:
return LN_WIFI_AUTH_WPA2_PSK;
case WIFI_AUTH_WPA_WPA2_PSK:
return LN_WIFI_AUTH_WPA_WPA2_PSK;
case WIFI_AUTH_WPA2_ENTERPRISE:
return LN_WIFI_AUTH_WPA2_ENTERPRISE;
case WIFI_AUTH_WPA3_PSK:
return LN_WIFI_AUTH_WPA3_SAE;
case WIFI_AUTH_WPA2_WPA3_PSK:
return LN_WIFI_AUTH_WPA2_PSK_WPA3_SAE;
}
return WIFI_AUTH_INVALID;
}

View File

@@ -0,0 +1,134 @@
/* Copyright (c) Etienne Le Cousin 2024-02-18. */
#include "WiFiPrivate.h"
static uint8_t psk_value[40] = {0x0};
static uint8_t mac_addr[6] = {0x00, 0x50, 0xC2, 0x7F, 0xBC, 0x01};
bool WiFiClass::softAP(const char *ssid, const char *passphrase, int channel, bool ssidHidden, int maxClients) {
if (!enableAP(true))
return false;
if (!validate(ssid, passphrase))
return false;
LT_HEAP_I();
WiFiNetworkInfo &info = DATA->ap;
if (info.ssid != ssid)
// free network info, if not called from restoreAPConfig()
resetNetworkInfo(info);
if (info.ssid != ssid)
info.ssid = strdup(ssid);
if (info.password != passphrase)
info.password = strdup(passphrase);
info.ssidHidden = ssidHidden;
info.bssid = softAPmacAddress(mac_addr);
info.channel = channel;
info.auth = passphrase ? LN_WIFI_AUTH_WPA2_PSK : LN_WIFI_AUTH_OPEN;
LT_IM(WIFI, "Creating SoftAP %s", ssid);
// clang-format off
wifi_softap_cfg_t ap_cfg = {
.ssid = info.ssid,
.pwd = info.password,
.bssid = info.bssid,
.ext_cfg = {
.channel = (uint8_t)info.channel,
.authmode = (ln_wifi_auth_mode_t)info.auth,
.ssid_hidden = info.ssidHidden,
.beacon_interval = 100,
.psk_value = NULL,
}
};
// clang-format on
if ((strlen(ap_cfg.pwd) != 0) && (ap_cfg.ext_cfg.authmode != LN_WIFI_AUTH_OPEN) &&
(ap_cfg.ext_cfg.authmode != LN_WIFI_AUTH_WEP)) {
memset(psk_value, 0, sizeof(psk_value));
if (0 == ln_psk_calc(ap_cfg.ssid, ap_cfg.pwd, psk_value, sizeof(psk_value))) {
ap_cfg.ext_cfg.psk_value = psk_value;
hexdump(psk_value, sizeof(psk_value));
}
}
// wifi start
__wrap_ln_printf_disable();
int ret = wifi_softap_start(&ap_cfg);
__wrap_ln_printf_enable();
if (ret != 0) {
LT_EM(WIFI, "SoftAP failed; ret=%d", ret);
return false;
}
return true;
}
bool WiFiClass::softAPConfig(IPAddress localIP, IPAddress gateway, IPAddress subnet) {
if (!enableAP(true))
return false;
WiFiNetworkInfo &info = DATA->ap;
// dhcp server config
server_config_t server_config;
server_config.server.addr = localIP;
server_config.port = 67;
server_config.lease = 2880;
server_config.renew = 2880;
server_config.ip_start.addr = IPAddress(localIP[0], localIP[1], localIP[2], 100);
server_config.ip_end.addr = IPAddress(localIP[0], localIP[1], localIP[2], 150);
server_config.client_max = 3;
dhcpd_curr_config_set(&server_config);
// net device config
tcpip_ip_info_t ip_info;
ip_info.ip.addr = info.localIP = localIP;
ip_info.netmask.addr = info.subnet = subnet;
ip_info.gw.addr = info.gateway = gateway;
netdev_set_ip_info(NETIF_IDX_AP, &ip_info);
netdev_set_active(NETIF_IDX_AP);
return true;
}
bool WiFiClass::softAPdisconnect(bool wifiOff) {
return enableAP(false);
}
uint8_t WiFiClass::softAPgetStationNum() {
return 0;
}
IPAddress WiFiClass::softAPIP() {
return netif_ip_addr4(netdev_get_netif(NETIF_IDX_AP))->addr;
}
IPAddress WiFiClass::softAPSubnetMask() {
return netif_ip_netmask4(netdev_get_netif(NETIF_IDX_AP))->addr;
}
const char *WiFiClass::softAPgetHostname() {
return netif_get_hostname(netdev_get_netif(NETIF_IDX_AP));
}
bool WiFiClass::softAPsetHostname(const char *hostname) {
netif_set_hostname(netdev_get_netif(NETIF_IDX_AP), (char *)hostname);
return true;
}
uint8_t *WiFiClass::softAPmacAddress(uint8_t *mac) {
memcpy(mac, netdev_get_netif(NETIF_IDX_AP)->hwaddr, MAC_ADDRESS_LEN);
return mac;
}
String WiFiClass::softAPmacAddress(void) {
uint8_t mac[MAC_ADDRESS_LEN];
softAPmacAddress(mac);
return macToString(mac);
}
const String WiFiClass::softAPSSID(void) {
WiFiNetworkInfo &info = DATA->ap;
return (char *)info.ssid;
}

View File

@@ -0,0 +1,122 @@
/* Copyright (c) Etienne Le Cousin 2024-03-10. */
#include "WiFiPrivate.h"
void wifiEventSendArduino(EventId event) {
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
pWiFi->postEvent(event, eventInfo);
}
static void wifiEventStaStartup(void *arg) {
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_START);
}
static void wifiEventStaConnected(void *arg) {
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
String ssid = pWiFi->SSID();
eventInfo.wifi_sta_connected.ssid_len = ssid.length();
eventInfo.wifi_sta_connected.channel = pWiFi->channel();
eventInfo.wifi_sta_connected.authmode = pWiFi->getEncryption();
memcpy(eventInfo.wifi_sta_connected.ssid, ssid.c_str(), eventInfo.wifi_sta_connected.ssid_len + 1);
memcpy(eventInfo.wifi_sta_connected.bssid, pWiFi->BSSID(), 6);
pWiFi->postEvent(ARDUINO_EVENT_WIFI_STA_CONNECTED, eventInfo);
}
static void wifiEventStaDisconnected(void *arg) {
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
eventInfo.wifi_sta_disconnected.ssid_len = 0;
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_ASSOC_LEAVE;
pWiFi->postEvent(ARDUINO_EVENT_WIFI_STA_DISCONNECTED, eventInfo);
}
static void wifiEventStaConnectFailed(void *arg) {
wifi_sta_connect_failed_reason_t reason = *(wifi_sta_connect_failed_reason_t *)arg;
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
eventInfo.wifi_sta_disconnected.ssid_len = 0;
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_UNSPECIFIED;
switch (reason) {
case WIFI_STA_CONN_WRONG_PWD:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_AUTH_FAIL;
break;
case WIFI_STA_CONN_TARGET_AP_NOT_FOUND:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_NO_AP_FOUND;
break;
case WIFI_STA_CONN_TIMEOUT:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_BEACON_TIMEOUT;
break;
case WIFI_STA_CONN_REFUSED:
eventInfo.wifi_sta_disconnected.reason = WIFI_REASON_CONNECTION_FAIL;
break;
}
pWiFi->postEvent(ARDUINO_EVENT_WIFI_STA_DISCONNECTED, eventInfo);
}
static void wifiEventSoftAPStartup(void *arg) {
netdev_set_state(NETIF_IDX_AP, NETDEV_UP);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_START);
}
static void wifiEventSoftAPAssociated(void *arg) {
const uint8_t *mac_addr = (const uint8_t *)arg;
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
memcpy(eventInfo.wifi_ap_staconnected.mac, mac_addr, 6);
pWiFi->postEvent(ARDUINO_EVENT_WIFI_AP_STACONNECTED, eventInfo);
}
static void wifiEventSoftAPDisassociated(void *arg) {
const uint8_t *mac_addr = (const uint8_t *)arg;
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi)
return; // failsafe
memcpy(eventInfo.wifi_ap_staconnected.mac, mac_addr, 6);
pWiFi->postEvent(ARDUINO_EVENT_WIFI_AP_STADISCONNECTED, eventInfo);
}
static void wifiEventIpReceived(struct netif *nif) {
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
if (!pWiFi || !nif)
return; // failsafe
eventInfo.got_ip.if_index = 0;
eventInfo.got_ip.ip_changed = true;
eventInfo.got_ip.ip_info.ip.addr = nif->ip_addr.addr;
eventInfo.got_ip.ip_info.gw.addr = nif->gw.addr;
eventInfo.got_ip.ip_info.netmask.addr = nif->netmask.addr;
pWiFi->postEvent(ARDUINO_EVENT_WIFI_STA_GOT_IP, eventInfo);
}
void registerWifiHandlers() {
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_STARTUP, &wifiEventStaStartup);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_CONNECTED, &wifiEventStaConnected);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_DISCONNECTED, &wifiEventStaDisconnected);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_CONNECT_FAILED, &wifiEventStaConnectFailed);
netdev_get_ip_cb_set(&wifiEventIpReceived);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_SOFTAP_STARTUP, &wifiEventSoftAPStartup);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_SOFTAP_ASSOCIATED, &wifiEventSoftAPAssociated);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_SOFTAP_DISASSOCIATED, &wifiEventSoftAPDisassociated);
}

View File

@@ -0,0 +1,144 @@
/* Copyright (c) Etienne Le Cousin 2024-03-10. */
#include "WiFiPrivate.h"
static uint8_t psk_value[40] = {0x0};
bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) {
__wrap_ln_printf_disable();
if (!DATA->initialized) {
// rf preprocess,img cal
wifi_rf_calibration();
// Init wifi stack.
wifi_init();
// Init lwip stack.
LT_IM(WIFI, "Initializing LwIP");
lwip_tcpip_init();
// Init wifi manager
wifi_manager_init();
// Register event handlers
registerWifiHandlers();
DATA->mode = WIFI_MODE_NULL;
DATA->initialized = true;
}
LT_HEAP_I();
WiFiMode currentMode = DATA->mode;
WiFiNetworkInfo &staInfo = DATA->sta;
WiFiNetworkInfo &apInfo = DATA->ap;
if (mode == WIFI_MODE_APSTA) {
LT_EM(WIFI, "AP+STA mode not supported!");
goto error;
}
if (sta == WLMODE_ENABLE) {
LT_DM(WIFI, "Enabling STA");
// 1. sta mac get
uint8_t mac_addr[6];
setMacAddress(macAddress(mac_addr));
// 2. net device(lwip)
netdev_set_active(NETIF_IDX_STA);
// 3. wifi start
wifi_sta_start(mac_addr, WIFI_NO_POWERSAVE);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_START);
} else if (sta == WLMODE_DISABLE) {
LT_DM(WIFI, "Disabling STA");
wifi_sta_disconnect();
netdev_set_state(NETIF_IDX_STA, NETDEV_DOWN);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_STA_STOP);
}
LT_HEAP_I();
if (ap == WLMODE_ENABLE) {
LT_DM(WIFI, "Enabling AP");
// 1. ap mac get
uint8_t mac_addr[6];
sysparam_softap_mac_get(mac_addr);
netdev_set_mac_addr(NETIF_IDX_AP, mac_addr);
// 2. net device(lwip)
netdev_set_active(NETIF_IDX_AP);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_START);
} else if (ap == WLMODE_DISABLE) {
LT_DM(WIFI, "Disabling AP");
wifi_softap_deauth_all();
netdev_set_state(NETIF_IDX_AP, NETDEV_DOWN);
wifiEventSendArduino(ARDUINO_EVENT_WIFI_AP_STOP);
}
DATA->mode = mode;
LT_HEAP_I();
__wrap_ln_printf_enable();
return true;
error:
__wrap_ln_printf_enable();
return false;
}
WiFiMode WiFiClass::getMode() {
if (!DATA->initialized)
return WIFI_MODE_NULL;
// return fake value because the chip doesn't report the mode before it is started
return DATA->mode;
/*switch (wifi_current_mode_get()) {
case LN_WIFI_MODE_STATION:
return WIFI_MODE_STA;
case LN_WIFI_MODE_AP:
return WIFI_MODE_AP;
case LN_WIFI_MODE_AP_STATION:
return WIFI_MODE_APSTA;
}
return WIFI_MODE_NULL;*/
}
WiFiStatus WiFiClass::status() {
wifi_sta_status_t status = WIFI_STA_STATUS_STARTUP;
wifi_get_sta_status(&status);
if (status == WIFI_STA_STATUS_CONNECTED) {
return WL_CONNECTED;
} else {
return WL_DISCONNECTED;
}
}
bool WiFiClass::setSleep(bool enable) {
LT_DM(WIFI, "WiFi sleep mode %u", enable);
if (enable) {
if (wifi_sta_set_powersave(WIFI_MAX_POWERSAVE))
return false;
} else {
if (wifi_sta_set_powersave(WIFI_NO_POWERSAVE))
return false;
}
DATA->sleep = enable;
return true;
}
bool WiFiClass::getSleep() {
return DATA->sleep;
}
IPAddress WiFiClass::hostByName(const char *hostname) {
ip_addr_t ip;
int ret = netconn_gethostbyname(hostname, &ip);
if (ret == ERR_OK) {
return ip.addr;
}
return IPAddress();
}

View File

@@ -0,0 +1,47 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
#include <WiFi.h>
#include <sdk_private.h>
extern "C" {
// copy defines from PIO builder (for IDE to understand)
#define LWIP_TIMEVAL_PRIVATE 0
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_SO_RCVBUF 1
#include <lwip/api.h>
#include <lwip/dns.h>
#include <lwip/ip_addr.h>
#include <lwip/netif.h>
#include <lwip/netifapi.h>
#include <netif/ethernetif.h>
#include <FreeRTOS.h>
#include <semphr.h>
} // extern "C"
// WiFi.cpp
extern WiFiAuthMode securityTypeToAuthMode(uint8_t type);
uint8_t authModeToSecurityType(WiFiAuthMode auth);
// WiFiEvents.cpp
extern void wifiEventSendArduino(EventId event);
extern void registerWifiHandlers();
typedef struct {
bool initialized;
bool sleep;
WiFiMode mode;
SemaphoreHandle_t scanSem;
WiFiNetworkInfo sta;
WiFiNetworkInfo ap;
} WiFiData;
#define DATA ((WiFiData *)data)
#define pDATA ((WiFiData *)pWiFi->data)
#define cDATA ((WiFiData *)cls->data)
#define IP_FMT "%u.%u.%u.%u"

View File

@@ -0,0 +1,213 @@
/* Copyright (c) Etienne Le Cousin 2024-03-10. */
#include "WiFiPrivate.h"
WiFiStatus WiFiClass::begin(
const char *ssid,
const char *passphrase,
int32_t channel,
const uint8_t *bssid,
bool connect
) {
if (!enableSTA(true))
return WL_CONNECT_FAILED;
if (!validate(ssid, passphrase))
return WL_CONNECT_FAILED;
LT_HEAP_I();
WiFiNetworkInfo &info = DATA->sta;
if (info.ssid != ssid)
// free network info, if not called from restoreSTAConfig()
resetNetworkInfo(info);
if (info.ssid != ssid)
info.ssid = strdup(ssid);
info.channel = channel;
info.auth = LN_WIFI_AUTH_OPEN;
if (passphrase) {
if (info.password != passphrase)
info.password = strdup(passphrase);
info.auth = LN_WIFI_AUTH_WPA_WPA2_PSK;
}
if (reconnect(bssid))
return WL_CONNECTED;
else
return WL_CONNECT_FAILED;
}
bool WiFiClass::config(IPAddress localIP, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) {
if (!enableSTA(true))
return false;
WiFiNetworkInfo &info = DATA->sta;
struct netif *ifs = netdev_get_netif(NETIF_IDX_STA);
ip4_addr_t d1, d2;
d1.addr = info.dns1 = dns1;
d2.addr = info.dns2 = dns2;
if (d1.addr)
dns_setserver(0, &d1);
if (d2.addr)
dns_setserver(0, &d2);
if (!localIP[0]) {
info.localIP = 0;
netifapi_dhcp_start(ifs);
return true;
}
ip4_addr_t ipaddr, netmask, gw;
ipaddr.addr = info.localIP = localIP;
netmask.addr = info.subnet = subnet;
gw.addr = info.gateway = gateway;
netif_set_addr(ifs, &ipaddr, &netmask, &gw);
netifapi_dhcp_release_and_stop(ifs);
return true;
}
bool WiFiClass::reconnect(const uint8_t *bssid) {
WiFiNetworkInfo &info = DATA->sta;
LT_IM(WIFI, "Connecting to %s (bssid=%p)", info.ssid, bssid);
if (bssid != info.bssid) {
if (!info.bssid)
info.bssid = (uint8_t *)malloc(BSSID_LEN);
memcpy(info.bssid, bssid, BSSID_LEN);
}
uint8_t psk_value[40] = {0x0};
wifi_sta_connect_t connect = {
.ssid = info.ssid,
.pwd = info.password,
.bssid = NULL, // info.bssid,
.psk_value = NULL,
};
wifi_scan_cfg_t scan_cfg = {
.channel = (uint8_t)info.channel,
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time = 5,
};
if (0 == ln_psk_calc(connect.ssid, connect.pwd, psk_value, sizeof(psk_value)))
connect.psk_value = psk_value;
LT_DM(WIFI, "Starting WiFi...");
__wrap_ln_printf_disable();
int ret = wifi_sta_connect(&connect, &scan_cfg);
// int ret = wifi_sta_connect_v2(&connect, &scan_cfg, 10);
__wrap_ln_printf_enable();
LT_DM(WIFI, "Start OK (%d)", ret);
return true;
}
bool WiFiClass::disconnect(bool wifiOff) {
free(DATA->sta.ssid);
DATA->sta.ssid = NULL;
int ret = wifi_sta_disconnect();
if (wifiOff)
enableSTA(false);
return ret == 0;
}
bool WiFiClass::setAutoReconnect(bool autoReconnect) {
return false;
}
bool WiFiClass::getAutoReconnect() {
return false;
}
IPAddress WiFiClass::localIP() {
tcpip_ip_info_t ip_info;
netdev_get_ip_info(NETIF_IDX_STA, &ip_info);
return IPAddress(ip_info.ip.addr);
}
IPAddress WiFiClass::subnetMask() {
tcpip_ip_info_t ip_info;
netdev_get_ip_info(NETIF_IDX_STA, &ip_info);
return IPAddress(ip_info.netmask.addr);
}
IPAddress WiFiClass::gatewayIP() {
tcpip_ip_info_t ip_info;
netdev_get_ip_info(NETIF_IDX_STA, &ip_info);
return IPAddress(ip_info.gw.addr);
}
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
return dns_getserver(dns_no)->addr;
}
IPAddress WiFiClass::broadcastIP() {
return calculateBroadcast(localIP(), subnetMask());
}
const char *WiFiClass::getHostname() {
struct netif *ifs = netdev_get_netif(NETIF_IDX_STA);
return netif_get_hostname(ifs);
}
bool WiFiClass::setHostname(const char *hostname) {
struct netif *ifs = netdev_get_netif(NETIF_IDX_STA);
netif_set_hostname(ifs, (char *)hostname);
return true;
}
uint8_t *WiFiClass::macAddress(uint8_t *mac) {
if (SYSPARAM_ERR_NONE == sysparam_sta_mac_get(mac))
return mac;
else
LT_WM(WIFI, "sysparam sta mac get failed!");
lt_get_device_mac(mac);
return mac;
}
bool WiFiClass::setMacAddress(const uint8_t *mac) {
sysparam_sta_mac_update(mac);
return netdev_set_mac_addr(NETIF_IDX_STA, (uint8_t *)mac) == 0;
}
const String WiFiClass::SSID() {
const char *ssid = NULL;
const uint8_t *bssid = NULL;
wifi_get_sta_conn_info(&ssid, &bssid);
return ssid;
}
const String WiFiClass::psk() {
if (!isConnected() || !DATA->sta.password)
return "";
return DATA->sta.password;
}
uint8_t *WiFiClass::BSSID() {
const char *ssid = NULL;
const uint8_t *bssid = NULL;
wifi_get_sta_conn_info(&ssid, &bssid);
return (uint8_t *)bssid;
}
int32_t WiFiClass::channel() {
uint8_t channel = 0;
wifi_get_channel(&channel);
return (int32_t)channel;
}
int8_t WiFiClass::RSSI() {
int8_t rssi = 0;
wifi_sta_get_rssi(&rssi);
return rssi;
}
WiFiAuthMode WiFiClass::getEncryption() {
ln_wifi_auth_mode_t mode;
wifi_sta_get_connected_ap_security(&mode);
return securityTypeToAuthMode(mode);
}

View File

@@ -0,0 +1,114 @@
/* Copyright (c) Etienne Le Cousin 2024-03-13. */
#include "WiFiPrivate.h"
static void scanHandler(void *arg) {
WiFiClass *cls = (WiFiClass *)pWiFi;
WiFiScanData *scan = cls->scan;
LT_HEAP_I();
ln_list_t *list;
uint8_t n = 0, node_count = 0;
ap_info_node_t *pnode;
wifi_manager_ap_list_update_enable(LN_FALSE);
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_SCAN_COMPLETE, NULL);
// 1.get ap info list.
if (wifi_manager_get_ap_list(&list, &node_count)) {
LT_EM(WIFI, "Failed to get scan result");
goto end;
}
LT_IM(WIFI, "Found %d APs", node_count);
cls->scanAlloc(node_count);
if (!scan->ap) {
LT_WM(WIFI, "scan->ap alloc failed");
goto end;
}
// 2.get all ap info in the list.
LN_LIST_FOR_EACH_ENTRY(pnode, ap_info_node_t, list, list) {
uint8_t *mac = (uint8_t *)pnode->info.bssid;
ap_info_t *ap_info = &pnode->info;
scan->ap[n].ssid = strdup(ap_info->ssid);
scan->ap[n].auth = securityTypeToAuthMode(ap_info->authmode);
scan->ap[n].rssi = ap_info->rssi;
scan->ap[n].channel = ap_info->channel;
memcpy(scan->ap[n].bssid.addr, mac, 6);
n++;
}
end:
scan->timeout = 0;
if (scan->running) {
// running == false means it was discarded (timeout)
scan->running = false;
xSemaphoreGive(cDATA->scanSem);
// Send event scan finished
EventInfo eventInfo;
memset(&eventInfo, 0, sizeof(EventInfo));
eventInfo.wifi_scan_done.status = 0;
eventInfo.wifi_scan_done.number = scan->count;
pWiFi->postEvent(ARDUINO_EVENT_WIFI_SCAN_DONE, eventInfo);
}
// wifi_manager_ap_list_update_enable(LN_TRUE);
// wifi_sta_disconnect();
LT_HEAP_I();
return;
}
int16_t WiFiClass::scanNetworks(bool async, bool showHidden, bool passive, uint32_t maxMsPerChannel, uint8_t channel) {
if (scan && scan->running) {
if (scan->timeout && millis() > scan->timeout) {
LT_WM(WIFI, "Scan timeout, discarding");
scan->running = false;
} else {
return WIFI_SCAN_RUNNING;
}
}
enableSTA(true);
scanDelete();
scanInit();
LT_IM(WIFI, "Starting WiFi scan");
__wrap_ln_printf_disable();
wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_SCAN_COMPLETE, &scanHandler);
wifi_manager_ap_list_update_enable(LN_TRUE);
wifi_manager_cleanup_scan_results();
wifi_scan_cfg_t scan_cfg = {
.channel = 0,
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time = 5,
};
wifi_sta_scan(&scan_cfg);
LT_HEAP_I();
scan->running = true;
scan->timeout = millis() + maxMsPerChannel * 20 + 1000;
int16_t ret = WIFI_SCAN_RUNNING;
if (!async) {
LT_IM(WIFI, "Waiting for results");
xSemaphoreTake(DATA->scanSem, 1); // reset the semaphore quickly
xSemaphoreTake(DATA->scanSem, pdMS_TO_TICKS(maxMsPerChannel * 20));
if (scan->running) {
scanDelete();
ret = WIFI_SCAN_FAILED;
goto exit;
}
ret = scan->count;
goto exit;
}
exit:
__wrap_ln_printf_enable();
return ret;
}

View File

@@ -0,0 +1,295 @@
/* Copyright (c) Etienne Le Cousin 2025-01-19. */
#include "wiring_private.h"
#include <sdk_private.h>
#define I2C_PRIV i2c_init_t_def
#include "Wire.h"
// Functions from I2C demo of SDK
static uint8_t hal_i2c_master_7bit_write(uint32_t i2c_x_base, uint8_t dev_addr, const uint8_t *buf, uint16_t buf_len);
static uint8_t hal_i2c_master_7bit_read(uint32_t i2c_x_base, uint8_t dev_addr, uint8_t *buf, uint16_t buf_len);
#ifdef PIN_WIRE0_SDA
// Wire object associated to I2C0 interface.
TwoWire Wire(PIN_WIRE0_SDA, PIN_WIRE0_SCL);
#endif
TwoWire::TwoWire(int8_t sda, int8_t scl) {
_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;
uint32_t pin_sda = pinInfo(sda)->gpio;
uint32_t pin_scl = pinInfo(scl)->gpio;
hal_gpio_pin_afio_select(GPIO_GET_BASE(pin_sda), GPIO_GET_PIN(pin_sda), I2C0_SDA); // TODO: check pin value
hal_gpio_pin_afio_select(GPIO_GET_BASE(pin_scl), GPIO_GET_PIN(pin_scl), I2C0_SCL);
hal_gpio_pin_afio_en(GPIO_GET_BASE(pin_sda), GPIO_GET_PIN(pin_sda), HAL_ENABLE);
hal_gpio_pin_afio_en(GPIO_GET_BASE(pin_scl), GPIO_GET_PIN(pin_scl), HAL_ENABLE);
// 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;
/* disable the i2c */
hal_i2c_en(I2C_BASE, HAL_DISABLE);
_freq = frequency;
_i2c = new i2c_init_t_def;
memset(&_i2c, 0, sizeof(_i2c));
_i2c->i2c_peripheral_clock_freq = 4;
_i2c->i2c_master_mode_sel = I2C_FM_MODE;
_i2c->i2c_fm_mode_duty_cycle = I2C_FM_MODE_DUTY_CYCLE_2;
/*
* TPCLK1 = 1/80MHz = 0.0125us
* Thigh = 9 x CCR x TPCLK1
* Tlow = 16 x CCR x TPCLK1
* Thigh + Tlow = 1/frequency
* ccr = 3.200.000/frequency
*/
_i2c->i2c_ccr = 3200000 / frequency;
_i2c->i2c_trise = 0xF;
hal_i2c_init(I2C_BASE, _i2c);
hal_i2c_en(I2C_BASE, HAL_ENABLE);
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
if (!begin(sda, scl, frequency))
return false;
hal_i2c_slave_set_add_mode(I2C_BASE, I2C_ADD_7BIT_MODE);
hal_i2c_slave_set_add1(I2C_BASE, address);
return true;
}
bool TwoWire::end() {
hal_i2c_deinit();
delete _i2c;
_i2c = NULL;
return true;
}
bool TwoWire::setClock(uint32_t freq) {
if (_i2c) {
_i2c->i2c_ccr = 3200000 / freq;
hal_i2c_init(I2C_BASE, _i2c);
}
_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;
uint8_t *buf = (uint8_t *)malloc(_txBuf.available());
uint8_t i = 0;
while (_txBuf.available()) {
buf[i++] = _txBuf.read_char();
}
if (hal_i2c_master_7bit_write(I2C_BASE, _txAddr, buf, i) == HAL_RESET)
return 4;
free(buf);
_txAddr = 0;
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();
uint8_t *buf = (uint8_t *)malloc(_txBuf.available());
if (hal_i2c_master_7bit_read(I2C_BASE, address, buf, len) == HAL_RESET)
return 0;
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() {}
//--------------------------------------------------------------------------------------------------------------------
#define TIMEOUT_CYCLE 4000
static uint8_t hal_i2c_master_7bit_write(uint32_t i2c_x_base, uint8_t dev_addr, const uint8_t *buf, uint16_t buf_len) {
// 1. check busy
if (hal_i2c_wait_bus_idle(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET) {
hal_i2c_master_reset(i2c_x_base);
return HAL_RESET;
}
// 2. send start
if (hal_i2c_master_start(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET)
return HAL_RESET;
// 3. send addr
hal_i2c_master_send_data(i2c_x_base, dev_addr);
// 4. wait send complete
if (hal_i2c_master_wait_addr(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET)
return HAL_RESET;
// 5. clear addr flag
hal_i2c_clear_sr(i2c_x_base);
// 6. send data
for (uint32_t i = 0; i < buf_len; i++) {
// wait tx empty flag
if (hal_i2c_wait_txe(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET) {
return HAL_RESET;
} else {
hal_i2c_master_send_data(i2c_x_base, buf[i]);
}
}
// 7. wait send complete.
if (hal_i2c_wait_btf(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET)
return HAL_RESET;
// 8. stop the i2c.
hal_i2c_master_stop(i2c_x_base);
return HAL_SET;
}
static uint8_t hal_i2c_master_7bit_read(uint32_t i2c_x_base, uint8_t dev_addr, uint8_t *buf, uint16_t buf_len) {
// 1. check busy
if (hal_i2c_wait_bus_idle(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET) {
hal_i2c_master_reset(i2c_x_base);
return HAL_RESET;
}
// 2. send start
if (hal_i2c_master_start(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET)
return HAL_RESET;
// 3. send addr (+1 is read operation)
hal_i2c_master_send_data(i2c_x_base, dev_addr + 1);
// 4. Wait for an ack after sending the address
if (hal_i2c_master_wait_addr(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET)
return HAL_RESET;
// 5. clear addr flag
hal_i2c_clear_sr(i2c_x_base);
// 6. clear the DR
hal_i2c_master_recv_data(i2c_x_base);
// 7. receive data
for (int i = buf_len; i > 0; i--) {
// when reading the last byte,do not send the ack
if (buf_len == 1) {
// do not send the ack
hal_i2c_ack_en(i2c_x_base, HAL_DISABLE);
/// wait rx not empty
if (hal_i2c_wait_rxne(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET) {
return HAL_RESET;
} else {
*buf = hal_i2c_master_recv_data(i2c_x_base);
}
// read data
} else {
// send ack
hal_i2c_ack_en(i2c_x_base, HAL_ENABLE);
// wait rx not empty
if (hal_i2c_wait_rxne(i2c_x_base, TIMEOUT_CYCLE) == HAL_RESET) {
return HAL_RESET;
} else {
*buf = hal_i2c_master_recv_data(i2c_x_base);
}
}
buf_len--;
buf++;
}
// 8. stop the i2c.
hal_i2c_master_stop(i2c_x_base);
return HAL_SET;
}

View File

@@ -0,0 +1,66 @@
/* Copyright (c) Etienne Le Cousin 2025-01-19. */
#pragma once
#include <Arduino.h>
#include <HardwareI2C.h>
#include <api/RingBuffer.h>
#define Wire1 Wire
#define WIRE_HAS_END 1
#define WIRE_DEFAULT_FREQ 100000
#ifndef I2C_PRIV
#define I2C_PRIV void
#endif
using arduino::RingBuffer;
class TwoWire : public HardwareI2C {
private:
I2C_PRIV *_i2c = NULL;
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

View File

@@ -0,0 +1,27 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
// Provide GPIO names to variant.cpp files
#define LT_VARIANT_INCLUDE "sdk_private.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
extern void vPortClearInterruptMask(uint32_t ulNewMaskValue);
extern uint32_t ulPortSetInterruptMask(void);
// TODO
// #define clockCyclesPerMicrosecond() (SystemCoreClock / 1000000L)
// #define clockCyclesToMicroseconds(a) (a * 1000L / (SystemCoreClock / 1000L))
// #define microsecondsToClockCycles(a) (a * (SystemCoreClock / 1000000L))
#define interrupts() vPortClearInterruptMask(0)
#define noInterrupts() ulPortSetInterruptMask()
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,8 @@
#pragma once
#error "Don't include this file directly"
#define LT_ARD_HAS_WIFI 1
#define LT_ARD_HAS_SERIAL 1
#define LT_ARD_HAS_WIRE 1
#define LT_ARD_MD5_MBEDTLS 1

View File

@@ -0,0 +1,35 @@
/* Copyright (c) Etienne Le Cousin 2024-02-17. */
#include <Arduino.h>
#include <sdk_private.h>
extern "C" {
#define MAIN_TASK_STACK_SIZE 8192
static OS_Thread_t g_mainTask_thread;
bool startMainTask() {
OS_Status ret = OS_ThreadCreate(
&g_mainTask_thread,
"main",
(OS_ThreadEntry_t)mainTask,
NULL,
OS_PRIORITY_BELOW_NORMAL,
MAIN_TASK_STACK_SIZE
);
if (ret != OS_OK)
return false;
OS_ThreadStartScheduler();
return true;
}
void wait_for_debug() {
while (((CoreDebug->DHCSR) & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
asm("nop");
}
delay(1000);
}
} // extern "C"

View File

@@ -0,0 +1,66 @@
/* Copyright (c) Etienne Le Cousin 2024-02-17. */
#include "wiring_private.h"
#ifndef portNVIC_SYSTICK_CURRENT_VALUE_REG
#define portNVIC_SYSTICK_CURRENT_VALUE_REG (*((volatile uint32_t *)0xe000e018))
#endif
void delayMicroseconds(unsigned int us) {
int i;
uint32_t t0, tn;
int dfactor = 20 * us - 10 + (81 * us / 100);
if (us > 100) {
t0 = micros();
do {
tn = micros();
} while (tn >= t0 && tn < (t0 + us - 1));
} else {
for (i = 0; i < dfactor; i++) {
asm("nop");
}
}
}
unsigned long millis(void) {
return (__get_IPSR() == 0) ? xTaskGetTickCount() : xTaskGetTickCountFromISR();
}
unsigned long micros(void) {
uint32_t tick1, tick2;
uint32_t us;
uint32_t tick_per_us = F_CPU / 1000;
if (__get_IPSR() == 0) {
tick1 = xTaskGetTickCount();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCount();
} else {
tick1 = xTaskGetTickCountFromISR();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCountFromISR();
}
if (tick1 == tick2) {
return tick1 * 1000 - us * 1000 / tick_per_us;
} else if ((us * 1000 / tick_per_us) < 500) {
return tick1 * 1000 - us * 1000 / tick_per_us;
} else {
return tick1 * 1000;
}
}
void pinRemoveMode(PinInfo *pin, uint32_t mask) {
PinData *data = pinData(pin);
if ((mask & PIN_GPIO) && (pin->enabled & PIN_GPIO)) {
hal_gpio_pin_direction_set(data->gpio_base, data->gpio->pin, GPIO_INPUT);
free(data->gpio);
pinDisable(pin, PIN_GPIO);
}
if ((mask & PIN_IRQ) && (pin->enabled & PIN_IRQ)) {
data->irqHandler = NULL;
hal_gpio_pin_it_en(data->gpio_base, data->gpio->pin, HAL_DISABLE);
pinDisable(pin, PIN_IRQ);
}
}

View File

@@ -0,0 +1,80 @@
/* Copyright (c) Etienne Le Cousin 2025-01-19. */
#include "wiring_private.h"
#ifndef PIN_ADC1
#define PIN_ADC1 -1
#endif
#ifndef PIN_ADC2
#define PIN_ADC2 -1
#endif
#ifndef PIN_ADC3
#define PIN_ADC3 -1
#endif
#ifndef PIN_ADC4
#define PIN_ADC4 -1
#endif
#ifndef PIN_ADC5
#define PIN_ADC5 -1
#endif
#ifndef PIN_ADC6
#define PIN_ADC6 -1
#endif
#ifndef PIN_ADC7
#define PIN_ADC7 -1
#endif
const uint32_t adc_channels[] = {
-1,
PIN_ADC1,
PIN_ADC2,
PIN_ADC3,
PIN_ADC4,
PIN_ADC5,
PIN_ADC6,
PIN_ADC7,
};
static adc_ch_t pinToAdcCh(uint32_t gpio) {
for (uint8_t i = 0; i < ADC_CH_NUM; i++) {
if (adc_channels[i] == gpio)
return (adc_ch_t)(1 << i);
}
return ADC_CH0;
}
// WARN: adc values are quite bad (zero value of ~ 1000lsb and full scale value ~ 3450lsb)
uint16_t analogReadVoltage(pin_size_t pinNumber) {
uint16_t ret = 0;
pinCheckGetInfo(pinNumber, PIN_ADC, 0);
hal_gpio_pin_mode_set(GPIO_GET_BASE(pin->gpio), pin->gpio, GPIO_MODE_ANALOG);
adc_ch_t ch = pinToAdcCh(pin->gpio);
adc_init_t_def adc_init;
memset(&adc_init, 0, sizeof(adc_init_t_def));
adc_init.adc_ch = ch;
adc_init.adc_conv_mode = ADC_CONV_MODE_CONTINUE;
adc_init.adc_presc = 80;
adc_init.adc_ov_smp_ratio = ADC_OVER_SAMPLING_RATIO_X8;
adc_init.adc_ov_smp_ratio_en = ADC_OVER_SAMPLING_EN_STATUS_BIT0;
hal_adc_init(ADC_BASE, &adc_init);
hal_adc_en(ADC_BASE, HAL_ENABLE);
hal_adc_start_conv(ADC_BASE);
while (hal_adc_get_conv_status(ADC_BASE, ch) == 0)
;
ret = hal_adc_get_data(ADC_BASE, ch);
hal_adc_clr_conv_status(ADC_BASE, ch);
return (uint16_t)(3300UL * ret / 4095);
}
uint16_t analogReadMaxVoltage(pin_size_t pinNumber) {
return 3300;
}

View File

@@ -0,0 +1,27 @@
/* Copyright (c) Etienne Le Cousin 2024-02-17. */
#pragma once
#include <Arduino.h>
#include <sdk_private.h>
#ifdef __cplusplus
extern "C" {
#endif
struct PinData_s {
gpio_init_t_def *gpio;
PinMode gpioMode;
uint32_t gpio_base;
PinStatus irqMode;
void *irqHandler;
void *irqParam;
};
#define GPIO_GET_PORT(pin) (pin >> 4)
#define GPIO_GET_PIN(pin) ((gpio_pin_t)(1 << (pin & 0xF)))
#define GPIO_GET_BASE(pin) (GPIO_GET_PORT(pin) == 1 ? GPIOB_BASE : GPIOA_BASE)
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,67 @@
/* Copyright (c) Etienne Le Cousin 2024-02-17. */
#include "wiring_private.h"
void pinMode(pin_size_t pinNumber, PinMode pinMode) {
pinCheckGetData(pinNumber, PIN_GPIO, );
if (pinEnabled(pin, PIN_GPIO) && data->gpioMode == pinMode)
return;
// GPIO can't be used together with PWM
pinRemoveMode(pin, PIN_PWM);
gpio_init_t_def *gpio = data->gpio;
if (!gpio) {
// allocate memory if pin not used before
data->gpio = gpio = malloc(sizeof(gpio_init_t_def));
pinEnable(pin, PIN_GPIO);
}
gpio->pin = GPIO_GET_PIN(pinNumber);
gpio->speed = GPIO_NORMAL_SPEED;
data->gpio_base = GPIO_GET_BASE(pinNumber);
data->gpioMode = pinMode;
switch (pinMode) {
case INPUT:
gpio->dir = GPIO_INPUT;
gpio->pull = GPIO_PULL_NONE;
break;
case INPUT_PULLDOWN:
gpio->dir = GPIO_INPUT;
gpio->pull = GPIO_PULL_DOWN;
break;
case INPUT_PULLUP:
gpio->dir = GPIO_INPUT;
gpio->pull = GPIO_PULL_UP;
break;
case OUTPUT:
gpio->dir = GPIO_OUTPUT;
gpio->pull = GPIO_PULL_NONE;
break;
case OUTPUT_OPENDRAIN:
gpio->dir = GPIO_INPUT;
gpio->pull = GPIO_PULL_NONE;
break;
default:
return;
}
hal_gpio_init(data->gpio_base, gpio);
}
void digitalWrite(pin_size_t pinNumber, PinStatus status) {
pinCheckGetData(pinNumber, PIN_GPIO, );
pinSetOutputPull(pin, data, pinNumber, status);
if (status)
hal_gpio_pin_set(data->gpio_base, data->gpio->pin);
else
hal_gpio_pin_reset(data->gpio_base, data->gpio->pin);
}
PinStatus digitalRead(pin_size_t pinNumber) {
pinCheckGetData(pinNumber, PIN_GPIO, LOW);
pinSetInputMode(pin, data, pinNumber);
return hal_gpio_pin_input_read(data->gpio_base, data->gpio->pin);
}

View File

@@ -0,0 +1,91 @@
/* Copyright (c) Etienne Le Cousin 2024-03-04. */
#include "wiring_private.h"
void GPIOA_IRQHandler() {
for (pin_size_t pinNumber = 0; pinNumber < 16; pinNumber++) {
gpio_pin_t gpio = GPIO_GET_PIN(pinNumber);
if (hal_gpio_pin_get_it_flag(GPIOA_BASE, gpio) == HAL_SET) {
hal_gpio_pin_clr_it_flag(GPIOA_BASE, gpio);
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
continue;
PinData *data = pinData(pin);
if (!data->irqHandler)
continue;
if (!data->irqParam)
((voidFuncPtr)data->irqHandler)();
else
((voidFuncPtrParam)data->irqHandler)(data->irqParam);
}
}
}
void GPIOB_IRQHandler() {
for (pin_size_t pinNumber = 16; pinNumber < 32; pinNumber++) {
gpio_pin_t gpio = GPIO_GET_PIN(pinNumber);
if (hal_gpio_pin_get_it_flag(GPIOB_BASE, gpio) == HAL_SET) {
hal_gpio_pin_clr_it_flag(GPIOB_BASE, gpio);
PinInfo *pin = pinInfo(pinNumber);
if (!pin)
continue;
PinData *data = pinData(pin);
if (!data->irqHandler)
continue;
if (!data->irqParam)
((voidFuncPtr)data->irqHandler)();
else
((voidFuncPtrParam)data->irqHandler)(data->irqParam);
}
}
}
void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) {
pinCheckGetData(interruptNumber, PIN_IRQ, );
data->irqHandler = callback;
data->irqParam = param;
if (pinEnabled(pin, PIN_IRQ) && data->irqMode == mode)
return;
// GPIO can't be used together with PWM
pinRemoveMode(pin, PIN_PWM);
uint32_t event = 0;
switch (mode) {
case LOW:
LT_W("LOW interrupts not supported, setting FALLING instead");
case FALLING:
event = GPIO_INT_FALLING;
break;
case HIGH:
LT_W("HIGH interrupts not supported, setting RISING instead");
case RISING:
event = GPIO_INT_RISING;
break;
case CHANGE:
event = GPIO_INT_RISING_FALLING;
break;
default:
return;
}
pinEnable(pin, PIN_IRQ);
data->irqMode = mode;
hal_gpio_pin_it_cfg(data->gpio_base, data->gpio->pin, event);
hal_gpio_pin_it_en(data->gpio_base, data->gpio->pin, HAL_ENABLE);
}
void detachInterrupt(pin_size_t interruptNumber) {
pinCheckGetData(interruptNumber, PIN_IRQ, );
hal_gpio_pin_it_en(data->gpio_base, data->gpio->pin, HAL_DISABLE);
pinModeRemove(interruptNumber, PIN_IRQ);
}

View File

@@ -0,0 +1,52 @@
#ifndef _PINNAMES_H_
#define _PINNAMES_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <hal/hal_gpio.h>
typedef enum {
PORT_A = 0,
PORT_B = 1,
} ln_PinPort;
typedef enum {
PA_0 = 0,
PA_1,
PA_2,
PA_3,
PA_4,
PA_5,
PA_6,
PA_7,
PA_8,
PA_9,
PA_10,
PA_11,
PA_12,
PA_13,
PA_14,
PA_15,
PB_0,
PB_1,
PB_2,
PB_3,
PB_4,
PB_5,
PB_6,
PB_7,
PB_8,
PB_9,
// Not connected
NC = -1
} ln_PinName;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Etienne Le Cousin 2024-02-24. */
#include <libretiny.h>
#include <sdk_private.h>
const char *lt_cpu_get_core_type() {
return "ARM Cortex-M4F (ARMv7E-M)";
}

View File

@@ -0,0 +1,27 @@
/* Copyright (c) Etienne Le Cousin 2024-03-03. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_get_device_mac(uint8_t *mac) {
ln_fotp_get_mac_val(mac);
}
void lt_reboot() {
ln_chip_reboot();
while (1) {}
}
lt_reboot_reason_t lt_get_reboot_reason() {
chip_reboot_cause_t reason = ln_chip_get_reboot_cause();
switch (reason) {
case CHIP_REBOOT_POWER_ON:
return REBOOT_REASON_POWER;
case CHIP_REBOOT_SOFTWARE:
return REBOOT_REASON_SOFTWARE;
case CHIP_REBOOT_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
default:
return REBOOT_REASON_UNKNOWN;
}
}

View File

@@ -0,0 +1,15 @@
/* Copyright (c) Etienne Le Cousin 2024-03-03. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t hal_flash_read_id(void);
lt_flash_id_t lt_flash_get_id() {
lt_flash_id_t id;
uint32_t fl_id = hal_flash_read_id();
id.manufacturer_id = (uint8_t)(fl_id >> 16);
id.chip_id = (uint8_t)(fl_id >> 8);
id.chip_size_id = (uint8_t)(fl_id >> 0);
return id;
}

View File

@@ -0,0 +1,58 @@
/* Copyright (c) Etienne Le Cousin 2024-02-24. */
#include <libretiny.h>
#include <sdk_private.h>
extern uint8_t uart_print_port;
extern Serial_t m_LogSerial;
static void lt_init_log(void) {
// default LT print port
uart_print_port = LT_UART_DEFAULT_LOGGER;
// default SDK print port
serial_init(&m_LogSerial, LT_UART_DEFAULT_PORT, CFG_UART_BAUDRATE_LOG, NULL);
}
void lt_init_family() {
// 0. check reboot cause
ln_chip_get_reboot_cause();
// 1. sys clock,interrupt
SetSysClock();
set_interrupt_priority();
switch_global_interrupt(HAL_ENABLE);
ln_runtime_measure_init();
// 2. register os heap mem
OS_DefineHeapRegions();
// 3. log init
lt_init_log();
cm_backtrace_init("LibreTiny - LN882H", "HW_V1.0", "SW_V1.0");
if (NVDS_ERR_OK != ln_nvds_init(FLASH_NVDS_OFFSET)) {
LT_E("NVDS init failed!");
}
if (KV_ERR_NONE != ln_kv_port_init(FLASH_KV_OFFSET, (FLASH_KV_OFFSET + FLASH_KV_LENGTH))) {
LT_E("KV init failed!");
}
// init system parameter
sysparam_integrity_check_all();
ln_pm_sleep_mode_set(ACTIVE);
// ln_pm_always_clk_disable_select(CLK_G_I2S | CLK_G_WS2811 | CLK_G_SDIO);
/*ln_pm_always_clk_disable_select(CLK_G_I2S | CLK_G_WS2811 | CLK_G_SDIO | CLK_G_AES);
ln_pm_lightsleep_clk_disable_select(CLK_G_GPIOA | CLK_G_GPIOB | CLK_G_SPI0 | CLK_G_SPI1 | CLK_G_I2C0 |
CLK_G_UART1 | CLK_G_UART2 | CLK_G_WDT | CLK_G_TIM1 | CLK_G_TIM2 | CLK_G_MAC |
CLK_G_DMA | CLK_G_RF | CLK_G_ADV_TIMER| CLK_G_TRNG);*/
}
void lt_init_arduino() {
#if LT_AUTO_DOWNLOAD_REBOOT && LT_ARD_HAS_SERIAL && LT_HW_UART0
// initialize auto-download-reboot parser
Serial0.begin(115200);
#endif
}

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Etienne Le Cousin 2024-02-24. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t lt_ram_get_size() {
return 296 * 1024;
}

View File

@@ -0,0 +1,45 @@
/* Copyright (c) Etienne Le Cousin 2024-12-21. */
#include <libretiny.h>
#include <ota_image.h>
#include <sdk_private.h>
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_SINGLE;
}
bool lt_ota_is_valid(uint8_t index) {
image_hdr_t ota_header;
if (OTA_ERR_NONE != image_header_fast_read(FLASH_OTA_OFFSET, &ota_header)) {
return false;
}
if (OTA_ERR_NONE != image_header_verify(&ota_header)) {
return false;
}
if (OTA_ERR_NONE != image_body_verify(FLASH_OTA_OFFSET, &ota_header)) {
return false;
}
return true;
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
void lt_ota_set_write_protect(uf2_ota_t *uf2) {
LT_DM(OTA, "lt_ota_set_write_protect");
ln_nvds_set_ota_upg_state(UPG_STATE_DOWNLOAD_ING);
}
bool lt_ota_switch(bool revert) {
LT_DM(OTA, "lt_ota_switch(%d)", revert);
ln_nvds_set_ota_upg_state(UPG_STATE_DOWNLOAD_OK);
return true;
}

View File

@@ -0,0 +1,43 @@
/* Copyright (c) Etienne Le Cousin 2025-01-19. */
#include <libretiny.h>
#include <sdk_private.h>
bool lt_wdt_enable(uint32_t timeout) {
wdt_top_value_t wdt_top_value = 0;
for (uint8_t i = 0; i < 15; i++) {
if ((0x100UL << i) < (timeout * 32768 / 1000))
wdt_top_value = i;
}
wdt_init_t_def wdt_init;
memset(&wdt_init, 0, sizeof(wdt_init));
wdt_init.wdt_rmod = WDT_RMOD_1;
wdt_init.wdt_rpl = WDT_RPL_32_PCLK;
wdt_init.top = wdt_top_value;
hal_wdt_init(WDT_BASE, &wdt_init);
/* Configure Interrupt */
NVIC_SetPriority(WDT_IRQn, 4);
NVIC_EnableIRQ(WDT_IRQn);
/* Watchdog enable */
/* Note : Watchdog cannot be disabled */
hal_wdt_en(WDT_BASE, HAL_ENABLE);
/* Start feeding */
hal_wdt_cnt_restart(WDT_BASE);
return true;
}
void lt_wdt_disable() {
hal_wdt_deinit();
hal_wdt_en(WDT_BASE, HAL_DISABLE);
}
void lt_wdt_feed() {
hal_wdt_cnt_restart(WDT_BASE);
}

View File

@@ -0,0 +1,31 @@
/* Copyright (c) Etienne Le Cousin 2024-03-10. */
#pragma once
#include_next "lwipopts.h"
#if (!defined(LWIP_IPV4) || LWIP_IPV4) && !LWIP_IPV6
#define ip_addr ip4_addr // LwIP 2.0.x compatibility
#define ip_addr_t ip4_addr_t // LwIP 2.0.x compatibility
#endif
#if !LWIP_IPV4 && LWIP_IPV6
#define ip_addr ip6_addr // LwIP 2.0.x compatibility
#define ip_addr_t ip6_addr_t // LwIP 2.0.x compatibility
#endif
#define in_addr_t u32_t
#define IN_ADDR_T_DEFINED 1
#ifndef INT_MAX
#define INT_MAX 2147483647 // for RECV_BUFSIZE_DEFAULT
#endif
#define LWIP_NUM_NETIF_CLIENT_DATA 1
#define LWIP_NETIF_EXT_STATUS_CALLBACK 1
#undef MEMP_NUM_UDP_PCB
#define MEMP_NUM_UDP_PCB 7
// LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define
// LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)
#define LWIP_COMPAT_MUTEX_ALLOWED 1
#define LWIP_TCPIP_TIMEOUT 1

View File

@@ -0,0 +1,82 @@
#ifndef _PROJ_CONFIG_H_
#define _PROJ_CONFIG_H_
#define DISABLE (0)
#define ENABLE (1)
#define __CONFIG_OS_KERNEL RTOS_FREERTOS
/*
* Clock settings section
* Note:
*
*/
#define XTAL_CLOCK (40000000)
#define RCO_CLOCK (32000)
#define PLL_CLOCK (160000000)
// TODO: SystemCoreClock
#define SYSTEM_CLOCK (160000000)
/*
* Module enable/disable control
*/
#define FLASH_XIP ENABLE
#define LN_ASSERT_EN ENABLE
#define HAL_ASSERT_EN ENABLE
#define PRINTF_OMIT DISABLE // when release software, set 1 to omit all printf logs
#define OS_TICK_COMPENSATE
// Check big and endian mode
#if defined(__CC_ARM)
#if defined(__BIG_ENDIAN)
#error "Please set the compiler to little-endian mode"
#endif
#elif defined(__GNUC__)
#if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__)
#error "Please set the compiler to little-endian mode"
#endif // __BYTE_ORDER__
#else
#error "Unsupported compiler"
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
/*
* flash image settings
*/
#define FLASH_IMAGE_VER_MAJOR 0
#define FLASH_IMAGE_VER_MINOR 1
#define SOC_CRP_FLAG 0
/*
* Hardware config
*/
#define CFG_UART0_TX_BUF_SIZE 256
#define CFG_UART0_RX_BUF_SIZE 256
#define CFG_UART1_TX_BUF_SIZE 128
#define CFG_UART1_RX_BUF_SIZE 256
#define CFG_UART2_TX_BUF_SIZE 128
#define CFG_UART2_RX_BUF_SIZE 256
#define CFG_UART_BAUDRATE_LOG 115200
#define CFG_UART_BAUDRATE_CONSOLE 115200
/*
* FreeRTOS config
*/
#define configTOTAL_HEAP_SIZE ((size_t)(160 * 1024))
/*
* log
*/
#ifndef __cplusplus
#define hexdump ln_hexdump
#endif // __cplusplus
#define mbedtls_platform_set_calloc_free(calloc, free)
#endif /* _PROJ_CONFIG_H_ */

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -0,0 +1,4 @@
#pragma once
#include "queue.h"

View File

@@ -0,0 +1,518 @@
#include "proj_config.h"
#include "hal/hal_uart.h"
#include "serial/ln_serial.h"
#include "serial_hw.h"
#include "utils/debug/ln_assert.h"
#include "hal/hal_gpio.h"
#include "hal/hal_misc.h"
#include "reg_sysc_cmp.h"//gpio fullmux
#define UART0_TX_BUF_SIZE CFG_UART0_TX_BUF_SIZE
#define UART0_RX_BUF_SIZE CFG_UART0_RX_BUF_SIZE
#define UART1_TX_BUF_SIZE CFG_UART1_TX_BUF_SIZE
#define UART1_RX_BUF_SIZE CFG_UART1_RX_BUF_SIZE
#define UART2_TX_BUF_SIZE CFG_UART2_TX_BUF_SIZE
#define UART2_RX_BUF_SIZE CFG_UART2_RX_BUF_SIZE
/* TX and RX fifo buffer */
uint8_t uart0_txbuf[UART0_TX_BUF_SIZE];
uint8_t uart0_rxbuf[UART0_RX_BUF_SIZE];
uint8_t uart1_txbuf[UART1_TX_BUF_SIZE];
uint8_t uart1_rxbuf[UART1_RX_BUF_SIZE];
uint8_t uart2_txbuf[UART2_TX_BUF_SIZE];
uint8_t uart2_rxbuf[UART2_RX_BUF_SIZE];
/* From the high-level serial driver */
extern Serial_t serial_handles[SER_PORT_NUM];
/* UART device*/
typedef struct
{
uint32_t uart_base;
uart_init_t_def init_cfg;
} uart_dev_t;
static uart_dev_t g_uart0;
static uart_dev_t g_uart1;
static uart_dev_t g_uart2;
/* serial */
typedef struct
{
struct SerialHardware Hardware;
struct Serial *serial;
} ln_serial_t;
ln_serial_t uart_serial[SER_PORT_NUM];
static void uart_io_pin_request(struct Serial *serial)
{
if (serial->port_id == SER_PORT_UART0)
{
hal_gpio_pin_afio_select(GPIOA_BASE,GPIO_PIN_2,UART0_TX);
hal_gpio_pin_afio_select(GPIOA_BASE,GPIO_PIN_3,UART0_RX);
hal_gpio_pin_afio_en(GPIOA_BASE,GPIO_PIN_2,HAL_ENABLE);
hal_gpio_pin_afio_en(GPIOA_BASE,GPIO_PIN_3,HAL_ENABLE);
}
else if (serial->port_id == SER_PORT_UART1)
{
hal_gpio_pin_afio_select(GPIOB_BASE,GPIO_PIN_8,UART1_RX);
hal_gpio_pin_afio_select(GPIOB_BASE,GPIO_PIN_9,UART1_TX);
hal_gpio_pin_afio_en(GPIOB_BASE,GPIO_PIN_8,HAL_ENABLE);
hal_gpio_pin_afio_en(GPIOB_BASE,GPIO_PIN_9,HAL_ENABLE);
}
else if (serial->port_id == SER_PORT_UART2)
{
}
}
static void uart_io_pin_release(struct Serial *serial)
{
if (serial == NULL)
{
return;
}
if (serial->port_id == SER_PORT_UART0)
{
hal_gpio_pin_afio_en(GPIOA_BASE,GPIO_PIN_2,HAL_DISABLE);
hal_gpio_pin_afio_en(GPIOA_BASE,GPIO_PIN_3,HAL_DISABLE);
}
else if (serial->port_id == SER_PORT_UART1)
{
hal_gpio_pin_afio_en(GPIOB_BASE,GPIO_PIN_8,HAL_DISABLE);
hal_gpio_pin_afio_en(GPIOB_BASE,GPIO_PIN_9,HAL_DISABLE);
}
else if (serial->port_id == SER_PORT_UART2)
{
}
}
static void hw_uart0_init(struct SerialHardware *_hw, struct Serial *serial, uint32_t baudrate)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw && serial);
hw = (ln_serial_t *)_hw;
hw->serial = serial;
g_uart0.uart_base = UART0_BASE;
g_uart0.init_cfg.baudrate = baudrate;//115200 921600 2000000
g_uart0.init_cfg.word_len = UART_WORD_LEN_8;
g_uart0.init_cfg.parity = UART_PARITY_NONE;
g_uart0.init_cfg.stop_bits = UART_STOP_BITS_1;
g_uart0.init_cfg.over_sampl= UART_OVER_SAMPL_8;
hal_uart_init(g_uart0.uart_base, &g_uart0.init_cfg);
hal_uart_rx_mode_en(g_uart0.uart_base, HAL_ENABLE);
hal_uart_tx_mode_en(g_uart0.uart_base, HAL_ENABLE);
hal_uart_en(g_uart0.uart_base, HAL_ENABLE);
hal_uart_it_en(g_uart0.uart_base, USART_IT_RXNE);
//uart_it_enable(g_uart0.uart_base, USART_IT_TXE);//uart_it_enable(g_uart0.uart_base, USART_IT_TXE);
NVIC_EnableIRQ(UART0_IRQn);
//request pin for uart
uart_io_pin_request(hw->serial);
}
static void hw_uart1_init(struct SerialHardware *_hw, struct Serial *serial, uint32_t baudrate)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw && serial);
hw = (ln_serial_t *)_hw;
hw->serial = serial;
g_uart1.uart_base = UART1_BASE;
g_uart1.init_cfg.baudrate = baudrate;//115200 921600 2000000
g_uart1.init_cfg.word_len = UART_WORD_LEN_8;
g_uart1.init_cfg.parity = UART_PARITY_NONE;
g_uart1.init_cfg.stop_bits = UART_STOP_BITS_1;
g_uart1.init_cfg.over_sampl= UART_OVER_SAMPL_8;
hal_uart_init(g_uart1.uart_base, &g_uart1.init_cfg);
hal_uart_rx_mode_en(g_uart1.uart_base, HAL_ENABLE);
hal_uart_tx_mode_en(g_uart1.uart_base, HAL_ENABLE);
hal_uart_en(g_uart1.uart_base, HAL_ENABLE);
hal_uart_it_en(g_uart1.uart_base, USART_IT_RXNE);
//uart_it_enable(g_uart1.uart_base, USART_IT_TXE);
NVIC_EnableIRQ(UART1_IRQn);
//request pin for uart
uart_io_pin_request(hw->serial);
}
static void hw_uart2_init(struct SerialHardware *_hw, struct Serial *serial, uint32_t baudrate)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw && serial);
hw = (ln_serial_t *)_hw;
hw->serial = serial;
g_uart2.uart_base = UART2_BASE;
g_uart2.init_cfg.baudrate = baudrate;//115200 921600 2000000
g_uart2.init_cfg.word_len = UART_WORD_LEN_8;
g_uart2.init_cfg.parity = UART_PARITY_NONE;
g_uart2.init_cfg.stop_bits = UART_STOP_BITS_1;
g_uart2.init_cfg.over_sampl= UART_OVER_SAMPL_8;
hal_uart_init(g_uart2.uart_base, &g_uart2.init_cfg);
hal_uart_rx_mode_en(g_uart2.uart_base, HAL_ENABLE);
hal_uart_tx_mode_en(g_uart2.uart_base, HAL_ENABLE);
hal_uart_en(g_uart2.uart_base, HAL_ENABLE);
hal_uart_it_en(g_uart2.uart_base, USART_IT_RXNE);
//uart_it_enable(g_uart2.uart_base, USART_IT_TXE);
NVIC_EnableIRQ(UART2_IRQn);
//request pin for uart
uart_io_pin_request(hw->serial);
}
static void hw_uart0_cleanup(struct SerialHardware *_hw)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw);
hal_misc_reset_uart0();
NVIC_ClearPendingIRQ(UART0_IRQn);
NVIC_DisableIRQ(UART0_IRQn);
hw = (ln_serial_t *)_hw;
uart_io_pin_release(hw->serial);
hw->serial = NULL; // must be reset to NULL
}
static void hw_uart1_cleanup(struct SerialHardware *_hw)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw);
hal_misc_reset_uart1();
NVIC_ClearPendingIRQ(UART1_IRQn);
NVIC_DisableIRQ(UART1_IRQn);
hw = (ln_serial_t *)_hw;
uart_io_pin_release(hw->serial);
hw->serial = NULL; // must be reset to NULL
}
static void hw_uart2_cleanup(struct SerialHardware *_hw)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw);
hal_misc_reset_uart2();
NVIC_ClearPendingIRQ(UART2_IRQn);
NVIC_DisableIRQ(UART2_IRQn);
hw = (ln_serial_t *)_hw;
uart_io_pin_release(hw->serial);
hw->serial = NULL; // must be reset to NULL
}
static void hw_uart_tx_start_polling(struct SerialHardware * _hw)
{
uint8_t ch;
ln_serial_t *hw = NULL;
uart_dev_t * pdev;
LN_ASSERT(_hw);
hw = (ln_serial_t *)_hw;
while(!fifo_isempty(&hw->serial->txfifo))
{
ch = fifo_pop(&hw->serial->txfifo);
pdev = (uart_dev_t *)hw->Hardware.hw_device;
while (hal_uart_flag_get(pdev->uart_base, USART_FLAG_TXE) != HAL_SET) {};
//while (uart_flag_get(pdev->uart_base, USART_FLAG_TX_FIFO_FULL) == HAL_SET) {};
hal_uart_send_data(pdev->uart_base, ch);
}
}
#if 0
static void hw_uart_tx_start_isr(struct SerialHardware * _hw)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw);
hw = (ln_serial_t *)_hw;
if (hw->Hardware.isSending){
return;
}
if(!fifo_isempty(&hw->serial->txfifo))
{
hw->Hardware.isSending = LN_TRUE;
/* Enable TX empty interrupts. */
uart_it_enable(UART0_BASE, USART_IT_TXE);
}
}
#endif
static int8_t hw_uart_tx_is_sending(struct SerialHardware * _hw)
{
ln_serial_t *hw = NULL;
LN_ASSERT(_hw);
hw = (ln_serial_t *)_hw;
return hw->Hardware.isSending;
}
static int8_t hw_uart_set_baudrate(struct SerialHardware * _hw, uint32_t baudrate)
{
ln_serial_t *hw = NULL;
uart_dev_t * pdev;
LN_ASSERT(_hw);
hw = (ln_serial_t *)_hw;
pdev = (uart_dev_t *)hw->Hardware.hw_device;
hal_uart_baudrate_set(pdev->uart_base, baudrate);
return LN_TRUE;
}
/*
* High-level interface data structures.
*/
static const struct SerialHardwareVT uart0_vtable =
{
.init = hw_uart0_init,
.cleanup = hw_uart0_cleanup,
.txStart = hw_uart_tx_start_polling,//hw_uart_tx_start_polling,//hw_uart_tx_start_isr
.txSending = hw_uart_tx_is_sending,
.setBaudrate = hw_uart_set_baudrate,
};
static const struct SerialHardwareVT uart1_vtable =
{
.init = hw_uart1_init,
.cleanup = hw_uart1_cleanup,
.txStart = hw_uart_tx_start_polling,//hw_uart_tx_start_isr
.txSending = hw_uart_tx_is_sending,
.setBaudrate = hw_uart_set_baudrate,
};
static const struct SerialHardwareVT uart2_vtable =
{
.init = hw_uart2_init,
.cleanup = hw_uart2_cleanup,
.txStart = hw_uart_tx_start_polling,//hw_uart_tx_start_isr
.txSending = hw_uart_tx_is_sending,
.setBaudrate = hw_uart_set_baudrate,
};
ln_serial_t uart_serial[SER_PORT_NUM] =
{
{
.Hardware =
{
.table = &uart0_vtable,
.txbuffer = uart0_txbuf,
.rxbuffer = uart0_rxbuf,
.txbuffer_size = sizeof(uart0_txbuf),
.rxbuffer_size = sizeof(uart0_rxbuf),
.hw_device = (void *)&g_uart0,
.isSending = LN_FALSE,
},
.serial = NULL,
},
{
.Hardware =
{
.table = &uart1_vtable,
.txbuffer = uart1_txbuf,
.rxbuffer = uart1_rxbuf,
.txbuffer_size = sizeof(uart1_txbuf),
.rxbuffer_size = sizeof(uart1_rxbuf),
.hw_device = (void *)&g_uart1,
.isSending = LN_FALSE,
},
.serial = NULL,
},
{
.Hardware =
{
.table = &uart2_vtable,
.txbuffer = uart2_txbuf,
.rxbuffer = uart2_rxbuf,
.txbuffer_size = sizeof(uart2_txbuf),
.rxbuffer_size = sizeof(uart2_rxbuf),
.hw_device = (void *)&g_uart2,
.isSending = LN_FALSE,
},
.serial = NULL,
},
};
struct SerialHardware *serial_hw_getdesc(serial_port_id_t port_id)
{
LN_ASSERT(port_id < SER_PORT_NUM);
return (struct SerialHardware *)&uart_serial[port_id].Hardware;
}
///=====================UART0/1/2 IQR Handle===============================///
static inline void uart0_send_data_isr(void)
{
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART0];
uint8_t tx_char = 0;
if (fifo_isempty(&hw->serial->txfifo))
{
hal_uart_it_disable(UART0_BASE, USART_IT_TXE);
hw->Hardware.isSending = LN_FALSE;
}
else
{
tx_char = fifo_pop(&hw->serial->txfifo);
hal_uart_send_data(UART0_BASE, tx_char);
while (hal_uart_flag_get(UART0_BASE, USART_FLAG_TX_FIFO_FULL) == HAL_SET) {};
}
}
static inline void uart0_recv_data_isr(void)
{
uint8_t ch = 0;
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART0];
while (fifo_isfull(&hw->serial->rxfifo)){
serial_purge_rx(hw->serial);
}
ch = hal_uart_recv_data(UART0_BASE);
fifo_push(&hw->serial->rxfifo, ch);
hw->serial->rx_callback();
}
static inline void serial_uart0_isr_callback(void)
{
if (hal_uart_it_en_status_get(UART0_BASE, USART_IT_RXNE) && \
hal_uart_flag_get(UART0_BASE, USART_FLAG_RXNE)) {
uart0_recv_data_isr();
}
if (hal_uart_it_en_status_get(UART0_BASE, USART_IT_TXE) && \
hal_uart_flag_get(UART0_BASE, USART_FLAG_TXE)) {
uart0_send_data_isr();
}
}
static inline void uart1_send_data_isr(void)
{
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART1];
uint8_t tx_char = 0;
if (fifo_isempty(&hw->serial->txfifo))
{
hal_uart_it_disable(UART1_BASE, USART_IT_TXE);
hw->Hardware.isSending = LN_FALSE;
}
else
{
tx_char = fifo_pop(&hw->serial->txfifo);
hal_uart_send_data(UART1_BASE, tx_char);
while (hal_uart_flag_get(UART1_BASE, USART_FLAG_TX_FIFO_FULL) == HAL_SET) {};
}
}
static inline void uart1_recv_data_isr(void)
{
uint8_t ch = 0;
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART1];
while (fifo_isfull(&hw->serial->rxfifo)){
serial_purge_rx(hw->serial);
}
ch = hal_uart_recv_data(UART1_BASE);
fifo_push(&hw->serial->rxfifo, ch);
hw->serial->rx_callback();
}
static inline void serial_uart1_isr_callback(void)
{
if (hal_uart_it_en_status_get(UART1_BASE, USART_IT_RXNE) && \
hal_uart_flag_get(UART1_BASE, USART_FLAG_RXNE)) {
uart1_recv_data_isr();
}
if (hal_uart_it_en_status_get(UART1_BASE, USART_IT_TXE) && \
hal_uart_flag_get(UART1_BASE, USART_FLAG_TXE)) {
uart1_send_data_isr();
}
}
static inline void uart2_send_data_isr(void)
{
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART2];
uint8_t tx_char = 0;
if (fifo_isempty(&hw->serial->txfifo))
{
hal_uart_it_disable(UART2_BASE, USART_IT_TXE);
hw->Hardware.isSending = LN_FALSE;
}
else
{
tx_char = fifo_pop(&hw->serial->txfifo);
hal_uart_send_data(UART2_BASE, tx_char);
while (hal_uart_flag_get(UART2_BASE, USART_FLAG_TX_FIFO_FULL) == HAL_SET) {};
}
}
static inline void uart2_recv_data_isr(void)
{
uint8_t ch = 0;
ln_serial_t *hw = (ln_serial_t *)&uart_serial[SER_PORT_UART2];
while (fifo_isfull(&hw->serial->rxfifo)){
serial_purge_rx(hw->serial);
}
ch = hal_uart_recv_data(UART2_BASE);
fifo_push(&hw->serial->rxfifo, ch);
hw->serial->rx_callback();
}
static inline void serial_uart2_isr_callback(void)
{
if (hal_uart_it_en_status_get(UART2_BASE, USART_IT_RXNE) && \
hal_uart_flag_get(UART2_BASE, USART_FLAG_RXNE)) {
uart2_recv_data_isr();
}
if (hal_uart_it_en_status_get(UART2_BASE, USART_IT_TXE) && \
hal_uart_flag_get(UART2_BASE, USART_FLAG_TXE)) {
uart2_send_data_isr();
}
}
void UART0_IRQHandler(void)
{
serial_uart0_isr_callback();
}
void UART1_IRQHandler(void)
{
serial_uart1_isr_callback();
}
void UART2_IRQHandler(void)
{
serial_uart2_isr_callback();
}

View File

@@ -0,0 +1,42 @@
#ifndef __SERIAL_HW_H__
#define __SERIAL_HW_H__
#include "hal/hal_uart.h"
#include "ln_types.h"
struct SerialHardware;
struct Serial;
typedef enum {
SER_PORT_UART0 = 0,
SER_PORT_UART1 = 1,
SER_PORT_UART2 = 2,
SER_PORT_NUM = 3, /**< Number of serial ports */
SER_PORT_ID_INVALID = SER_PORT_NUM
}serial_port_id_t;
struct SerialHardwareVT
{
void (*init )(struct SerialHardware *ctx, struct Serial *ser, uint32_t baudrate);
void (*cleanup )(struct SerialHardware *ctx);
void (*txStart )(struct SerialHardware *ctx);
int8_t (*txSending )(struct SerialHardware *ctx);
int8_t (*setBaudrate)(struct SerialHardware *ctx, uint32_t baudrate);
};
struct SerialHardware
{
const struct SerialHardwareVT *table;
unsigned char *txbuffer;
unsigned char *rxbuffer;
size_t txbuffer_size;
size_t rxbuffer_size;
void *hw_device;
volatile int8_t isSending;
};
struct SerialHardware *serial_hw_getdesc(serial_port_id_t port_id);
#endif /* __SERIAL_HW_H__ */

View File

@@ -0,0 +1,262 @@
#include "ln882h.h"
#include "ln_compiler.h"
/*----------------------------------------------------------------------------
Linker generated Symbols
*----------------------------------------------------------------------------*/
extern uint32_t __copysection_ram0_load;
extern uint32_t __copysection_ram0_start;
extern uint32_t __copysection_ram0_end;
extern uint32_t __etext;
extern uint32_t __bss_ram0_start__;
extern uint32_t __bss_ram0_end__;
extern uint32_t __StackTop;
extern uint32_t __retention_start__;
extern uint32_t __retention_end__;
/*----------------------------------------------------------------------------
Exception / Interrupt Handler Function Prototype
*----------------------------------------------------------------------------*/
typedef void( *pFunc )( void );
/*----------------------------------------------------------------------------
External References
*----------------------------------------------------------------------------*/
//extern void _start (void) __attribute__((noreturn)); /* PreeMain (C library entry point) */
extern void lt_main (void);
/*----------------------------------------------------------------------------
Internal References
*----------------------------------------------------------------------------*/
void Reset_Handler (void) ;
__WEAK__ void NMI_Handler (void);
__WEAK__ void HardFault_Handler (void);
__WEAK__ void MemManage_Handler (void);
__WEAK__ void BusFault_Handler (void);
__WEAK__ void UsageFault_Handler (void);
__WEAK__ void SVC_Handler (void);
__WEAK__ void DebugMon_Handler (void);
__WEAK__ void PendSV_Handler (void);
__WEAK__ void SysTick_Handler (void);
__WEAK__ void WDT_IRQHandler (void);
__WEAK__ void EXT_IRQHandler (void);
__WEAK__ void RTC_IRQHandler (void);
__WEAK__ void RFSLP_IRQHandler (void);
__WEAK__ void MAC_IRQHandler (void);
__WEAK__ void BLE_WAKE_IRQHandler (void);
__WEAK__ void BLE_ERR_IRQHandler (void);
__WEAK__ void BLE_MAC_IRQHandler (void);
__WEAK__ void DMA_IRQHandler (void);
__WEAK__ void QSPI_IRQHandler (void);
__WEAK__ void SDIO_F1_IRQHandler (void);
__WEAK__ void SDIO_F2_IRQHandler (void);
__WEAK__ void SDIO_F3_IRQHandler (void);
__WEAK__ void CM4_FPIXC_IRQHandler (void);
__WEAK__ void CM4_FPOFC_IRQHandler (void);
__WEAK__ void CM4_FPUFC_IRQHandler (void);
__WEAK__ void CM4_FPIOC_IRQHandler (void);
__WEAK__ void CM4_FPDZC_IRQHandler (void);
__WEAK__ void CM4_FPIDC_IRQHandler (void);
__WEAK__ void I2C_IRQHandler (void);
__WEAK__ void SPI0_IRQHandler (void);
__WEAK__ void SPI1_IRQHandler (void);
__WEAK__ void UART0_IRQHandler (void);
__WEAK__ void UART1_IRQHandler (void);
__WEAK__ void UART2_IRQHandler (void);
__WEAK__ void ADC_IRQHandler (void);
__WEAK__ void WS_IRQHandler (void);
__WEAK__ void I2S_IRQHandler (void);
__WEAK__ void GPIOA_IRQHandler (void);
__WEAK__ void GPIOB_IRQHandler (void);
__WEAK__ void TIMER0_IRQHandler (void);
__WEAK__ void TIMER1_IRQHandler (void);
__WEAK__ void TIMER2_IRQHandler (void);
__WEAK__ void TIMER3_IRQHandler (void);
__WEAK__ void ADV_TIMER_IRQHandler (void);
__WEAK__ void AES_IRQHandler (void);
__WEAK__ void TRNG_IRQHandler (void);
__WEAK__ void PAOTD_IRQHandler (void);
/*----------------------------------------------------------------------------
User Initial Stack & Heap
*----------------------------------------------------------------------------*/
//<h> Stack Configuration
// <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
//</h>
#define __STACK_SIZE 0x00000600
static uint8_t stack[__STACK_SIZE] __attribute__ ((aligned(8), used, section(".stack")));
#if 0
//<h> Heap Configuration
// <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
//</h>
#define __HEAP_SIZE 0x00004000
#if __HEAP_SIZE > 0
static uint8_t heap[__HEAP_SIZE] __attribute__ ((aligned(8), used, section(".heap")));
#endif
#endif
/*----------------------------------------------------------------------------
Exception / Interrupt Handler
*----------------------------------------------------------------------------*/
/* Exceptions */
__WEAK__ void NMI_Handler (void) { while(1); }
__WEAK__ void HardFault_Handler (void) { while(1); }
__WEAK__ void MemManage_Handler (void) { while(1); }
__WEAK__ void BusFault_Handler (void) { while(1); }
__WEAK__ void UsageFault_Handler (void) { while(1); }
__WEAK__ void SVC_Handler (void) { while(1); }
__WEAK__ void DebugMon_Handler (void) { while(1); }
__WEAK__ void PendSV_Handler (void) { while(1); }
__WEAK__ void SysTick_Handler (void) { while(1); }
__WEAK__ void WDT_IRQHandler (void) { while(1); }
__WEAK__ void EXT_IRQHandler (void) { while(1); }
__WEAK__ void RTC_IRQHandler (void) { while(1); }
__WEAK__ void RFSLP_IRQHandler (void) { while(1); }
__WEAK__ void MAC_IRQHandler (void) { while(1); }
__WEAK__ void BLE_WAKE_IRQHandler (void) { while(1); }
__WEAK__ void BLE_ERR_IRQHandler (void) { while(1); }
__WEAK__ void BLE_MAC_IRQHandler (void) { while(1); }
__WEAK__ void DMA_IRQHandler (void) { while(1); }
__WEAK__ void QSPI_IRQHandler (void) { while(1); }
__WEAK__ void SDIO_F1_IRQHandler (void) { while(1); }
__WEAK__ void SDIO_F2_IRQHandler (void) { while(1); }
__WEAK__ void SDIO_F3_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPIXC_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPOFC_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPUFC_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPIOC_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPDZC_IRQHandler (void) { while(1); }
__WEAK__ void CM4_FPIDC_IRQHandler (void) { while(1); }
__WEAK__ void I2C_IRQHandler (void) { while(1); }
__WEAK__ void SPI0_IRQHandler (void) { while(1); }
__WEAK__ void SPI1_IRQHandler (void) { while(1); }
__WEAK__ void UART0_IRQHandler (void) { while(1); }
__WEAK__ void UART1_IRQHandler (void) { while(1); }
__WEAK__ void UART2_IRQHandler (void) { while(1); }
__WEAK__ void ADC_IRQHandler (void) { while(1); }
__WEAK__ void WS_IRQHandler (void) { while(1); }
__WEAK__ void I2S_IRQHandler (void) { while(1); }
__WEAK__ void GPIOA_IRQHandler (void) { while(1); }
__WEAK__ void GPIOB_IRQHandler (void) { while(1); }
__WEAK__ void TIMER0_IRQHandler (void) { while(1); }
__WEAK__ void TIMER1_IRQHandler (void) { while(1); }
__WEAK__ void TIMER2_IRQHandler (void) { while(1); }
__WEAK__ void TIMER3_IRQHandler (void) { while(1); }
__WEAK__ void ADV_TIMER_IRQHandler (void) { while(1); }
__WEAK__ void AES_IRQHandler (void) { while(1); }
__WEAK__ void TRNG_IRQHandler (void) { while(1); }
__WEAK__ void PAOTD_IRQHandler (void) { while(1); }
/*----------------------------------------------------------------------------
Exception / Interrupt Vector table
*----------------------------------------------------------------------------*/
extern const pFunc __Vectors[240];
const pFunc __Vectors[240] __attribute__ ((section(".vectors"))) = {
(pFunc)(&__StackTop), /* (0x00)Top of Stack */
Reset_Handler, /* (0x04)IRQ -15 Reset Handler */
NMI_Handler, /* (0x08)IRQ -14 NMI Handler */
HardFault_Handler, /* (0x0C)IRQ -13 Hard Fault Handler */
MemManage_Handler, /* (0x10)IRQ -12 MPU Fault Handler */
BusFault_Handler, /* (0x14)IRQ -11 Bus Fault Handler */
UsageFault_Handler, /* (0x18)IRQ -10 Usage Fault Handler */
0, /* (0x1C)IRQ -9 Reserved */
0, /* (0x20)IRQ -8 Reserved */
0, /* (0x24)IRQ -7 Reserved */
0, /* (0x28)IRQ -6 Reserved */
SVC_Handler, /* (0x2C)IRQ -5 SVCall Handler */
DebugMon_Handler, /* (0x30)IRQ -4 Debug Monitor Handler */
0, /* (0x34)IRQ -3 Reserved */
PendSV_Handler, /* (0x38)IRQ -2 PendSV Handler */
SysTick_Handler, /* (0x3C)IRQ -1 SysTick Handler */
/* Interrupts */
WDT_IRQHandler, /* (0x40)IRQ0 */
EXT_IRQHandler, /* (0x44)IRQ1 */
RTC_IRQHandler, /* (0x48)IRQ2 */
RFSLP_IRQHandler, /* (0x4C)IRQ3 */
MAC_IRQHandler, /* (0x50)IRQ4 */
BLE_WAKE_IRQHandler, /* (0x54)IRQ5 */
BLE_ERR_IRQHandler, /* (0x58)IRQ6 */
BLE_MAC_IRQHandler, /* (0x5C)IRQ7 */
DMA_IRQHandler, /* (0x60)IRQ8 */
QSPI_IRQHandler, /* (0x64)IRQ9 */
SDIO_F1_IRQHandler, /* (0x68)IRQ10 */
SDIO_F2_IRQHandler, /* (0x6C)IRQ11 */
SDIO_F3_IRQHandler, /* (0x70)IRQ12 */
CM4_FPIXC_IRQHandler, /* (0x74)IRQ13 */
CM4_FPOFC_IRQHandler, /* (0x78)IRQ14 */
CM4_FPUFC_IRQHandler, /* (0x7C)IRQ15 */
CM4_FPIOC_IRQHandler, /* (0x80)IRQ16 */
CM4_FPDZC_IRQHandler, /* (0x84)IRQ17 */
CM4_FPIDC_IRQHandler, /* (0x88)IRQ18 */
I2C_IRQHandler, /* (0x8C)IRQ19 */
SPI0_IRQHandler, /* (0x90)IRQ20 */
SPI1_IRQHandler, /* (0x94)IRQ21 */
UART0_IRQHandler, /* (0x98)IRQ22 */
UART1_IRQHandler, /* (0x9C)IRQ23 */
UART2_IRQHandler, /* (0xA0)IRQ24 */
ADC_IRQHandler, /* (0xA4)IRQ25 */
WS_IRQHandler, /* (0xA8)IRQ26 */
I2S_IRQHandler, /* (0xAC)IRQ27 */
GPIOA_IRQHandler, /* (0xB0)IRQ28 */
GPIOB_IRQHandler, /* (0xB4)IRQ29 */
TIMER0_IRQHandler, /* (0xB8)IRQ30 */
TIMER1_IRQHandler, /* (0xBC)IRQ31 */
TIMER2_IRQHandler, /* (0xC0)IRQ32 */
TIMER3_IRQHandler, /* (0xC4)IRQ33 */
ADV_TIMER_IRQHandler, /* (0xC8)IRQ34 */
AES_IRQHandler, /* (0xCC)IRQ35 */
TRNG_IRQHandler, /* (0xD0)IRQ36 */
PAOTD_IRQHandler, /* (0xD4)IRQ37 */
};
/*----------------------------------------------------------------------------
Reset Handler called on controller reset
*----------------------------------------------------------------------------*/
void Reset_Handler(void) {
uint32_t *pSrc, *pDest;
uint32_t *pTable __attribute__((unused));
/* Firstly it copies data from read only memory to RAM.
* There are two schemes to copy. One can copy more than one sections.
* Another can copy only one section. The former scheme needs more
* instructions and read-only data to implement than the latter.
* Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes.
*/
pSrc = &__copysection_ram0_load;
pDest = &__copysection_ram0_start;
for ( ; (pDest < &__copysection_ram0_end); ) {
*pDest++ = *pSrc++;
}
/* Single BSS section scheme.
*
* The BSS section is specified by following symbols
* __bss_start__: start of the BSS section.
* __bss_end__: end of the BSS section.
*
* Both addresses must be aligned to 4 bytes boundary.
*/
pDest = &__bss_ram0_start__;
for ( ; pDest < &__bss_ram0_end__ ; ) {
*pDest++ = 0UL;
}
pDest = &__retention_start__;
for ( ; pDest < &__retention_end__ ; ) {
*pDest++ = 0UL;
}
SystemInit(); /* CMSIS System Initialization */
lt_main();
}

View File

@@ -0,0 +1,16 @@
#ifndef __cplusplus
#undef log_printf
#define __wrap_sprintf __wrap_ln_sprintf
#include_next "utils/debug/log.h"
#undef __wrap_sprintf
#undef log_printf
#define log_printf(...) __wrap_ln_printf(__VA_ARGS__)
#define __sprintf(tag, fct, ...) __wrap_ln_printf(tag);__wrap_ln_vprintf(__VA_ARGS__)
// Redefine LOG_LVL_CTRL
#undef LOG_LVL_CTRL
#define LOG_LVL_CTRL LOG_LVL_DEBUG
#endif //__cplusplus */

View File

@@ -0,0 +1,19 @@
#pragma once
#error "Don't include this file directly"
#define LT_HAS_FLASH 1
#define LT_HAS_FREERTOS 1
#define LT_HAS_LWIP 1
#define LT_HAS_LWIP2 1
#define LT_HAS_MBEDTLS 1
#define LT_HAS_OTA 1
#define LT_HAS_PRINTF 1
#define LT_HW_DEEP_SLEEP 0
#define LT_HW_WATCHDOG 1
#define LT_HW_WIFI 1
#define LT_REMALLOC 1
#define LT_HEAP_FUNC xPortGetFreeHeapSize
#define LT_REALLOC_FUNC pvPortReAlloc

View File

@@ -0,0 +1,18 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
#include <lt_pins.h>
// Choose the main UART output port
#ifndef LT_UART_DEFAULT_PORT
#if LT_HW_UART0
#define LT_UART_DEFAULT_PORT 0
#elif LT_HW_UART1
#define LT_UART_DEFAULT_PORT 1
#elif LT_HW_UART2
#define LT_UART_DEFAULT_PORT 2
#else
#error "No serial port is available"
#endif
#endif

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#include <libretiny.h>
#include <sdk_private.h>
#include <fal.h>
#include <hal/hal_flash.h>
#define FLASH_ERASE_MIN_SIZE (4 * 1024)
static int init() {
return 0;
}
static int read(long offset, uint8_t *buf, size_t size) {
hal_flash_read(offset, size, buf);
return size;
}
static int write(long offset, const uint8_t *buf, size_t size) {
hal_flash_program(offset, size, (uint8_t *)buf);
return size;
}
static int erase(long offset, size_t size) {
offset &= ~(FLASH_ERASE_MIN_SIZE - 1);
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
hal_flash_erase(offset, size * FLASH_ERASE_MIN_SIZE);
return size * FLASH_ERASE_MIN_SIZE;
}
const struct fal_flash_dev flash0 = {
.name = FAL_FLASH_DEV_NAME,
.addr = 0x0,
.len = FLASH_LENGTH,
.blk_size = FLASH_ERASE_MIN_SIZE,
.ops = {init, read, write, erase},
.write_gran = 1,
};

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Etienne Le Cousin 2024-02-19. */
#include <libretiny.h>
#include <sdk_private.h>
#include <printf/printf.h>
extern Serial_t *serial_handles[SER_PORT_NUM];
uint8_t uart_print_port = LT_UART_DEFAULT_LOGGER;
void putchar_(char c) {
putchar_p(c, uart_print_port);
}
void putchar_p(char c, unsigned long port) {
serial_putchar(serial_handles[port], c);
}
WRAP_PRINTF(ln_printf);
WRAP_VPRINTF(ln_vprintf);

View File

@@ -0,0 +1,15 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
#include <printf_config.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
WRAP_DISABLE_DEF(ln_printf);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,87 @@
/* Copyright (c) Etienne Le Cousin 2024-02-10. */
#pragma once
#ifdef __cplusplus
// Fix to not include SDK reg_xx.h files in C++
// These files declare inline functions with unallowed volatile assignments in C++
#define __REG_CACHE_H__
#define __REG_GPIO_H__
#define __REG_I2C_H__
#define __REG_LN_UART_H__
#define __REG_QSPI_H__
#define __REG_WDT_H__
#define __REG_SYSC_AWO_H__
#define __REG_SYSC_CMP_H__
extern "C" {
#endif // __cplusplus
// PinNames
#include "PinNames.h"
// undefine ROM stdio in favor of printf() library (wrappers)
#undef printf
#undef sprintf
#undef vsprintf
#undef snprintf
#undef vsnprintf
#undef vprintf
#include <stdio.h>
// Conflicting types
#define WIFI_MODE_STATION LN_WIFI_MODE_STATION
#define WIFI_MODE_AP LN_WIFI_MODE_AP
#define WIFI_MODE_AP_STATION LN_WIFI_MODE_AP_STATION
#define WIFI_MODE_MAX LN_WIFI_MODE_MAX
#define WIFI_AUTH_OPEN LN_WIFI_AUTH_OPEN
#define WIFI_AUTH_WEP LN_WIFI_AUTH_WEP
#define WIFI_AUTH_WPA_PSK LN_WIFI_AUTH_WPA_PSK
#define WIFI_AUTH_WPA2_PSK LN_WIFI_AUTH_WPA2_PSK
#define WIFI_AUTH_WPA_WPA2_PSK LN_WIFI_AUTH_WPA_WPA2_PSK
#define WIFI_AUTH_WPA2_ENTERPRISE LN_WIFI_AUTH_WPA2_ENTERPRISE
#define WIFI_AUTH_WPA3_SAE LN_WIFI_AUTH_WPA3_SAE
#define WIFI_AUTH_WPA2_PSK_WPA3_SAE LN_WIFI_AUTH_WPA2_PSK_WPA3_SAE
#define WIFI_AUTH_MAX LN_WIFI_AUTH_MAX
#define wifi_mode_t ln_wifi_mode_t
#define wifi_auth_mode_t ln_wifi_auth_mode_t
// SDK
#include "ln_kv_api.h"
#include "ln_nvds.h"
#include "utils/debug/CmBacktrace/cm_backtrace.h"
#include "utils/ln_psk_calc.h"
#include "utils/power_mgmt/ln_pm.h"
#include "utils/reboot_trace/reboot_trace.h"
#include "utils/runtime/runtime.h"
#include "utils/system_parameter.h"
#include <hal/hal_adc.h>
#include <hal/hal_gpio.h>
#include <hal/hal_i2c.h>
#include <hal/hal_interrupt.h>
#include <hal/hal_wdt.h>
#include <ln_wifi.h>
#include <osal/osal.h>
#include <serial/ln_serial.h>
#include <wifi_manager.h>
#ifndef LN882H_SDK
#undef WIFI_MODE_STATION
#undef WIFI_MODE_AP
#undef WIFI_MODE_AP_STATION
#undef WIFI_MODE_MAX
#undef WIFI_AUTH_OPEN
#undef WIFI_AUTH_WEP
#undef WIFI_AUTH_WPA_PSK
#undef WIFI_AUTH_WPA2_PSK
#undef WIFI_AUTH_WPA_WPA2_PSK
#undef WIFI_AUTH_WPA2_ENTERPRISE
#undef WIFI_AUTH_WPA3_SAE
#undef WIFI_AUTH_WPA2_PSK_WPA3_SAE
#undef WIFI_AUTH_MAX
#undef wifi_mode_t
#undef wifi_auth_mode_t
#endif
#ifdef __cplusplus
} // extern "C"
#endif

Binary file not shown.

View File

@@ -0,0 +1,177 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(Reset_Handler)
/* Linker script to configure memory regions. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x10007100, LENGTH = 1228544
RAM0 (rwx) : ORIGIN = 0x20000000, LENGTH = 295K
RETENTION (rwx) : ORIGIN = 0x20049C00, LENGTH = 1K
CACHE_MEM (rwx) : ORIGIN = 0x2004A000, LENGTH = 32K
}
SECTIONS
{
.flash_copysection :
{
__copysection_ram0_load = LOADADDR(.flash_copysection);
__copysection_ram0_start = .;
KEEP(*(.vectors)) /* startup code: ARM vectors */
*hal_qspi.c.o*(.text*)
*hal_cache.c.o*(.text*)
*hal_dma.c.o*(.text*)
*hal_rtc.c.o*(.text*)
*qspi.c.o*(.text*)
*hal_flash.c.o*(.text*)
*port.c.o*(.text*)
*port.o*(.text*)
/* Optional .text part*/
/*
*hal_uart.o*(.text)
*log.o*(.text)
*serial.o*(.text)
*serial_hw.o*(.text)
*fifobuf.o*(.text)
*/
. = ALIGN(4);
/* all RW data in ram0 of wlan shared memory and lwip pubf pool are not in this section, they are in .bss section */
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__copysection_ram0_end = .;
} >RAM0 AT>FLASH
.flash_text :
{
PROVIDE(_stext = .);
*(.text*)
PROVIDE(_etext = .);
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
/* section information for ln_at */
. = ALIGN(4);
__ln_at_cmd_tbl_start = .;
KEEP(*(ln_at_cmd_tbl))
__ln_at_cmd_tbl_end = .;
} >FLASH
/* .stack_dummy section doesn't contain any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
PROVIDE(__stack_start__ = .);
KEEP(*(.stack*))
PROVIDE(__c_stack_top__ = .);
PROVIDE(__stack_end__ = .);
} >RAM0
/* Location counter can end up 2byte aligned with narrow Thumb code but
__etext is assumed by startup code to be the LMA of a section in RAM
which must be 4byte aligned */
__etext = ALIGN (4);
.bss_ram0 (NOLOAD):
{
. = ALIGN(4);
__bss_ram0_start__ = .;
*memp.o(.bss* COMMON)
*(wlan_mem_local)
*(wlan_mem_pkt)
*(wlan_mem_dscr)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_ram0_end__ = .;
} >RAM0
.no_init_data (NOLOAD):
{
. = ALIGN(4);
*(.no_init_data)
*(.noinit .noinit.*)
. = ALIGN(4);
__retention_start__ = .;
*(retention_data)
. = ALIGN(4);
__retention_end__ = .;
} >RETENTION
/* Group unwind sections together: */
.ARM.extab : { *(.ARM.extab*) }
.ARM.exidx :
{
__exidx_start = .;
*(.ARM.exidx*)
*(.gnu.linkonce.armexidx.*)
__exidx_end = .;
}
/DISCARD/ : {
*(.ARM.exidx.exit.text)
*(.ARM.extab.exit.text)
*(.ARM.exidx.devexit.text)
*(.ARM.extab.devexit.text)
}
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
PROVIDE(__StackTop = __c_stack_top__);
PROVIDE(__StackLimit = __stack_start__);
PROVIDE(__stack = __StackTop);
PROVIDE(heap0_start = __bss_ram0_end__);
PROVIDE(heap0_end = ORIGIN(RAM0) + LENGTH(RAM0)); /* RAM0 end */
PROVIDE(heap0_len = heap0_end - heap0_start);
/* Check if data + heap + stack exceeds RAM limit */
/* ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") */
ASSERT(heap0_start < heap0_end, "region RAM0 overflowed with .bss")
}

View File

@@ -0,0 +1,59 @@
# Main file for ln882x series Cortex-M3 parts
#
# !!!!!!
#
set CHIPNAME ln882h
set CHIPSERIES ln882x
transport select swd
# Adapt based on what transport is active.
source [find target/swj-dp.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
error "CHIPNAME not set. Please do not include ln882x.cfg directly."
}
if { [info exists CHIPSERIES] } {
# Validate chip series is supported
if { $CHIPSERIES != "ln882x" } {
error "Unsupported chip series specified."
}
set _CHIPSERIES $CHIPSERIES
} else {
error "CHIPSERIES not set. Please do not include ln882x.cfg directly."
}
if { [info exists CPUTAPID] } {
# Allow user override
set _CPUTAPID $CPUTAPID
} else {
# ln882x use a Cortex M4 core.
if { $_CHIPSERIES == "ln882x" } {
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID
set _TARGETNAME $_CHIPNAME.cpu
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
set _ENDIAN little
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
adapter speed 1000
adapter srst delay 200
# ln882x (Cortex M4 core) support SYSRESETREQ
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
#$_TARGETNAME configure -event reset-init {ln882x_init}

View File

@@ -0,0 +1,59 @@
# Main file for ln882x series Cortex-M3 parts
#
# !!!!!!
#
set CHIPNAME ln882h
set CHIPSERIES ln882x
transport select swd
# Adapt based on what transport is active.
source [find target/swj-dp.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
error "CHIPNAME not set. Please do not include ln882x.cfg directly."
}
if { [info exists CHIPSERIES] } {
# Validate chip series is supported
if { $CHIPSERIES != "ln882x" } {
error "Unsupported chip series specified."
}
set _CHIPSERIES $CHIPSERIES
} else {
error "CHIPSERIES not set. Please do not include ln882x.cfg directly."
}
if { [info exists CPUTAPID] } {
# Allow user override
set _CPUTAPID $CPUTAPID
} else {
# ln882x use a Cortex M4 core.
if { $_CHIPSERIES == "ln882x" } {
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID
set _TARGETNAME $_CHIPNAME.cpu
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
set _ENDIAN little
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
adapter speed 1000
adapter srst delay 200
# ln882x (Cortex M4 core) support SYSRESETREQ
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
#$_TARGETNAME configure -event reset-init {ln882x_init}

View File

@@ -2,3 +2,4 @@
* [Beken BK72xx](beken-72xx/README.md)
* [Realtek RTL8710Bx](realtek-ambz/README.md)
* [Lightning LN882x](lightning-ln882x/README.md)

View File

@@ -0,0 +1,101 @@
# Lightning LN882x
## Introduction
Lightning LN882x is a family of Wi-Fi and BLE microcontrollers designed for indoor short-range IoT.
Features:
- 32-bit Cortex M4F CPU (160 MHz)
- 296 KiB SRAM
- built-in 512 KiB to 2 MiB flash with XiP
- 802.11b/g/n Wi-Fi
- Bluetooth 5.1 (LN882H only)
Resources:
- [LN882H](http://www.lightningsemi.com/ln882h) : MCU Combo Wi-Fi/BLE IoT
- [LN882H Datasheet](https://gitee.com/lightningsemi/ln882h-document-collection/blob/master/1.%E8%8A%AF%E7%89%87%E8%A7%84%E6%A0%BC%E4%B9%A6/LN882H_Datasheet.pdf) : LN882H Datasheet
- [LN882H DOC](https://gitee.com/lightningsemi/ln882h-document-collection) : Official LN882H Document Collection
- [LN882H SDK](https://gitee.com/lightningsemi/ln882h) : Official LN882H SDK
- [LN882X SDK](https://gitee.com/lightningsemi/ln882x-sdk) : Official LN882X SDK
- [LN8825A/B](http://www.lightningsemi.com/LN8825AB) : Wi-Fi SOC
- [LN8825A/B SDK](https://gitee.com/lightningsemi/ln882x-mcu) : Official LN8825A/B SDK
- [Flashing tool (GUI)](https://gitee.com/lightningsemi/ln882h-document-collection/blob/master/4.%E7%83%A7%E5%BD%95%E5%B7%A5%E5%85%B7/%E4%B8%B2%E5%8F%A3%E7%83%A7%E5%BD%95/%E4%BA%AE%E7%89%9B%E4%B8%B2%E5%8F%A3%E7%83%A7%E5%BD%95%E5%B7%A5%E5%85%B7V3.0.5.zip) : Flashing tool with GUI (chinese)
- [Flashing tool (CMD)](https://gitee.com/lightningsemi/ln882h-document-collection/blob/master/4.%E7%83%A7%E5%BD%95%E5%B7%A5%E5%85%B7/%E4%B8%B2%E5%8F%A3%E7%83%A7%E5%BD%95/LN882H%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7_V1.0.16.zip) : Command-line Flashing tool
## Finding your board
{%
include-markdown "../../inc/find-board.md"
%}
---
## Flashing
{%
include-markdown "../../inc/flashing-note.md"
%}
LN882x has two UART ports - The UART0 port is used for flashing and text output.
You need to find which pins correspond to UART0 TX and RX. If your board is supported, you'll find the pinout on its documentation page. Otherwise (and for generic boards), you'll have to find the pinout online.
### Wiring
Connect UART0 of the LN882H to the USB-TTL adapter:
PC | LN882H
----|-----------------------
RX | TX0 (GPIOA2 / P2)
TX | RX0 (GPIOA3 / P3)
GND | GND
{%
include-markdown "../../inc/uart-power.md"
%}
{%
include-markdown "../../inc/uart-cen.md"
%}
The download mode is entered when the chip communicates with the flasher program. Hence, the first step is running the flasher program (described below). While the program is trying to establish communication, **the chip has to be rebooted**. In order to do that, you need to bridge BOOT1 pin to GND with a wire **before** powering the chip.
After linking with the chip, the flasher program will begin writing (or reading) the firmware automatically. If that doesn't happen, try resetting the chip again until it works.
If you're getting a `No response received` (or similar) error, this means that:
- the power supply is too weak (read above)
- you're resetting the chip too quickly, i.e. you resetted it *after* the program started communicating with it
### Flashing
The recommended tool to flash (or dump firmware) is `ltchiptool`.
**Read [Using ltchiptool](../../flashing/tools/ltchiptool.md) to learn the flashing procedure**
!!! tip
Because the UART uploading code is programmed in the ROM of the chip, it can't be software-bricked, even if you damage the bootloader.
## Firmware output files
These files are present in the build directory after successful compilation:
File | Description
--------------------------------|----------------------------------------
**firmware.uf2** | **UF2 package for UART and OTA upload**
**image_firmware.0x000000.bin** | **Full flash binary image** - flashable at 0x0
image_boot.0x000000.bin | Boot partition binary image - flashable at 0x0
image_part_tab.0x006000.bin | Partition table binary image - flashable at 0x6000
image_app.0x007000.bin | Application partition binary image - flashable at 0x7000
image_ota.0x133000.bin | OTA partition binary image - flashable at 0x133000
## Other tools/guides
- Official command-line [flashing tool](https://gitee.com/lightningsemi/ln882h-document-collection/blob/master/4.%E7%83%A7%E5%BD%95%E5%B7%A5%E5%85%B7/%E4%B8%B2%E5%8F%A3%E7%83%A7%E5%BD%95/LN882H%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7_V1.0.16.zip)
Usage Examples:<br/>
`LN882H_CMD_Tool.exe COM1 download flash 921600 0x0 image_firmware.0x000000.bin`<br/>
`LN882H_CMD_Tool.exe COM1 download flash 921600 0x7000 image_app.0x007000.bin`

View File

@@ -0,0 +1,59 @@
# Debugging
Debugging of Lightning LN882x chips is possible and was tested with a JLink probe on OpenOCD and JLinkGDBServer.
*(the following is applicable to Arduino framework, and was not tested with SDK framework)*
LibreTiny has ready-to-use OpenOCD config files:
- [platform/lightning-ln882x/openocd/ln882x.cfg](../../../cores/lightning-ln882x/misc/ln882x.cfg)
## Local debugger
It should be possible to use PlatformIO's built-in debugging capabilities directly, when plugging an OpenOCD-compatible debugger into your PC. As there are no debugger interfaces built into these IoT boards, you need to specify your interface of choice in `platformio.ini`:
```ini
[env:my_board]
openocd_interface = <interface name>
```
where `<interface name>` is for example `raspberrypi2-native`, `stlink`, etc.
## JLink configuration
```ini
[env:my_board]
board = ln-02
debug_tool = custom
debug_server =
../JLink_Linux_V698e_x86_64/JLinkGDBServerExe
-singlerun
-if
SWD
-select
USB
-port
3333
-device
Cortex-M4
debug_init_cmds =
define pio_reset_halt_target
monitor reset
end
target extended-remote $DEBUG_PORT
;monitor speed auto
monitor reset
$LOAD_CMDS
$INIT_BREAK
upload_protocol = custom
upload_flags =
-auto
-startapp
-exit
upload_command = ../JLink_Linux_V698e_x86_64/JFlashExe -openprj"../JLink_Linux_V698e_x86_64/ln882h.jflash" -open"${BUILD_DIR}/image_firmware.bin",0 $UPLOAD_FLAGS
debug_init_break = tbreak Reset_Handler
```
## Technical details
GDB is first configured with `mem 0x0000000 0x1000000 ro` in order to mark flash memory as read-only. This makes GDB use hardware breakpoints, as software BPs don't work on these boards.

View File

@@ -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.
&nbsp; | `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 | ✔️ | ✔️ | ✔️ | ❓ | ❓
&nbsp; | `BK7231T` | `BK7231N` | `RTL8710B` | `RTL8720C` | `BK7231Q` | `LN882H`
-------------------------|-----------|-----------|------------|------------|------------|------------
Stability | 5/5 | 5/5 | 4/5 | 2/5 | 1/5 | 3/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:

View File

@@ -84,6 +84,28 @@
"FREERTOS_PORT_${FREERTOS_PORT_DEFINE}": "1"
}
},
"lwip-ln882h": {
"package": "library-lwip",
"sources": [
"+<src/api/*.c>",
"+<src/core/*.c>",
"-<src/core/mem.c>",
"+<src/core/ipv4/*.c>",
"+<src/netif/ethernet.c>",
"+<src/netif/etharp.c>",
"+<src/apps/mdns/mdns.c>",
"+<src/apps/sntp/sntp.c>",
"+<port/ln_osal/netif/ethernetif.c>",
"+<port/ln_osal/arch/sys_arch.c>"
],
"includes": [
"+<src/include>",
"+<src/include/ipv4>",
"+<port/ln_osal/include>",
"+<port/ln_osal/include/netif>",
"+<port/ln_osal/include/arch>"
]
},
"lwip-amb1": {
"package": "library-lwip",
"sources": [

View File

@@ -100,5 +100,18 @@
"parent": null,
"code": "wmiot",
"description": "WinnerMicro W60x/W800x"
},
"lightning-ln882x": {
"parent": null,
"code": "ln882x",
"description": "Lightning LN882x Family"
},
"lightning-ln882h": {
"parent": "lightning-ln882x",
"code": "ln882h",
"id": "0xA38090A8",
"short_name": "LN882H",
"description": "Lightning LN882H",
"package": "framework-lightning-ln882h"
}
}

View File

@@ -42,6 +42,7 @@ plugins:
"link/config-debug.md": "docs/dev/config.md#per-module-logging-debugging"
"link/config-serial.md": "docs/dev/config.md#serial-output"
"link/flashing-beken-72xx.md": "docs/platform/beken-72xx/README.md#flashing"
"link/flashing-ln882h.md": "docs/platform/lightning-ln882x/README.md#flashing"
"link/flashing-realtek-ambz.md": "docs/platform/realtek-ambz/README.md#flashing"
"link/bk72xx-keys.md": "docs/platform/beken-72xx/keys.md"
"link/kickstart.md": "https://github.com/libretiny-eu/esphome-kickstart/releases/latest"

View File

@@ -72,6 +72,22 @@
}
}
},
"framework-lightning-ln882h": {
"type": "framework",
"optional": true,
"version": "https://github.com/libretiny-eu/framework-lightning-ln882h",
"version_prefix": true,
"toolchains": {
"any": "gccarmnoneeabi@~1.100301.0"
},
"libraries": {
"freertos": "9.0.0",
"lwip": {
"2.1.3": "2.1.3-ln882h",
"default": "2.1.3-ln882h"
}
}
},
"framework-arduino-api": {
"type": "framework",
"optional": true,
@@ -89,7 +105,7 @@
},
"library-freertos-port": {
"type": "framework",
"version": "https://github.com/libretiny-eu/library-freertos-port#2023.05.23"
"version": "https://github.com/libretiny-eu/library-freertos-port#2025.01.18"
},
"library-flashdb": {
"type": "framework",