diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8794182af..3fc7e73a0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,7 +25,8 @@ jobs: run: | mkdir -p site/ boardgen ltci - python docs/scripts/update_docs.py + python docs/scripts/write_boards.py + python docs/scripts/write_apis.py python docs/scripts/prepare_doxygen.py python docs/scripts/build_json.py cp *.json site/ diff --git a/.gitignore b/.gitignore index d4c122cfb..95bd98f43 100644 --- a/.gitignore +++ b/.gitignore @@ -264,3 +264,5 @@ docs/status/supported_*.md docs/status/unsupported_boards_*.md boards/**/*.svg boards/**/*.md +# other generated files +docs/contrib/lt-api-functions.md diff --git a/SUMMARY.md b/SUMMARY.md index fbe2623fe..51d41790c 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -47,10 +47,13 @@ * [Macros](ltapi/macros.md) * [File list](ltapi/files.md) * đŸ‘· Contributor's manual (WIP) - * [📁 Project structure](docs/dev/project-structure.md) - * [✈ OTA format](docs/dev/ota/README.md) - * [uf2ota.py tool](docs/dev/ota/uf2ota.md) - * [uf2ota.h library](docs/dev/ota/library.md) + * [Porting new families](docs/contrib/porting.md) + * [API functions guide](docs/contrib/lt-api.md) + * [C standard library](docs/contrib/stdlib.md) + * [📁 Project structure](docs/contrib/project-structure.md) + * [✈ OTA format](docs/contrib/ota/README.md) + * [uf2ota.py tool](docs/contrib/ota/uf2ota.md) + * [uf2ota.h library](docs/contrib/ota/library.md) * [📓 TODO](docs/TODO.md) * [](SUMMARY.md) * [🔗 Resources](docs/resources/) diff --git a/boards/_base/beken-7231-crypt-tuya.json b/boards/_base/beken-7231-tuya.json similarity index 62% rename from boards/_base/beken-7231-crypt-tuya.json rename to boards/_base/beken-7231-tuya.json index c04f77b45..a96531fff 100644 --- a/boards/_base/beken-7231-crypt-tuya.json +++ b/boards/_base/beken-7231-tuya.json @@ -1,5 +1,8 @@ { "build": { "bkcrypt_coeffs": "510fb093a3cbeadc5993a17ec7adeb03" + }, + "flash": { + "tuya": "0x1ED000+0x13000" } } diff --git a/boards/_base/pcb/bw15.json b/boards/_base/pcb/bw15.json index 57e8fa7ea..217692d34 100644 --- a/boards/_base/pcb/bw15.json +++ b/boards/_base/pcb/bw15.json @@ -2,14 +2,9 @@ "pcb": { "templates": [ "esp12s", - "esp12s-shield", + "pcb-black", "rf-type1" ], - "vars": { - "MASK_PRESET": "mask_black", - "TRACE_COLOR": "#FAFD9D", - "SILK_COLOR": "white" - }, "pinout_hidden": "I2S,TRIG,WAKE,CTS,RTS,SD", "pinout": { "1": { diff --git a/boards/_base/realtek-ambz-2mb-468k.json b/boards/_base/realtek-ambz-2mb-468k.json index 6b8346c45..2e016b20d 100644 --- a/boards/_base/realtek-ambz-2mb-468k.json +++ b/boards/_base/realtek-ambz-2mb-468k.json @@ -5,9 +5,8 @@ "flash": { "ota1": "0x00B000+0x75000", "ota2": "0x080000+0x75000", - "kvs": "0xF5000+0x6000", - "userdata": "0xFB000+0x104000", - "rdp": "0x1FF000+0x1000" + "kvs": "0x0F5000+0x8000", + "userdata": "0x0FD000+0x102000" }, "upload": { "flash_size": 2097152, diff --git a/boards/_base/realtek-ambz-2mb-788k.json b/boards/_base/realtek-ambz-2mb-788k.json index 7df3e6e87..9cf5546e6 100644 --- a/boards/_base/realtek-ambz-2mb-788k.json +++ b/boards/_base/realtek-ambz-2mb-788k.json @@ -5,9 +5,8 @@ "flash": { "ota1": "0x00B000+0xC5000", "ota2": "0x0D0000+0xC5000", - "kvs": "0x195000+0x6000", - "userdata": "0x19B000+0x64000", - "rdp": "0x1FF000+0x1000" + "kvs": "0x195000+0x8000", + "userdata": "0x19D000+0x62000" }, "upload": { "flash_size": 2097152, diff --git a/boards/_base/realtek-ambz-4mb-980k.json b/boards/_base/realtek-ambz-4mb-980k.json index 5f5fd0e94..e90cf7f97 100644 --- a/boards/_base/realtek-ambz-4mb-980k.json +++ b/boards/_base/realtek-ambz-4mb-980k.json @@ -6,8 +6,7 @@ "ota1": "0x00B000+0xF5000", "ota2": "0x100000+0xF5000", "kvs": "0x1F5000+0x8000", - "userdata": "0x1FD000+0x202000", - "rdp": "0x3FF000+0x1000" + "userdata": "0x1FD000+0x202000" }, "upload": { "flash_size": 4194304, diff --git a/boards/_base/realtek-ambz-tuya.json b/boards/_base/realtek-ambz-tuya.json new file mode 100644 index 000000000..629b69cd4 --- /dev/null +++ b/boards/_base/realtek-ambz-tuya.json @@ -0,0 +1,5 @@ +{ + "flash": { + "tuya": "0x1EB000+0x15000" + } +} diff --git a/boards/_base/realtek-ambz.json b/boards/_base/realtek-ambz.json index 8fbbdae35..21dc7f631 100644 --- a/boards/_base/realtek-ambz.json +++ b/boards/_base/realtek-ambz.json @@ -10,7 +10,8 @@ "boot_xip": "0x000000+0x4000", "boot_ram": "0x004000+0x4000", "system": "0x009000+0x1000", - "calibration": "0x00A000+0x1000" + "calibration": "0x00A000+0x1000", + "rdp": "0x1FF000+0x1000" }, "connectivity": [ "wifi" @@ -47,10 +48,6 @@ "General info": "../../docs/platform/realtek-amb/README.md", "Flashing guide": "../../docs/platform/realtek-ambz/flashing.md", "Debugging": "../../docs/platform/realtek-ambz/debugging.md" - }, - "extra": [ - "RDP is most likely not used in Tuya firmwares, as the System Data partition contains an incorrect offset 0xFF000 for RDP, which is in the middle of OTA2 image.", - "Additionally, Tuya firmware uses an encrypted KV or file storage, which resides at the end of flash memory. This seems to overlap system RDP area." - ] + } } } diff --git a/boards/_base/realtek-ambz2-2mb-900k.json b/boards/_base/realtek-ambz2-2mb-900k.json new file mode 100644 index 000000000..b1f9ed8f9 --- /dev/null +++ b/boards/_base/realtek-ambz2-2mb-900k.json @@ -0,0 +1,12 @@ +{ + "flash": { + "ota1": "0x010000+0xE0000", + "ota2": "0x0F0000+0xE0000", + "kvs": "0x1D0000+0x8000", + "userdata": "0x1D8000+0x28000" + }, + "upload": { + "flash_size": 2097152, + "maximum_size": 917504 + } +} diff --git a/boards/_base/realtek-ambz2-2mb-992k.json b/boards/_base/realtek-ambz2-2mb-992k.json index ce06c5423..58fb342b0 100644 --- a/boards/_base/realtek-ambz2-2mb-992k.json +++ b/boards/_base/realtek-ambz2-2mb-992k.json @@ -2,8 +2,7 @@ "flash": { "ota1": "0x00C000+0xF8000", "ota2": "0x104000+0xF8000", - "kvs": "0x1FC000+0x2000", - "userdata": "0x1FE000+0x2000" + "kvs": "0x1FC000+0x4000" }, "upload": { "flash_size": 2097152, diff --git a/boards/_base/realtek-ambz2-image.json b/boards/_base/realtek-ambz2-image.json new file mode 100644 index 000000000..1ac54e338 --- /dev/null +++ b/boards/_base/realtek-ambz2-image.json @@ -0,0 +1,96 @@ +{ + "image": { + "keys": { + "decryption": "a0d6dae7e062ca94cbb294bf896b9f68cf8438774256ac7403ca4fd9a1c9564f", + "keyblock": { + "part_table": "882aa16c8c44a7760aa8c9ab22e3568c6fa16c2afa4f0cea29a10abcdf60e44f", + "boot": "882aa16c8c44a7760aa8c9ab22e3568c6fa16c2afa4f0cea29a10abcdf60e44f" + }, + "hash_keys": { + "part_table": "47e5661335a4c5e0a94d69f3c737d54f2383791332939753ef24279608f6d72b", + "boot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ota1": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e5f", + "ota2": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e5f" + }, + "user_keys": { + "boot": "aa0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "ota1": "bb0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "ota2": "bb0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + }, + "xip_sce_key": "a0d6dae7e062ca94cbb294bf896b9f68", + "xip_sce_iv": "94879487948794879487948794879487" + }, + "ptable": { + "boot": "BOOT", + "ota1": "FW1", + "ota2": "FW2" + }, + "boot": { + "name": "boot.sram", + "type": "SRAM", + "entry": "__ram_start_table_start__", + "elf": [ + ".ram.func.table", + ".data", + ".ram.code_text", + ".ram.code_rodata" + ], + "is_boot": true + }, + "fw": [ + { + "type": "FWHS_S", + "sections": [ + { + "name": "fwhs.sram", + "type": "SRAM", + "entry": "__ram_start_table_start__", + "elf": [ + ".ram.img.signature", + ".ram.func.table", + ".data", + ".ram.code_text", + ".ram.code_rodata" + ] + }, + { + "name": "fwhs.psram", + "type": "PSRAM", + "entry": "__psram_start__", + "elf": [ + ".psram.data", + ".psram.code_text", + ".psram.code_rodata" + ] + } + ] + }, + { + "type": "XIP", + "sections": [ + { + "name": "fwhs.xip_c", + "entry": "XIP_RamImgSignature_s", + "type": "XIP", + "elf": [ + ".xip.code_c" + ] + } + ] + }, + { + "type": "XIP", + "sections": [ + { + "name": "fwhs.xip_p", + "entry": "__xip_code_rodata_start__", + "type": "XIP", + "elf": [ + ".xip.code_p" + ] + } + ] + } + ] + } +} diff --git a/boards/_base/realtek-ambz2-tuya.json b/boards/_base/realtek-ambz2-tuya.json new file mode 100644 index 000000000..1ad51a578 --- /dev/null +++ b/boards/_base/realtek-ambz2-tuya.json @@ -0,0 +1,5 @@ +{ + "flash": { + "tuya": "0x1D5000+0x10000" + } +} diff --git a/boards/_base/realtek-ambz2.json b/boards/_base/realtek-ambz2.json index be1f31642..05d4ffd86 100644 --- a/boards/_base/realtek-ambz2.json +++ b/boards/_base/realtek-ambz2.json @@ -13,10 +13,20 @@ }, "debug": { "protocol": "openocd", - "protocols": [] + "protocols": [ + "openocd" + ], + "openocd_config": "amebaz2.cfg", + "gdb_init": [ + "mem 0x9b000000 0x9c000000 ro" + ] }, "upload": { - "maximum_ram_size": 262144 + "maximum_ram_size": 262144, + "protocol": "uart", + "protocols": [ + "uart" + ] }, "doc": { "params": { diff --git a/boards/bw15.json b/boards/bw15.json new file mode 100644 index 000000000..e2b972e61 --- /dev/null +++ b/boards/bw15.json @@ -0,0 +1,26 @@ +{ + "_base": [ + "realtek-ambz2", + "realtek-ambz2-8720", + "realtek-ambz2-image", + "realtek-ambz2-2mb-992k", + "ic/rtl8720cf", + "pcb/bw15" + ], + "build": { + "mcu": "rtl8720cf", + "variant": "bw15" + }, + "name": "BW15", + "url": "https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf", + "vendor": "Ai-Thinker Co., Ltd.", + "pcb": { + "symbol": "BW15" + }, + "doc": { + "fccid": "2AXVG-BW15", + "links": { + "Vendor datasheet": "https://docs.ai-thinker.com/_media/rtl8710/docs/bw15_datasheet_en.pdf" + } + } +} diff --git a/boards/cb1s.json b/boards/cb1s.json index 5f9b77bfb..b97e6ea4c 100644 --- a/boards/cb1s.json +++ b/boards/cb1s.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb1s", "pcb/cb1s-test" diff --git a/boards/cb2l.json b/boards/cb2l.json index a857fbf68..84413c938 100644 --- a/boards/cb2l.json +++ b/boards/cb2l.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb2l", "pcb/cb2l-test" diff --git a/boards/cb2s.json b/boards/cb2s.json index 4f69cfeaa..a4df4e2a0 100644 --- a/boards/cb2s.json +++ b/boards/cb2s.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb2s", "pcb/cb2s-test" diff --git a/boards/cb3l.json b/boards/cb3l.json index ad97c78da..ea2161a05 100644 --- a/boards/cb3l.json +++ b/boards/cb3l.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb3l" ], diff --git a/boards/cb3s.json b/boards/cb3s.json index d6ee84a29..9aea41171 100644 --- a/boards/cb3s.json +++ b/boards/cb3s.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb3s" ], diff --git a/boards/cb3se.json b/boards/cb3se.json index c4aa266bf..104e9d091 100644 --- a/boards/cb3se.json +++ b/boards/cb3se.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cb3se" ], diff --git a/boards/cblc5.json b/boards/cblc5.json index a0073fc2d..7e91a732b 100644 --- a/boards/cblc5.json +++ b/boards/cblc5.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cblc5", "pcb/cblc5-test" diff --git a/boards/cbu.json b/boards/cbu.json index c0703d1d6..92431d75e 100644 --- a/boards/cbu.json +++ b/boards/cbu.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/cbu", "pcb/cbu-test" diff --git a/boards/generic-bk7231n-qfn32-tuya.json b/boards/generic-bk7231n-qfn32-tuya.json index 092d24a75..c38eaabd9 100644 --- a/boards/generic-bk7231n-qfn32-tuya.json +++ b/boards/generic-bk7231n-qfn32-tuya.json @@ -3,7 +3,7 @@ "generic", "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32" ], "build": { diff --git a/boards/generic-bk7231t-qfn32-tuya.json b/boards/generic-bk7231t-qfn32-tuya.json index 79ae3dbc6..cfa840cf7 100644 --- a/boards/generic-bk7231t-qfn32-tuya.json +++ b/boards/generic-bk7231t-qfn32-tuya.json @@ -4,7 +4,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32" ], "build": { diff --git a/boards/generic-rtl8720cf-2mb-992k.json b/boards/generic-rtl8720cf-2mb-992k.json new file mode 100644 index 000000000..906f2972f --- /dev/null +++ b/boards/generic-rtl8720cf-2mb-992k.json @@ -0,0 +1,102 @@ +{ + "_base": [ + "generic", + "realtek-ambz2", + "realtek-ambz2-8720", + "realtek-ambz2-image", + "realtek-ambz2-2mb-992k", + "ic/rtl8720cf" + ], + "build": { + "mcu": "rtl8720cf", + "variant": "generic-rtl8720cf-2mb-992k" + }, + "name": "Generic - RTL8720CF (2M/992k)", + "symbol": "RTL8720CF (2M/992k)", + "url": "https://docs.libretiny.eu/boards/generic-rtl8720cf-2mb-992k/", + "vendor": "Generic", + "pcb": { + "pinout": { + "1": { + "IC": 15, + "ARD": "D0" + }, + "2": { + "IC": 16, + "ARD": "D1" + }, + "3": { + "IC": 18, + "ARD": "D2" + }, + "4": { + "IC": 19, + "ARD": "D3" + }, + "5": { + "IC": 20, + "ARD": "D4" + }, + "6": { + "IC": 21, + "ARD": "D5" + }, + "7": { + "IC": 22, + "ARD": "D6" + }, + "8": { + "IC": 23, + "ARD": "D7" + }, + "9": { + "IC": 24, + "ARD": "D8" + }, + "10": { + "IC": 25, + "ARD": "D9" + }, + "11": { + "IC": 26, + "ARD": "D10" + }, + "12": { + "IC": 33, + "ARD": "D11" + }, + "13": { + "IC": 34, + "ARD": "D12" + }, + "14": { + "IC": 36, + "ARD": "D13" + }, + "15": { + "IC": 37, + "ARD": "D14" + }, + "16": { + "IC": 38, + "ARD": "D15" + }, + "17": { + "IC": 39, + "ARD": "D16" + }, + "18": { + "IC": 40, + "ARD": "D17" + }, + "19": { + "IC": 1, + "ARD": "D18" + }, + "20": { + "IC": 3, + "ARD": "D19" + } + } + } +} diff --git a/boards/lsc-lma35-t.json b/boards/lsc-lma35-t.json index bedb6d445..60a7cbad9 100644 --- a/boards/lsc-lma35-t.json +++ b/boards/lsc-lma35-t.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/lsc-lma35" ], diff --git a/boards/lsc-lma35.json b/boards/lsc-lma35.json index 4ea7fb1b1..2f97e6e15 100644 --- a/boards/lsc-lma35.json +++ b/boards/lsc-lma35.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/lsc-lma35" ], diff --git a/boards/wb1s.json b/boards/wb1s.json index 26035c58b..f9097cb5f 100644 --- a/boards/wb1s.json +++ b/boards/wb1s.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb1s" ], diff --git a/boards/wb2l-m1.json b/boards/wb2l-m1.json index 6fc840615..a0252faa1 100644 --- a/boards/wb2l-m1.json +++ b/boards/wb2l-m1.json @@ -2,7 +2,7 @@ "_base": [ "beken-72xx", "beken-7231n", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb2l", "pcb/wb2l-test", diff --git a/boards/wb2l.json b/boards/wb2l.json index 9ca3f6d9c..06527101c 100644 --- a/boards/wb2l.json +++ b/boards/wb2l.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb2l", "pcb/wb2l-test" diff --git a/boards/wb2s.json b/boards/wb2s.json index 76bb511c8..3f9966cd9 100644 --- a/boards/wb2s.json +++ b/boards/wb2s.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb2s", "pcb/wb2s-test" diff --git a/boards/wb3l.json b/boards/wb3l.json index 227686c6a..81711e4f0 100644 --- a/boards/wb3l.json +++ b/boards/wb3l.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb3l" ], diff --git a/boards/wb3s.json b/boards/wb3s.json index 619318312..cd50b811f 100644 --- a/boards/wb3s.json +++ b/boards/wb3s.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wb3s" ], diff --git a/boards/wblc5.json b/boards/wblc5.json index 1f4663177..89ee15186 100644 --- a/boards/wblc5.json +++ b/boards/wblc5.json @@ -3,7 +3,7 @@ "beken-72xx", "beken-7231", "beken-7231t", - "beken-7231-crypt-tuya", + "beken-7231-tuya", "ic/bk7231-qfn32", "pcb/wblc5", "pcb/wblc5-test" diff --git a/boards/wr2.json b/boards/wr2.json index 1dd26bfba..654a2f997 100644 --- a/boards/wr2.json +++ b/boards/wr2.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "ic/rtl8710bn", "pcb/wr2-base", diff --git a/boards/wr2e.json b/boards/wr2e.json index 015526a10..0157b6b83 100644 --- a/boards/wr2e.json +++ b/boards/wr2e.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "ic/rtl8710bn", "pcb/wr2-base", diff --git a/boards/wr2l.json b/boards/wr2l.json index 83343d397..4225f7b6d 100644 --- a/boards/wr2l.json +++ b/boards/wr2l.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "realtek-ambz-bx", "ic/rtl8710bn", diff --git a/boards/wr2le.json b/boards/wr2le.json index d620d69ff..c60d6eb68 100644 --- a/boards/wr2le.json +++ b/boards/wr2le.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "realtek-ambz-bx", "ic/rtl8710bn", diff --git a/boards/wr3.json b/boards/wr3.json index 4f68710ce..b36440620 100644 --- a/boards/wr3.json +++ b/boards/wr3.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "ic/rtl8710bn", "pcb/wr3-base", diff --git a/boards/wr3e.json b/boards/wr3e.json index 84a0417db..853346837 100644 --- a/boards/wr3e.json +++ b/boards/wr3e.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "ic/rtl8710bn", "pcb/wr3-base", diff --git a/boards/wr3l.json b/boards/wr3l.json index fb815f534..0bda55d98 100644 --- a/boards/wr3l.json +++ b/boards/wr3l.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "realtek-ambz-bx", "ic/rtl8710bn", diff --git a/boards/wr3le.json b/boards/wr3le.json index 44cff152a..a06a6ed79 100644 --- a/boards/wr3le.json +++ b/boards/wr3le.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "realtek-ambz-bx", "ic/rtl8710bn", diff --git a/boards/wr3n.json b/boards/wr3n.json index ae35e26da..0a5c404d2 100644 --- a/boards/wr3n.json +++ b/boards/wr3n.json @@ -1,6 +1,7 @@ { "_base": [ "realtek-ambz", + "realtek-ambz-tuya", "realtek-ambz-2mb-788k", "ic/rtl8710bn", "pcb/wr3-base", diff --git a/builder/family/beken-72xx.py b/builder/family/beken-72xx.py index 5ad16a176..534a4e518 100644 --- a/builder/family/beken-72xx.py +++ b/builder/family/beken-72xx.py @@ -538,7 +538,7 @@ def to_offset(addr: int) -> int: image_ota_rbl = "${BUILD_DIR}/image_${MCULC}_app.ota.rbl" env.Replace( # linker command (encryption + packaging) - LINK="${LTCHIPTOOL} link2bin ${BOARD_JSON} '' ''", + LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""', # UF2OTA input list UF2OTA=[ # app binary image (enc+crc) for flasher diff --git a/builder/family/realtek-ambz2.py b/builder/family/realtek-ambz2.py index 2cab2fe69..e593bec4f 100644 --- a/builder/family/realtek-ambz2.py +++ b/builder/family/realtek-ambz2.py @@ -1,6 +1,7 @@ # Copyright (c) Kuba SzczodrzyƄski 2022-07-20. -from os.path import join +from os.path import isfile, join +from shutil import copyfile from platformio.platform.base import PlatformBase from platformio.platform.board import PlatformBoardConfig @@ -52,9 +53,10 @@ "-mthumb", "-mcmse", "-mfloat-abi=soft", - "--specs=nosys.specs", + "--specs=nano.specs", "-Wl,--use-blx", "-Wl,--undefined=gRamStartFun", + "-Wl,--warn-section-align", "-Wl,-wrap,aesccmp_construct_mic_iv", "-Wl,-wrap,aesccmp_construct_mic_header1", "-Wl,-wrap,aesccmp_construct_ctr_preload", @@ -91,6 +93,7 @@ "-Wl,-wrap,memset", # TODO remove this if possible "-Wl,-wrap,putc", + # rt_printf wrappers are not here, as they're just changing code using #defines ], ) @@ -103,17 +106,7 @@ # cmsis "+", "+", - # console - "+", - "+", - "+", - "+", - "+", - "+", - "+", - "+", - "+", - "+", + "+", # utilities "+", "+", @@ -125,11 +118,6 @@ "+", "+", "+", - # os - freertos - "+", - # os - freertos - portable - "+", - "+", # peripheral - api "+", # peripheral - hal @@ -146,13 +134,9 @@ "+", "+", "+", - # TODO remove this - "+", - "+", ], includes=[ "+<$SDK_DIR/project/realtek_amebaz2_v0_example/inc>", - "+", "+", "+", "+", @@ -170,11 +154,8 @@ "+", "+", "+", - "+", - "+", "+", "+", - "+", "+", "+", "+", @@ -205,6 +186,14 @@ ), ) +# Sources - FreeRTOS +env.Replace(FREERTOS_PORT=env["FAMILY_NAME"], FREERTOS_PORT_DEFINE="REALTEK_AMBZ2") +queue.AddExternalLibrary("freertos") +queue.AddExternalLibrary("freertos-port") + +# Sources - lwIP +queue.AddExternalLibrary("lwip", port="ambz2") + # Sources - network utilities queue.AddLibrary( name="ambz2_net", @@ -214,6 +203,7 @@ "+", # network - api - wifi "+", + "ARDUINO" in "ENV" and "-", # network - api - wifi - rtw_wpa_supplicant "+", "+", @@ -231,7 +221,6 @@ "+", # network "+", - "+", # network - websocket "+", # network - mdns @@ -285,6 +274,7 @@ # "+", "+", "+", + "-", ], includes=[ "+<.>", @@ -315,34 +305,6 @@ ), ) - -# Sources - lwIP 2.0.2 -queue.AddLibrary( - name="ambz2_lwip", - base_dir=join(COMPONENT_DIR, "common", "network", "lwip", "lwip_v2.0.2"), - srcs=[ - "+", - "+", - "+", - "+", - "+", - "+", - "+", - "+", - ], - includes=[ - "+", - "+", - "+", - "+", - ], - options=dict( - CFLAGS=[ - "-Wno-implicit-function-declaration", - ], - ), -) - # Sources - mbedTLS queue.AddLibrary( name="ambz2_mbedtls", @@ -436,10 +398,38 @@ SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES", ) +# Bootloader - copy for linking +# fmt: off +bootloader_src = env.subst("${SDK_DIR}/component/soc/realtek/8710c/misc/bsp/image/bootloader.axf") +bootloader_dst = env.subst("${BUILD_DIR}/bootloader.axf") +# fmt: on +if not isfile(bootloader_dst): + copyfile(bootloader_src, bootloader_dst) + +# OTA2 clearing - 4096 bytes of 0xFF +image_ota_clear = env.subst("${BUILD_DIR}/raw_ota_clear.bin") +if not isfile(image_ota_clear): + with open(image_ota_clear, "wb") as f: + f.write(b"\xFF" * 4096) + # Build all libraries queue.BuildLibraries() # Main firmware outputs and actions +image_part_table = "${BUILD_DIR}/image_part_table.${FLASH_PART_TABLE_OFFSET}.bin" +image_bootloader = "${BUILD_DIR}/image_bootloader.${FLASH_BOOT_OFFSET}.bin" +image_firmware_is = "${BUILD_DIR}/image_firmware_is.${FLASH_OTA1_OFFSET}.bin" env.Replace( - # TODO + # linker command (dual .bin outputs) + LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""', + # UF2OTA input list + UF2OTA=[ + # same OTA images for flasher and device + f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2;flasher:ota1,ota2", + # having flashed an application image, update the bootloader and partition table (incl. keys) + f"{image_bootloader}=device:boot;flasher:boot", + f"{image_part_table}=device:part_table;flasher:part_table", + # clearing headers of the "other" OTA image (hence the indexes are swapped) + f"{image_ota_clear},{image_ota_clear}=device:ota2,ota1;flasher:ota2,ota1", + ], ) diff --git a/builder/frameworks/arduino.py b/builder/frameworks/arduino.py index 3d150e115..5dbe110bc 100644 --- a/builder/frameworks/arduino.py +++ b/builder/frameworks/arduino.py @@ -34,7 +34,7 @@ code = f"{f.code}_arduino" path = join("$CORES_DIR", f.name, "arduino") - found = found or env.AddCoreSources(queue, name=code, path=join(path, "src")) + found = env.AddCoreSources(queue, name=code, path=join(path, "src")) or found env.AddArduinoLibraries(queue, name=code, path=join(path, "libraries")) if f.short_name: @@ -73,13 +73,6 @@ ("ARDUINO_SDK", 1), ], LINKFLAGS=[ - "--specs=nosys.specs", - "-Wl,--as-needed", - "-Wl,--build-id=none", - "-Wl,--cref", - "-Wl,--no-enum-size-warning", - "-Wl,--no-undefined", - "-Wl,--warn-common", # wrappers from posix/time.c "-Wl,-wrap,gettimeofday", "-Wl,-wrap,settimeofday", diff --git a/builder/frameworks/base.py b/builder/frameworks/base.py index bf1eaa63d..c9317c23a 100644 --- a/builder/frameworks/base.py +++ b/builder/frameworks/base.py @@ -95,7 +95,7 @@ env.Prepend(CPPDEFINES=[(f"LT_{f.code.upper()}", "1")]) # Sources - external libraries -queue.AddExternalLibrary("ltchiptool") # uf2ota source code +queue.AddExternalLibrary("uf2ota") queue.AddExternalLibrary("flashdb") queue.AddExternalLibrary("printf") @@ -138,11 +138,14 @@ LINKFLAGS=[ "-g2", "-Os", + "-Wl,--as-needed", "-Wl,--build-id=none", "-Wl,--cref", "-Wl,--gc-sections", "-Wl,--no-enum-size-warning", "-Wl,--no-wchar-size-warning", + "-Wl,--no-undefined", + "-Wl,--warn-common", # malloc.c wrappers "-Wl,-wrap,malloc", "-Wl,-wrap,calloc", diff --git a/builder/utils/config.py b/builder/utils/config.py index 8a495894a..8ebf70172 100644 --- a/builder/utils/config.py +++ b/builder/utils/config.py @@ -20,13 +20,21 @@ def env_load_defines(env: Environment, path: str): line = line[7:].strip() line = line.split(None, 2) if len(line) == 1: - env.Append(CPPDEFINES=[(line[0], 1)]) - config[line[0]] = 1 + key, value = line[0], 1 elif len(line) == 2: - env.Append(CPPDEFINES=[(line[0], line[1])]) - config[line[0]] = line[1] + key, value = line[0], line[1] else: raise ValueError(f"Unknown directive: {line}") + for tpl in env["CPPDEFINES"]: + if isinstance(tpl, tuple): + k = tpl[0] + else: + k = tpl + if k == key: + env["CPPDEFINES"].remove(tpl) + break + env.Append(CPPDEFINES=[(key, value)]) + config[key] = value env.Append( CONFIG=config, ) diff --git a/builder/utils/cores.py b/builder/utils/cores.py index f4a8ad471..8848e08fd 100644 --- a/builder/utils/cores.py +++ b/builder/utils/cores.py @@ -46,10 +46,12 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool: base_dir=path, srcs=[ "+<*.c*>", + "+", "+", "+", "+", "+", + "+", "+", ], includes=[ @@ -58,6 +60,7 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool: "!", "!", "!", + "!", ], ) queue.AddLibrary( diff --git a/builder/utils/ltchiptool.py b/builder/utils/ltchiptool.py index fc1238d39..4ac01c195 100644 --- a/builder/utils/ltchiptool.py +++ b/builder/utils/ltchiptool.py @@ -59,14 +59,23 @@ def env_uf2ota(env: Environment, *args, **kwargs): def env_flash_write(env: Environment): + # UPLOAD_PROTOCOL = upload_protocol or board->upload.protocol + # UPLOAD_PORT = upload_port (PIO can choose this automatically I guess) + # UPLOAD_SPEED = upload_speed or board->upload.speed (**can be empty**) protocol = env.subst("${UPLOAD_PROTOCOL}") + speed = env.subst("${UPLOAD_SPEED}") if protocol == "uart": # upload via UART + if speed: + return [ + "-d", + "${UPLOAD_PORT}", + "-b", + "${UPLOAD_SPEED}", + ] return [ "-d", "${UPLOAD_PORT}", - "-b", - "${UPLOAD_SPEED}", ] else: # can't upload via ltchiptool diff --git a/cores/beken-7231n/base/lt_defs.h b/cores/beken-7231n/base/lt_defs.h new file mode 100644 index 000000000..e0995057c --- /dev/null +++ b/cores/beken-7231n/base/lt_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#error "Don't include this file directly" + +#define LT_HW_BLE 1 diff --git a/cores/beken-7231t/base/lt_defs.h b/cores/beken-7231t/base/lt_defs.h new file mode 100644 index 000000000..e0995057c --- /dev/null +++ b/cores/beken-7231t/base/lt_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#error "Don't include this file directly" + +#define LT_HW_BLE 1 diff --git a/cores/beken-7251/base/lt_defs.h b/cores/beken-7251/base/lt_defs.h new file mode 100644 index 000000000..e0995057c --- /dev/null +++ b/cores/beken-7251/base/lt_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#error "Don't include this file directly" + +#define LT_HW_BLE 1 diff --git a/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp b/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp index 6e90c20a0..c4a071dd1 100644 --- a/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp +++ b/cores/beken-72xx/arduino/libraries/Serial/Serial.cpp @@ -1,59 +1,40 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-23. */ -#include "Serial.h" +#include "SerialPrivate.h" -#include - -extern "C" { - -#include - -extern void bk_send_byte(uint8_t uport, uint8_t data); -extern void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config); -extern int uart_rx_callback_set(int uport, uart_callback callback, void *param); - -} // extern "C" - -#if HAS_SERIAL1 +#if LT_HW_UART1 SerialClass Serial1(UART1_PORT); #endif -#if HAS_SERIAL2 +#if LT_HW_UART2 SerialClass Serial2(UART2_PORT); #endif -SerialClass::SerialClass(uint8_t port) { - this->port = port; - this->buf = NULL; -} - -#if LT_AUTO_DOWNLOAD_REBOOT -static uint8_t adrState = 0; -static const uint8_t adrCmd[] = {0x01, 0xE0, 0xFC, 0x01, 0x00}; - -static void adrParse(uint8_t c) { - // parse and respond to link check command (CMD_LinkCheck=0) - adrState = (adrState + 1) * (c == adrCmd[adrState]); - if (adrState == 5) { - LT_I("Auto download mode: rebooting"); - LT.restart(); - } -} -#endif - static void callback(int port, void *param) { - RingBuffer *buf = (RingBuffer *)param; int ch; while ((ch = uart_read_byte(port)) != -1) { -#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL1_RX) +#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) && PIN_SERIAL1_RX != PIN_INVALID // parse UART protocol commands on UART1 if (port == UART1_PORT) - adrParse(ch); + SerialClass::adrParse(ch); #endif - buf->store_char(ch); + pBUF->store_char(ch); } } 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; + uint8_t dataWidth = ((config & SERIAL_DATA_MASK) >> 8) - 1; // 0x100..0x400 -> 0..3 uint8_t parity = 3 - (config & SERIAL_PARITY_MASK); // 0x3..0x1 -> 0..2 uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; // 0x10..0x30 -> 0..1 @@ -66,17 +47,17 @@ void SerialClass::begin(unsigned long baudrate, uint16_t config) { .flow_control = FLOW_CTRL_DISABLED, }; - if (this->buf) { - this->buf->clear(); - } else { - this->buf = new RingBuffer(); - } - uart_hw_set_change(port, &cfg); - uart_rx_callback_set(port, callback, this->buf); + uart_rx_callback_set(port, callback, &BUF); + + this->baudrate = baudrate; + this->config = config; } void SerialClass::end() { + if (!this->data) + return; + uart_rx_callback_set(port, NULL, NULL); switch (port) { case 1: @@ -86,26 +67,22 @@ void SerialClass::end() { uart2_exit(); break; } - delete this->buf; -} - -int SerialClass::available() { - return buf->available(); -} - -int SerialClass::peek() { - return buf->peek(); -} -int SerialClass::read() { - return buf->read_char(); + delete DATA; + this->data = NULL; + this->buf = NULL; + this->baudrate = 0; } void SerialClass::flush() { + if (!this->data) + return; uart_wait_tx_over(); } size_t SerialClass::write(uint8_t c) { + if (!this->data) + return 0; bk_send_byte(port, c); return 1; } diff --git a/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h b/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h new file mode 100644 index 000000000..c481237c0 --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/Serial/SerialPrivate.h @@ -0,0 +1,14 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include +#include + +typedef struct { + RingBuffer buf; +} SerialData; + +#define DATA ((SerialData *)data) +#define BUF (DATA->buf) +#define pBUF ((RingBuffer *)param) diff --git a/cores/beken-72xx/arduino/src/lt_defs.h b/cores/beken-72xx/arduino/src/lt_defs.h index 6250f1cdf..3acbe6415 100644 --- a/cores/beken-72xx/arduino/src/lt_defs.h +++ b/cores/beken-72xx/arduino/src/lt_defs.h @@ -2,7 +2,6 @@ #error "Don't include this file directly" -#define LT_ARD_HAS_WIFI 1 -#define LT_ARD_HAS_SERIAL 1 - +#define LT_ARD_HAS_WIFI 1 +#define LT_ARD_HAS_SERIAL 1 #define LT_ARD_MD5_HOSTAPD 1 diff --git a/cores/beken-72xx/arduino/src/wiring.c b/cores/beken-72xx/arduino/src/wiring.c index 8a02984f3..97c9dc0fb 100644 --- a/cores/beken-72xx/arduino/src/wiring.c +++ b/cores/beken-72xx/arduino/src/wiring.c @@ -1,13 +1,6 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-19. */ -#include -#include - -#include -#include -#include -#include -#include +#include "wiring_private.h" #if LT_BK7231Q #undef LT_MICROS_HIGH_RES @@ -18,10 +11,6 @@ #define US_PER_OVERFLOW (portTICK_PERIOD_MS * 1000) #define TICKS_PER_OVERFLOW (TICKS_PER_US * US_PER_OVERFLOW) -void delayMilliseconds(unsigned long ms) { - rtos_delay_milliseconds(ms); -} - #if LT_MICROS_HIGH_RES static uint32_t getTicksCount() { // copied from bk_timer_ctrl(), for speeds @@ -107,8 +96,22 @@ unsigned long micros() { #endif } -void yield() { - runPeriodicTasks(); - vTaskDelay(1); - taskYIELD(); +void pinRemoveMode(PinInfo *pin, uint32_t mask) { + PinData *data = pinData(pin); + if ((mask & PIN_GPIO) && (pin->enabled & PIN_GPIO)) { + gpio_config(pin->gpio, GMODE_INPUT_PULLDOWN); + pinDisable(pin, PIN_GPIO); + } + if ((mask & PIN_IRQ) && (pin->enabled & PIN_IRQ)) { + data->irqHandler = NULL; + gpio_int_disable(pin->gpio); + pinDisable(pin, PIN_IRQ); + } + if ((mask & PIN_PWM) && (pin->enabled & PIN_PWM)) { + data->pwm->cfg.bits.en = PWM_DISABLE; + __wrap_bk_printf_disable(); + sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, data->pwm); + __wrap_bk_printf_enable(); + pinDisable(pin, PIN_PWM); + } } diff --git a/cores/beken-72xx/arduino/src/wiring_analog.c b/cores/beken-72xx/arduino/src/wiring_analog.c index 71971df52..5808b996d 100644 --- a/cores/beken-72xx/arduino/src/wiring_analog.c +++ b/cores/beken-72xx/arduino/src/wiring_analog.c @@ -1,10 +1,6 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-20. */ -#include - -#include -#include -#include +#include "wiring_private.h" static GPIO_INDEX pwmToGpio[] = { GPIO6, // PWM0 @@ -59,11 +55,7 @@ static pwm_param_t pwm; static uint16_t adcData[1]; uint16_t analogReadVoltage(pin_size_t pinNumber) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return 0; - if (!pinSupported(pin, PIN_ADC)) - return 0; + pinCheckGetInfo(pinNumber, PIN_ADC, 0); UINT32 status; saradc_desc_t adc; @@ -90,11 +82,10 @@ uint16_t analogReadMaxVoltage(pin_size_t pinNumber) { } void analogWrite(pin_size_t pinNumber, int value) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - if (!pinSupported(pin, PIN_PWM)) - return; + pinCheckGetData(pinNumber, PIN_PWM, ); + + // GPIO can't be used together with PWM + pinRemoveMode(pin, PIN_GPIO | PIN_IRQ); float percent = value * 1.0 / ((1 << _analogWriteResolution) - 1); uint32_t frequency = 26 * _analogWritePeriod - 1; @@ -122,8 +113,9 @@ void analogWrite(pin_size_t pinNumber, int value) { sddev_control(PWM_DEV_NAME, CMD_PWM_INIT_LEVL_SET_HIGH, &pwm.channel); sddev_control(PWM_DEV_NAME, CMD_PWM_UNIT_ENABLE, &pwm.channel); __wrap_bk_printf_enable(); - pin->enabled &= ~PIN_GPIO; - pin->enabled |= PIN_PWM; + // pass global PWM object pointer + data->pwm = &pwm; + pinEnable(pin, PIN_PWM); } else { // update duty cycle sddev_control(PWM_DEV_NAME, CMD_PWM_SET_DUTY_CYCLE, &pwm); @@ -131,11 +123,7 @@ void analogWrite(pin_size_t pinNumber, int value) { } else { if (pinEnabled(pin, PIN_PWM)) { // disable PWM - pwm.cfg.bits.en = PWM_DISABLE; - __wrap_bk_printf_disable(); - sddev_control(PWM_DEV_NAME, CMD_PWM_DEINIT_PARAM, &pwm); - __wrap_bk_printf_enable(); - pin->enabled &= ~PIN_PWM; + pinRemoveMode(pin, PIN_PWM); } // force level as LOW pinMode(pinNumber, OUTPUT); diff --git a/cores/beken-72xx/arduino/src/wiring_data.h b/cores/beken-72xx/arduino/src/wiring_data.h new file mode 100644 index 000000000..c6d521068 --- /dev/null +++ b/cores/beken-72xx/arduino/src/wiring_data.h @@ -0,0 +1,23 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct PinData_s { + pwm_param_t *pwm; + PinMode gpioMode; + PinStatus irqMode; + void *irqHandler; + void *irqParam; + bool irqChange; +}; + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/beken-72xx/arduino/src/wiring_digital.c b/cores/beken-72xx/arduino/src/wiring_digital.c index 27f001688..ab7dcffa0 100644 --- a/cores/beken-72xx/arduino/src/wiring_digital.c +++ b/cores/beken-72xx/arduino/src/wiring_digital.c @@ -1,20 +1,16 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-20. */ -#include - -#include +#include "wiring_private.h" void pinMode(pin_size_t pinNumber, PinMode pinMode) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - if (!pinSupported(pin, PIN_GPIO)) - return; - if (pinEnabled(pin, PIN_PWM)) - // disable PWM before using the pin - analogWrite(pinNumber, 0); - if (pinEnabled(pin, PIN_GPIO) && pin->mode == 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); + switch (pinMode) { case INPUT: gpio_config(pin->gpio, GMODE_INPUT); @@ -31,34 +27,21 @@ void pinMode(pin_size_t pinNumber, PinMode pinMode) { case OUTPUT_OPENDRAIN: gpio_config(pin->gpio, GMODE_SET_HIGH_IMPENDANCE); break; + default: + return; } - pin->enabled |= PIN_GPIO; - pin->mode = pinMode; + pinEnable(pin, PIN_GPIO); + data->gpioMode = pinMode; } void digitalWrite(pin_size_t pinNumber, PinStatus status) { - // verify level is 0 or 1 - if (status > HIGH) - return; - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - // pin is not GPIO yet or not OUTPUT; enable or disable input pullup - if (!pinEnabled(pin, PIN_GPIO) || !pinIsOutput(pin)) { - pinMode(pinNumber, status * INPUT_PULLUP); - return; - } - // write the new state - gpio_output(pin->gpio, status); + pinCheckGetData(pinNumber, PIN_GPIO, ); + pinSetOutputPull(pin, data, pinNumber, status); + gpio_output(pin->gpio, !!status); } PinStatus digitalRead(pin_size_t pinNumber) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return 0; - // pin is not GPIO yet or not INPUT; change the mode - if (!pinEnabled(pin, PIN_GPIO) || !pinIsInput(pin)) - pinMode(pinNumber, INPUT); - // read the value + pinCheckGetData(pinNumber, PIN_GPIO, LOW); + pinSetInputMode(pin, data, pinNumber); return gpio_input(pin->gpio); } diff --git a/cores/beken-72xx/arduino/src/wiring_irq.c b/cores/beken-72xx/arduino/src/wiring_irq.c index d014b420f..884bc03f2 100644 --- a/cores/beken-72xx/arduino/src/wiring_irq.c +++ b/cores/beken-72xx/arduino/src/wiring_irq.c @@ -1,98 +1,70 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-07-31. */ -#include - -#include - -static void *irqHandlerList[PINS_COUNT] = {NULL}; -static void *irqHandlerArgs[PINS_COUNT] = {NULL}; -static bool irqChangeList[PINS_COUNT]; +#include "wiring_private.h" static void irqHandler(unsigned char gpio) { PinInfo *pin = pinByGpio(gpio); if (pin == NULL) return; - uint32_t index = pinIndex(pin); - if (!irqHandlerList[index]) + PinData *data = pinData(pin); + if (!data->irqHandler) return; - if (irqChangeList[index]) { - if (pin->mode == INPUT_PULLDOWN) { - pin->mode = INPUT_PULLUP; + if (data->irqChange) { + if (data->gpioMode == INPUT_PULLDOWN) { + data->gpioMode = INPUT_PULLUP; gpio_int_enable(pin->gpio, GPIO_INT_LEVEL_FALLING, irqHandler); - } else if (pin->mode == INPUT_PULLUP) { - pin->mode = INPUT_PULLDOWN; + } else if (data->gpioMode == INPUT_PULLUP) { + data->gpioMode = INPUT_PULLDOWN; gpio_int_enable(pin->gpio, GPIO_INT_LEVEL_RISING, irqHandler); } } - if (irqHandlerArgs[index] == NULL) { - ((voidFuncPtr)irqHandlerList[index])(); - } else { - ((voidFuncPtrParam)irqHandlerList[index])(irqHandlerArgs[index]); - } -} - -void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) { - attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL); + if (!data->irqParam) + ((voidFuncPtr)data->irqHandler)(); + else + ((voidFuncPtrParam)data->irqHandler)(data->irqParam); } void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) { - PinInfo *pin = pinInfo(interruptNumber); - if (!pin) - return; - if (!pinSupported(pin, PIN_IRQ)) + pinCheckGetData(interruptNumber, PIN_IRQ, ); + + data->irqHandler = callback; + data->irqParam = param; + + if (pinEnabled(pin, PIN_IRQ) && data->irqMode == mode) return; - uint32_t index = pinIndex(pin); - uint32_t event = 0; - PinMode modeNew = 0; - bool change = 0; + // GPIO can't be used together with PWM + pinRemoveMode(pin, PIN_PWM); + + uint32_t event = 0; + bool change = false; switch (mode) { case LOW: - event = GPIO_INT_LEVEL_LOW; - modeNew = INPUT_PULLUP; - change = false; + event = GPIO_INT_LEVEL_LOW; break; case HIGH: - event = GPIO_INT_LEVEL_HIGH; - modeNew = INPUT_PULLDOWN; - change = false; + event = GPIO_INT_LEVEL_HIGH; break; case FALLING: - event = GPIO_INT_LEVEL_FALLING; - modeNew = INPUT_PULLUP; - change = false; + event = GPIO_INT_LEVEL_FALLING; break; case RISING: - event = GPIO_INT_LEVEL_RISING; - modeNew = INPUT_PULLDOWN; - change = false; + event = GPIO_INT_LEVEL_RISING; break; case CHANGE: - event = GPIO_INT_LEVEL_FALLING; - modeNew = INPUT_PULLUP; - change = true; + event = GPIO_INT_LEVEL_FALLING; + change = true; break; default: return; } - irqHandlerList[index] = callback; - irqHandlerArgs[index] = param; - irqChangeList[index] = change; + pinEnable(pin, PIN_IRQ); + data->irqMode = mode; + data->irqChange = change; + gpio_int_enable(pin->gpio, event, irqHandler); - pin->enabled |= PIN_IRQ | PIN_GPIO; - pin->mode = modeNew; } void detachInterrupt(pin_size_t interruptNumber) { - PinInfo *pin = pinInfo(interruptNumber); - if (!pin) - return; - if (!pinSupported(pin, PIN_IRQ)) - return; - uint32_t index = pinIndex(pin); - irqHandlerList[index] = NULL; - irqHandlerArgs[index] = NULL; - irqChangeList[index] = false; - gpio_int_disable(pin->gpio); - pin->enabled &= ~PIN_IRQ; + pinModeRemove(interruptNumber, PIN_IRQ); } diff --git a/cores/beken-72xx/base/api/lt_cpu.c b/cores/beken-72xx/base/api/lt_cpu.c new file mode 100644 index 000000000..d767cbbbc --- /dev/null +++ b/cores/beken-72xx/base/api/lt_cpu.c @@ -0,0 +1,13 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +lt_cpu_model_t lt_cpu_get_model() { + uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID); + return CPU_MODEL_ENUM(FAMILY, chipId); +} + +const char *lt_cpu_get_core_type() { + return "ARM968E-S (ARMv5TE)"; +} diff --git a/cores/beken-72xx/base/api/lt_device.c b/cores/beken-72xx/base/api/lt_device.c new file mode 100644 index 000000000..200802724 --- /dev/null +++ b/cores/beken-72xx/base/api/lt_device.c @@ -0,0 +1,41 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +void lt_get_device_mac(uint8_t *mac) { + cfg_load_mac(mac); +} + +void lt_reboot() { + bk_reboot(); +} + +bool lt_reboot_download_mode() { + bk_reboot(); + return true; +} + +lt_reboot_reason_t lt_get_reboot_reason() { + switch (bk_misc_get_start_type()) { + case RESET_SOURCE_POWERON: + return REBOOT_REASON_POWER; + case RESET_SOURCE_REBOOT: + return REBOOT_REASON_SOFTWARE; + case RESET_SOURCE_WATCHDOG: + return REBOOT_REASON_WATCHDOG; + case RESET_SOURCE_CRASH_XAT0: + case RESET_SOURCE_CRASH_UNDEFINED: + case RESET_SOURCE_CRASH_PREFETCH_ABORT: + case RESET_SOURCE_CRASH_DATA_ABORT: + case RESET_SOURCE_CRASH_UNUSED: + case RESET_SOURCE_CRASH_PER_XAT0: + return REBOOT_REASON_CRASH; + case RESET_SOURCE_DEEPPS_GPIO: + case RESET_SOURCE_DEEPPS_RTC: + case RESET_SOURCE_DEEPPS_USB: + return REBOOT_REASON_SLEEP; + default: + return REBOOT_REASON_UNKNOWN; + } +} diff --git a/cores/beken-72xx/base/api/lt_flash.c b/cores/beken-72xx/base/api/lt_flash.c new file mode 100644 index 000000000..9eaed7888 --- /dev/null +++ b/cores/beken-72xx/base/api/lt_flash.c @@ -0,0 +1,26 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +// can't include as it collides with on Windows -_- +#define REG_FLASH_BASE 0x00803000 +#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4) +#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4) +#define FLASH_BUSY_SW (0x01UL << 31) +#define FLASH_WP_VALUE (0x01UL << 30) +#define FLASH_OP_SW (0x01UL << 29) +#define FLASH_OP_TYPE_POS 24 +#define FLASH_OP_RDID 20 + +lt_flash_id_t lt_flash_get_id() { + uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE; + REG_WRITE(REG_FLASH_OPERATE_SW, data); + while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {} + lt_flash_id_t id = { + .manufacturer_id = REG_RD8(REG_FLASH_RDID, 2), + .chip_id = REG_RD8(REG_FLASH_RDID, 1), + .chip_size_id = REG_RD8(REG_FLASH_RDID, 0), + }; + return id; +} diff --git a/cores/beken-72xx/base/api/lt_init.c b/cores/beken-72xx/base/api/lt_init.c new file mode 100644 index 000000000..05d28b1c2 --- /dev/null +++ b/cores/beken-72xx/base/api/lt_init.c @@ -0,0 +1,9 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +void lt_init_family() { + // set default UART output port + uart_print_port = LT_UART_DEFAULT_PORT - 1; +} diff --git a/cores/beken-72xx/base/api/lt_mem.c b/cores/beken-72xx/base/api/lt_mem.c new file mode 100644 index 000000000..f6038fede --- /dev/null +++ b/cores/beken-72xx/base/api/lt_mem.c @@ -0,0 +1,21 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +uint32_t lt_ram_get_size() { + return 256 * 1024; +} + +uint32_t lt_heap_get_size() { +#if configDYNAMIC_HEAP_SIZE + extern unsigned char _empty_ram; +#if CFG_SOC_NAME == SOC_BK7231N + return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram); +#else + return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram); +#endif +#else + return configTOTAL_HEAP_SIZE; +#endif +} diff --git a/cores/beken-72xx/base/api/lt_ota.c b/cores/beken-72xx/base/api/lt_ota.c new file mode 100644 index 000000000..2961e76f4 --- /dev/null +++ b/cores/beken-72xx/base/api/lt_ota.c @@ -0,0 +1,39 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +lt_ota_type_t lt_ota_get_type() { + return OTA_TYPE_SINGLE; +} + +bool lt_ota_is_valid(uint8_t index) { + if (index != 0) + return false; + // check download RBL + // TODO: maybe check header CRC or even binary hashes + uint32_t magic; + lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4); + return magic == 0x004C4252; // "RBL\0", little-endian +} + +uint8_t lt_ota_dual_get_current() { + return 0; +} + +uint8_t lt_ota_dual_get_stored() { + return 0; +} + +bool lt_ota_switch(bool revert) { + if (!lt_ota_is_valid(0)) + // no valid "download" image + // - return false when trying to activate + // - return true when trying to revert + return revert; + if (revert) { + // there's a valid "download" image, which has to be removed + return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET); + } + return true; +} diff --git a/cores/beken-72xx/base/api/lt_wdt.c b/cores/beken-72xx/base/api/lt_wdt.c new file mode 100644 index 000000000..b0648d087 --- /dev/null +++ b/cores/beken-72xx/base/api/lt_wdt.c @@ -0,0 +1,18 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +bool lt_wdt_enable(uint32_t timeout) { + wdt_ctrl(WCMD_SET_PERIOD, &timeout); + wdt_ctrl(WCMD_POWER_UP, NULL); + return true; +} + +void lt_wdt_disable() { + wdt_ctrl(WCMD_POWER_DOWN, NULL); +} + +void lt_wdt_feed() { + wdt_ctrl(WCMD_RELOAD_PERIOD, NULL); +} diff --git a/cores/beken-72xx/base/lt_api.c b/cores/beken-72xx/base/lt_api.c deleted file mode 100644 index c8994e918..000000000 --- a/cores/beken-72xx/base/lt_api.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ - -#include -#include - -// can't include as it collides with on Windows -_- -#define REG_FLASH_BASE 0x00803000 -#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4) -#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4) -#define FLASH_BUSY_SW (0x01UL << 31) -#define FLASH_WP_VALUE (0x01UL << 30) -#define FLASH_OP_SW (0x01UL << 29) -#define FLASH_OP_TYPE_POS 24 -#define FLASH_OP_RDID 20 - -void lt_init_family() { - // set default UART output port - uart_print_port = LT_UART_DEFAULT_PORT - 1; -} - -/* _____ _____ _ _ - / ____| __ \| | | | - | | | |__) | | | | - | | | ___/| | | | - | |____| | | |__| | - \_____|_| \____*/ -lt_cpu_model_t lt_cpu_get_model() { - uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID); - return CPU_MODEL_ENUM(FAMILY, chipId); -} - -uint32_t lt_cpu_get_unique_id() { - return lt_cpu_get_mac_id(); -} - -uint32_t lt_cpu_get_mac_id() { - uint8_t mac[6]; - cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC) - return (mac[3]) | (mac[4] << 8) | (mac[5] << 16); -} - -const char *lt_cpu_get_core_type() { - return "ARM968E-S"; -} - -/*_____ _ - | __ \ (_) - | | | | _____ ___ ___ ___ - | | | |/ _ \ \ / / |/ __/ _ \ - | |__| | __/\ V /| | (_| __/ - |_____/ \___| \_/ |_|\___\__*/ -void lt_reboot() { - bk_reboot(); -} - -bool lt_reboot_download_mode() { - bk_reboot(); - return true; -} - -lt_reboot_reason_t lt_get_reboot_reason() { - switch (bk_misc_get_start_type()) { - case RESET_SOURCE_POWERON: - return REBOOT_REASON_POWER; - case RESET_SOURCE_REBOOT: - return REBOOT_REASON_SOFTWARE; - case RESET_SOURCE_WATCHDOG: - return REBOOT_REASON_WATCHDOG; - case RESET_SOURCE_CRASH_XAT0: - case RESET_SOURCE_CRASH_UNDEFINED: - case RESET_SOURCE_CRASH_PREFETCH_ABORT: - case RESET_SOURCE_CRASH_DATA_ABORT: - case RESET_SOURCE_CRASH_UNUSED: - case RESET_SOURCE_CRASH_PER_XAT0: - return REBOOT_REASON_CRASH; - case RESET_SOURCE_DEEPPS_GPIO: - case RESET_SOURCE_DEEPPS_RTC: - case RESET_SOURCE_DEEPPS_USB: - return REBOOT_REASON_SLEEP; - default: - return REBOOT_REASON_UNKNOWN; - } -} - -/*______ _ _ - | ____| | | | - | |__ | | __ _ ___| |__ - | __| | |/ _` / __| '_ \ - | | | | (_| \__ \ | | | - |_| |_|\__,_|___/_| |*/ -lt_flash_id_t lt_flash_get_id() { - uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE; - REG_WRITE(REG_FLASH_OPERATE_SW, data); - while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {} - lt_flash_id_t id = { - .manufacturer_id = REG_RD8(REG_FLASH_RDID, 2), - .chip_id = REG_RD8(REG_FLASH_RDID, 1), - .chip_size_id = REG_RD8(REG_FLASH_RDID, 0), - }; - return id; -} - -/*__ __ - | \/ | - | \ / | ___ _ __ ___ ___ _ __ _ _ - | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | - | | | | __/ | | | | | (_) | | | |_| | - |_| |_|\___|_| |_| |_|\___/|_| \__, | - __/ | - |__*/ -uint32_t lt_ram_get_size() { - return 256 * 1024; -} - -uint32_t lt_heap_get_size() { -#if configDYNAMIC_HEAP_SIZE - extern unsigned char _empty_ram; -#if CFG_SOC_NAME == SOC_BK7231N - return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram); -#else - return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram); -#endif -#else - return configTOTAL_HEAP_SIZE; -#endif -} - -/* ____ _______ - / __ \__ __|/\ - | | | | | | / \ - | | | | | | / /\ \ - | |__| | | |/ ____ \ - \____/ |_/_/ \*/ - -lt_ota_type_t lt_ota_get_type() { - return OTA_TYPE_SINGLE; -} - -bool lt_ota_is_valid(uint8_t index) { - if (index != 0) - return false; - // check download RBL - // TODO: maybe check header CRC or even binary hashes - uint32_t magic; - lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4); - return magic == 0x004C4252; // "RBL\0", little-endian -} - -uint8_t lt_ota_dual_get_current() { - return 0; -} - -uint8_t lt_ota_dual_get_stored() { - return 0; -} - -bool lt_ota_switch(bool revert) { - if (!lt_ota_is_valid(0)) - // no valid "download" image - // - return false when trying to activate - // - return true when trying to revert - return revert; - if (revert) { - // there's a valid "download" image, which has to be removed - return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET); - } - return true; -} - -/*_ __ _ _ _ - \ \ / / | | | | | | - \ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _ - \ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` | - \ /\ / (_| | || (__| | | | (_| | (_) | (_| | - \/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, | - __/ | - |___*/ -bool lt_wdt_enable(uint32_t timeout) { - wdt_ctrl(WCMD_SET_PERIOD, &timeout); - wdt_ctrl(WCMD_POWER_UP, NULL); - return true; -} - -void lt_wdt_disable() { - wdt_ctrl(WCMD_POWER_DOWN, NULL); -} - -void lt_wdt_feed() { - wdt_ctrl(WCMD_RELOAD_PERIOD, NULL); -} diff --git a/cores/beken-72xx/base/lt_defs.h b/cores/beken-72xx/base/lt_defs.h index e80ef9d0b..3aac8ce9d 100644 --- a/cores/beken-72xx/base/lt_defs.h +++ b/cores/beken-72xx/base/lt_defs.h @@ -2,11 +2,16 @@ #error "Don't include this file directly" -#define LT_HAS_PRINTF 1 -#define LT_HAS_LWIP 1 -#define LT_HAS_LWIP2 1 -#define LT_HAS_FREERTOS 1 -#define LT_HAS_MBEDTLS 1 +#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 1 +#define LT_HW_WATCHDOG 1 +#define LT_HW_WIFI 1 #define LT_HEAP_FUNC xPortGetFreeHeapSize #define LT_REALLOC_FUNC pvPortRealloc diff --git a/cores/beken-72xx/base/lt_family.h b/cores/beken-72xx/base/lt_family.h index 4484a1d73..cf3139979 100644 --- a/cores/beken-72xx/base/lt_family.h +++ b/cores/beken-72xx/base/lt_family.h @@ -2,15 +2,19 @@ #pragma once -#include LT_VARIANT_H +#include // Choose the main UART output port #ifndef LT_UART_DEFAULT_PORT -#if HAS_SERIAL2 +#if LT_HW_UART2 #define LT_UART_DEFAULT_PORT 2 -#elif HAS_SERIAL1 +#elif LT_HW_UART1 #define LT_UART_DEFAULT_PORT 1 #else #error "No serial port is available" #endif #endif + +// Auto-download-reboot detection pattern +// Link check command (CMD_LinkCheck=0) +#define LT_UART_ADR_PATTERN 0x01, 0xE0, 0xFC, 0x01, 0x00 diff --git a/cores/beken-72xx/base/sdk_extern.h b/cores/beken-72xx/base/sdk_extern.h new file mode 100644 index 000000000..08452acf9 --- /dev/null +++ b/cores/beken-72xx/base/sdk_extern.h @@ -0,0 +1,24 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include + +// SDK +extern uint8_t system_mac[]; +extern int uart_print_port; +uint32_t wdt_ctrl(uint32_t cmd, void *param); +void bk_send_byte(uint8_t uport, uint8_t data); +void uart_hw_set_change(uint8_t uport, bk_uart_config_t *uart_config); +int uart_rx_callback_set(int uport, uart_callback callback, void *param); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/beken-72xx/base/sdk_private.h b/cores/beken-72xx/base/sdk_private.h index 41d6a56c9..268c7dfa4 100644 --- a/cores/beken-72xx/base/sdk_private.h +++ b/cores/beken-72xx/base/sdk_private.h @@ -6,12 +6,18 @@ extern "C" { #endif // __cplusplus -// most stuff is here +// most stuff is here - this has to be before other includes! #include // other includes +#include +#include +#include #include #include #include +#include +#include +#include #include #include #include @@ -19,9 +25,7 @@ extern "C" { #include #include -extern uint8_t system_mac[]; -extern uint32_t wdt_ctrl(uint32_t cmd, void *param); -extern int uart_print_port; +#include // conflict with stl_algobase.h #undef min diff --git a/cores/common/arduino/libraries/api/Serial/Serial.cpp b/cores/common/arduino/libraries/api/Serial/Serial.cpp new file mode 100644 index 000000000..509d3539b --- /dev/null +++ b/cores/common/arduino/libraries/api/Serial/Serial.cpp @@ -0,0 +1,36 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#include "Serial.h" + +SerialClass::SerialClass(uint32_t port, pin_size_t rx, pin_size_t tx) { + this->port = port; + this->rx = rx; + this->tx = tx; + this->buf = NULL; + this->data = NULL; +} + +#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) +static uint8_t adrState = 0; +static uint8_t adrCmd[] = {LT_UART_ADR_PATTERN}; + +void SerialClass::adrParse(uint8_t c) { + adrState = (adrState + 1) * (c == adrCmd[adrState]); + if (adrState == sizeof(adrCmd) / sizeof(uint8_t)) { + LT_I("Auto download mode: rebooting"); + LT.restartDownloadMode(); + } +} +#endif + +int SerialClass::available() { + return this->buf && this->buf->available(); +} + +int SerialClass::peek() { + return this->buf ? this->buf->peek() : -1; +} + +int SerialClass::read() { + return this->buf ? this->buf->read_char() : -1; +} diff --git a/cores/beken-72xx/arduino/libraries/Serial/Serial.h b/cores/common/arduino/libraries/api/Serial/Serial.h similarity index 52% rename from cores/beken-72xx/arduino/libraries/Serial/Serial.h rename to cores/common/arduino/libraries/api/Serial/Serial.h index 5c77c4d4f..1b80b6809 100644 --- a/cores/beken-72xx/arduino/libraries/Serial/Serial.h +++ b/cores/common/arduino/libraries/api/Serial/Serial.h @@ -2,7 +2,7 @@ #pragma once -#include +#include #include #include @@ -10,17 +10,31 @@ using namespace arduino; class SerialClass : public HardwareSerial { private: - uint8_t port; + uint32_t port; + pin_size_t rx; + pin_size_t tx; + + public: + void *data; + + private: RingBuffer *buf; + uint32_t baudrate; + uint16_t config; public: - SerialClass(uint8_t port); + SerialClass(uint32_t port, pin_size_t rx = PIN_INVALID, pin_size_t tx = PIN_INVALID); inline void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); } + inline void configure(unsigned long baudrate) { + configure(baudrate, SERIAL_8N1); + } + void begin(unsigned long baudrate, uint16_t config); + void configure(unsigned long baudrate, uint16_t config); void end(); int available(); int peek(); @@ -32,5 +46,12 @@ class SerialClass : public HardwareSerial { return !!buf; } + public: +#if LT_AUTO_DOWNLOAD_REBOOT + static void adrParse(uint8_t c); +#endif + using Print::write; }; + +#define HAS_SERIAL_CLASS 1 diff --git a/cores/common/arduino/libraries/api/WiFi/WiFi.cpp b/cores/common/arduino/libraries/api/WiFi/WiFi.cpp index 12344dc81..d20012a9e 100644 --- a/cores/common/arduino/libraries/api/WiFi/WiFi.cpp +++ b/cores/common/arduino/libraries/api/WiFi/WiFi.cpp @@ -3,11 +3,11 @@ #include "WiFi.h" void WiFiClass::printDiag(Print &dest) { - const char *modes[] = {"NULL", "STA", "AP", "STA+AP"}; - const char *enc[] = {"Open", "WEP", "WPA PSK", "WPA2 PSK", "WPA/WPA2", "WPA", "WPA2"}; - dest.print("Mode: "); - dest.println(modes[getMode()]); + dest.println(WiFiModeText[getMode()]); + + dest.print("Status: "); + dest.println(WiFiStatusText[status()]); if (getMode() & WIFI_MODE_STA) { dest.println("-- Station --"); @@ -21,7 +21,7 @@ void WiFiClass::printDiag(Print &dest) { dest.print("RSSI: "); dest.println(RSSI()); dest.print("Encryption: "); - dest.println(enc[getEncryption()]); + dest.println(WiFiAuthModeText[getEncryption()]); dest.print("IP: "); dest.println(localIP()); dest.print("MAC: "); diff --git a/cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp b/cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp index 7e205bca9..e3fddb44a 100644 --- a/cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp +++ b/cores/common/arduino/libraries/api/WiFi/WiFiGeneric.cpp @@ -11,7 +11,7 @@ bool WiFiClass::mode(WiFiMode mode) { pWiFi = this; WiFiMode currentMode = getMode(); - LT_DM(WIFI, "Mode changing %u -> %u", currentMode, mode); + LT_DM(WIFI, "Mode changing %s -> %s", WiFiModeText[currentMode], WiFiModeText[mode]); if (mode == currentMode) return true; diff --git a/cores/common/arduino/libraries/api/WiFi/WiFiType.h b/cores/common/arduino/libraries/api/WiFi/WiFiType.h index 027f00b89..3d5942a50 100644 --- a/cores/common/arduino/libraries/api/WiFi/WiFiType.h +++ b/cores/common/arduino/libraries/api/WiFi/WiFiType.h @@ -162,3 +162,28 @@ typedef enum { WLMODE_DISABLE = 1, WLMODE_ENABLE = 2, } WiFiModeAction; + +static const char *WiFiModeText[] = {"NULL", "STA", "AP", "AP+STA"}; +static const char *WiFiStatusText[] = { + "Idle", + "No SSID", + "Scan Completed", + "Connected", + "Connect failed", + "Connection lost", + "Disconnected", +}; +static const char *WiFiAuthModeText[] = { + "Open", + "WEP", + "WPA PSK", + "WPA2 PSK", + "WPA/WPA2 PSK", + "WPA2 EAP", + "WPA3 PSK", + "WPA2/WPA3 PSK", + "WAPI PSK", + "WPA", + "WPA2", + "Auto", +}; diff --git a/cores/common/arduino/libraries/common/Update/Update.cpp b/cores/common/arduino/libraries/common/Update/Update.cpp index 13f83de8a..8c1ff70d8 100644 --- a/cores/common/arduino/libraries/common/Update/Update.cpp +++ b/cores/common/arduino/libraries/common/Update/Update.cpp @@ -2,9 +2,25 @@ #include "Update.h" -UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) { - cleanup(); -} +static const UpdateError errorMap[] = { + UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */ + UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */ + UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */ + UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA scheme */ + UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_INVALID - invalid partition info tag */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */ + UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */ + UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */ + UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */ + UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */ + UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_PROTECT - target area is write-protected */ + UPDATE_ERROR_WRITE, /* UF2_ERR_ALLOC_FAILED - dynamic memory allocation failed */ +}; /** * @brief Initialize the update process. @@ -13,55 +29,93 @@ UpdateClass::UpdateClass() : ctx(NULL), info(NULL), buf(NULL) { * @param command must be U_FLASH * @return false if parameters are invalid or update is running, true otherwise */ -bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, const char *unused4) { - if (ctx) +bool UpdateClass::begin( + size_t size, + int command, + __attribute__((unused)) int ledPin, + __attribute__((unused)) uint8_t ledOn, + __attribute__((unused)) const char *label +) { +#if !LT_HAS_OTA + LT_E("OTA is not yet supported on this chip!"); + this->errArd = UPDATE_ERROR_BAD_ARGUMENT; + return false; +#endif + if (this->ctx) { return false; - cleanup(); - - LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, scheme: %u", size, lt_ota_dual_get_current(), lt_ota_get_uf2_scheme()); - - ctx = uf2_ctx_init(lt_ota_get_uf2_scheme(), FAMILY); - info = uf2_info_init(); - - if (!size) { - cleanup(UPDATE_ERROR_SIZE); + } + this->clearError(); + if (size == 0) { + this->errArd = UPDATE_ERROR_SIZE; return false; } - if (command != U_FLASH) { - cleanup(UPDATE_ERROR_BAD_ARGUMENT); + this->errArd = UPDATE_ERROR_BAD_ARGUMENT; return false; } + if (size == UPDATE_SIZE_UNKNOWN) { + size = 0; + } - bytesTotal = size; + this->ctx = static_cast(malloc(sizeof(lt_ota_ctx_t))); + lt_ota_begin(this->ctx, size); + this->ctx->callback = reinterpret_cast(progressHandler); + this->ctx->callback_param = this; return true; } /** * @brief Finalize the update process. Check for errors and update completion, then activate the new firmware image. * - * @param evenIfRemaining no idea - * @return false in case of errors or no update running, true otherwise + * @param evenIfRemaining don't raise errors if still in progress + * @return false in case of errors or no update running; true otherwise */ bool UpdateClass::end(bool evenIfRemaining) { - if (hasError() || !ctx) - // false if not running + if (!this->ctx) return false; - if (!isFinished() && !evenIfRemaining) { + // update is running or finished; cleanup and end it + if (!isFinished() && !evenIfRemaining) // abort if not finished - cleanup(UPDATE_ERROR_ABORT); - return false; - } - // TODO what is evenIfRemaining for? - // try to activate the second OTA - if (!lt_ota_switch(/* revert= */ false)) { - cleanup(UPDATE_ERROR_ACTIVATE); - return false; + this->errArd = UPDATE_ERROR_ABORT; + + this->cleanup(/* clearError= */ evenIfRemaining); + return !this->hasError(); +} + +/** + * @brief Cleanup (free) the update context. + * Try activating the firmware if possible, set local error codes. + * + * @param clearError assume successful finish after correct activation + */ +void UpdateClass::cleanup(bool clearError) { + if (!this->ctx) + return; + + if (!lt_ota_end(this->ctx)) { + // activating firmware failed + this->errArd = UPDATE_ERROR_ACTIVATE; + this->errUf2 = UF2_ERR_OK; + } else if (clearError) { + // successful finish and activation, clear error codes + this->clearError(); + } else if (this->ctx->error > UF2_ERR_IGNORE) { + // make error code based on UF2OTA code + this->errArd = errorMap[this->ctx->error]; + this->errUf2 = this->ctx->error; + } else { + // only keep Arduino error code (set by the caller) + this->errUf2 = UF2_ERR_OK; } - cleanup(); - return true; +#if LT_DEBUG_OTA + if (this->hasError()) + this->printErrorContext(); +#endif + + free(this->ctx); + this->ctx = nullptr; } /** @@ -69,60 +123,44 @@ bool UpdateClass::end(bool evenIfRemaining) { * * It's advised to write in 512-byte chunks (or its multiples). * - * @param data - * @param len - * @return size_t + * @param data chunk of data + * @param len length of the chunk + * @return size_t amount of bytes written */ -size_t UpdateClass::write(uint8_t *data, size_t len) { - size_t written = 0; - if (hasError() || !ctx) - // 0 if not running +size_t UpdateClass::write(const uint8_t *data, size_t len) { + if (!this->ctx) return 0; - LT_VM(OTA, "write(%u) / buf %u/512", len, bufSize()); - - /* while (buf == bufPos && len >= UF2_BLOCK_SIZE) { - // buffer empty and entire block is in data - if (!tryWriteData(data, UF2_BLOCK_SIZE)) { - // returns 0 if data contains an invalid block - return written; - } - data += UF2_BLOCK_SIZE; - len -= UF2_BLOCK_SIZE; - written += UF2_BLOCK_SIZE; - } */ - - // write until buffer space is available - uint16_t toWrite; // 1..512 - while (len && (toWrite = min(len, bufLeft()))) { - tryWriteData(data, toWrite); - if (hasError()) { - // return on errors - printErrorContext2(data, toWrite); - return written; - } - data += toWrite; - len -= toWrite; - written += toWrite; - } + size_t written = lt_ota_write(ctx, data, len); + if (written != len) + this->cleanup(/* clearError= */ false); return written; } +/** + * @brief Write all data remaining in the given stream. + * + * If the stream doesn't produce any data within UPDATE_TIMEOUT_MS, + * the update process will be aborted. + * + * @param data stream to read from + * @return size_t amount of bytes written + */ size_t UpdateClass::writeStream(Stream &data) { - size_t written = 0; - if (hasError() || !ctx) - // 0 if not running + if (!this->ctx) return 0; + size_t written = 0; uint32_t lastData = millis(); // loop until the update is complete while (remaining()) { // check stream availability - int available = data.available(); + auto available = data.available(); if (available <= 0) { if (millis() - lastData > UPDATE_TIMEOUT_MS) { // waited for data too long; abort with error - cleanup(UPDATE_ERROR_STREAM); + this->errArd = UPDATE_ERROR_STREAM; + this->cleanup(/* clearError= */ false); return written; } continue; @@ -131,94 +169,21 @@ size_t UpdateClass::writeStream(Stream &data) { lastData = millis(); // read data to fit in the remaining buffer space - bufAlloc(); - uint16_t read = data.readBytes(bufPos, bufLeft()); - bufPos += read; - written += read; - tryWriteData(); + auto bufSize = this->ctx->buf_pos - this->ctx->buf; + auto read = data.readBytes(this->ctx->buf_pos, UF2_BLOCK_SIZE - bufSize); + // increment buffer writing head + this->ctx->buf_pos += read; + // process the block if complete + if (bufSize + read == UF2_BLOCK_SIZE) + lt_ota_write_block(this->ctx, reinterpret_cast(this->ctx->buf)); + // abort on errors if (hasError()) { - // return on errors - printErrorContext2(NULL, read); // buf is not valid anymore + this->cleanup(/* clearError= */ false); return written; } + written += read; } return written; } -/** - * @brief Try to use the buffer as a block to write. In case of UF2 errors, - * error codes are set, the update is aborted and 0 is returned - * - * @param data received data to copy to buffer or NULL if already in buffer - * @param len received data length - must be at most bufLeft() - * @return size_t "used" data size - 0 or 512 - */ -size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) { - uf2_block_t *block = NULL; - - LT_VM(OTA, "Writing %u to buffer (%u/512)", len, bufSize()); - - if (len == UF2_BLOCK_SIZE) { - // data has a complete block - block = (uf2_block_t *)data; - } else if (data && len) { - // data has a part of a block, copy it to buffer - bufAlloc(); - memcpy(bufPos, data, len); - bufPos += len; - } - - if (!block && bufSize() == UF2_BLOCK_SIZE) { - // use buffer as block (only if not found above) - block = (uf2_block_t *)buf; - } - - // a complete block has been found - if (block) { - if (checkUf2Error(uf2_check_block(ctx, block))) - // block is invalid - return 0; - - if (errUf2 == UF2_ERR_IGNORE) - // treat ignored blocks as valid - return UF2_BLOCK_SIZE; - - if (!bytesWritten) { - // parse header block to allow retrieving firmware info - if (checkUf2Error(uf2_parse_header(ctx, block, info))) - // header is invalid - return 0; - - LT_IM(OTA, "%s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board); - - if (bytesTotal == UPDATE_SIZE_UNKNOWN) { - // set total update size from block count info - bytesTotal = block->block_count * UF2_BLOCK_SIZE; - } else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) { - // given update size does not match the block count - LT_EM(OTA, "Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE); - cleanup(UPDATE_ERROR_SIZE); - return 0; - } - } else { - // write data blocks normally - if (checkUf2Error(uf2_write(ctx, block))) - // block writing failed - return 0; - } - - // increment total writing progress - bytesWritten += UF2_BLOCK_SIZE; - // call progress callback - if (callback) - callback(bytesWritten, bytesTotal); - // reset the buffer as it's used already - if (bufSize() == UF2_BLOCK_SIZE) - bufPos = buf; - return UF2_BLOCK_SIZE; - } - - return 0; -} - UpdateClass Update; diff --git a/cores/common/arduino/libraries/common/Update/Update.h b/cores/common/arduino/libraries/common/Update/Update.h index 7e817906b..e5277e583 100644 --- a/cores/common/arduino/libraries/common/Update/Update.h +++ b/cores/common/arduino/libraries/common/Update/Update.h @@ -4,39 +4,30 @@ #include #include -// No Error -#define UPDATE_ERROR_OK (0) -// Flash Write Failed -#define UPDATE_ERROR_WRITE (1) -// Flash Erase Failed -#define UPDATE_ERROR_ERASE (2) -// Flash Read Failed -#define UPDATE_ERROR_READ (3) -// Not Enough Space -#define UPDATE_ERROR_SPACE (4) -// Bad Size Given -#define UPDATE_ERROR_SIZE (5) -// Stream Read Timeout -#define UPDATE_ERROR_STREAM (6) -// MD5 Check Failed -#define UPDATE_ERROR_MD5 (7) -// Wrong Magic Byte -#define UPDATE_ERROR_MAGIC_BYTE (8) -// Could Not Activate The Firmware -#define UPDATE_ERROR_ACTIVATE (9) -// Partition Could Not be Found -#define UPDATE_ERROR_NO_PARTITION (10) -// Bad Argument -#define UPDATE_ERROR_BAD_ARGUMENT (11) -// Aborted -#define UPDATE_ERROR_ABORT (12) +typedef enum { + UPDATE_ERROR_OK = 0, //!< No Error + UPDATE_ERROR_WRITE = 1, //!< Flash Write Failed + UPDATE_ERROR_ERASE = 2, //!< Flash Erase Failed + UPDATE_ERROR_READ = 3, //!< Flash Read Failed + UPDATE_ERROR_SPACE = 4, //!< Not Enough Space + UPDATE_ERROR_SIZE = 5, //!< Bad Size Given + UPDATE_ERROR_STREAM = 6, //!< Stream Read Timeout + UPDATE_ERROR_MD5 = 7, //!< MD5 Check Failed + UPDATE_ERROR_MAGIC_BYTE = 8, //!< Wrong Magic Byte + UPDATE_ERROR_ACTIVATE = 9, //!< Could Not Activate The Firmware + UPDATE_ERROR_NO_PARTITION = 10, //!< Partition Could Not be Found + UPDATE_ERROR_BAD_ARGUMENT = 11, //!< Bad Argument + UPDATE_ERROR_ABORT = 12, //!< Aborted +} UpdateError; + +typedef enum { + U_FLASH = 0, + U_SPIFFS = 100, + U_AUTH = 200, +} UpdateCommand; #define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF -#define U_FLASH 0 -#define U_SPIFFS 100 -#define U_AUTH 200 - #define ENCRYPTED_BLOCK_SIZE 16 #define UPDATE_TIMEOUT_MS 30 * 1000 @@ -46,109 +37,132 @@ class UpdateClass { typedef std::function THandlerFunction_Progress; public: /* Update.cpp */ - UpdateClass(); bool begin( - size_t size = UPDATE_SIZE_UNKNOWN, - int command = U_FLASH, - int unused2 = -1, - uint8_t unused3 = LOW, - const char *unused4 = NULL // this is for SPIFFS + size_t size = UPDATE_SIZE_UNKNOWN, + int command = U_FLASH, + int ledPin = -1, + uint8_t ledOn = LOW, + const char *label = nullptr ); bool end(bool evenIfRemaining = false); - size_t write(uint8_t *data, size_t len); + + size_t write(const uint8_t *data, size_t len); size_t writeStream(Stream &data); - bool canRollBack(); - bool rollBack(); - // bool setMD5(const char *expected_md5); private: /* Update.cpp */ - size_t tryWriteData(uint8_t *data = NULL, size_t len = 0); + void cleanup(bool clearError = false); public: /* UpdateUtil.cpp */ - UpdateClass &onProgress(THandlerFunction_Progress callback); - void abort(); - void printError(Print &out); - const char *errorString(); - const char *getFirmwareName(); - const char *getFirmwareVersion(); - const char *getLibreTinyVersion(); - const char *getBoardName(); + UpdateClass &onProgress(THandlerFunction_Progress handler); + static bool canRollBack(); + static bool rollBack(); + uint16_t getErrorCode() const; + bool hasError() const; + void clearError(); + const char *errorString() const; + void printError(Print &out) const; private: /* UpdateUtil.cpp */ - void cleanup(uint8_t ardErr = UPDATE_ERROR_OK, uf2_err_t uf2Err = UF2_ERR_OK); - bool checkUf2Error(uf2_err_t err); - void bufAlloc(); - void printErrorContext1(); - void printErrorContext2(const uint8_t *data, size_t len); - uint16_t bufLeft(); - uint16_t bufSize(); + static void progressHandler(UpdateClass *self); + void printErrorContext(); private: - // uf2ota context - uf2_ota_t *ctx; - uf2_info_t *info; - // block buffer - uint8_t *buf; - uint8_t *bufPos; - // update progress - multiplies of 512 bytes - uint32_t bytesWritten; - uint32_t bytesTotal; - // errors - uf2_err_t errUf2; - uint8_t errArd; - // progress callback - THandlerFunction_Progress callback; - // String _target_md5; - // MD5Builder _md5; + lt_ota_ctx_t *ctx{nullptr}; + uf2_err_t errUf2{UF2_ERR_OK}; + UpdateError errArd{UPDATE_ERROR_OK}; + THandlerFunction_Progress callback{nullptr}; public: - String md5String(void) { - // return _md5.toString(); - return ""; + /** + * @brief Get Arduino error code of the update. + */ + inline UpdateError getError() const { + return this->errArd; } - void md5(uint8_t *result) { - // return _md5.getBytes(result); + /** + * @brief Get UF2OTA error code of the update. + */ + inline uf2_err_t getUF2Error() const { + return this->ctx ? this->ctx->error : this->errUf2; } - uint8_t getError() { - return errArd; + /** + * @brief Same as end(). + */ + inline void abort() { + this->end(); } - uf2_err_t getUF2Error() { - return errUf2; + /** + * @brief Check if the update process has been started. + */ + inline bool isRunning() { + return this->ctx; } - uint16_t getErrorCode() { - return (errArd << 8) | errUf2; + /** + * @brief Check if the update process hasn't been started or has been completed. + */ + inline bool isFinished() { + return !(this->ctx && this->ctx->bytes_written != this->ctx->bytes_total); } - void clearError() { - cleanup(UPDATE_ERROR_OK); + /** + * @brief Return complete update image size. + */ + inline size_t size() { + return this->ctx ? this->ctx->bytes_total : 0; } - bool hasError() { - return errArd != UPDATE_ERROR_OK; + /** + * @brief Return amount of bytes already written. + */ + inline size_t progress() { + return this->ctx ? this->ctx->bytes_written : 0; } - bool isRunning() { - return ctx != NULL; + /** + * @brief Return amount of bytes remaining to write. + */ + inline size_t remaining() { + return this->size() - this->progress(); } - bool isFinished() { - return bytesWritten == bytesTotal; + /** + * @brief Get firmware name from UF2 info. + */ + inline const char *getFirmwareName() { + if (this->ctx) + return this->ctx->info.fw_name; + return nullptr; } - size_t size() { - return bytesTotal; + /** + * @brief Get firmware version from UF2 info. + */ + inline const char *getFirmwareVersion() { + if (this->ctx) + return this->ctx->info.fw_version; + return nullptr; } - size_t progress() { - return bytesWritten; + /** + * @brief Get LibreTiny version from UF2 info. + */ + inline const char *getLibreTinyVersion() { + if (this->ctx) + return this->ctx->info.lt_version; + return nullptr; } - size_t remaining() { - return bytesTotal - bytesWritten; + /** + * @brief Get target board name from UF2 info. + */ + inline const char *getBoardName() { + if (this->ctx) + return this->ctx->info.board; + return nullptr; } }; diff --git a/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp b/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp index d355c7a40..4fe09ee87 100644 --- a/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp +++ b/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp @@ -2,26 +2,46 @@ #include "Update.h" +#include + extern "C" { #include } -static const uint8_t errorMap[] = { - UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */ - UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */ - UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_MAGIC - wrong magic numbers */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */ - UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA scheme */ - UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_INVALID - invalid partition info tag */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */ - UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */ - UPDATE_ERROR_ERASE, /* UF2_ERR_ERASE_FAILED - erasing flash failed */ - UPDATE_ERROR_WRITE, /* UF2_ERR_WRITE_FAILED - writing to flash failed */ - UPDATE_ERROR_WRITE /* UF2_ERR_WRITE_LENGTH - wrote fewer data than requested */ +static const char *errUf2Text[] = { + nullptr, // UF2_ERR_OK (0) + nullptr, // UF2_ERR_IGNORE (1) + "Bad Magic Number", // UF2_ERR_MAGIC (2) + "Bad Family ID", // UF2_ERR_FAMILY (3) + "Missing Header", // UF2_ERR_NOT_HEADER (4) + "Old OTA Format Found", // UF2_ERR_OTA_VER (5) + "Image Not Applicable", // UF2_ERR_OTA_WRONG (6) + "Partition Not Found", // UF2_ERR_PART_404 (7) + "Partition Info Invalid", // UF2_ERR_PART_INVALID (8) + "Partition Info Missing", // UF2_ERR_PART_UNSET (9) + "Block Data Too Long", // UF2_ERR_DATA_TOO_LONG (10) + "Bad Block Sequence Number", // UF2_ERR_SEQ_MISMATCH (11) + "Flash Erase Failed", // UF2_ERR_ERASE_FAILED (12) + "Flash Write Failed", // UF2_ERR_WRITE_FAILED (13) + "Write Failed Length", // UF2_ERR_WRITE_LENGTH (14) + "Partition Write-Protected", // UF2_ERR_WRITE_PROTECT (15) + "Memory Alloc Failed", // UF2_ERR_ALLOC_FAILED (16) +}; + +static const char *errArdText[] = { + nullptr, // UPDATE_ERROR_OK (0) + nullptr, // UPDATE_ERROR_WRITE (1) + nullptr, // UPDATE_ERROR_ERASE (2) + nullptr, // UPDATE_ERROR_READ (3) + nullptr, // UPDATE_ERROR_SPACE (4) + "Bad Size Given", // UPDATE_ERROR_SIZE (5) + "Stream Read Timeout", // UPDATE_ERROR_STREAM (6) + "MD5 Check Failed", // UPDATE_ERROR_MD5 (7) + nullptr, // UPDATE_ERROR_MAGIC_BYTE (8) + "Could Not Activate The Firmware", // UPDATE_ERROR_ACTIVATE (9) + nullptr, // UPDATE_ERROR_NO_PARTITION (10) + "Bad Argument", // UPDATE_ERROR_BAD_ARGUMENT (11) + "Aborted", // UPDATE_ERROR_ABORT (12) }; static char errorStr[14]; @@ -29,163 +49,104 @@ static char errorStr[14]; /** * @brief Set the callback invoked after writing data to flash. */ -UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress callback) { - this->callback = callback; +UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress handler) { + this->callback = std::move(handler); return *this; } -void UpdateClass::cleanup(uint8_t ardErr, uf2_err_t uf2Err) { - errUf2 = uf2Err; - errArd = ardErr; - -#if LT_DEBUG_OTA - if (hasError()) - printErrorContext1(); -#endif - - uf2_ctx_free(ctx); // NULL in constructor - ctx = NULL; - uf2_info_free(info); // NULL in constructor - info = NULL; - free(buf); // NULL in constructor - buf = bufPos = NULL; +void UpdateClass::progressHandler(UpdateClass *self) { + if (self->callback) + self->callback(self->ctx->bytes_written, self->ctx->bytes_total); +} - bytesWritten = 0; - bytesTotal = 0; +/** @copydoc lt_ota_can_rollback() */ +bool UpdateClass::canRollBack() { + return lt_ota_can_rollback(); } -/** - * @brief Check for UF2 errors. Set errArd and errUf2 in case of errors. - * Ignored blocks are not reported as errors. - * Abort the update. - * Use like: "if (errorUf2(...)) return false;" - * @return true if err is not OK, false otherwise - */ -bool UpdateClass::checkUf2Error(uf2_err_t err) { - if (err <= UF2_ERR_IGNORE) +/** @copydoc lt_ota_switch() */ +bool UpdateClass::rollBack() { + if (!lt_ota_can_rollback()) return false; - cleanup(errorMap[err], err); - return true; + return lt_ota_switch(/* revert= */ false); } /** - * @brief Abort the update with UPDATE_ERROR_ABORT reason. + * @brief Get combined error code of the update. */ -void UpdateClass::abort() { - LT_DM(OTA, "Aborting update"); - cleanup(UPDATE_ERROR_ABORT); +uint16_t UpdateClass::getErrorCode() const { + return (this->getError() << 8) | this->getUF2Error(); } -void UpdateClass::bufAlloc() { - if (!buf) - buf = bufPos = (uint8_t *)malloc(UF2_BLOCK_SIZE); +/** + * @brief Check if any error has occurred (incl. aborting the update). + */ +bool UpdateClass::hasError() const { + return this->getError() != UPDATE_ERROR_OK || this->getUF2Error() > UF2_ERR_IGNORE; } -uint16_t UpdateClass::bufLeft() { - return buf + UF2_BLOCK_SIZE - bufPos; +/** + * @brief Clear all errors. This is NOT recommended. + */ +void UpdateClass::clearError() { + this->errArd = UPDATE_ERROR_OK; + this->errUf2 = UF2_ERR_OK; + if (this->ctx) + this->ctx->error = UF2_ERR_OK; } -uint16_t UpdateClass::bufSize() { - return bufPos - buf; +/** + * @brief Get a textual description of the error. + */ +const char *UpdateClass::errorString() const { + uint8_t err; + if ((err = this->getUF2Error()) > UF2_ERR_IGNORE) + return errUf2Text[err]; + if ((err = this->getError()) != UPDATE_ERROR_OK) + return errArdText[err]; + if (!this->hasError()) + return ""; + sprintf(errorStr, "ard=%u,uf2=%u", this->getError(), this->getUF2Error()); + return errorStr; } /** * @brief Print string error info to the stream. */ -void UpdateClass::printError(Print &out) { +void UpdateClass::printError(Print &out) const { out.println(errorString()); } /** * @brief Print details about the error and current OTA state. */ -void UpdateClass::printErrorContext1() { +void UpdateClass::printErrorContext() { #if LT_DEBUG_OTA + if (!this->ctx) + return; + LT_EM(OTA, "Error: %s", errorString()); if (errArd == UPDATE_ERROR_ABORT) return; - LT_EM(OTA, "- written: %u of %u", bytesWritten, bytesTotal); - LT_EM(OTA, "- buf: size=%u, left=%u", bufSize(), bufLeft()); - hexdump(buf, bufSize()); + LT_EM(OTA, "- written: %u of %u", this->ctx->bytes_written, this->ctx->bytes_total); + LT_EM( + OTA, + "- buf: size=%lld, left=%lld", + this->ctx->buf_pos - this->ctx->buf, + this->ctx->buf + UF2_BLOCK_SIZE - this->ctx->buf_pos + ); + hexdump(this->ctx->buf, this->ctx->buf_pos - this->ctx->buf); if (ctx) LT_EM( OTA, "- ctx: seq=%u, part=%s", - ctx->seq - 1, // print last parsed block seq - ctx->part ? ctx->part->name : NULL + this->ctx->uf2.seq - 1, // print last parsed block seq + this->ctx->uf2.part ? this->ctx->uf2.part->name : nullptr ); - uf2_block_t *block = (uf2_block_t *)buf; - if (buf) - LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len); -#endif -} - -void UpdateClass::printErrorContext2(const uint8_t *data, size_t len) { -#if LT_DEBUG_OTA - LT_EM(OTA, "- while writing %u bytes", len); - if (data) - hexdump(data, len); + auto *block = (uf2_block_t *)this->ctx->buf; + LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len); #endif } - -/** - * @brief Get string representation of the error in format - * "ard=..,uf2=..". Returns "" if no error. - */ -const char *UpdateClass::errorString() { - if (!errArd && !errUf2) - return ""; - sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2); - return errorStr; -} - -/** - * @brief Get firmware name from UF2 info. - */ -const char *UpdateClass::getFirmwareName() { - if (info) - return info->fw_name; - return NULL; -} - -/** - * @brief Get firmware version from UF2 info. - */ -const char *UpdateClass::getFirmwareVersion() { - if (info) - return info->fw_version; - return NULL; -} - -/** - * @brief Get LibreTiny version from UF2 info. - */ -const char *UpdateClass::getLibreTinyVersion() { - if (info) - return info->lt_version; - return NULL; -} - -/** - * @brief Get target board name from UF2 info. - */ -const char *UpdateClass::getBoardName() { - if (info) - return info->board; - return NULL; -} - -/** @copydoc lt_ota_can_rollback() */ -bool UpdateClass::canRollBack() { - return lt_ota_can_rollback(); -} - -/** @copydoc lt_ota_switch() */ -bool UpdateClass::rollBack() { - if (!lt_ota_can_rollback()) - return false; - return lt_ota_switch(false); -} diff --git a/cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp b/cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp index 6e2dedeca..bf5e0e1ab 100644 --- a/cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp +++ b/cores/common/arduino/libraries/common/WiFiUdp/LwIPUdp.cpp @@ -72,11 +72,11 @@ uint8_t LwIPUDP::begin(IPAddress address, uint16_t port) { } uint8_t LwIPUDP::begin(uint16_t p) { - return begin(IPAddress(INADDR_ANY), p); + return begin(IPAddress((uint32_t)INADDR_ANY), p); } uint8_t LwIPUDP::beginMulticast(IPAddress a, uint16_t p) { - if (begin(IPAddress(INADDR_ANY), p)) { + if (begin(IPAddress((uint32_t)INADDR_ANY), p)) { if ((uint32_t)a != 0) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = (in_addr_t)a; @@ -111,14 +111,14 @@ void LwIPUDP::stop() { mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip; mreq.imr_interface.s_addr = (in_addr_t)0; setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - multicast_ip = IPAddress(INADDR_ANY); + multicast_ip = IPAddress((uint32_t)INADDR_ANY); } close(udp_server); udp_server = -1; } int LwIPUDP::beginMulticastPacket() { - if (!server_port || multicast_ip == IPAddress(INADDR_ANY)) + if (!server_port || multicast_ip == IPAddress((uint32_t)INADDR_ANY)) return 0; remote_ip = multicast_ip; remote_port = server_port; diff --git a/cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp b/cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp index 40c16caf3..0f602dbc5 100644 --- a/cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp +++ b/cores/common/arduino/libraries/common/mDNS/LwIPmDNS.cpp @@ -13,6 +13,13 @@ extern "C" { #include } +#if LWIP_VERSION_SIMPLE < 20100 && defined(LWIP_NETIF_EXT_STATUS_CALLBACK) +#warning "LWIP_NETIF_EXT_STATUS_CALLBACK not available before lwIP 2.1.0" +#undef LWIP_NETIF_EXT_STATUS_CALLBACK +#endif + +#if LWIP_MDNS_RESPONDER + static std::vector services_name; static std::vector services; static std::vector protos; @@ -81,7 +88,11 @@ static bool enableMDNS(struct netif *netif) { err_t ret = mdns_resp_add_netif(netif, hostName, 255); if (ret == ERR_OK) { LT_DM(MDNS, "mDNS started on netif %u, announcing it to network", netif->num); +#if LWIP_VERSION_SIMPLE >= 20100 mdns_resp_announce(netif); +#else +#warning "lwIP version older than 2.1.0, mdns_resp_announce() unavailable" +#endif return true; } else { LT_DM(MDNS, "Cannot start mDNS on netif %u; ret=%d, errno=%d", netif->num, ret, errno); @@ -188,3 +199,5 @@ bool mDNS::addServiceTxtImpl(const char *service, uint8_t proto, const char *ite MDNSResponder MDNS; #endif + +#endif diff --git a/cores/common/arduino/src/Arduino.h b/cores/common/arduino/src/Arduino.h index dec3c7ef8..4688e28ff 100644 --- a/cores/common/arduino/src/Arduino.h +++ b/cores/common/arduino/src/Arduino.h @@ -25,6 +25,7 @@ using std::min; #include #ifdef __cplusplus #include +using namespace arduino; #endif // Include family-specific code @@ -42,15 +43,17 @@ using std::min; #if defined(__cplusplus) && LT_ARD_HAS_SERIAL #include -#if HAS_SERIAL0 +#if HAS_SERIAL_CLASS +#if LT_HW_UART0 extern SerialClass Serial0; #endif -#if HAS_SERIAL1 +#if LT_HW_UART1 extern SerialClass Serial1; #endif -#if HAS_SERIAL2 +#if LT_HW_UART2 extern SerialClass Serial2; #endif +#endif #define SerialN(x) Serial##x #define SerialM(x) SerialN(x) diff --git a/cores/common/arduino/src/common/hooks.c b/cores/common/arduino/src/common/hooks.c deleted file mode 100644 index 3f9b0a92c..000000000 --- a/cores/common/arduino/src/common/hooks.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (c) 2015 Arduino LLC. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * Empty yield() hook. - * - * This function is intended to be used by library writers to build - * libraries or sketches that supports cooperative threads. - * - * Its defined as a weak symbol and it can be redefined to implement a - * real cooperative scheduler. - */ -static void __empty() { - // Empty -} - -void yield(void) __attribute__((weak, alias("__empty"))); - -/** - * SysTick hook - * - * This function is called from SysTick handler, before the default - * handler provided by Arduino. - */ -static int __false() { - // Return false - return 0; -} - -int sysTickHook(void) __attribute__((weak, alias("__false"))); - -/** - * SVC hook - * PendSV hook - * - * These functions are called from SVC handler, and PensSV handler. - * Default action is halting. - */ -static void __halt() { - // Halts - while (1) - ; -} - -void svcHook(void) __attribute__((weak, alias("__halt"))); -void pendSVHook(void) __attribute__((weak, alias("__halt"))); diff --git a/cores/common/arduino/src/wiring/wiring.c b/cores/common/arduino/src/wiring/wiring.c new file mode 100644 index 000000000..f96fa62a9 --- /dev/null +++ b/cores/common/arduino/src/wiring/wiring.c @@ -0,0 +1,22 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#include + +#if LT_HAS_FREERTOS + +__attribute__((weak)) void delay(uint32_t ms) { + vTaskDelay(pdMS_TO_TICKS(ms)); +} + +__attribute__((weak)) void yield() { + runPeriodicTasks(); + vTaskDelay(1); + taskYIELD(); + lt_wdt_feed(); +} + +#else + +__attribute__((weak)) void yield() {} + +#endif diff --git a/cores/common/arduino/src/wiring_compat.cpp b/cores/common/arduino/src/wiring/wiring_compat.cpp similarity index 100% rename from cores/common/arduino/src/wiring_compat.cpp rename to cores/common/arduino/src/wiring/wiring_compat.cpp diff --git a/cores/common/arduino/src/wiring_compat.h b/cores/common/arduino/src/wiring/wiring_compat.h similarity index 100% rename from cores/common/arduino/src/wiring_compat.h rename to cores/common/arduino/src/wiring/wiring_compat.h diff --git a/cores/common/arduino/src/wiring_custom.c b/cores/common/arduino/src/wiring/wiring_custom.c similarity index 89% rename from cores/common/arduino/src/wiring_custom.c rename to cores/common/arduino/src/wiring/wiring_custom.c index a002ea169..31bd58265 100644 --- a/cores/common/arduino/src/wiring_custom.c +++ b/cores/common/arduino/src/wiring/wiring_custom.c @@ -1,6 +1,6 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-20. */ -#include "wiring_custom.h" +#include "wiring_private.h" #if LT_HAS_FREERTOS #include @@ -32,6 +32,18 @@ void runPeriodicTasks() { #endif } +/** + * @brief Disable modes specified by 'mask'. + */ +void pinModeRemove(pin_size_t pinNumber, uint32_t mask) { + PinInfo *pin = pinInfo(pinNumber); + if (!pin) + return; + pinRemoveMode(pin, mask); + if (pin->enabled == PIN_NONE && mask == PIN_MODE_ALL) + pinRemoveData(pin); +} + /** * @brief Get PinInfo struct for the specified number. * Returns NULL if pin number is invalid. @@ -85,20 +97,6 @@ bool pinEnabled(PinInfo *pin, uint32_t mask) { return (pin->enabled & mask) == mask; } -/** - * @brief Check if GPIO pin is configured as output. - */ -bool pinIsOutput(PinInfo *pin) { - return pin->mode == OUTPUT || pin->mode == OUTPUT_OPENDRAIN; -} - -/** - * @brief Check if GPIO pin is configured as output. - */ -bool pinIsInput(PinInfo *pin) { - return pin->mode == INPUT || pin->mode == INPUT_PULLUP || pin->mode == INPUT_PULLDOWN; -} - /** * @brief Read voltage from ADC and return a value between 0 and * the current reading resolution. diff --git a/cores/common/arduino/src/wiring_custom.h b/cores/common/arduino/src/wiring/wiring_custom.h similarity index 81% rename from cores/common/arduino/src/wiring_custom.h rename to cores/common/arduino/src/wiring/wiring_custom.h index 4dd43f15f..7be9b6b83 100644 --- a/cores/common/arduino/src/wiring_custom.h +++ b/cores/common/arduino/src/wiring/wiring_custom.h @@ -21,6 +21,10 @@ extern "C" { #define PIN_SWD (1 << 10) #define PIN_UART (1 << 11) +#define PIN_MODE_ALL 0xFFFFFFFF + +typedef struct PinData_s PinData; + typedef struct { /** * @brief GPIO name in the family SDK. @@ -35,9 +39,9 @@ typedef struct { */ uint32_t enabled; /** - * @brief Pin mode (direction, IRQ level, etc.). + * @brief Pin data (direction, IRQ level, etc.). The structure is family-specific. */ - uint32_t mode; + PinData *data; } PinInfo; extern PinInfo lt_arduino_pin_info_list[PINS_COUNT]; @@ -55,14 +59,21 @@ bool startMainTask(void); void mainTask(const void *arg); // implemented in main.cpp void runPeriodicTasks(); // implemented in wiring_custom.c +void pinModeRemove(pin_size_t pinNumber, uint32_t mask); PinInfo *pinInfo(pin_size_t pinNumber); PinInfo *pinByIndex(uint32_t index); PinInfo *pinByGpio(uint32_t gpio); uint32_t pinIndex(PinInfo *pin); bool pinSupported(PinInfo *pin, uint32_t mask); bool pinEnabled(PinInfo *pin, uint32_t mask); -bool pinIsOutput(PinInfo *pin); -bool pinIsInput(PinInfo *pin); +void pinRemoveMode(PinInfo *pin, uint32_t mask); + +/** + * @brief Deinitialize the pin, by removing all enabled modes. + */ +inline void pinModeNone(pin_size_t pinNumber) { + pinModeRemove(pinNumber, PIN_MODE_ALL); +} int analogRead(pin_size_t pinNumber); void analogReadResolution(int res); diff --git a/cores/common/arduino/src/wiring/wiring_irq.c b/cores/common/arduino/src/wiring/wiring_irq.c new file mode 100644 index 000000000..f8efc37c8 --- /dev/null +++ b/cores/common/arduino/src/wiring/wiring_irq.c @@ -0,0 +1,7 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#include + +void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) { + attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL); +} diff --git a/cores/common/arduino/src/common/WMath.cpp b/cores/common/arduino/src/wiring/wiring_math.cpp similarity index 100% rename from cores/common/arduino/src/common/WMath.cpp rename to cores/common/arduino/src/wiring/wiring_math.cpp diff --git a/cores/common/arduino/src/wiring/wiring_private.c b/cores/common/arduino/src/wiring/wiring_private.c new file mode 100644 index 000000000..0810d805e --- /dev/null +++ b/cores/common/arduino/src/wiring/wiring_private.c @@ -0,0 +1,25 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#include "wiring_private.h" + +#if __has_include() +/** + * @brief Allocate and return a PinData structure (family-specific). + */ +PinData *pinData(PinInfo *pin) { + if (pin->data == NULL) { + pin->data = calloc(1, sizeof(PinData)); + } + return (PinData *)pin->data; +} + +/** + * @brief Deallocate the PinData structure. + */ +void pinRemoveData(PinInfo *pin) { + if (pin->data != NULL) { + free(pin->data); + } + pin->data = NULL; +} +#endif diff --git a/cores/common/arduino/src/wiring/wiring_private.h b/cores/common/arduino/src/wiring/wiring_private.h new file mode 100644 index 000000000..5d0b9c358 --- /dev/null +++ b/cores/common/arduino/src/wiring/wiring_private.h @@ -0,0 +1,64 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include + +#if __has_include() +#include +#endif + +#if __has_include() +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PinData *pinData(PinInfo *pin); +void pinRemoveData(PinInfo *pin); + +inline void pinEnable(PinInfo *pin, uint32_t mask) { + pin->enabled |= mask; +} + +inline void pinDisable(PinInfo *pin, uint32_t mask) { + pin->enabled &= ~mask; +} + +#define pinCheckGetInfo(pinNumber, mask, ret) \ + PinInfo *pin = pinInfo(pinNumber); \ + if (!pin) \ + return ret; \ + if (!pinSupported(pin, mask)) \ + return ret; + +#define pinCheckGetData(pinNumber, mask, ret) \ + PinInfo *pin = pinInfo(pinNumber); \ + if (!pin) \ + return ret; \ + if (!pinSupported(pin, mask)) \ + return ret; \ + PinData *data = pinData(pin); + +#define pinIsOutput(pin, data) (pinEnabled(pin, PIN_GPIO) && (data->gpioMode ^ 0b101) < 5) +#define pinIsInput(pin, data) (pinEnabled(pin, PIN_GPIO) && (data->gpioMode ^ 0b101) > 4) + +#define pinSetOutputPull(pin, data, pinNumber, status) \ + do { \ + if (!pinIsOutput(pin, data)) { \ + pinMode(pinNumber, INPUT_PULLDOWN ^ !!status); \ + return; \ + } \ + } while (0); + +#define pinSetInputMode(pin, data, pinNumber) \ + do { \ + if (!pinIsInput(pin, data)) \ + pinMode(pinNumber, INPUT); \ + } while (0); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/common/arduino/src/common/wiring_shift.c b/cores/common/arduino/src/wiring/wiring_shift.c similarity index 100% rename from cores/common/arduino/src/common/wiring_shift.c rename to cores/common/arduino/src/wiring/wiring_shift.c diff --git a/cores/common/base/api/lt_cpu.c b/cores/common/base/api/lt_cpu.c new file mode 100644 index 000000000..56aa518e8 --- /dev/null +++ b/cores/common/base/api/lt_cpu.c @@ -0,0 +1,58 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_cpu.h" + +#if LT_HAS_FREERTOS +#include +#include +#endif + +lt_cpu_family_t lt_cpu_get_family() { + return FAMILY; +} + +const char *lt_cpu_get_family_name() { + return STRINGIFY_MACRO(FAMILY) + 2; +} + +__attribute__((weak)) lt_cpu_model_t lt_cpu_get_model() { + return MCU; +} + +const char *lt_cpu_get_model_name() { + return STRINGIFY_MACRO(MCU); +} + +const char *lt_cpu_get_model_code() { + return STRINGIFY_MACRO(MCULC); +} + +__attribute__((weak)) uint32_t lt_cpu_get_unique_id() { + return lt_cpu_get_mac_id(); +} + +__attribute__((weak)) uint32_t lt_cpu_get_mac_id() { + uint8_t mac[6]; + lt_get_device_mac(mac); + return (mac[3] << 0) | (mac[4] << 8) | (mac[5] << 16); +} + +__attribute__((weak)) uint8_t lt_cpu_get_core_count() { + return 1; +} + +#if LT_HAS_FREERTOS +__attribute__((weak)) uint32_t lt_cpu_get_freq() { + return configCPU_CLOCK_HZ; +} +#endif + +uint32_t lt_cpu_get_freq_mhz() { + return lt_cpu_get_freq() / 1000000; +} + +#if LT_HAS_FREERTOS +__attribute__((weak)) uint32_t lt_cpu_get_cycle_count() { + return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ); +} +#endif diff --git a/cores/common/base/api/lt_cpu.h b/cores/common/base/api/lt_cpu.h index 7feb52349..f2c6b80c2 100644 --- a/cores/common/base/api/lt_cpu.h +++ b/cores/common/base/api/lt_cpu.h @@ -38,6 +38,7 @@ uint32_t lt_cpu_get_unique_id(); /** * @brief Get CPU ID based on the last three octets of MAC address. * Note: the number is 24-bit (with the MSB being zero). + * The 3rd-to-last octet is least-significant, the last octet is most-significant. */ uint32_t lt_cpu_get_mac_id(); diff --git a/cores/common/base/api/lt_device.c b/cores/common/base/api/lt_device.c new file mode 100644 index 000000000..983ddadad --- /dev/null +++ b/cores/common/base/api/lt_device.c @@ -0,0 +1,80 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_device.h" + +static char *device_name = NULL; + +const char *lt_get_version() { + return LT_VERSION_STR; +} + +const char *lt_get_board_code() { + return LT_BOARD_STR; +} + +const char *lt_get_device_name() { + if (device_name) + return device_name; + uint32_t chip_id = lt_cpu_get_mac_id(); + uint8_t *id = (uint8_t *)&chip_id; + + const char *model = lt_cpu_get_model_code(); + uint8_t model_len = strlen(model); + device_name = (char *)malloc(3 + model_len + 1 + 6 + 1); + + sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]); + return device_name; +} + +__attribute__((weak)) void lt_reboot() { + // The Watchdog Way + lt_wdt_enable(1L); + while (1) {} +} + +__attribute__((weak)) bool lt_reboot_wdt() { + if (!lt_wdt_enable(1L)) + return false; + while (1) {} +} + +__attribute__((weak)) bool lt_reboot_download_mode() { + return false; +} + +__attribute__((weak)) lt_reboot_reason_t lt_get_reboot_reason() { + return REBOOT_REASON_UNKNOWN; +} + +const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) { + if (!reason) + reason = lt_get_reboot_reason(); + switch (reason) { + case REBOOT_REASON_POWER: + return "Power-On"; + case REBOOT_REASON_BROWNOUT: + return "Brownout"; + case REBOOT_REASON_HARDWARE: + return "HW Reboot"; + case REBOOT_REASON_SOFTWARE: + return "SW Reboot"; + case REBOOT_REASON_WATCHDOG: + return "WDT Reset"; + case REBOOT_REASON_CRASH: + return "Crash"; + case REBOOT_REASON_SLEEP: + return "Sleep Wakeup"; + case REBOOT_REASON_DEBUGGER: + return "Debugger"; + default: + return "Unknown"; + } +} + +__attribute__((weak)) bool lt_set_debug_mode(lt_debug_mode_t mode) { + return false; +} + +__attribute__((weak)) void lt_gpio_recover() { + lt_set_debug_mode(DEBUG_MODE_OFF); +} diff --git a/cores/common/base/api/lt_device.h b/cores/common/base/api/lt_device.h index d57560ec5..5074c2820 100644 --- a/cores/common/base/api/lt_device.h +++ b/cores/common/base/api/lt_device.h @@ -4,6 +4,41 @@ #include +#define RESET_REASON_UNKNOWN REBOOT_REASON_UNKNOWN +#define RESET_REASON_POWER REBOOT_REASON_POWER +#define RESET_REASON_BROWNOUT REBOOT_REASON_BROWNOUT +#define RESET_REASON_HARDWARE REBOOT_REASON_HARDWARE +#define RESET_REASON_SOFTWARE REBOOT_REASON_SOFTWARE +#define RESET_REASON_WATCHDOG REBOOT_REASON_WATCHDOG +#define RESET_REASON_CRASH REBOOT_REASON_CRASH +#define RESET_REASON_SLEEP REBOOT_REASON_SLEEP +#define RESET_REASON_MAX REBOOT_REASON_MAX + +/** + * @brief Reset reason enumeration. + */ +typedef enum { + REBOOT_REASON_UNKNOWN = 1, + REBOOT_REASON_POWER = 2, + REBOOT_REASON_BROWNOUT = 3, + REBOOT_REASON_HARDWARE = 4, + REBOOT_REASON_SOFTWARE = 5, + REBOOT_REASON_WATCHDOG = 6, + REBOOT_REASON_CRASH = 7, + REBOOT_REASON_SLEEP = 8, + REBOOT_REASON_DEBUGGER = 9, + REBOOT_REASON_MAX = 10, +} lt_reboot_reason_t; + +/** + * @brief Debugging mode enumeration. + */ +typedef enum { + DEBUG_MODE_OFF = 0, + DEBUG_MODE_JTAG = 1, + DEBUG_MODE_SWD = 2, +} lt_debug_mode_t; + /** * @brief Get LibreTiny version string. */ @@ -15,11 +50,18 @@ const char *lt_get_version(); const char *lt_get_board_code(); /** - * @brief Get device friendly name in format "LT--". + * @brief Get device friendly name in format "LT--". * Can be used as hostname. */ const char *lt_get_device_name(); +/** + * @brief Read device's *default* MAC address into 'mac' array. + * This can be used even without Wi-Fi enabled, and will ignore + * user-changed Wi-Fi MAC (if changing is possible). + */ +void lt_get_device_mac(uint8_t *mac); + /** * @brief Reboot the CPU. */ @@ -51,6 +93,13 @@ lt_reboot_reason_t lt_get_reboot_reason(); */ const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason); +/** + * @brief Set debugger mode (JTAG, SWD or OFF). + * + * @return whether the mode is supported, and setting it was successful + */ +bool lt_set_debug_mode(lt_debug_mode_t mode); + /** * @brief Reconfigure GPIO pins used for debugging * (SWD/JTAG), so that they can be used as normal I/O. diff --git a/cores/common/base/api/lt_flash.c b/cores/common/base/api/lt_flash.c new file mode 100644 index 000000000..b97c5d53b --- /dev/null +++ b/cores/common/base/api/lt_flash.c @@ -0,0 +1,39 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_flash.h" + +#include + +__attribute__((weak)) uint32_t lt_flash_get_size() { + lt_flash_id_t id = lt_flash_get_id(); + if (id.chip_size_id >= 0x14 && id.chip_size_id <= 0x19) { + return (1 << id.chip_size_id); + } +#ifdef FLASH_LENGTH + return FLASH_LENGTH; +#else + return 0; +#endif +} + +bool lt_flash_erase(uint32_t offset, size_t length) { + return fal_partition_erase(fal_root_part, offset, length) >= 0; +} + +bool lt_flash_erase_block(uint32_t offset) { + return fal_partition_erase(fal_root_part, offset, 1) >= 0; +} + +uint32_t lt_flash_read(uint32_t offset, uint8_t *data, size_t length) { + int ret = fal_partition_read(fal_root_part, offset, data, length); + if (ret == -1) + return 0; + return ret; +} + +uint32_t lt_flash_write(uint32_t offset, const uint8_t *data, size_t length) { + int ret = fal_partition_write(fal_root_part, offset, data, length); + if (ret == -1) + return 0; + return ret; +} diff --git a/cores/common/base/api/lt_flash.h b/cores/common/base/api/lt_flash.h index 549d6cbf4..6aba88392 100644 --- a/cores/common/base/api/lt_flash.h +++ b/cores/common/base/api/lt_flash.h @@ -4,6 +4,15 @@ #include +/** + * @brief Flash chip ID structure. + */ +typedef struct { + uint8_t manufacturer_id; + uint8_t chip_id; + uint8_t chip_size_id; +} lt_flash_id_t; + /** * @brief Read flash chip ID and return a lt_flash_id_t struct. */ diff --git a/cores/common/base/api/lt_mem.c b/cores/common/base/api/lt_mem.c new file mode 100644 index 000000000..eae8685d9 --- /dev/null +++ b/cores/common/base/api/lt_mem.c @@ -0,0 +1,26 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_mem.h" + +#if LT_HAS_FREERTOS +#include +#include +#endif + +#if LT_HAS_FREERTOS +__attribute__((weak)) uint32_t lt_heap_get_size() { + return configTOTAL_HEAP_SIZE; +} + +__attribute__((weak)) uint32_t lt_heap_get_free() { + return xPortGetFreeHeapSize(); +} + +__attribute__((weak)) uint32_t lt_heap_get_min_free() { + return xPortGetMinimumEverFreeHeapSize(); +} +#endif + +__attribute__((weak)) uint32_t lt_heap_get_max_alloc() { + return 0; +} diff --git a/cores/common/base/api/lt_ota.c b/cores/common/base/api/lt_ota.c new file mode 100644 index 000000000..7eb27e2a4 --- /dev/null +++ b/cores/common/base/api/lt_ota.c @@ -0,0 +1,156 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_ota.h" + +#include + +static inline size_t lt_ota_buf_left(lt_ota_ctx_t *ctx) { + return ctx->buf + UF2_BLOCK_SIZE - ctx->buf_pos; +} + +static inline size_t lt_ota_buf_size(lt_ota_ctx_t *ctx) { + return ctx->buf_pos - ctx->buf; +} + +void lt_ota_begin(lt_ota_ctx_t *ctx, size_t size) { + if (!ctx) + return; + + memset(ctx, 0, sizeof(lt_ota_ctx_t)); + uf2_ctx_init(&ctx->uf2, lt_ota_get_uf2_scheme(), lt_cpu_get_family()); + uf2_info_init(&ctx->info); + ctx->buf_pos = ctx->buf; + ctx->bytes_total = size; + ctx->running = true; + + lt_ota_set_write_protect(&ctx->uf2); + + LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, scheme: %u", size, lt_ota_dual_get_current(), lt_ota_get_uf2_scheme()); +} + +bool lt_ota_end(lt_ota_ctx_t *ctx) { + if (!ctx || !ctx->running) + return true; + + uf2_ctx_free(&ctx->uf2); + uf2_info_free(&ctx->info); + ctx->running = false; + + if (ctx->bytes_written && ctx->bytes_written == ctx->bytes_total) { + // try to activate the 2nd image + return lt_ota_switch(/* revert= */ false); + } + + // activation not attempted (update aborted) + return true; +} + +__attribute__((weak)) void lt_ota_set_write_protect(uf2_ota_t *uf2) {} + +size_t lt_ota_write(lt_ota_ctx_t *ctx, const uint8_t *data, size_t len) { + if (!ctx || !ctx->running) + return 0; + + // write until buffer space is available + size_t written = 0; + uint16_t to_write; // 1..512 + while (len && (to_write = MIN((uint16_t)len, lt_ota_buf_left(ctx)))) { + LT_VM(OTA, "Writing %u to buffer (%u/512)", len, lt_ota_buf_size(ctx)); + + uf2_block_t *block = NULL; + if (to_write == UF2_BLOCK_SIZE) { + // data has a complete block; don't use the buffer + block = (uf2_block_t *)data; + } else { + // data has a part of a block; append it to the buffer + memcpy(ctx->buf_pos, data, to_write); + ctx->buf_pos += to_write; + if (lt_ota_buf_size(ctx) == UF2_BLOCK_SIZE) { + // the block is complete now + block = (uf2_block_t *)ctx->buf; + } + } + + // write if a block is ready + if (block && lt_ota_write_block(ctx, block) == false) + // return on errors + return written; + data += to_write; + len -= to_write; + written += to_write; + } + return written; +} + +bool lt_ota_write_block(lt_ota_ctx_t *ctx, uf2_block_t *block) { + ctx->error = uf2_check_block(&ctx->uf2, block); + if (ctx->error > UF2_ERR_IGNORE) + // block is invalid + return false; + + if (!ctx->bytes_written) { + // parse header block to allow retrieving firmware info + ctx->error = uf2_parse_header(&ctx->uf2, block, &ctx->info); + if (ctx->error != UF2_ERR_OK) + return false; + + LT_IM( + OTA, + "%s v%s - LT v%s @ %s", + ctx->info.fw_name, + ctx->info.fw_version, + ctx->info.lt_version, + ctx->info.board + ); + + if (ctx->bytes_total == 0) { + // set total update size from block count info + ctx->bytes_total = block->block_count * UF2_BLOCK_SIZE; + } else if (ctx->bytes_total != block->block_count * UF2_BLOCK_SIZE) { + // given update size does not match the block count + LT_EM( + OTA, + "Image size wrong; got %u, calculated %llu", + ctx->bytes_total, + block->block_count * UF2_BLOCK_SIZE + ); + return false; + } + } else if (ctx->error == UF2_ERR_OK) { + // write data blocks normally + ctx->error = uf2_write(&ctx->uf2, block); + if (ctx->error > UF2_ERR_IGNORE) + // block writing failed + return false; + } + + // increment total writing progress + ctx->bytes_written += UF2_BLOCK_SIZE; + // call progress callback + if (ctx->callback) + ctx->callback(ctx->callback_param); + // reset the buffer as it's used already + if (lt_ota_buf_size(ctx) == UF2_BLOCK_SIZE) + ctx->buf_pos = ctx->buf; + + return true; +} + +bool lt_ota_can_rollback() { + if (lt_ota_get_type() != OTA_TYPE_DUAL) + return false; + uint8_t current = lt_ota_dual_get_current(); + if (current == 0) + return false; + return lt_ota_is_valid(current ^ 0b11); +} + +uf2_ota_scheme_t lt_ota_get_uf2_scheme() { + if (lt_ota_get_type() == OTA_TYPE_SINGLE) + return UF2_SCHEME_DEVICE_SINGLE; + uint8_t current = lt_ota_dual_get_current(); + if (current == 0) + return UF2_SCHEME_DEVICE_DUAL_1; + // UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2 + return (uf2_ota_scheme_t)(current ^ 0b11); +} diff --git a/cores/common/base/api/lt_ota.h b/cores/common/base/api/lt_ota.h index ef0ffca0c..e86bf8632 100644 --- a/cores/common/base/api/lt_ota.h +++ b/cores/common/base/api/lt_ota.h @@ -5,6 +5,81 @@ #include #include +/** + * @brief Chip's OTA type enumeration. + */ +typedef enum { + OTA_TYPE_SINGLE = 0, + OTA_TYPE_DUAL = 1, + OTA_TYPE_FILE = 2, +} lt_ota_type_t; + +/** + * @brief OTA update process context. + */ +typedef struct { + uf2_ota_t uf2; + uf2_info_t info; + uint8_t buf[UF2_BLOCK_SIZE]; // block data buffer + uint8_t *buf_pos; // buffer writing position + uint32_t bytes_written; // update progress + uint32_t bytes_total; // total update size + uf2_err_t error; // LT OTA/uf2ota error code + bool running; // whether update has begun + void (*callback)(void *param); // progress callback + void *callback_param; // callback argument +} lt_ota_ctx_t; + +/** + * @brief Initialize the update context to begin OTA process. + * + * @param ctx OTA context + * @param size length of the update file; 0 if unknown + */ +void lt_ota_begin(lt_ota_ctx_t *ctx, size_t size); + +/** + * @brief Finish the update process. If the update has been written completely, + * try to activate the target image. Free allocated internal structures, regardless + * of the activation result. + * + * @param ctx OTA context + * @return false if activation was attempted and not successful; true otherwise + */ +bool lt_ota_end(lt_ota_ctx_t *ctx); + +/** + * @brief Set family-specific, write-protected flash areas in the OTA update context. + * This shouldn't be called manually, as it's done by lt_ota_begin(). + * + * @param uf2 uf2ota context + */ +void lt_ota_set_write_protect(uf2_ota_t *uf2); + +/** + * @brief Process a chunk of data. + * + * Data is written to the buffer, unless a full UF2 block is already available, + * in which case it's also processed by UF2OTA and written to flash. + * + * It's advised to write in 512-byte chunks (or its multiples). + * + * @param ctx OTA context + * @param data chunk of bytes to process + * @param len size of the chunk + * @return number of bytes correctly processed; should equal 'len' in case of no errors + */ +size_t lt_ota_write(lt_ota_ctx_t *ctx, const uint8_t *data, size_t len); + +/** + * @brief Try to write the block. In case of UF2 errors, error code is set in the context. + * Note: use lt_ota_write() instead. This is for internal usage only. + * + * @param block UF2 block to check and write; cannot be NULL + * @return whether no error has occurred + */ +bool lt_ota_write_block(lt_ota_ctx_t *ctx, uf2_block_t *block); + /** * @brief Get OTA type of the device's chip. */ diff --git a/cores/common/base/api/lt_utils.c b/cores/common/base/api/lt_utils.c new file mode 100644 index 000000000..521a5bd14 --- /dev/null +++ b/cores/common/base/api/lt_utils.c @@ -0,0 +1,41 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_utils.h" + +void lt_rand_bytes(uint8_t *buf, size_t len) { + int *data = (int *)buf; + size_t i; + for (i = 0; len >= sizeof(int); len -= sizeof(int)) { + data[i++] = rand(); + } + if (len) { + int rem = rand(); + unsigned char *pRem = (unsigned char *)&rem; + memcpy(buf + i * sizeof(int), pRem, len); + } +} + +void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) { + uint16_t pos = 0; + while (pos < len) { + // print hex offset + printf("%06lx ", offset + pos); + // calculate current line width + uint8_t lineWidth = MIN(width, len - pos); + // print hexadecimal representation + for (uint8_t i = 0; i < lineWidth; i++) { + if (i % 8 == 0) { + printf(" "); + } + printf("%02x ", buf[pos + i]); + } + // print ascii representation + printf(" |"); + for (uint8_t i = 0; i < lineWidth; i++) { + char c = buf[pos + i]; + putchar((c >= 0x20 && c <= 0x7f) ? c : '.'); + } + puts("|\r"); + pos += lineWidth; + } +} diff --git a/cores/common/base/api/lt_wdt.c b/cores/common/base/api/lt_wdt.c new file mode 100644 index 000000000..a33788024 --- /dev/null +++ b/cores/common/base/api/lt_wdt.c @@ -0,0 +1,11 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ + +#include "lt_wdt.h" + +__attribute__((weak)) bool lt_wdt_enable(uint32_t timeout) { + return false; +} + +__attribute__((weak)) void lt_wdt_disable() {} + +__attribute__((weak)) void lt_wdt_feed() {} diff --git a/cores/common/base/config/lwipopts.h b/cores/common/base/config/lwipopts.h index 833df8549..2557d02c8 100644 --- a/cores/common/base/config/lwipopts.h +++ b/cores/common/base/config/lwipopts.h @@ -33,6 +33,9 @@ // clang-format on #endif +// lwIP version as a decimal number, with 2 digits for each part (major, minor, patch) +#define LWIP_VERSION_SIMPLE (LWIP_VERSION_MAJOR * 10000 + LWIP_VERSION_MINOR * 100 + LWIP_VERSION_REVISION) + // remove family-defined debugging options (use lwIP defaults, or user-defined) #undef ETHARP_DEBUG #undef NETIF_DEBUG diff --git a/cores/common/base/libretiny.h b/cores/common/base/libretiny.h index 22a133a59..1d394e9e5 100644 --- a/cores/common/base/libretiny.h +++ b/cores/common/base/libretiny.h @@ -44,6 +44,7 @@ // APIs #include "lt_api.h" // main API function definitions #include "lt_logger.h" // UART logger utility +#include "lt_pins.h" // additional pin macros #include "lt_posix_api.h" // POSIX compat functions // printf silencing methods #include diff --git a/cores/common/base/lt_api.c b/cores/common/base/lt_api.c deleted file mode 100644 index 7259fd028..000000000 --- a/cores/common/base/lt_api.c +++ /dev/null @@ -1,270 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2022-04-29. */ - -#include "lt_api.h" - -#include - -#if LT_HAS_FREERTOS -#include -#include -#endif - -/* _____ _____ _ _ - / ____| __ \| | | | - | | | |__) | | | | - | | | ___/| | | | - | |____| | | |__| | - \_____|_| \____*/ -lt_cpu_family_t lt_cpu_get_family() { - return FAMILY; -} - -const char *lt_cpu_get_family_name() { - return STRINGIFY_MACRO(FAMILY) + 2; -} - -__attribute__((weak)) lt_cpu_model_t lt_cpu_get_model() { - return MCU; -} - -const char *lt_cpu_get_model_name() { - return STRINGIFY_MACRO(MCU); -} - -const char *lt_cpu_get_model_code() { - return STRINGIFY_MACRO(MCULC); -} - -__attribute__((weak)) uint8_t lt_cpu_get_core_count() { - return 1; -} - -#if LT_HAS_FREERTOS -__attribute__((weak)) uint32_t lt_cpu_get_freq() { - return configCPU_CLOCK_HZ; -} -#endif - -uint32_t lt_cpu_get_freq_mhz() { - return lt_cpu_get_freq() / 1000000; -} - -#if LT_HAS_FREERTOS -__attribute__((weak)) uint32_t lt_cpu_get_cycle_count() { - return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ); -} -#endif - -/*_____ _ - | __ \ (_) - | | | | _____ ___ ___ ___ - | | | |/ _ \ \ / / |/ __/ _ \ - | |__| | __/\ V /| | (_| __/ - |_____/ \___| \_/ |_|\___\__*/ -static char *device_name = NULL; - -const char *lt_get_version() { - return LT_VERSION_STR; -} - -const char *lt_get_board_code() { - return LT_BOARD_STR; -} - -const char *lt_get_device_name() { - if (device_name) - return device_name; - uint32_t chip_id = lt_cpu_get_mac_id(); - uint8_t *id = (uint8_t *)&chip_id; - - const char *model = lt_cpu_get_model_code(); - uint8_t model_len = strlen(model); - device_name = (char *)malloc(3 + model_len + 1 + 6 + 1); - - sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]); - return device_name; -} - -__attribute__((weak)) bool lt_reboot_wdt() { - if (!lt_wdt_enable(1L)) - return false; - while (1) {} -} - -__attribute__((weak)) bool lt_reboot_download_mode() { - return false; -} - -const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) { - if (!reason) - reason = lt_get_reboot_reason(); - switch (reason) { - case RESET_REASON_POWER: - return "Power-On"; - case RESET_REASON_BROWNOUT: - return "Brownout"; - case RESET_REASON_HARDWARE: - return "HW Reboot"; - case RESET_REASON_SOFTWARE: - return "SW Reboot"; - case RESET_REASON_WATCHDOG: - return "WDT Reset"; - case RESET_REASON_CRASH: - return "Crash"; - case RESET_REASON_SLEEP: - return "Sleep Wakeup"; - default: - return "Unknown"; - } -} - -__attribute__((weak)) void lt_gpio_recover() { - // nop by default -} - -/*______ _ _ - | ____| | | | - | |__ | | __ _ ___| |__ - | __| | |/ _` / __| '_ \ - | | | | (_| \__ \ | | | - |_| |_|\__,_|___/_| |*/ -__attribute__((weak)) uint32_t lt_flash_get_size() { - lt_flash_id_t id = lt_flash_get_id(); - if (id.chip_size_id >= 0x14 && id.chip_size_id <= 0x19) { - return (1 << id.chip_size_id); - } -#ifdef FLASH_LENGTH - return FLASH_LENGTH; -#else - return 0; -#endif -} - -bool lt_flash_erase(uint32_t offset, size_t length) { - return fal_partition_erase(fal_root_part, offset, length) >= 0; -} - -bool lt_flash_erase_block(uint32_t offset) { - return fal_partition_erase(fal_root_part, offset, 1) >= 0; -} - -uint32_t lt_flash_read(uint32_t offset, uint8_t *data, size_t length) { - int ret = fal_partition_read(fal_root_part, offset, data, length); - if (ret == -1) - return 0; - return ret; -} - -uint32_t lt_flash_write(uint32_t offset, const uint8_t *data, size_t length) { - int ret = fal_partition_write(fal_root_part, offset, data, length); - if (ret == -1) - return 0; - return ret; -} - -/*__ __ - | \/ | - | \ / | ___ _ __ ___ ___ _ __ _ _ - | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | - | | | | __/ | | | | | (_) | | | |_| | - |_| |_|\___|_| |_| |_|\___/|_| \__, | - __/ | - |__*/ -#if LT_HAS_FREERTOS -__attribute__((weak)) uint32_t lt_heap_get_size() { - return configTOTAL_HEAP_SIZE; -} - -__attribute__((weak)) uint32_t lt_heap_get_free() { - return xPortGetFreeHeapSize(); -} - -__attribute__((weak)) uint32_t lt_heap_get_min_free() { - return xPortGetMinimumEverFreeHeapSize(); -} -#endif - -__attribute__((weak)) uint32_t lt_heap_get_max_alloc() { - return 0; -} - -/* ____ _______ - / __ \__ __|/\ - | | | | | | / \ - | | | | | | / /\ \ - | |__| | | |/ ____ \ - \____/ |_/_/ \*/ -bool lt_ota_can_rollback() { - if (lt_ota_get_type() != OTA_TYPE_DUAL) - return false; - uint8_t current = lt_ota_dual_get_current(); - return lt_ota_is_valid(current ^ 0b11); -} - -uf2_ota_scheme_t lt_ota_get_uf2_scheme() { - if (lt_ota_get_type() == OTA_TYPE_SINGLE) - return UF2_SCHEME_DEVICE_SINGLE; - uint8_t current = lt_ota_dual_get_current(); - // UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2 - return (uf2_ota_scheme_t)(current ^ 0b11); -} - -/*_ _ _ _ _ - | | | | | (_) | - | | | | |_ _| |___ - | | | | __| | / __| - | |__| | |_| | \__ \ - \____/ \__|_|_|__*/ -void lt_rand_bytes(uint8_t *buf, size_t len) { - int *data = (int *)buf; - size_t i; - for (i = 0; len >= sizeof(int); len -= sizeof(int)) { - data[i++] = rand(); - } - if (len) { - int rem = rand(); - unsigned char *pRem = (unsigned char *)&rem; - memcpy(buf + i * sizeof(int), pRem, len); - } -} - -void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) { - uint16_t pos = 0; - while (pos < len) { - // print hex offset - printf("%06lx ", offset + pos); - // calculate current line width - uint8_t lineWidth = MIN(width, len - pos); - // print hexadecimal representation - for (uint8_t i = 0; i < lineWidth; i++) { - if (i % 8 == 0) { - printf(" "); - } - printf("%02x ", buf[pos + i]); - } - // print ascii representation - printf(" |"); - for (uint8_t i = 0; i < lineWidth; i++) { - char c = buf[pos + i]; - putchar((c >= 0x20 && c <= 0x7f) ? c : '.'); - } - puts("|\r"); - pos += lineWidth; - } -} - -/*_ __ _ _ _ - \ \ / / | | | | | | - \ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _ - \ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` | - \ /\ / (_| | || (__| | | | (_| | (_) | (_| | - \/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, | - __/ | - |___*/ -__attribute__((weak)) bool lt_wdt_enable(uint32_t timeout) { - return false; -} - -__attribute__((weak)) void lt_wdt_disable() {} - -__attribute__((weak)) void lt_wdt_feed() {} diff --git a/cores/common/base/lt_api.h b/cores/common/base/lt_api.h index 8564dbd9e..4bb98c8ea 100644 --- a/cores/common/base/lt_api.h +++ b/cores/common/base/lt_api.h @@ -3,7 +3,7 @@ #pragma once // This file collects all LibreTiny C API includes. -// The functions are implemented in lt_api.c, which is located +// The functions are implemented in api/*.c units, which are located // in the common core, and in the family cores. #ifdef __cplusplus diff --git a/cores/common/base/lt_logger.c b/cores/common/base/lt_logger.c index bec236103..8b8018629 100644 --- a/cores/common/base/lt_logger.c +++ b/cores/common/base/lt_logger.c @@ -1,7 +1,10 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-04-28. */ #include "lt_logger.h" + +#if __has_include() #include +#endif #if LT_HAS_PRINTF #include @@ -33,7 +36,11 @@ #define COLOR_BRIGHT_CYAN 0x16 #define COLOR_BRIGHT_WHITE 0x17 -static uint32_t uart_port = LT_UART_DEFAULT_LOGGER; +#ifdef LT_UART_DEFAULT_PORT +static uint32_t uart_port = LT_UART_DEFAULT_LOGGER; +#else +static uint32_t uart_port = 0; +#endif static const char levels[] = {'V', 'D', 'I', 'W', 'E', 'F'}; #if LT_LOGGER_COLOR diff --git a/cores/common/base/lt_main.c b/cores/common/base/lt_main.c index 92a4bd012..c66eefe31 100644 --- a/cores/common/base/lt_main.c +++ b/cores/common/base/lt_main.c @@ -20,7 +20,7 @@ int lt_main(void) { // initialize C library __libc_init_array(); // inform about the reset reason - LT_I("Reset reason: %u", lt_get_reboot_reason()); + LT_I("Reset reason: %s", lt_get_reboot_reason_name(0)); // initialize FAL fal_init(); // provide root partition diff --git a/cores/common/base/lt_pins.h b/cores/common/base/lt_pins.h new file mode 100644 index 000000000..7e9f626e5 --- /dev/null +++ b/cores/common/base/lt_pins.h @@ -0,0 +1,69 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-06-21. */ + +#include LT_VARIANT_H + +#define PIN_INVALID 255 + +#define LT_HW_UART0 HAS_SERIAL0 +#define LT_HW_UART1 HAS_SERIAL1 +#define LT_HW_UART2 HAS_SERIAL2 +#define LT_HW_I2C0 HAS_WIRE0 +#define LT_HW_I2C1 HAS_WIRE1 +#define LT_HW_I2C2 HAS_WIRE2 +#define LT_HW_SPI0 HAS_SPI0 +#define LT_HW_SPI1 HAS_SPI1 +#define LT_HW_SPI2 HAS_SPI2 + +#if LT_HW_UART0 +#ifndef PIN_SERIAL0_RX +#define PIN_SERIAL0_RX PIN_INVALID +#endif +#ifndef PIN_SERIAL0_TX +#define PIN_SERIAL0_TX PIN_INVALID +#endif +#endif + +#if LT_HW_UART1 +#ifndef PIN_SERIAL1_RX +#define PIN_SERIAL1_RX PIN_INVALID +#endif +#ifndef PIN_SERIAL1_TX +#define PIN_SERIAL1_TX PIN_INVALID +#endif +#endif + +#if LT_HW_UART2 +#ifndef PIN_SERIAL2_RX +#define PIN_SERIAL2_RX PIN_INVALID +#endif +#ifndef PIN_SERIAL2_TX +#define PIN_SERIAL2_TX PIN_INVALID +#endif +#endif + +#if LT_HW_I2C0 +#ifndef PIN_WIRE0_SDA +#define PIN_WIRE0_SDA PIN_INVALID +#endif +#ifndef PIN_WIRE0_SCL +#define PIN_WIRE0_SCL PIN_INVALID +#endif +#endif + +#if LT_HW_I2C1 +#ifndef PIN_WIRE1_SDA +#define PIN_WIRE1_SDA PIN_INVALID +#endif +#ifndef PIN_WIRE1_SCL +#define PIN_WIRE1_SCL PIN_INVALID +#endif +#endif + +#if LT_HW_I2C2 +#ifndef PIN_WIRE2_SDA +#define PIN_WIRE2_SDA PIN_INVALID +#endif +#ifndef PIN_WIRE2_SCL +#define PIN_WIRE2_SCL PIN_INVALID +#endif +#endif diff --git a/cores/common/base/lt_types.h b/cores/common/base/lt_types.h index 69a1d3578..a8a4870c4 100644 --- a/cores/common/base/lt_types.h +++ b/cores/common/base/lt_types.h @@ -7,16 +7,6 @@ #define CPU_MODEL(family, chip_id) (((family >> 24) << 8) | chip_id) #define CPU_MODEL_ENUM(family, chip_id) (lt_cpu_model_t) CPU_MODEL(family, chip_id) -#define RESET_REASON_UNKNOWN REBOOT_REASON_UNKNOWN -#define RESET_REASON_POWER REBOOT_REASON_POWER -#define RESET_REASON_BROWNOUT REBOOT_REASON_BROWNOUT -#define RESET_REASON_HARDWARE REBOOT_REASON_HARDWARE -#define RESET_REASON_SOFTWARE REBOOT_REASON_SOFTWARE -#define RESET_REASON_WATCHDOG REBOOT_REASON_WATCHDOG -#define RESET_REASON_CRASH REBOOT_REASON_CRASH -#define RESET_REASON_SLEEP REBOOT_REASON_SLEEP -#define RESET_REASON_MAX REBOOT_REASON_MAX - typedef enum { F_RTL8710A = 0x9FFFD543, // Realtek Ameba1 F_RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz) @@ -41,6 +31,11 @@ typedef enum { RTL8711BU = CPU_MODEL(F_RTL8710B, 0xFC), // CHIPID_8711BG / QFN68 MX1290 = RTL8710BN, MX1290V2 = RTL8710BX, + W302 = RTL8710BN, + // Realtek AmebaZ2 (chip_id << 2 | flash_mode) + RTL8720CM = CPU_MODEL(F_RTL8720C, 0xEC), // 0xFB << 2 | 0 + RTL8720CF = CPU_MODEL(F_RTL8720C, 0xED), // 0xFB << 2 | 1 + RTL8720CX = RTL8720CM, // Beken 72XX BK7231Q = CPU_MODEL(F_BK7231Q, 0x31), // *SCTRL_CHIP_ID = 0x7231 BK7231T = CPU_MODEL(F_BK7231T, 0x1A), // *SCTRL_CHIP_ID = 0x7231a @@ -50,36 +45,3 @@ typedef enum { BK7231S = BK7231T, BK7231U = BK7231T, } lt_cpu_model_t; - -/** - * @brief Reset reason enumeration. - */ -typedef enum { - REBOOT_REASON_UNKNOWN = 1, - REBOOT_REASON_POWER = 2, - REBOOT_REASON_BROWNOUT = 3, - REBOOT_REASON_HARDWARE = 4, - REBOOT_REASON_SOFTWARE = 5, - REBOOT_REASON_WATCHDOG = 6, - REBOOT_REASON_CRASH = 7, - REBOOT_REASON_SLEEP = 8, - REBOOT_REASON_MAX = 9, -} lt_reboot_reason_t; - -/** - * @brief Flash chip ID structure. - */ -typedef struct { - uint8_t manufacturer_id; - uint8_t chip_id; - uint8_t chip_size_id; -} lt_flash_id_t; - -/** - * @brief Chip's OTA type enumeration. - */ -typedef enum { - OTA_TYPE_SINGLE = 0, - OTA_TYPE_DUAL = 1, - OTA_TYPE_FILE = 2, -} lt_ota_type_t; diff --git a/cores/realtek-amb/arduino/libraries/Serial/Serial.cpp b/cores/realtek-amb/arduino/libraries/Serial/Serial.cpp deleted file mode 100644 index c387d471f..000000000 --- a/cores/realtek-amb/arduino/libraries/Serial/Serial.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2022-07-03. */ - -#include "Serial.h" - -#include -#include - -#if HAS_SERIAL0 -SerialClass Serial0(UART0_DEV, UART0_IRQ, PIN_SERIAL0_RX, PIN_SERIAL0_TX); -#endif -#if HAS_SERIAL1 -SerialClass Serial1(UART1_DEV, UART1_IRQ, PIN_SERIAL1_RX, PIN_SERIAL1_TX); -#endif -#if HAS_SERIAL2 -SerialClass Serial2(UART2_DEV, UART_LOG_IRQ, PIN_SERIAL2_RX, PIN_SERIAL2_TX); -#endif - -SerialClass::SerialClass(void *uart, uint8_t irq, pin_size_t rx, pin_size_t tx) { - data.uart = uart; - data.buf = NULL; - this->irq = irq; - this->rx = rx; - this->tx = tx; -} - -#if LT_AUTO_DOWNLOAD_REBOOT -static uint8_t adrState = 0; - -// clang-format off -// Family ID, big-endian -static const uint8_t adrCmd[] = { - 0x55, 0xAA, - (FAMILY >> 24) & 0xFF, - (FAMILY >> 16) & 0xFF, - (FAMILY >> 8) & 0xFF, - (FAMILY >> 0) & 0xFF -}; - -// clang-format on - -static void adrParse(uint8_t c) { - adrState = (adrState + 1) * (c == adrCmd[adrState]); - if (adrState == 6) { - LT_I("Auto download mode: rebooting"); - LT.restartDownloadMode(); - } -} -#endif - -static uint32_t callback(void *param) { - SerialData *data = (SerialData *)param; - UART_TypeDef *uart = (UART_TypeDef *)data->uart; - - uint32_t intcr = uart->DLH_INTCR; - uart->DLH_INTCR = 0; - - uint8_t c; - while (UART_Readable(uart)) { - UART_CharGet(uart, &c); -#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL2_RX) - // parse UART protocol commands on UART2 - if (uart == UART2_DEV) - adrParse(c); -#endif - data->buf->store_char(c); - } - - uart->DLH_INTCR = intcr; - return 0; -} - -void SerialClass::begin(unsigned long baudrate, uint16_t config) { - // RUART_WLS_7BITS / RUART_WLS_8BITS - uint8_t dataWidth = (config & SERIAL_DATA_MASK) == SERIAL_DATA_8; - // RUART_PARITY_DISABLE / RUART_PARITY_ENABLE - uint8_t parity = (config & SERIAL_PARITY_MASK) != SERIAL_PARITY_NONE; - // RUART_ODD_PARITY / RUART_EVEN_PARITY - uint8_t parityType = (config & SERIAL_PARITY_MASK) == SERIAL_PARITY_EVEN; - // RUART_STOP_BIT_1 / RUART_STOP_BIT_2 - uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; - - switch ((uint32_t)data.uart) { - case UART0_REG_BASE: - RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); - break; - case UART1_REG_BASE: - RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, ENABLE); - break; - } - - Pinmux_Config(pinInfo(this->rx)->gpio, PINMUX_FUNCTION_UART); - Pinmux_Config(pinInfo(this->tx)->gpio, PINMUX_FUNCTION_UART); - PAD_PullCtrl(pinInfo(this->rx)->gpio, GPIO_PuPd_UP); - - UART_InitTypeDef cfg; - UART_StructInit(&cfg); - cfg.WordLen = dataWidth; - cfg.Parity = parity; - cfg.ParityType = parityType; - cfg.StopBit = stopBits; - UART_Init((UART_TypeDef *)data.uart, &cfg); - UART_SetBaud((UART_TypeDef *)data.uart, baudrate); - - if (data.buf) { - data.buf->clear(); - } else { - data.buf = new RingBuffer(); - } - - VECTOR_IrqUnRegister(this->irq); - VECTOR_IrqRegister(callback, this->irq, (uint32_t)&data, 10); - VECTOR_IrqEn(this->irq, 10); - - UART_RxCmd((UART_TypeDef *)data.uart, ENABLE); - UART_INTConfig((UART_TypeDef *)data.uart, RUART_IER_ERBI, ENABLE); -} - -void SerialClass::end() { - if (data.uart == UART2_DEV) { - // restore command line mode - DIAG_UartReInit((IRQ_FUN)UartLogIrqHandle); - } - delete data.buf; -} - -int SerialClass::available() { - return data.buf->available(); -} - -int SerialClass::peek() { - return data.buf->peek(); -} - -int SerialClass::read() { - return data.buf->read_char(); -} - -void SerialClass::flush() { - UART_WaitBusy((UART_TypeDef *)data.uart, 10); -} - -size_t SerialClass::write(uint8_t c) { - while (UART_Writable((UART_TypeDef *)data.uart) == 0) {} - UART_CharPut((UART_TypeDef *)data.uart, c); - return 1; -} diff --git a/cores/realtek-amb/arduino/libraries/Serial/Serial.h b/cores/realtek-amb/arduino/libraries/Serial/Serial.h deleted file mode 100644 index aa0a78b7a..000000000 --- a/cores/realtek-amb/arduino/libraries/Serial/Serial.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2022-07-03. */ - -#pragma once - -#include -#include -#include - -using namespace arduino; - -typedef struct { - void *uart; // UART_TypeDef - RingBuffer *buf; -} SerialData; - -class SerialClass : public HardwareSerial { - private: - // data accessible to IRQ handler - SerialData data; - uint8_t irq; // IRQn - pin_size_t rx; - pin_size_t tx; - - public: - SerialClass(void *uart, uint8_t irq, pin_size_t rx, pin_size_t tx); - - inline void begin(unsigned long baudrate) { - begin(baudrate, SERIAL_8N1); - } - - void begin(unsigned long baudrate, uint16_t config); - void end(); - int available(); - int peek(); - int read(); - void flush(); - size_t write(uint8_t c); - - operator bool() { - return !!data.buf; - } - - using Print::write; -}; diff --git a/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp b/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp index ebd7f0f31..1176126a5 100644 --- a/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/cores/realtek-amb/arduino/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -1,5 +1,7 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-07-03. */ +#if LT_RTL8710B + #include #include @@ -94,3 +96,5 @@ void SoftwareSerial::startTx() { void SoftwareSerial::endTx() { gtimer_stop(OBJ); } + +#endif diff --git a/cores/realtek-amb/arduino/libraries/WiFi/WiFiEvents.cpp b/cores/realtek-amb/arduino/libraries/WiFi/WiFiEvents.cpp index b889a9a9f..b0c5263dd 100644 --- a/cores/realtek-amb/arduino/libraries/WiFi/WiFiEvents.cpp +++ b/cores/realtek-amb/arduino/libraries/WiFi/WiFiEvents.cpp @@ -68,7 +68,6 @@ void wifi_unreg_event_handler(unsigned int event_cmds, rtw_event_handler_t handl // function called by wext_wlan_indicate void wifi_indication(rtw_event_indicate_t event, char *buf, int buf_len, int flags) { - LT_HEAP_I(); if (event >= WIFI_EVENT_MAX) return; if (wifiEventQueueHandle && wifiEventTaskHandle) { diff --git a/cores/realtek-amb/arduino/libraries/WiFi/WiFiGeneric.cpp b/cores/realtek-amb/arduino/libraries/WiFi/WiFiGeneric.cpp index 4c6a7f299..8448e1beb 100644 --- a/cores/realtek-amb/arduino/libraries/WiFi/WiFiGeneric.cpp +++ b/cores/realtek-amb/arduino/libraries/WiFi/WiFiGeneric.cpp @@ -9,8 +9,7 @@ int32_t WiFiClass::channel() { } bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) { - __wrap_rtl_printf_disable(); - __wrap_DiagPrintf_disable(); + DIAG_PRINTF_DISABLE(); startWifiTask(); if (!DATA->initialized) { @@ -102,7 +101,11 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) { LT_DM(WIFI, "Mode: %s DISABLE", WLAN1_NAME); netif_set_link_down(WLAN1_NETIF); netif_set_down(WLAN1_NETIF); +#if !LT_RTL8720C rltk_stop_softap(WLAN1_NAME); +#else + rltk_suspend_softap(WLAN1_NAME); +#endif rltk_wlan_init(WLAN1_IDX, RTW_MODE_NONE); wext_set_mode(WLAN1_NAME, IW_MODE_INFRA); } @@ -152,13 +155,11 @@ bool WiFiClass::modePriv(WiFiMode mode, WiFiModeAction sta, WiFiModeAction ap) { } LT_HEAP_I(); - __wrap_rtl_printf_enable(); - __wrap_DiagPrintf_enable(); + DIAG_PRINTF_ENABLE(); return true; error: - __wrap_rtl_printf_enable(); - __wrap_DiagPrintf_enable(); + DIAG_PRINTF_ENABLE(); return false; } diff --git a/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h b/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h index aa3079dd1..99745a259 100644 --- a/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h +++ b/cores/realtek-amb/arduino/libraries/WiFi/WiFiPrivate.h @@ -31,7 +31,9 @@ extern void dns_server_init(struct netif *pnetif); extern void dns_server_deinit(void); // wifi_util.c +#if !LT_RTL8720C extern void rltk_stop_softap(const char *ifname); +#endif extern void rltk_suspend_softap(const char *ifname); extern void rltk_suspend_softap_beacon(const char *ifname); @@ -65,3 +67,33 @@ typedef struct { #define DATA ((WiFiData *)data) #define pDATA ((WiFiData *)pWiFi->data) #define cDATA ((WiFiData *)cls->data) + +#if LT_RTL8710B // Realtek AmebaZ +#define DIAG_PRINTF_ENABLE() \ + do { \ + __wrap_rtl_printf_enable(); \ + __wrap_DiagPrintf_enable(); \ + } while (0); +#define DIAG_PRINTF_DISABLE() \ + do { \ + __wrap_rtl_printf_disable(); \ + __wrap_DiagPrintf_disable(); \ + } while (0); + +#elif LT_RTL8720C // Realtek AmebaZ2 +#define DIAG_PRINTF_ENABLE() \ + do { \ + __wrap_rt_printf_enable(); \ + __wrap_rt_log_printf_enable(); \ + } while (0); +#define DIAG_PRINTF_DISABLE() \ + do { \ + __wrap_rt_printf_disable(); \ + __wrap_rt_log_printf_disable(); \ + } while (0); + +#else +#define DIAG_PRINTF_ENABLE() +#define DIAG_PRINTF_DISABLE() + +#endif diff --git a/cores/realtek-amb/arduino/libraries/WiFi/WiFiSTA.cpp b/cores/realtek-amb/arduino/libraries/WiFi/WiFiSTA.cpp index 7067a8f35..f5164cec7 100644 --- a/cores/realtek-amb/arduino/libraries/WiFi/WiFiSTA.cpp +++ b/cores/realtek-amb/arduino/libraries/WiFi/WiFiSTA.cpp @@ -67,8 +67,7 @@ bool WiFiClass::reconnect(const uint8_t *bssid) { WiFiNetworkInfo &info = DATA->sta; LT_IM(WIFI, "Connecting to %s (bssid=%p)", info.ssid, bssid); - __wrap_rtl_printf_disable(); - __wrap_DiagPrintf_disable(); + DIAG_PRINTF_DISABLE(); wext_set_ssid(WLAN0_NAME, (uint8_t *)"-", 1); @@ -116,8 +115,7 @@ bool WiFiClass::reconnect(const uint8_t *bssid) { wifi_indication(WIFI_EVENT_CONNECT, (char *)eventInfo, ARDUINO_EVENT_WIFI_STA_GOT_IP, -2); // free memory as wifi_indication creates a copy free(eventInfo); - __wrap_rtl_printf_enable(); - __wrap_DiagPrintf_enable(); + DIAG_PRINTF_ENABLE(); return true; } LT_EM(WIFI, "DHCP failed; dhcpRet=%d", dhcpRet); @@ -126,8 +124,7 @@ bool WiFiClass::reconnect(const uint8_t *bssid) { } LT_EM(WIFI, "Connection failed; ret=%d", ret); error: - __wrap_rtl_printf_enable(); - __wrap_DiagPrintf_enable(); + DIAG_PRINTF_ENABLE(); return false; } @@ -158,10 +155,7 @@ IPAddress WiFiClass::localIP() { uint8_t *WiFiClass::macAddress(uint8_t *mac) { if ((getMode() & WIFI_MODE_STA) == 0) { - uint8_t *efuse = (uint8_t *)malloc(512); - EFUSE_LogicalMap_Read(efuse); - memcpy(mac, efuse + 0x11A, ETH_ALEN); - free(efuse); + lt_get_device_mac(mac); return mac; } memcpy(mac, NETIF_RTW_STA.hwaddr, ETH_ALEN); diff --git a/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp b/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp index aa93e65ae..09c7f252e 100644 --- a/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp +++ b/cores/realtek-amb/arduino/libraries/Wire/Wire.cpp @@ -1,5 +1,7 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-05-08. */ +#if LT_RTL8710B + #include "Wire.h" #include @@ -191,3 +193,5 @@ int TwoWire::peek() { } void TwoWire::flush() {} + +#endif diff --git a/cores/realtek-amb/arduino/src/Tone.cpp b/cores/realtek-amb/arduino/src/Tone.cpp deleted file mode 100644 index 816114e12..000000000 --- a/cores/realtek-amb/arduino/src/Tone.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -extern void pinRemoveMode(pin_size_t pinNumber); -extern void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration); - -void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) { - _tone(ulPin, frequency, duration); -} - -void noTone(uint32_t ulPin) { - pinRemoveMode(ulPin); -} diff --git a/cores/realtek-amb/arduino/src/lt_api.c b/cores/realtek-amb/arduino/src/api/lt_cpu.c similarity index 100% rename from cores/realtek-amb/arduino/src/lt_api.c rename to cores/realtek-amb/arduino/src/api/lt_cpu.c diff --git a/cores/realtek-amb/arduino/src/lt_defs.h b/cores/realtek-amb/arduino/src/lt_defs.h index 20839622a..948ea5612 100644 --- a/cores/realtek-amb/arduino/src/lt_defs.h +++ b/cores/realtek-amb/arduino/src/lt_defs.h @@ -4,13 +4,5 @@ #define LT_ARD_HAS_WIFI 1 #define LT_ARD_HAS_SOFTSERIAL 1 -#define LT_ARD_HAS_SERIAL 1 - -#define LT_ARD_MD5_POLARSSL 1 #define ARDUINO_AMEBA - -// the SDK declares bool if not defined before -// which conflicts with C++ built-in bool -// so it's either -fpermissive or this: -#define bool bool diff --git a/cores/realtek-amb/arduino/src/main.cpp b/cores/realtek-amb/arduino/src/main.cpp index 9587dc703..4fcc21ff6 100644 --- a/cores/realtek-amb/arduino/src/main.cpp +++ b/cores/realtek-amb/arduino/src/main.cpp @@ -5,12 +5,9 @@ extern "C" { -#include -#include - osThreadId main_tid = 0; -#if LT_AUTO_DOWNLOAD_REBOOT && defined(PIN_SERIAL2_RX) && defined(PIN_SERIAL2_TX) +#if LT_AUTO_DOWNLOAD_REBOOT && LT_ARD_HAS_SERIAL && LT_HW_UART2 void lt_init_arduino() { // initialize auto-download-reboot parser Serial2.begin(115200); diff --git a/cores/realtek-amb/arduino/src/wiring.c b/cores/realtek-amb/arduino/src/wiring.c index 0cc3839f1..bb0fc6bfa 100644 --- a/cores/realtek-amb/arduino/src/wiring.c +++ b/cores/realtek-amb/arduino/src/wiring.c @@ -1,48 +1,11 @@ -/* - Copyright (c) 2011 Arduino. All right reserved. +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-23. */ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include - -#include +#include "wiring_private.h" #ifndef portNVIC_SYSTICK_CURRENT_VALUE_REG #define portNVIC_SYSTICK_CURRENT_VALUE_REG (*((volatile uint32_t *)0xe000e018)) #endif -extern uint32_t xTaskGetTickCount(); -extern uint32_t xTaskGetTickCountFromISR(); - -static __inline uint32_t __get_ipsr__(void) { - volatile uint32_t __regIPSR __asm("ipsr"); - return (__regIPSR); -} - -void *gpio_pin_struct[PINS_COUNT] = {NULL}; - -void delay(uint32_t ms) { - /* osStatus ret; */ - - /* ret = */ osDelay(ms); - /* if ((ret != osEventTimeout) && (ret != osOK)) { - printf("delay : ERROR : 0x%x \n", ret); - } */ -} - void delayMicroseconds(unsigned int us) { int i; uint32_t t0, tn; @@ -61,7 +24,7 @@ void delayMicroseconds(unsigned int us) { } unsigned long millis(void) { - return (__get_ipsr__() == 0) ? xTaskGetTickCount() : xTaskGetTickCountFromISR(); + return (__get_IPSR() == 0) ? xTaskGetTickCount() : xTaskGetTickCountFromISR(); } unsigned long micros(void) { @@ -69,7 +32,7 @@ unsigned long micros(void) { uint32_t us; uint32_t tick_per_us = F_CPU / 1000; - if (__get_ipsr__() == 0) { + if (__get_IPSR() == 0) { tick1 = xTaskGetTickCount(); us = portNVIC_SYSTICK_CURRENT_VALUE_REG; tick2 = xTaskGetTickCount(); @@ -88,8 +51,22 @@ unsigned long micros(void) { } } -void yield(void) { - runPeriodicTasks(); - vTaskDelay(1); - taskYIELD(); +void pinRemoveMode(PinInfo *pin, uint32_t mask) { + PinData *data = pinData(pin); + if ((mask & PIN_GPIO) && (pin->enabled & PIN_GPIO)) { + gpio_deinit(data->gpio); + free(data->gpio); + pinDisable(pin, PIN_GPIO); + } + if ((mask & PIN_IRQ) && (pin->enabled & PIN_IRQ)) { + data->irqHandler = NULL; + gpio_irq_free(data->irq); + free(data->irq); + pinDisable(pin, PIN_IRQ); + } + if ((mask & PIN_PWM) && (pin->enabled & PIN_PWM)) { + pwmout_free(data->pwm); + free(data->pwm); + pinDisable(pin, PIN_PWM); + } } diff --git a/cores/realtek-amb/arduino/src/wiring_analog.c b/cores/realtek-amb/arduino/src/wiring_analog.c index 05d07969f..68b7f8307 100644 --- a/cores/realtek-amb/arduino/src/wiring_analog.c +++ b/cores/realtek-amb/arduino/src/wiring_analog.c @@ -1,23 +1,6 @@ -/* - Copyright (c) 2011 Arduino. All right reserved. +/* Copyright (c) Kuba SzczodrzyƄski 2022-06-20. */ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include +#include "wiring_private.h" /* ADC */ static analogin_t adc1; @@ -28,17 +11,13 @@ static bool g_adc_enabled[] = {false, false, false}; // from realtek_amebaz_va0_example/example_sources/adc_vbat/src/main.c #define AD2MV(ad, offset, gain) (((ad >> 4) - offset) * 1000 / gain) -extern void *gpio_pin_struct[]; - -extern void pinRemoveMode(pin_size_t pinNumber); - // TODO implement custom ADC calibration uint16_t analogReadVoltage(pin_size_t pinNumber) { uint16_t ret = 0; switch (pinNumber) { -#ifdef PIN_A1 - case PIN_A1: +#ifdef PIN_ADC2 + case PIN_ADC2: if (g_adc_enabled[1] == false) { analogin_init(&adc2, AD_2); g_adc_enabled[1] = true; @@ -47,8 +26,8 @@ uint16_t analogReadVoltage(pin_size_t pinNumber) { // AD_1 - 0.0V-5.0V return AD2MV(ret, 0x496, 0xBA); #endif -#ifdef PIN_A0 - case PIN_A0: +#ifdef PIN_ADC1 + case PIN_ADC1: if (g_adc_enabled[0] == false) { analogin_init(&adc1, AD_1); g_adc_enabled[0] = true; @@ -56,8 +35,8 @@ uint16_t analogReadVoltage(pin_size_t pinNumber) { ret = analogin_read_u16(&adc1); break; #endif -#ifdef PIN_A2 - case PIN_A2: +#ifdef PIN_ADC3 + case PIN_ADC3: if (g_adc_enabled[2] == false) { analogin_init(&adc3, AD_3); g_adc_enabled[2] = true; @@ -82,27 +61,20 @@ uint16_t analogReadMaxVoltage(pin_size_t pinNumber) { } void analogWrite(pin_size_t pinNumber, int value) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - pwmout_t *obj; + pinCheckGetData(pinNumber, PIN_PWM, ); - if (pinSupported(pin, PIN_PWM)) { - float percent = value * 1.0 / (1 << _analogWriteResolution); - if (pin->enabled != PIN_PWM) { - if ((pin->enabled == PIN_GPIO) || (pin->enabled == PIN_IRQ)) { - pinRemoveMode(pinNumber); - } - gpio_pin_struct[pinNumber] = malloc(sizeof(pwmout_t)); - pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber]; - pwmout_init(obj, pin->gpio); - pwmout_period_us(obj, _analogWritePeriod); - pwmout_write(obj, percent); - pin->enabled = PIN_PWM; - } else { - pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber]; - // pwmout_period_us(obj, _writePeriod); - pwmout_write(obj, percent); - } + // GPIO can't be used together with PWM + pinRemoveMode(pin, PIN_GPIO | PIN_IRQ); + + pwmout_t *pwm = data->pwm; + if (!pwm) { + // allocate memory if pin not used before + data->pwm = pwm = malloc(sizeof(pwmout_t)); + pwmout_init(pwm, pin->gpio); + pwmout_period_us(pwm, _analogWritePeriod); + pinEnable(pin, PIN_PWM); } + + float percent = value * 1.0 / (1 << _analogWriteResolution); + pwmout_write(pwm, percent); } diff --git a/cores/realtek-amb/arduino/src/wiring_data.h b/cores/realtek-amb/arduino/src/wiring_data.h new file mode 100644 index 000000000..1f0038f57 --- /dev/null +++ b/cores/realtek-amb/arduino/src/wiring_data.h @@ -0,0 +1,24 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct PinData_s { + gpio_t *gpio; + gpio_irq_t *irq; + pwmout_t *pwm; + PinMode gpioMode; + PinStatus irqMode; + void *irqHandler; + void *irqParam; +}; + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/realtek-amb/arduino/src/wiring_digital.c b/cores/realtek-amb/arduino/src/wiring_digital.c index 0c8cb327f..1eb2f9c6c 100644 --- a/cores/realtek-amb/arduino/src/wiring_digital.c +++ b/cores/realtek-amb/arduino/src/wiring_digital.c @@ -1,68 +1,32 @@ -#include -#include +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-23. */ -extern void *gpio_pin_struct[PINS_COUNT]; - -void pinRemoveMode(pin_size_t pinNumber) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - if (pinEnabled(pin, PIN_PWM)) { - pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber]; - pwmout_free(obj); - } - if (pinEnabled(pin, PIN_GPIO)) { - gpio_t *obj = (gpio_t *)gpio_pin_struct[pinNumber]; - gpio_deinit(obj); - free(obj); - } - if (pinEnabled(pin, PIN_IRQ)) { - gpio_irq_t *obj = (gpio_irq_t *)gpio_pin_struct[pinNumber]; - gpio_irq_deinit(obj); - free(obj); - } - gpio_pin_struct[pinNumber] = NULL; - pin->enabled = PIN_NONE; -} +#include "wiring_private.h" void pinMode(pin_size_t pinNumber, PinMode pinMode) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - - if (pinEnabled(pin, PIN_GPIO) && pin->mode == pinMode) - // Nothing changes in pin mode - return; + pinCheckGetData(pinNumber, PIN_GPIO, ); - if (!pinSupported(pin, PIN_GPIO)) - // cannot set ADC as I/O + if (pinEnabled(pin, PIN_GPIO) && data->gpioMode == pinMode) return; - /* if (pin->enabled == PIN_PWM) { - // If this pin has been configured as PWM, then it cannot change to another mode - return; - } */ - - if (pin->enabled != PIN_GPIO) - // pin mode changes; deinit gpio and free memory - pinRemoveMode(pinNumber); - - gpio_t *gpio; - - if (pin->enabled == PIN_NONE) { +#if LT_RTL8720C + // apparently IRQ can't be used with any kind of pull-up/down + // TODO verify if it can be used on AmebaZ + pinRemoveMode(pin, PIN_PWM | PIN_IRQ); +#else + // GPIO can't be used together with PWM + pinRemoveMode(pin, PIN_PWM); +#endif + + gpio_t *gpio = data->gpio; + if (!gpio) { // allocate memory if pin not used before - gpio = malloc(sizeof(gpio_t)); - gpio_pin_struct[pinNumber] = gpio; + data->gpio = gpio = malloc(sizeof(gpio_t)); gpio_init(gpio, pin->gpio); - pin->enabled = PIN_GPIO; - } else { - // pin already used as gpio - gpio = (gpio_t *)gpio_pin_struct[pinNumber]; + pinEnable(pin, PIN_GPIO); } - pin->mode = pinMode; PinDirection dir; - PinMode mode; + PinModeRTL mode; switch (pinMode) { case INPUT: @@ -88,29 +52,20 @@ void pinMode(pin_size_t pinNumber, PinMode pinMode) { default: return; } + data->gpioMode = pinMode; gpio_dir(gpio, dir); gpio_mode(gpio, mode); } void digitalWrite(pin_size_t pinNumber, PinStatus status) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return; - if (pin->enabled != PIN_GPIO) - return; - - gpio_t *gpio = (gpio_t *)gpio_pin_struct[pinNumber]; - gpio_write(gpio, status); + pinCheckGetData(pinNumber, PIN_GPIO, ); + pinSetOutputPull(pin, data, pinNumber, status); + gpio_write(data->gpio, !!status); } PinStatus digitalRead(pin_size_t pinNumber) { - PinInfo *pin = pinInfo(pinNumber); - if (!pin) - return LOW; - if (pin->enabled != PIN_GPIO) - return LOW; - - gpio_t *gpio = (gpio_t *)gpio_pin_struct[pinNumber]; - return gpio_read(gpio); + pinCheckGetData(pinNumber, PIN_GPIO, LOW); + pinSetInputMode(pin, data, pinNumber); + return gpio_read(data->gpio); } diff --git a/cores/realtek-amb/arduino/src/wiring_irq.c b/cores/realtek-amb/arduino/src/wiring_irq.c index 4fd7cd5d2..f387fccca 100644 --- a/cores/realtek-amb/arduino/src/wiring_irq.c +++ b/cores/realtek-amb/arduino/src/wiring_irq.c @@ -1,59 +1,50 @@ -#include -#include +/* Copyright (c) Kuba SzczodrzyƄski 2022-04-23. */ -extern void *gpio_pin_struct[PINS_COUNT]; -static void *gpio_irq_handler_list[PINS_COUNT] = {NULL}; -static void *gpio_irq_handler_args[PINS_COUNT] = {NULL}; - -extern void pinRemoveMode(pin_size_t pinNumber); +#include "wiring_private.h" static void gpioIrqHandler(uint32_t id, gpio_irq_event event) { - // id is pin index - if (gpio_irq_handler_list[id] != NULL) { - if (gpio_irq_handler_args[id] == NULL) - ((voidFuncPtr)gpio_irq_handler_list[id])(); - else - ((voidFuncPtrParam)gpio_irq_handler_list[id])(gpio_irq_handler_args[id]); - } -} - -void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) { - attachInterruptParam(interruptNumber, (voidFuncPtrParam)callback, mode, NULL); + // id is pin data + PinData *data = (PinData *)id; + if (!data->irqHandler) + return; + if (!data->irqParam) + ((voidFuncPtr)data->irqHandler)(); + else + ((voidFuncPtrParam)data->irqHandler)(data->irqParam); } void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, PinStatus mode, void *param) { - PinInfo *pin = pinInfo(interruptNumber); - if (pin == NULL) - return; - uint32_t index = pinIndex(pin); + pinCheckGetData(interruptNumber, PIN_IRQ, ); - gpio_irq_handler_list[index] = callback; - gpio_irq_handler_args[index] = param; + data->irqHandler = callback; + data->irqParam = param; - if (pin->enabled == PIN_IRQ && pin->mode == mode) - // Nothing changes in pin mode + if (pinEnabled(pin, PIN_IRQ) && data->irqMode == mode) return; - if (pin->enabled != PIN_IRQ) - // pin mode changes; deinit gpio and free memory - pinRemoveMode(interruptNumber); - - gpio_irq_t *gpio; +#if LT_RTL8720C + // apparently IRQ can't be used with any kind of pull-up/down + // TODO verify if it can be used on AmebaZ + pinRemoveMode(pin, PIN_PWM | PIN_GPIO); +#else + // GPIO can't be used together with PWM + pinRemoveMode(pin, PIN_PWM); +#endif - if (pin->enabled == PIN_NONE) { + gpio_irq_t *irq = data->irq; + if (!irq) { // allocate memory if pin not used before - gpio = malloc(sizeof(gpio_irq_t)); - gpio_pin_struct[index] = gpio; - gpio_irq_init(gpio, pin->gpio, gpioIrqHandler, index); - pin->enabled = PIN_IRQ; - } else { - // pin already used as irq - gpio = (gpio_irq_t *)gpio_pin_struct[index]; + data->irq = irq = malloc(sizeof(gpio_irq_t)); + if (gpio_irq_init(irq, pin->gpio, gpioIrqHandler, (uint32_t)data) != 0) { + LT_W("IRQ init failed"); + free(data->irq); + data->irq = NULL; + return; + } + pinEnable(pin, PIN_IRQ); } - pin->mode = mode; gpio_irq_event event; - switch (mode) { case LOW: event = IRQ_LOW; @@ -67,22 +58,22 @@ void attachInterruptParam(pin_size_t interruptNumber, voidFuncPtrParam callback, case RISING: event = IRQ_RISE; break; + case CHANGE: +#if LT_RTL8720C + event = IRQ_FALL_RISE; +#else + LT_W("CHANGE interrupts not supported"); +#endif + break; default: return; } + data->irqMode = mode; - gpio_irq_set(gpio, event, 1); - gpio_irq_enable(gpio); + gpio_irq_set(irq, event, 1); + gpio_irq_enable(irq); } void detachInterrupt(pin_size_t interruptNumber) { - PinInfo *pin = pinInfo(interruptNumber); - if (pin == NULL) - return; - uint32_t index = pinIndex(pin); - - if (pin->enabled == PIN_IRQ) { - pinRemoveMode(interruptNumber); - } - gpio_irq_handler_list[index] = NULL; + pinModeRemove(interruptNumber, PIN_IRQ); } diff --git a/cores/realtek-amb/arduino/src/wiring_pulse.c b/cores/realtek-amb/arduino/src/wiring_pulse.c index 85161e696..9fcd794da 100644 --- a/cores/realtek-amb/arduino/src/wiring_pulse.c +++ b/cores/realtek-amb/arduino/src/wiring_pulse.c @@ -16,10 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include - -extern void *gpio_pin_struct[]; +#include "wiring_private.h" /* Measures the length (in microseconds) of a pulse on the pin; state is HIGH * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds @@ -31,22 +28,20 @@ extern unsigned long pulseIn(uint8_t pinNumber, uint8_t state, unsigned long tim // digitalRead() instead yields much coarser resolution. PinInfo *pin = pinInfo(pinNumber); if (pin == NULL) - return; + return 0; uint32_t index = pinIndex(pin); - gpio_t *pGpio_t; - uint32_t start_ticks, cur_ticks; - if (pin < 0 || pin > PINS_COUNT || (pin->gpio == NC)) + if (pin->gpio == NC) return 0; /* Handle */ - if (pin->enabled != PIN_GPIO) { + if (!pinEnabled(pin, PIN_GPIO)) { return 0; } - - pGpio_t = (gpio_t *)gpio_pin_struct[index]; + PinData *data = pinData(pin); + gpio_t *pGpio_t = data->gpio; // wait for any previous pulse to end start_ticks = us_ticker_read(); diff --git a/cores/realtek-amb/base/api/lt_flash.c b/cores/realtek-amb/base/api/lt_flash.c new file mode 100644 index 000000000..df7c40939 --- /dev/null +++ b/cores/realtek-amb/base/api/lt_flash.c @@ -0,0 +1,14 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#include +#include + +lt_flash_id_t lt_flash_get_id() { + lt_flash_id_t id; + uint8_t idBytes[3]; + flash_read_id(<_flash_obj, idBytes, 3); + id.manufacturer_id = idBytes[0]; + id.chip_id = idBytes[1]; + id.chip_size_id = idBytes[2]; + return id; +} diff --git a/cores/realtek-amb/base/api/lt_wdt.c b/cores/realtek-amb/base/api/lt_wdt.c new file mode 100644 index 000000000..4adac4210 --- /dev/null +++ b/cores/realtek-amb/base/api/lt_wdt.c @@ -0,0 +1,18 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#include +#include + +bool lt_wdt_enable(uint32_t timeout) { + watchdog_init(timeout); + watchdog_start(); + return true; +} + +void lt_wdt_disable() { + watchdog_stop(); +} + +void lt_wdt_feed() { + watchdog_refresh(); +} diff --git a/cores/realtek-amb/base/config/lwipopts.h b/cores/realtek-amb/base/config/lwipopts.h index 97af786f7..fae78d468 100644 --- a/cores/realtek-amb/base/config/lwipopts.h +++ b/cores/realtek-amb/base/config/lwipopts.h @@ -4,11 +4,30 @@ #include_next "lwipopts.h" -#define ip_addr ip4_addr // LwIP 2.0.x compatibility -#define ip_addr_t ip4_addr_t // LwIP 2.0.x compatibility +#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 + +// - 2022-05-23 set LWIP_NUM_NETIF_CLIENT_DATA to 1 +#define LWIP_NUM_NETIF_CLIENT_DATA 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 +// - 2022-05-23 set MEMP_NUM_UDP_PCB to 7 +#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 diff --git a/cores/realtek-ambz/base/fixups/basic_types.h b/cores/realtek-amb/base/fixups/basic_types.h similarity index 51% rename from cores/realtek-ambz/base/fixups/basic_types.h rename to cores/realtek-amb/base/fixups/basic_types.h index 600279d29..c9a7f104b 100644 --- a/cores/realtek-ambz/base/fixups/basic_types.h +++ b/cores/realtek-amb/base/fixups/basic_types.h @@ -4,5 +4,16 @@ #ifdef ARDUINO #define boolean boolean_rtl #endif + +#include + +// the SDK declares bool if not defined before +// which conflicts with C++ built-in bool +// so it's either -fpermissive or this: +#ifndef bool +#define bool bool +#endif + #include_next "basic_types.h" + #undef boolean diff --git a/cores/realtek-ambz/base/fixups/cmsis_ipsr.c b/cores/realtek-amb/base/fixups/cmsis_ipsr.c similarity index 72% rename from cores/realtek-ambz/base/fixups/cmsis_ipsr.c rename to cores/realtek-amb/base/fixups/cmsis_ipsr.c index d6307fd05..7819c32ba 100644 --- a/cores/realtek-ambz/base/fixups/cmsis_ipsr.c +++ b/cores/realtek-amb/base/fixups/cmsis_ipsr.c @@ -4,9 +4,8 @@ // for some reason, cmsis_os.c does not link properly when this method is inlined in core_cmFunc.h // (or I am too stupid to understand this) -__attribute__((weak)) uint32_t __get_IPSR() -{ +__attribute__((weak)) uint32_t __get_IPSR() { uint32_t result; - asm volatile ("MRS %0, ipsr" : "=r" (result) ); + asm volatile("MRS %0, ipsr" : "=r"(result)); return result; } diff --git a/cores/realtek-ambz/base/fixups/machine/endian.h b/cores/realtek-amb/base/fixups/machine/endian.h similarity index 100% rename from cores/realtek-ambz/base/fixups/machine/endian.h rename to cores/realtek-amb/base/fixups/machine/endian.h diff --git a/cores/realtek-ambz/base/fixups/platform/platform_stdlib.h b/cores/realtek-amb/base/fixups/platform/platform_stdlib.h similarity index 100% rename from cores/realtek-ambz/base/fixups/platform/platform_stdlib.h rename to cores/realtek-amb/base/fixups/platform/platform_stdlib.h diff --git a/cores/realtek-ambz/base/fixups/platform_stdlib.h b/cores/realtek-amb/base/fixups/platform_stdlib.h similarity index 77% rename from cores/realtek-ambz/base/fixups/platform_stdlib.h rename to cores/realtek-amb/base/fixups/platform_stdlib.h index ebe54e11a..170a1abc2 100644 --- a/cores/realtek-ambz/base/fixups/platform_stdlib.h +++ b/cores/realtek-amb/base/fixups/platform_stdlib.h @@ -16,7 +16,11 @@ #include #include "basic_types.h" // fixup: replaces typedef boolean for Arduino compatibility -#include "memproc.h" // fixup: redirects to stdlib -#include "strproc.h" // fixup: redirects to stdlib +#if __has_include() +#include // fixup: redirects to stdlib +#endif +#if __has_include() +#include "strproc.h" // fixup: redirects to stdlib +#endif #include "diag.h" diff --git a/cores/realtek-ambz/base/fixups/section_config.h b/cores/realtek-amb/base/fixups/section_config.h similarity index 100% rename from cores/realtek-ambz/base/fixups/section_config.h rename to cores/realtek-amb/base/fixups/section_config.h diff --git a/cores/realtek-ambz/base/fixups/wifi_mode.c b/cores/realtek-amb/base/fixups/wifi_mode.c similarity index 100% rename from cores/realtek-ambz/base/fixups/wifi_mode.c rename to cores/realtek-amb/base/fixups/wifi_mode.c diff --git a/cores/realtek-amb/base/lt_api.c b/cores/realtek-amb/base/lt_api.c deleted file mode 100644 index a225a14da..000000000 --- a/cores/realtek-amb/base/lt_api.c +++ /dev/null @@ -1,219 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ - -#include -#include - -extern uint32_t GlobalDebugEnable; -extern uint16_t GlobalDebugLevel; -extern uint8_t GlobalPrivateLog; -extern uint8_t lt_uart_port; - -void lt_init_family() { - // make the SDK less verbose by default - GlobalDebugEnable = 0; - GlobalPrivateLog = 0; - lt_uart_port = LT_UART_DEFAULT_PORT; -} - -/* _____ _____ _ _ - / ____| __ \| | | | - | | | |__) | | | | - | | | ___/| | | | - | |____| | | |__| | - \_____|_| \____*/ -lt_cpu_model_t lt_cpu_get_model() { - uint8_t chipId; - EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE); - return CPU_MODEL_ENUM(FAMILY, chipId); -} - -uint32_t lt_cpu_get_unique_id() { - return lt_cpu_get_mac_id(); -} - -uint32_t lt_cpu_get_mac_id() { - uint32_t chipId = 0; - uint8_t *id = (uint8_t *)&chipId; - // 9902 was extracted from ROM disassembly, probably not needed - /* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE); - EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE); - EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */ - // new method, based on EFUSE logical map - uint8_t *efuse = (uint8_t *)malloc(512); - // TODO do what EFUSE_LogicalMapRead() does, and read only the used data - EFUSE_LogicalMap_Read(efuse); - memcpy(id, efuse + 0x11A + 3, 3); - free(efuse); - return chipId; -} - -const char *lt_cpu_get_core_type() { - return "ARM Cortex-M4F"; -} - -uint32_t lt_cpu_get_freq() { - return CPU_ClkGet(false); -} - -/*_____ _ - | __ \ (_) - | | | | _____ ___ ___ ___ - | | | |/ _ \ \ / / |/ __/ _ \ - | |__| | __/\ V /| | (_| __/ - |_____/ \___| \_/ |_|\___\__*/ -void lt_reboot() { - // The Watchdog Way - lt_wdt_enable(1L); - while (1) {} -} - -bool lt_reboot_download_mode() { - // mww 0x40000138 0x8 - HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08); - // reboot it the ugly way - sys_reset(); - while (1) {} - return true; -} - -lt_reboot_reason_t lt_get_reboot_reason() { - // TODO - return REBOOT_REASON_UNKNOWN; -} - -void lt_gpio_recover() { - // PA14 and PA15 are apparently unusable with SWD enabled - sys_jtag_off(); - Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO); - Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO); -} - -/*______ _ _ - | ____| | | | - | |__ | | __ _ ___| |__ - | __| | |/ _` / __| '_ \ - | | | | (_| \__ \ | | | - |_| |_|\__,_|___/_| |*/ -lt_flash_id_t lt_flash_get_id() { - lt_flash_id_t id; - uint8_t idBytes[3]; - flash_read_id(NULL, idBytes, 3); - id.manufacturer_id = idBytes[0]; - id.chip_id = idBytes[1]; - id.chip_size_id = idBytes[2]; - return id; -} - -/*__ __ - | \/ | - | \ / | ___ _ __ ___ ___ _ __ _ _ - | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | - | | | | __/ | | | | | (_) | | | |_| | - |_| |_|\___|_| |_| |_|\___/|_| \__, | - __/ | - |__*/ -uint32_t lt_ram_get_size() { - return 256 * 1024; -} - -/* ____ _______ - / __ \__ __|/\ - | | | | | | / \ - | | | | | | / /\ \ - | |__| | | |/ ____ \ - \____/ |_/_/ \*/ -lt_ota_type_t lt_ota_get_type() { - return OTA_TYPE_DUAL; -} - -bool lt_ota_is_valid(uint8_t index) { - uint32_t offset; - switch (index) { - case 1: - offset = FLASH_OTA1_OFFSET; - break; - case 2: - offset = FLASH_OTA2_OFFSET; - break; - default: - return false; - } - uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset); - return memcmp(address, "81958711", 8) == 0; -} - -uint8_t lt_ota_dual_get_current() { - // RTL8710B is XIP, so check the code offset in flash - uint32_t addr = (uint32_t)lt_log; - uint32_t offs = addr - SPI_FLASH_BASE; - return offs > FLASH_OTA2_OFFSET ? 2 : 1; -} - -uint8_t lt_ota_dual_get_stored() { - uint32_t *ota_address = (uint32_t *)0x8009000; - if (*ota_address == 0xFFFFFFFF) - return 1; - uint32_t ota_counter = *((uint32_t *)0x8009004); - // even count of zero-bits means OTA1, odd count means OTA2 - // this allows to switch OTA images by simply clearing next bits, - // without needing to erase the flash - uint8_t count = 0; - for (uint8_t i = 0; i < 32; i++) { - if ((ota_counter & (1 << i)) == 0) - count++; - } - return 1 + (count % 2); -} - -bool lt_ota_switch(bool revert) { - uint8_t current = lt_ota_dual_get_current(); - uint8_t stored = lt_ota_dual_get_stored(); - if ((current == stored) == revert) - return true; - - if (!lt_ota_is_valid(stored ^ 0b11)) - return false; - - // - read current OTA switch value from 0x9004 - // - reset OTA switch to 0xFFFFFFFE if it's 0x0 - // - else check first non-zero bit of OTA switch - // - write OTA switch with first non-zero bit cleared - - uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4); - if (value == 0) { - uint8_t *system = (uint8_t *)malloc(64); - lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64); - // reset OTA switch - ((uint32_t *)system)[1] = -2; - lt_flash_erase_block(FLASH_SYSTEM_OFFSET); - return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64); - } - - // clear first non-zero bit - value <<= 1; - // write OTA switch to flash - flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value); - return true; -} - -/*_ __ _ _ _ - \ \ / / | | | | | | - \ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _ - \ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` | - \ /\ / (_| | || (__| | | | (_| | (_) | (_| | - \/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, | - __/ | - |___*/ -bool lt_wdt_enable(uint32_t timeout) { - watchdog_init(timeout); - watchdog_start(); - return true; -} - -void lt_wdt_disable() { - watchdog_stop(); -} - -void lt_wdt_feed() { - watchdog_refresh(); -} diff --git a/cores/realtek-amb/base/lt_defs.h b/cores/realtek-amb/base/lt_defs.h index e6250fff6..1b387c654 100644 --- a/cores/realtek-amb/base/lt_defs.h +++ b/cores/realtek-amb/base/lt_defs.h @@ -2,7 +2,9 @@ #error "Don't include this file directly" -#define LT_HAS_PRINTF 1 +#define LT_HAS_FLASH 1 +#define LT_HW_WATCHDOG 1 +#define LT_HW_WIFI 1 #define LT_HEAP_FUNC xPortGetFreeHeapSize #define LT_REALLOC_FUNC pvPortReAlloc diff --git a/cores/realtek-amb/base/port/fal_flash_ambz_port.c b/cores/realtek-amb/base/port/fal_flash_ambz_port.c index cea509be8..9cf5f0a33 100644 --- a/cores/realtek-amb/base/port/fal_flash_ambz_port.c +++ b/cores/realtek-amb/base/port/fal_flash_ambz_port.c @@ -7,24 +7,26 @@ #define FLASH_ERASE_MIN_SIZE (4 * 1024) +flash_t lt_flash_obj; + static int init() { - flash_get_status(NULL); + flash_get_status(<_flash_obj); return 0; } static int read(long offset, uint8_t *buf, size_t size) { - return size * flash_stream_read(NULL, offset, size, buf); + return size * flash_stream_read(<_flash_obj, offset, size, buf); } static int write(long offset, const uint8_t *buf, size_t size) { - return size * flash_stream_write(NULL, offset, size, (uint8_t *)buf); + return size * flash_stream_write(<_flash_obj, offset, size, (uint8_t *)buf); } static int erase(long offset, size_t size) { offset &= ~(FLASH_ERASE_MIN_SIZE - 1); size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1; for (uint16_t i = 0; i < size; i++) { - flash_erase_sector(NULL, offset + i * FLASH_ERASE_MIN_SIZE); + flash_erase_sector(<_flash_obj, offset + i * FLASH_ERASE_MIN_SIZE); } return size * FLASH_ERASE_MIN_SIZE; } diff --git a/cores/realtek-amb/base/sdk_private.h b/cores/realtek-amb/base/sdk_private.h index 4b8905184..1a4f5dfe4 100644 --- a/cores/realtek-amb/base/sdk_private.h +++ b/cores/realtek-amb/base/sdk_private.h @@ -14,21 +14,34 @@ extern "C" { // remove log_printf() if included before sdk_private.h #undef log_printf +// CMSIS & Realtek APIs #if LT_RTL8710B #include #include #include #endif #if LT_RTL8720C +#include +#include #include #endif +#include +#undef malloc +#undef free +#undef calloc + +// mbed APIs #include #undef MBED_GPIO_API_H // ..no comment #include #include #include +#include +#if LT_RTL8720C +#include +#endif #include #include #include @@ -36,12 +49,18 @@ extern "C" { #include #include #include +#include #include #include #include #include #include +// other SDK APIs +#if __has_include() +#include +#endif + // remove previously defined workarounds #undef PinMode @@ -66,14 +85,7 @@ extern "C" { #define _write __rtl_write #define _sbrk __rtl_sbrk -#define delay_us wait_us - -extern void wait_us(int us); -extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c -extern void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70! -extern void SystemCoreClockUpdate(void); - -extern int _sscanf_patch(const char *buf, const char *fmt, ...); +extern flash_t lt_flash_obj; #ifdef __cplusplus } // extern "C" diff --git a/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp b/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp new file mode 100644 index 000000000..6f5d8e310 --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/Serial/Serial.cpp @@ -0,0 +1,135 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-07-03. */ + +#include "SerialPrivate.h" + +#if LT_HW_UART0 +SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX); +#endif +#if LT_HW_UART1 +SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX); +#endif +#if LT_HW_UART2 +SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX); +#endif + +static UART_TypeDef *PORT_UART[3] = {UART0_DEV, UART1_DEV, UART2_DEV}; +static const IRQn PORT_IRQ[3] = {UART0_IRQ, UART1_IRQ, UART_LOG_IRQ}; + +static uint32_t callback(void *param) { + UART_TypeDef *uart = pdUART; + + uint32_t intcr = uart->DLH_INTCR; + uart->DLH_INTCR = 0; + + uint8_t c; + while (UART_Readable(uart)) { + UART_CharGet(uart, &c); +#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) && PIN_SERIAL2_RX != PIN_INVALID + // parse UART protocol commands on UART2 + if (uart == UART2_DEV) + SerialClass::adrParse(c); +#endif + pdBUF.store_char(c); + } + + uart->DLH_INTCR = intcr; + return 0; +} + +void SerialClass::begin(unsigned long baudrate, uint16_t config) { + if (!this->data) { + this->data = new SerialData(); + this->buf = &BUF; + DATA->uart = PORT_UART[this->port]; + DATA->irq = PORT_IRQ[this->port]; + + switch ((uint32_t)DATA->uart) { + case UART0_REG_BASE: + RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); + break; + case UART1_REG_BASE: + RCC_PeriphClockCmd(APBPeriph_UART1, APBPeriph_UART1_CLOCK, ENABLE); + break; + } + + if (this->tx != PIN_INVALID) { + Pinmux_Config(this->tx, PINMUX_FUNCTION_UART); + } + if (this->rx != PIN_INVALID) { + Pinmux_Config(this->rx, PINMUX_FUNCTION_UART); + PAD_PullCtrl(this->rx, GPIO_PuPd_UP); + } + } + + if (this->baudrate != baudrate || this->config != config) + this->configure(baudrate, config); + + if (this->rx != PIN_INVALID) { + VECTOR_IrqUnRegister(DATA->irq); + VECTOR_IrqRegister(callback, DATA->irq, (uint32_t)this->data, 10); + VECTOR_IrqEn(DATA->irq, 10); + UART_RxCmd(UART, ENABLE); + UART_INTConfig(UART, RUART_IER_ERBI, ENABLE); + } +} + +void SerialClass::configure(unsigned long baudrate, uint16_t config) { + if (!this->data) + return; + + // RUART_WLS_7BITS / RUART_WLS_8BITS + uint8_t dataWidth = (config & SERIAL_DATA_MASK) == SERIAL_DATA_8; + // RUART_PARITY_DISABLE / RUART_PARITY_ENABLE + uint8_t parity = (config & SERIAL_PARITY_MASK) != SERIAL_PARITY_NONE; + // RUART_ODD_PARITY / RUART_EVEN_PARITY + uint8_t parityType = (config & SERIAL_PARITY_MASK) == SERIAL_PARITY_EVEN; + // RUART_STOP_BIT_1 / RUART_STOP_BIT_2 + uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2; + + UART_InitTypeDef cfg; + UART_StructInit(&cfg); + cfg.WordLen = dataWidth; + cfg.Parity = parity; + cfg.ParityType = parityType; + cfg.StopBit = stopBits; + UART_Init(UART, &cfg); + UART_SetBaud(UART, baudrate); + + this->baudrate = baudrate; + this->config = config; +} + +void SerialClass::end() { + if (!this->data) + return; + + UART_INTConfig(UART, RUART_IER_ERBI, DISABLE); + UART_RxCmd(UART, DISABLE); + VECTOR_IrqDis(DATA->irq); + VECTOR_IrqUnRegister(DATA->irq); + UART_DeInit(UART); + + if (UART == UART2_DEV) { + // restore command line mode + DIAG_UartReInit((IRQ_FUN)UartLogIrqHandle); + } + + delete DATA; + this->data = NULL; + this->buf = NULL; + this->baudrate = 0; +} + +void SerialClass::flush() { + if (!this->data) + return; + UART_WaitBusy(UART, 10); +} + +size_t SerialClass::write(uint8_t c) { + if (!this->data) + return 0; + while (UART_Writable(UART) == 0) {} + UART_CharPut(UART, c); + return 1; +} diff --git a/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h b/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h new file mode 100644 index 000000000..315113531 --- /dev/null +++ b/cores/realtek-ambz/arduino/libraries/Serial/SerialPrivate.h @@ -0,0 +1,19 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#pragma once + +#include +#include + +typedef struct { + UART_TypeDef *uart; + IRQn irq; + RingBuffer buf; +} SerialData; + +#define DATA ((SerialData *)data) +#define pDATA ((SerialData *)param) +#define BUF (DATA->buf) +#define pdBUF (pDATA->buf) +#define UART (DATA->uart) +#define pdUART (pDATA->uart) diff --git a/cores/realtek-ambz/arduino/src/lt_defs.h b/cores/realtek-ambz/arduino/src/lt_defs.h new file mode 100644 index 000000000..39692e027 --- /dev/null +++ b/cores/realtek-ambz/arduino/src/lt_defs.h @@ -0,0 +1,6 @@ +#pragma once + +#error "Don't include this file directly" + +#define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_MD5_POLARSSL 1 diff --git a/cores/realtek-ambz/base/api/lt_cpu.c b/cores/realtek-ambz/base/api/lt_cpu.c new file mode 100644 index 000000000..eb346cf26 --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_cpu.c @@ -0,0 +1,34 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +lt_cpu_model_t lt_cpu_get_model() { + uint8_t chipId; + EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE); + return CPU_MODEL_ENUM(FAMILY, chipId); +} + +uint32_t lt_cpu_get_mac_id() { + uint32_t chipId = 0; + uint8_t *id = (uint8_t *)&chipId; + // 9902 was extracted from ROM disassembly, probably not needed + /* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE); + EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE); + EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */ + // new method, based on EFUSE logical map + uint8_t *efuse = (uint8_t *)malloc(512); + // TODO do what EFUSE_LogicalMapRead() does, and read only the used data + EFUSE_LogicalMap_Read(efuse); + memcpy(id, efuse + 0x11A + 3, 3); + free(efuse); + return chipId; +} + +const char *lt_cpu_get_core_type() { + return "ARM Cortex-M4F (ARMv7E-M)"; +} + +uint32_t lt_cpu_get_freq() { + return CPU_ClkGet(false); +} diff --git a/cores/realtek-ambz/base/api/lt_device.c b/cores/realtek-ambz/base/api/lt_device.c new file mode 100644 index 000000000..19511d71d --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_device.c @@ -0,0 +1,39 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +void lt_get_device_mac(uint8_t *mac) { + uint8_t *efuse = (uint8_t *)malloc(512); + EFUSE_LogicalMap_Read(efuse); + memcpy(mac, efuse + 0x11A, 6); + free(efuse); +} + +bool lt_reboot_download_mode() { + // mww 0x40000138 0x8 + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08); + // reboot it the ugly way + sys_reset(); + while (1) {} + return true; +} + +bool lt_set_debug_mode(lt_debug_mode_t mode) { + uint32_t *swd; + switch (mode) { + case DEBUG_MODE_OFF: + sys_jtag_off(); + Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO); + Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO); + return true; + case DEBUG_MODE_SWD: + Pinmux_Config(PA_14, PINMUX_FUNCTION_SWD); + Pinmux_Config(PA_15, PINMUX_FUNCTION_SWD); + uint32_t *swd = (uint32_t *)0x400000A4; + *swd |= 0x1000; + return true; + default: + return false; + } +} diff --git a/cores/realtek-ambz/base/api/lt_flash.c b/cores/realtek-ambz/base/api/lt_flash.c new file mode 100644 index 000000000..73438795e --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_flash.c @@ -0,0 +1,14 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +lt_flash_id_t lt_flash_get_id() { + lt_flash_id_t id; + uint8_t idBytes[3]; + flash_read_id(NULL, idBytes, 3); + id.manufacturer_id = idBytes[0]; + id.chip_id = idBytes[1]; + id.chip_size_id = idBytes[2]; + return id; +} diff --git a/cores/realtek-ambz/base/api/lt_init.c b/cores/realtek-ambz/base/api/lt_init.c new file mode 100644 index 000000000..c9c0167d7 --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_init.c @@ -0,0 +1,16 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +extern uint32_t GlobalDebugEnable; +extern uint16_t GlobalDebugLevel; +extern uint8_t GlobalPrivateLog; +extern uint8_t lt_uart_port; + +void lt_init_family() { + // make the SDK less verbose by default + GlobalDebugEnable = 0; + GlobalPrivateLog = 0; + lt_uart_port = LT_UART_DEFAULT_PORT; +} diff --git a/cores/realtek-ambz/base/api/lt_mem.c b/cores/realtek-ambz/base/api/lt_mem.c new file mode 100644 index 000000000..c3e5a4a08 --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_mem.c @@ -0,0 +1,8 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +uint32_t lt_ram_get_size() { + return 256 * 1024; +} diff --git a/cores/realtek-ambz/base/api/lt_ota.c b/cores/realtek-ambz/base/api/lt_ota.c new file mode 100644 index 000000000..7ed571025 --- /dev/null +++ b/cores/realtek-ambz/base/api/lt_ota.c @@ -0,0 +1,78 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-02-27. */ + +#include +#include + +lt_ota_type_t lt_ota_get_type() { + return OTA_TYPE_DUAL; +} + +bool lt_ota_is_valid(uint8_t index) { + uint32_t offset; + switch (index) { + case 1: + offset = FLASH_OTA1_OFFSET; + break; + case 2: + offset = FLASH_OTA2_OFFSET; + break; + default: + return false; + } + uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset); + return memcmp(address, "81958711", 8) == 0; +} + +uint8_t lt_ota_dual_get_current() { + // RTL8710B is XIP, so check the code offset in flash + uint32_t addr = (uint32_t)lt_log; + uint32_t offs = addr - SPI_FLASH_BASE; + return offs > FLASH_OTA2_OFFSET ? 2 : 1; +} + +uint8_t lt_ota_dual_get_stored() { + uint32_t *ota_address = (uint32_t *)0x8009000; + if (*ota_address == 0xFFFFFFFF) + return 1; + uint32_t ota_counter = *((uint32_t *)0x8009004); + // even count of zero-bits means OTA1, odd count means OTA2 + // this allows to switch OTA images by simply clearing next bits, + // without needing to erase the flash + uint8_t count = 0; + for (uint8_t i = 0; i < 32; i++) { + if ((ota_counter & (1 << i)) == 0) + count++; + } + return 1 + (count % 2); +} + +bool lt_ota_switch(bool revert) { + uint8_t current = lt_ota_dual_get_current(); + uint8_t stored = lt_ota_dual_get_stored(); + if ((current == stored) == revert) + return true; + + if (!lt_ota_is_valid(stored ^ 0b11)) + return false; + + // - read current OTA switch value from 0x9004 + // - reset OTA switch to 0xFFFFFFFE if it's 0x0 + // - else check first non-zero bit of OTA switch + // - write OTA switch with first non-zero bit cleared + + uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4); + if (value == 0) { + uint8_t *system = (uint8_t *)malloc(64); + lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64); + // reset OTA switch + ((uint32_t *)system)[1] = -2; + lt_flash_erase_block(FLASH_SYSTEM_OFFSET); + return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64); + } + + // clear first non-zero bit + value <<= 1; + // write OTA switch to flash + flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value); + return true; +} diff --git a/cores/realtek-ambz/base/config/lwipopts.h b/cores/realtek-ambz/base/config/lwipopts.h deleted file mode 100644 index 8c5aa4072..000000000 --- a/cores/realtek-ambz/base/config/lwipopts.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright (c) Kuba SzczodrzyƄski 2023-03-02. */ - -#pragma once - -#include_next "lwipopts.h" - -// - 2022-05-23 set LWIP_NUM_NETIF_CLIENT_DATA to 1 -#define LWIP_NUM_NETIF_CLIENT_DATA 1 -#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 -// - 2022-05-23 set MEMP_NUM_UDP_PCB to 7 -#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 diff --git a/cores/realtek-ambz/base/lt_defs.h b/cores/realtek-ambz/base/lt_defs.h index 0125e4bc0..a8f13449e 100644 --- a/cores/realtek-ambz/base/lt_defs.h +++ b/cores/realtek-ambz/base/lt_defs.h @@ -2,7 +2,9 @@ #error "Don't include this file directly" +#define LT_HAS_FREERTOS 1 #define LT_HAS_LWIP 1 #define LT_HAS_LWIP2 1 -#define LT_HAS_FREERTOS 1 #define LT_HAS_MBEDTLS 1 +#define LT_HAS_OTA 1 +#define LT_HAS_PRINTF 1 diff --git a/cores/realtek-ambz/base/lt_family.h b/cores/realtek-ambz/base/lt_family.h index 9723ffb79..205b9c80a 100644 --- a/cores/realtek-ambz/base/lt_family.h +++ b/cores/realtek-ambz/base/lt_family.h @@ -2,17 +2,27 @@ #pragma once -#include LT_VARIANT_H +#include // Choose the main UART output port #ifndef LT_UART_DEFAULT_PORT -#if HAS_SERIAL2 +#if LT_HW_UART2 #define LT_UART_DEFAULT_PORT 2 -#elif HAS_SERIAL0 +#elif LT_HW_UART0 #define LT_UART_DEFAULT_PORT 0 -#elif HAS_SERIAL1 +#elif LT_HW_UART1 #define LT_UART_DEFAULT_PORT 1 #else #error "No serial port is available" #endif #endif + +// clang-format off +// Auto-download-reboot detection pattern +// Family ID, big-endian +#define LT_UART_ADR_PATTERN 0x55, 0xAA, \ + (FAMILY >> 24) & 0xFF, \ + (FAMILY >> 16) & 0xFF, \ + (FAMILY >> 8) & 0xFF, \ + (FAMILY >> 0) & 0xFF +// clang-format on diff --git a/cores/realtek-amb/base/port/printf.c b/cores/realtek-ambz/base/port/printf.c similarity index 100% rename from cores/realtek-amb/base/port/printf.c rename to cores/realtek-ambz/base/port/printf.c diff --git a/cores/realtek-amb/base/port/printf_port.h b/cores/realtek-ambz/base/port/printf_port.h similarity index 100% rename from cores/realtek-amb/base/port/printf_port.h rename to cores/realtek-ambz/base/port/printf_port.h diff --git a/cores/realtek-ambz/base/sdk_extern.h b/cores/realtek-ambz/base/sdk_extern.h new file mode 100644 index 000000000..131f623f3 --- /dev/null +++ b/cores/realtek-ambz/base/sdk_extern.h @@ -0,0 +1,17 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c +void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70! + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp b/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp new file mode 100644 index 000000000..9fb18d66c --- /dev/null +++ b/cores/realtek-ambz2/arduino/libraries/Serial/Serial.cpp @@ -0,0 +1,102 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#include "SerialPrivate.h" + +#if LT_HW_UART0 +SerialClass Serial0(0, PIN_SERIAL0_RX, PIN_SERIAL0_TX); +#endif +#if LT_HW_UART1 +SerialClass Serial1(1, PIN_SERIAL1_RX, PIN_SERIAL1_TX); +#endif +#if LT_HW_UART2 +SerialClass Serial2(2, PIN_SERIAL2_RX, PIN_SERIAL2_TX); +#endif + +static void callback(uint32_t param, uint32_t event) { + if (event != RxIrq) + return; + hal_uart_adapter_t *uart = pdUART; + + uint8_t c; + while (hal_uart_rgetc(uart, (char *)&c)) { +#if LT_AUTO_DOWNLOAD_REBOOT && defined(LT_UART_ADR_PATTERN) && PIN_SERIAL2_RX != PIN_INVALID + // parse UART protocol commands on UART2 + if (uart->base_addr == UART2) + SerialClass::adrParse(c); +#endif + pdBUF.store_char(c); + } +} + +void SerialClass::begin(unsigned long baudrate, uint16_t config) { + if (!this->data) { + this->data = new SerialData(); + this->buf = &BUF; + + if (this->port == 2) { + DATA->uart = &log_uart; + } else { + UART = new hal_uart_adapter_t(); + // TODO handle PIN_INVALID + hal_uart_init(UART, this->tx, this->rx, NULL); + } + + if (this->rx != PIN_INVALID) { + hal_uart_enter_critical(); + hal_uart_rxind_hook(UART, callback, (uint32_t)this->data, RxIrq); + UART->base_addr->ier_b.erbi = 1; + UART->base_addr->ier_b.etbei = 0; + hal_uart_exit_critical(); + } + } + + if (this->baudrate != baudrate || this->config != config) + this->configure(baudrate, config); +} + +void SerialClass::configure(unsigned long baudrate, uint16_t config) { + if (!this->data) + return; + + uint8_t dataWidth = (config & SERIAL_DATA_MASK) == SERIAL_DATA_7 ? 7 : 8; + uint8_t parity = (config & SERIAL_PARITY_MASK) ^ 0b11; + uint8_t stopBits = (config & SERIAL_STOP_BIT_MASK) == SERIAL_STOP_BIT_2 ? 2 : 1; + + hal_uart_set_baudrate(UART, baudrate); + hal_uart_set_format(UART, dataWidth, parity, stopBits); + + this->baudrate = baudrate; + this->config = config; +} + +void SerialClass::end() { + if (!this->data) + return; + + if (this->port == 2) { + UART->base_addr->ier_b.erbi = 0; + hal_uart_rxind_hook(UART, NULL, 0, RxIrq); + } else { + hal_uart_deinit(UART); + delete UART; + } + + delete DATA; + this->data = NULL; + this->buf = NULL; + this->baudrate = 0; +} + +void SerialClass::flush() { + if (!this->data) + return; + while (UART->base_addr->tflvr_b.tx_fifo_lv != 0) {} +} + +size_t SerialClass::write(uint8_t c) { + if (!this->data) + return 0; + while (!hal_uart_writeable(UART)) {} + hal_uart_putc(UART, c); + return 1; +} diff --git a/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h b/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h new file mode 100644 index 000000000..6fb1ec947 --- /dev/null +++ b/cores/realtek-ambz2/arduino/libraries/Serial/SerialPrivate.h @@ -0,0 +1,18 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-24. */ + +#pragma once + +#include +#include + +typedef struct { + hal_uart_adapter_t *uart; + RingBuffer buf; +} SerialData; + +#define DATA ((SerialData *)data) +#define pDATA ((SerialData *)param) +#define BUF (DATA->buf) +#define pdBUF (pDATA->buf) +#define UART (DATA->uart) +#define pdUART (pDATA->uart) diff --git a/cores/realtek-ambz2/arduino/src/lt_defs.h b/cores/realtek-ambz2/arduino/src/lt_defs.h new file mode 100644 index 000000000..f6cceb071 --- /dev/null +++ b/cores/realtek-ambz2/arduino/src/lt_defs.h @@ -0,0 +1,9 @@ +#pragma once + +#error "Don't include this file directly" + +#define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_HAS_SOFTSERIAL 0 +#define LT_ARD_HAS_WIFI 1 +#define LT_ARD_HAS_WIRE 0 +#define LT_ARD_MD5_MBEDTLS 1 diff --git a/cores/realtek-ambz2/base/api/lt_cpu.c b/cores/realtek-ambz2/base/api/lt_cpu.c new file mode 100644 index 000000000..7f9c1ef14 --- /dev/null +++ b/cores/realtek-ambz2/base/api/lt_cpu.c @@ -0,0 +1,21 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-22. */ + +#include +#include + +lt_cpu_model_t lt_cpu_get_model() { + uint32_t *addr = (uint32_t *)0x40000038; + uint8_t flash_mode = (addr[0] >> 5) & 0b11; + uint32_t chip_id = 0; + hal_get_chip_id(&chip_id); + chip_id <<= 2; + return CPU_MODEL_ENUM(FAMILY, (chip_id & 0xFF) | flash_mode); +} + +const char *lt_cpu_get_core_type() { + return "ARM Cortex-M33 (ARMv8-M)"; +} + +uint32_t lt_cpu_get_freq() { + return hal_syson_query_sys_clk(); +} diff --git a/cores/realtek-ambz2/base/api/lt_device.c b/cores/realtek-ambz2/base/api/lt_device.c new file mode 100644 index 000000000..5cfff7e30 --- /dev/null +++ b/cores/realtek-ambz2/base/api/lt_device.c @@ -0,0 +1,61 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-22. */ + +#include +#include + +void lt_get_device_mac(uint8_t *mac) { + efuse_logical_read(0x11A, 6, mac); +} + +void lt_reboot() { + sys_cpu_reset(); + while (1) {} +} + +bool lt_reboot_download_mode() { + sys_uart_download_mode(); + while (1) {} + return true; +} + +lt_reboot_reason_t lt_get_reboot_reason() { + hal_reset_reason_t reason = -1; + rtl8710c_reset_reason_get(&reason); + switch (reason) { + case HAL_RESET_REASON_POWER_ON: + return REBOOT_REASON_POWER; + case HAL_RESET_REASON_SOFTWARE: + return REBOOT_REASON_SOFTWARE; + case HAL_RESET_REASON_WATCHDOG: + return REBOOT_REASON_WATCHDOG; + case HAL_RESET_REASON_JTAG: + return REBOOT_REASON_DEBUGGER; + default: + return REBOOT_REASON_UNKNOWN; + } +} + +bool lt_set_debug_mode(lt_debug_mode_t mode) { + switch (mode) { + case DEBUG_MODE_OFF: + if (hal_misc_jtag_pin_ctrl(0) != HAL_OK) + return false; + if (hal_misc_swd_pin_ctrl(0) != HAL_OK) + return false; + return true; + case DEBUG_MODE_JTAG: + if (hal_misc_swd_pin_ctrl(0) != HAL_OK) + return false; + if (hal_misc_jtag_pin_ctrl(1) != HAL_OK) + return false; + return true; + case DEBUG_MODE_SWD: + if (hal_misc_jtag_pin_ctrl(0) != HAL_OK) + return false; + if (hal_misc_swd_pin_ctrl(1) != HAL_OK) + return false; + return true; + default: + return false; + } +} diff --git a/cores/realtek-ambz2/base/api/lt_init.c b/cores/realtek-ambz2/base/api/lt_init.c new file mode 100644 index 000000000..f83ce88a7 --- /dev/null +++ b/cores/realtek-ambz2/base/api/lt_init.c @@ -0,0 +1,14 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-22. */ + +#include +#include + +extern uint8_t lt_uart_port; + +void lt_init_family() { + // make the SDK less verbose by default + ConfigDebugErr = _DBG_MISC_ | _DBG_FAULT_ | _DBG_BOOT_; + ConfigDebugWarn = 0; + ConfigDebugInfo = 0; + lt_uart_port = LT_UART_DEFAULT_PORT; +} diff --git a/cores/realtek-ambz2/base/api/lt_mem.c b/cores/realtek-ambz2/base/api/lt_mem.c new file mode 100644 index 000000000..b079d4c14 --- /dev/null +++ b/cores/realtek-ambz2/base/api/lt_mem.c @@ -0,0 +1,8 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-22. */ + +#include +#include + +uint32_t lt_ram_get_size() { + return 256 * 1024; +} diff --git a/cores/realtek-ambz2/base/api/lt_ota.c b/cores/realtek-ambz2/base/api/lt_ota.c new file mode 100644 index 000000000..d96af85d1 --- /dev/null +++ b/cores/realtek-ambz2/base/api/lt_ota.c @@ -0,0 +1,24 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-22. */ + +#include +#include + +lt_ota_type_t lt_ota_get_type() { + return OTA_TYPE_DUAL; +} + +bool lt_ota_is_valid(uint8_t index) { + return false; +} + +uint8_t lt_ota_dual_get_current() { + return 0; +} + +uint8_t lt_ota_dual_get_stored() { + return 0; +} + +bool lt_ota_switch(bool revert) { + return false; +} diff --git a/cores/realtek-ambz2/base/config/lwipopts.h b/cores/realtek-ambz2/base/config/lwipopts.h new file mode 100644 index 000000000..642ba3529 --- /dev/null +++ b/cores/realtek-ambz2/base/config/lwipopts.h @@ -0,0 +1,10 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-27. */ + +#pragma once + +// lwipopts.h defines `unsigned int sys_now()`, while lwip/sys.h defines `u32_t sys_now()` +// since u32_t is unsigned long, these are incompatible +#define sys_now sys_now_dummy +#include_next "lwipopts.h" +#undef sys_now +extern unsigned long sys_now(void); diff --git a/cores/realtek-ambz2/base/config/platform_conf.h b/cores/realtek-ambz2/base/config/platform_conf.h new file mode 100644 index 000000000..fbf831f0b --- /dev/null +++ b/cores/realtek-ambz2/base/config/platform_conf.h @@ -0,0 +1,22 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#pragma once + +#include_next "platform_conf.h" + +#undef CONFIG_DEBUG_LOG +#undef CONFIG_DEBUG_ERROR +#undef CONFIG_DEBUG_WARN +#undef CONFIG_DEBUG_INFO +#define CONFIG_DEBUG_LOG 0 +#define CONFIG_DEBUG_ERROR 0 +#define CONFIG_DEBUG_WARN 0 +#define CONFIG_DEBUG_INFO 0 + +// diag.h doesn't define this if CONFIG_DEBUG_LOG is 0 +#define DBG_SCE_ERR(...) +#define DBG_SCE_WARN(...) +#define DBG_SCE_INFO(...) + +// ...? +#define CONFIG_ADC_EN diff --git a/cores/realtek-ambz2/base/config/platform_opts.h b/cores/realtek-ambz2/base/config/platform_opts.h new file mode 100644 index 000000000..95f84c411 --- /dev/null +++ b/cores/realtek-ambz2/base/config/platform_opts.h @@ -0,0 +1,9 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-25. */ + +#pragma once + +#include_next "platform_opts.h" + +// this needs example_wlan_fast_connect.c +#undef CONFIG_EXAMPLE_WLAN_FAST_CONNECT +#undef CONFIG_FAST_DHCP diff --git a/cores/realtek-ambz2/base/fixups/cmd_shell.c b/cores/realtek-ambz2/base/fixups/cmd_shell.c new file mode 100644 index 000000000..622802c7e --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/cmd_shell.c @@ -0,0 +1,7 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-04-12. */ + +extern void lt_main(); + +void shell_cmd_init() { + lt_main(); +} diff --git a/cores/realtek-ambz2/base/fixups/cmsis.h b/cores/realtek-ambz2/base/fixups/cmsis.h new file mode 100644 index 000000000..112417652 --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/cmsis.h @@ -0,0 +1,10 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +// use platform_conf.h from realtek-ambz2/base/config +#include +// use basic_types.h from realtek-amb/base/fixups +#include +// use basic_types.h from realtek-ambz2/base/fixups +#include "diag.h" + +#include_next "cmsis.h" diff --git a/cores/realtek-ambz2/base/fixups/diag.h b/cores/realtek-ambz2/base/fixups/diag.h new file mode 100644 index 000000000..96e30da90 --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/diag.h @@ -0,0 +1,19 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +// remove component/soc/realtek/8710c/app/rtl_printf/include/rt_printf.h +// replace with #defines below +#ifndef _RT_PRINTF__H +#define _RT_PRINTF__H +#endif + +#undef log_printf +#include_next "diag.h" +#undef log_printf + +#define rt_printf(...) __wrap_rt_printf(__VA_ARGS__) +#define rt_printfl(...) __wrap_rt_printf(__VA_ARGS__) +#define rt_sprintf(...) __wrap_rt_sprintf(__VA_ARGS__) +#define rt_sprintfl(...) __wrap_rt_sprintf(__VA_ARGS__) +#define rt_snprintf(...) __wrap_rt_snprintf(__VA_ARGS__) +#define rt_snprintfl(...) __wrap_rt_snprintf(__VA_ARGS__) +#define rt_log_printf(...) __wrap_rt_log_printf(__VA_ARGS__) diff --git a/cores/realtek-ambz2/base/fixups/hal_flash.h b/cores/realtek-ambz2/base/fixups/hal_flash.h new file mode 100644 index 000000000..ebbf1f70c --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/hal_flash.h @@ -0,0 +1,13 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-31. */ + +// - flash_api.c and flash_api_ext.c are both compiled in libambz2_sdk.a +// - the former declares weak functions 'flash_resource_lock' and 'flash_resource_unlock', +// while the latter actually implements them +// - for some reason, the linker resolves the weak functions only, +// and doesn't include them in the final binary + +#include_next "hal_flash.h" + +// try to remove the weak attribute +#undef __weak +#define __weak diff --git a/cores/realtek-ambz2/base/fixups/rtl8710c_freertos_pmu.h b/cores/realtek-ambz2/base/fixups/rtl8710c_freertos_pmu.h new file mode 100644 index 000000000..ab014d880 --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/rtl8710c_freertos_pmu.h @@ -0,0 +1,11 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#include + +// rtl8710c_freertos_pmu.h needs the u32/u16/u8 integer typedefs, +// so it relies on the caller (freertos_pmu.c) to provide these, +// which relies on FreeRTOS.h to include FreeRTOSConfig.h, +// which relies on diag.h included to get printf() functions, +// which just happens to include basic_types.h as well, +// and that's what makes the whole thing even compile +#include_next "rtl8710c_freertos_pmu.h" diff --git a/cores/realtek-ambz2/base/fixups/strproc.c b/cores/realtek-ambz2/base/fixups/strproc.c index 18d9de884..3feac7663 100644 --- a/cores/realtek-ambz2/base/fixups/strproc.c +++ b/cores/realtek-ambz2/base/fixups/strproc.c @@ -151,7 +151,7 @@ unsigned int atoui(const char *num) { return strproc_stubs.atoui(num); } -size_t strnlen(const char *s, size_t count) { +__attribute__((weak)) size_t strnlen(const char *s, size_t count) { return strproc_stubs.strnlen(s, count); } @@ -163,6 +163,6 @@ int strnicmp(const char *s1, const char *s2, size_t len) { return strproc_stubs.strnicmp(s1, s2, len); } -char *strsep(char **s, const char *ct) { +__attribute__((weak)) char *strsep(char **s, const char *ct) { return strproc_stubs.strsep(s, ct); } diff --git a/cores/realtek-ambz2/base/fixups/wireless.h b/cores/realtek-ambz2/base/fixups/wireless.h new file mode 100644 index 000000000..c9915859b --- /dev/null +++ b/cores/realtek-ambz2/base/fixups/wireless.h @@ -0,0 +1,5 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-27. */ + +#undef IFNAMSIZ + +#include_next "wireless.h" diff --git a/cores/realtek-ambz2/base/lt_defs.h b/cores/realtek-ambz2/base/lt_defs.h index 0125e4bc0..4868572f3 100644 --- a/cores/realtek-ambz2/base/lt_defs.h +++ b/cores/realtek-ambz2/base/lt_defs.h @@ -2,7 +2,9 @@ #error "Don't include this file directly" +#define LT_HAS_FREERTOS 1 #define LT_HAS_LWIP 1 #define LT_HAS_LWIP2 1 -#define LT_HAS_FREERTOS 1 #define LT_HAS_MBEDTLS 1 +#define LT_HAS_PRINTF 1 +#define LT_HW_BLE 1 diff --git a/cores/realtek-ambz2/base/lt_family.h b/cores/realtek-ambz2/base/lt_family.h index 9723ffb79..a574f6cda 100644 --- a/cores/realtek-ambz2/base/lt_family.h +++ b/cores/realtek-ambz2/base/lt_family.h @@ -2,17 +2,21 @@ #pragma once -#include LT_VARIANT_H +#include // Choose the main UART output port #ifndef LT_UART_DEFAULT_PORT -#if HAS_SERIAL2 +#if LT_HW_UART2 #define LT_UART_DEFAULT_PORT 2 -#elif HAS_SERIAL0 +#elif LT_HW_UART0 #define LT_UART_DEFAULT_PORT 0 -#elif HAS_SERIAL1 +#elif LT_HW_UART1 #define LT_UART_DEFAULT_PORT 1 #else #error "No serial port is available" #endif #endif + +// Auto-download-reboot detection pattern +// "ping" command for BootROM +#define LT_UART_ADR_PATTERN 'p', 'i', 'n', 'g', '\n' diff --git a/cores/realtek-ambz2/base/port/printf.c b/cores/realtek-ambz2/base/port/printf.c new file mode 100644 index 000000000..d15178060 --- /dev/null +++ b/cores/realtek-ambz2/base/port/printf.c @@ -0,0 +1,29 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-04-12. */ + +#include +#include + +#include + +static UART0_Type *uart_dev[4] = { + UART0, + UART1, + UART2, + UART3, +}; + +uint8_t lt_uart_port = 2; + +void putchar_(char c) { + putchar_p(c, lt_uart_port); +} + +void putchar_p(char c, unsigned long port) { + while (uart_dev[port]->tflvr_b.tx_fifo_lv >= Uart_Tx_FIFO_Size) {} + uart_dev[port]->thr = c; +} + +WRAP_PRINTF(rt_printf); +WRAP_SPRINTF(rt_sprintf); +WRAP_SNPRINTF(rt_snprintf); +WRAP_PRINTF(rt_log_printf); diff --git a/cores/realtek-ambz2/base/port/printf_port.h b/cores/realtek-ambz2/base/port/printf_port.h new file mode 100644 index 000000000..4c65b6cb2 --- /dev/null +++ b/cores/realtek-ambz2/base/port/printf_port.h @@ -0,0 +1,16 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-06-20. */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +WRAP_DISABLE_DEF(rt_printf); +WRAP_DISABLE_DEF(rt_log_printf); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/realtek-ambz2/base/sdk_extern.h b/cores/realtek-ambz2/base/sdk_extern.h new file mode 100644 index 000000000..e5d5c1ad5 --- /dev/null +++ b/cores/realtek-ambz2/base/sdk_extern.h @@ -0,0 +1,28 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2023-05-23. */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// SDK +extern hal_uart_adapter_t log_uart; +void software_reset(); +void sys_swd_off(); +void sys_uart_download_mode(); +void sys_download_mode(uint8_t mode); + +// blobs +bool Hal_GetPhyEfuseMACAddr(uint32_t xFFFFE5BB, uint8_t *buf); +bool rtw_efuse_map_read(uint32_t xFFFFE5BB, uint32_t offset, uint32_t length, uint8_t *buf); +uint32_t efuse_OneByteRead(uint32_t x33300000, uint32_t addr, uint8_t *buf, uint32_t ldo); +uint32_t EFUSERead8(uint32_t x33300000, uint32_t addr, uint8_t *buf, uint32_t ldo); +uint32_t hal_get_chip_id(uint32_t *id); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/realtek-ambz2/misc/rtl8710c_ram.ld b/cores/realtek-ambz2/misc/rtl8710c_ram.ld index 3977d812c..7dbe1334d 100644 --- a/cores/realtek-ambz2/misc/rtl8710c_ram.ld +++ b/cores/realtek-ambz2/misc/rtl8710c_ram.ld @@ -1,6 +1,10 @@ /* Linker script to configure memory regions. */ -/* LT changes: added .ARM.exidx section */ +/* LibreTiny changes: + - added .ARM.exidx section + - added .ARM.extab + - added missing C++ support +*/ /* !! the include symbole may failed if the symbol file name is too long!! */ INCLUDE "romsym_is.so" @@ -130,26 +134,6 @@ SECTIONS *(.sram.data*) *(.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 */ @@ -292,10 +276,51 @@ SECTIONS *(.rodata*) *(.rodata.str1*) + /* https://www.embedded.com/building-bare-metal-arm-systems-with-gnu-part-3/ */ + KEEP(*crtbegin.o(.ctors)) + KEEP(*(EXCLUDE_FILE (*ctrend.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*crtend.o(.ctors)) + KEEP(*crtbegin.o(.dtors)) + KEEP(*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*crtend.o(.dtors)) + *(.rodata .rodata.* .gnu.linkonce.r.*) + + /* Add This for C++ support */ + /* ambd_arduino/Arduino_package/hardware/variants/rtl8720dn_bw16/linker_scripts/gcc/rlx8721d_img2_is_arduino.ld */ + . = ALIGN(4); + __preinit_array_start = .; + KEEP(*(.preinit_array)) + __preinit_array_end = .; + . = ALIGN(4); + __init_array_start = .; + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + __init_array_end = .; + . = ALIGN(4); + __fini_array_start = .; + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + __fini_array_end = .; + /*-----------------*/ + + /* https://community.silabs.com/s/article/understand-the-gnu-linker-script-of-cortex-m4?language=en_US */ + KEEP(*(.init)) + KEEP(*(.fini)) + *(.init) + *(.fini) + . = ALIGN(4); __xip_code_rodata_end__ = .; } > XIP_FLASH_P + /* Add This for C++ support */ + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > XIP_FLASH_P + .ARM.exidx : { __exidx_start = .; @@ -360,5 +385,11 @@ SECTIONS PROVIDE(__sram_end__ = __StackLimit); /* Check if data + heap + stack exceeds RAM limit */ - /* TODO: ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + ASSERT(__bss_start__ >= __ram_code_text_end__, "sram heap will overwrite sram text") + ASSERT(__bss_start__ >= __ram_code_rodata_end__, "sram heap will overwrite sram rodata") + ASSERT(__bss_start__ >= __data_end__, "sram heap will overwrite sram data") + ASSERT(__psram_bss_start__ >= __psram_code_text_end__, "psram heap will overwrite psram text") + ASSERT(__psram_bss_start__ >= __psram_code_rodata_end__, "psram heap will overwrite psram rodata") + ASSERT(__psram_bss_start__ >= __psram_data_end__, "psram heap will overwrite psram data") } diff --git a/docs/TODO.md b/docs/TODO.md index 937adbcab..1d69e4843 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -43,7 +43,6 @@ Explicit is better than implicit. ### Other -- refactor `SerialClass` to have a shared header `Serial.h` in the common core (and `SerialData`, just like WiFi). Move ADR to common core - implement Wire on BK, refactor the API and class - watchdog API - `Preferences` library diff --git a/docs/boards_tuya_all.json b/docs/boards_tuya_all.json index 2832f16fe..290f6d0a0 100644 --- a/docs/boards_tuya_all.json +++ b/docs/boards_tuya_all.json @@ -441,6 +441,34 @@ "datasheet_id": "Kc5x1p35fs5zf" } }, + "t2": { + "t2-u": { + "mcu": "bk7231n", + "flash": 2097152, + "ram": 262144, + "pins_total": 21, + "connectivity": [ + "wifi", + "ble" + ], + "datasheet_name": "T2-U-module-datasheet", + "datasheet_id": "Kce1tncb80ldq" + } + }, + "t1": { + "t1-2s": { + "mcu": "t1a", + "flash": 1048576, + "ram": 294912, + "pins_total": 11, + "connectivity": [ + "wifi", + "ble" + ], + "datasheet_name": "T1-2S-module-datasheet", + "datasheet_id": "Kcl2d5xjy1rly" + } + }, "axy": { "axy2s": { "mcu": "ecr6600", @@ -493,7 +521,7 @@ }, "wt": { "wt3": { - "mcu": "t2", + "mcu": "bk7231n", "flash": 2097152, "ram": 262144, "pins_total": 16, diff --git a/docs/contrib/lt-api.md b/docs/contrib/lt-api.md new file mode 100644 index 000000000..44e70328b --- /dev/null +++ b/docs/contrib/lt-api.md @@ -0,0 +1,13 @@ +# API functions guide + +The [LibreTiny C API](../dev/lt-api.md) functions are split between three types: common, weak and family. + +- Common functions are implemented in the base, common core and are the same between all families. +- Weak functions are provided in the common core, but can (and sometimes should) be overridden by family cores. They sometimes provide usable default implementations (which *can* be overriden to provide e.g. a better way to do something), otherwise they're empty (e.g. if a family doesn't support such a feature). +- Family functions are not provided in the common core and have to be implemented in the family core. + +A quick outline of all available functions and their types: + +{% + include-markdown "lt-api-functions.md" +%} diff --git a/docs/dev/ota/README.md b/docs/contrib/ota/README.md similarity index 100% rename from docs/dev/ota/README.md rename to docs/contrib/ota/README.md diff --git a/docs/dev/ota/library.md b/docs/contrib/ota/library.md similarity index 100% rename from docs/dev/ota/library.md rename to docs/contrib/ota/library.md diff --git a/docs/dev/ota/uf2ota.md b/docs/contrib/ota/uf2ota.md similarity index 100% rename from docs/dev/ota/uf2ota.md rename to docs/contrib/ota/uf2ota.md diff --git a/docs/contrib/porting.md b/docs/contrib/porting.md new file mode 100644 index 000000000..3a11fc967 --- /dev/null +++ b/docs/contrib/porting.md @@ -0,0 +1,101 @@ +# Porting new families + +This document briefly outlines what needs to be done, in order to port a new chip family to LibreTiny. + +## Base framework + builders + +The base framework is the core part, that provides little functionality and a small HAL (over some things like OTA or sys control). It also includes a builder script for the vendor SDK. + +Here's what has to be done to make that work: + +1. Find vendor SDK - should be self-explanatory. We can't work without a working SDK (yet). +2. Test vendor SDK - compile a sample program "as it was meant to be done". + + - Most SDKs provide some example programs (like Hello World, WiFi scanning, etc.) that can usually be compiled by running a single "make" command. + - Sometimes you need to configure your environment in a weird and complicated way. For me, using Cygwin on Windows was usually enough, though. + - You need to flash this to the chip as well. The SDK usually bundles some flashing tools. + - This step is crucial to understand the vendor build system, and to have working binaries to compare out results against. + +3. "Clean up" vendor SDK. + + - SDKs usually bundle entire compiler toolchains, which can take up hundreds of megabytes. We want to keep the downloaded PlatformIO packages as small as possible. + - On existing families, GitHub Workflows produce the packages by removing some files and adding `package.json` to them. See [framework-beken-bdk/.github/workflows/platformio-package.yml](https://github.com/libretiny-eu/framework-beken-bdk/blob/actions/.github/workflows/platformio-package.yml) for an example. + +4. Write base family and board definitions. + + - `families.json` needs to have the new family added to it. + - `platform.json` needs to know the vendor SDK repository. + - Add any boards and base JSONs to the `boards/` directory. It's easiest to start with generic boards. + - Use `boardgen ltci` to generate variant sources (.c and .h). + +5. Add base core code. + + - `lt_defs.h`, `lt_family.h` and `lt_api.c` files need to be created, and initialized with (even empty) functions and definitions. + - The list of family functions can be found [here](lt-api.md). + - Make the SDK call `lt_main()` as the entrypoint. If needed, use fixups. + +6. Write a binary manipulation tool. + + - While this step could be optional, as these tools are provided in the SDK, they're usually platform-specific (i.e. Windows-only) and use proprietary executables, with no source code nor documentation. This is unacceptable for LibreTiny, as we need to support multiple architectures & platforms (Windows, Linux, Raspberry Pi, etc.). Naturally, doing that in Python seems to be the best choice. + - All binary tools are currently in [ltchiptool/soc/.../binary.py](https://github.com/libretiny-eu/ltchiptool/blob/master/ltchiptool/soc/bk72xx/binary.py). The `elf2bin()` function is what takes an .ELF file, and generates a set of binaries that can be flashed to the chip. + - It's best to test if the generation is correct, by taking an .ELF compiled by vendor SDK, running it through ltchiptool and checking if the resulting binaries are identical. + - Ghidra/IDA Pro is your friend here; you can decompile the SDK tools. + +7. Write a flashing tool. + + - mostly the same as above. Refer to the existing tools for examples. It's useful to make the flasher class "standalone", i.e. a class that is then wrapped by ltchiptool, like in [`realtek-ambz2`](https://github.com/libretiny-eu/ltchiptool/blob/master/ltchiptool/soc/ambz2/util/ambz2tool.py). + +8. Write builder scripts. + + - `builder/family/xxx.py` files are builders, which contain all SDK sources and include paths. Write the script, based on the existing families, and any Makefiles or other scripts from the SDK. + - Make sure not to make a mess in the `CCFLAGS`/`CPPDEFINES`, and only include what's needed there. Some flags are project-wide (family-independent) in `builder/frameworks/base.py`. + - Use a **pure PlatformIO** project - **not ESPHome!**. Pass one of the generic boards you created before, and `framework = base` in `platformio.ini`. Generally, try to get the thing to compile. + - Use a simple Hello World program - C, not C++. Only add `main()` function with a `printf()` and a `while(1)` loop. + - I've noticed that using `nano.specs` instead of `nosys.specs` produces smaller binaries. + +9. When you get it to link successfully, build a UF2 file. + + - UF2 packages are for flashing and for OTA. + - Add `UF2OTA` to the env, to provide binaries that will go to the UF2. Some understanding of the chip's partition and flash layout will be needed. + +10. Flash it, test if it works! + + - It probably won't. You may need to remove `__libc_init_array()` from `cores/common/base/lt_api.c` so that it doesn't crash. Most SDKs don't support C++ properly. + +## Making it *actually* work + +1. Write `flashdb` and `printf` ports. + + - The ports are in `cores/.../base/port/`. It's a simple flash access layer, and a character printing function. Not a lot of work, but it needs to be done first. + +2. Add fixups so that string & memory stdlib functions are not from SDK. + + - Refer to [stdlib.md](stdlib.md) to find functions that need to be wrapped. + - SDK should not define them, you have to figure out a way to remove them from headers. Fixups can mess with includes and trick the SDK into using our own functions. + +3. Clean up FreeRTOS. + + - FreeRTOS' headers usually include some SDK headers, which pull in a lot of macros and typedefs, which usually break lots of non-SDK code, which doesn't expect these macros. + - [library-freertos](https://github.com/libretiny-eu/library-freertos) repo contains some FreeRTOS versions, adapted for SDKs. Basically, copy a clean (straight from FreeRTOS github) version to the repo, commit it. Then copy the version from SDK and compare the differences. + - Try to make it look as "stock" as possible. Discard any formatting differences (and backports). + - Annotate any parts that can't be removed with `#ifdef FREERTOS_PORT_REALTEK_AMB1`. + - Put the FreeRTOS vendor-specific port in [library-freertos-port](https://github.com/libretiny-eu/library-freertos-port). + - Remove all FreeRTOS sources from builder scripts. Replace with: + + ```py + env.Replace(FREERTOS_PORT=env["FAMILY_NAME"], FREERTOS_PORT_DEFINE="REALTEK_AMB1") + queue.AddExternalLibrary("freertos") + queue.AddExternalLibrary("freertos-port") + ``` + +4. Do the same with lwIP - later. + +5. Write LibreTiny C APIs - in `lt_api.c`. + +6. At this point, your Hello World code should work fine. + +## Porting Arduino Core - C++ support + +1. Add main.cpp and write wiring_*.c ports. GPIOs and stuff should work even without proper C++ support. + +2. Port Serial library first. This should already show whether C++ works fine or if it doesn't. For example, calling `Serial.println()` refers to the virtual function `Print::write`, which will probably crash the chip if C++ is not being linked properly. diff --git a/docs/dev/project-structure.md b/docs/contrib/project-structure.md similarity index 100% rename from docs/dev/project-structure.md rename to docs/contrib/project-structure.md diff --git a/docs/dev/stdlib.md b/docs/contrib/stdlib.md similarity index 100% rename from docs/dev/stdlib.md rename to docs/contrib/stdlib.md diff --git a/docs/dev/config.md b/docs/dev/config.md index 94950fbdc..1a976f3dc 100644 --- a/docs/dev/config.md +++ b/docs/dev/config.md @@ -70,7 +70,7 @@ build_flags = #### Per-module logging & debugging -The following options enable library-specific logging output. They are effective **for all loglevels** - i.e. disabling `LT_DEBUG_WIFI` will disable WiFi debug messages, as well as errors. +The following options enable library-specific logging output. They are effective **for all loglevels** - i.e. disabling `LT_DEBUG_WIFI` will disable WiFi debug messages, warnings, as well as errors. To see debug messages from i.e. OTA, loglevel must also be changed. @@ -119,24 +119,43 @@ Options for controlling default UART log output. - `LT_MICROS_HIGH_RES` (1) - count runtime microseconds using a high-resolution timer (if possible); disable if your application doesn't need `micros()` - `LT_AUTO_DOWNLOAD_REBOOT` (1) - automatically reboot into "download mode" after detecting a flashing protocol command; [read more](../flashing/tools/adr.md) -### Family feature config +### Family configuration !!! bug "Warning" These options are not meant for end-users. They're provided here as a reference for developers. **Do not set these options manually**. These options are selectively set by all families, as part of the build process. They are used for enabling LT core API parts, if the family has support for it. Files named `lt_defs.h`, containing these options, are read by the PlatformIO builders (note: they're never included by C code). -The `LT_ARD_*` options are only used with Arduino frameworks. - -The meaning of most flags is as follows: - -- `LT_HAS_FREERTOS` - FreeRTOS supported and used -- `LT_HAS_LWIP` - LwIP in SDK (any version) -- `LT_HAS_LWIP2` - LwIP v2.0.0 or newer -- `LT_HAS_MBEDTLS` - mbedTLS in SDK -- `LT_HAS_PRINTF` - printf library implemented -- `LT_ARD_HAS_SERIAL` - Serial class implemented, `Serial.h` available -- `LT_ARD_HAS_SOFTSERIAL` - SoftwareSerial library implemented, `SoftwareSerial.h` available -- `LT_ARD_HAS_WIFI` - WiFi library implemented, `WiFiData.h` available -- `LT_HEAP_FUNC` - function name used to get available heap size (for `LT_HEAP_I()`) -- `LT_REALLOC_FUNC` - function name used for `realloc()` call +Checking for option value should be done with `#if` (not with `#ifdef`!) - if it's not defined, it will evaluate to 0. Otherwise, it will use the defined value, either 0 or 1. + +- family-/chip-specific hardware peripherals + - `LT_HW_WIFI` - WiFi supported on the chip + - `LT_HW_BT` - Bluetooth Classic supported on the chip + - `LT_HW_BLE` - Bluetooth Low Energy supported on the chip + - `LT_HW_WATCHDOG` - watchdog available + - `LT_HW_DEEP_SLEEP` - deep sleep possible +- board-specific peripherals (note: defined in `lt_pins.h`, depending on available pin numbers) + - `LT_HW_UART#` - UART number # available on the board + - `LT_HW_I2C#` - IÂČC number # available on the board + - `LT_HW_SPI#` - SPI number # available on the board +- family software options (SDK features, LT implementation status) + - `LT_HAS_FREERTOS` - FreeRTOS supported and used + - `LT_HAS_LWIP` - LwIP in SDK (any version) + - `LT_HAS_LWIP2` - LwIP v2.0.0 or newer + - `LT_HAS_MBEDTLS` - mbedTLS in SDK + - `LT_HAS_PRINTF` - printf library implemented + - `LT_HAS_FLASH` - FAL flash port implemented + - `LT_HAS_OTA` - OTA implemented in base framework +- Arduino Core implementation status (only available and used along with Arduino framework) + - `LT_ARD_HAS_SERIAL` - Serial class implemented + - `LT_ARD_HAS_SOFTSERIAL` - SoftwareSerial library implemented + - `LT_ARD_HAS_WIFI` - WiFi library implemented + - `LT_ARD_HAS_WIRE` - Wire (IÂČC) library implemented + - `LT_ARD_HAS_SPI` - SPI library implemented + - `LT_ARD_MD5_POLARSSL` - use PolarSSL for MD5 library + - `LT_ARD_MD5_MBEDTLS` - use mbedTLS for MD5 library + - `LT_ARD_MD5_HOSTAPD` - use hostapd for MD5 library +- misc options + - `LT_HEAP_FUNC` - function name used to get available heap size (for `LT_HEAP_I()`) + - `LT_REALLOC_FUNC` - function name used for `realloc()` call + - `LT_REMALLOC` - use `malloc()` and `memcpy()` in `realloc()` call diff --git a/docs/flashing/tools/adr.md b/docs/flashing/tools/adr.md index a976b6741..3c412992b 100644 --- a/docs/flashing/tools/adr.md +++ b/docs/flashing/tools/adr.md @@ -16,3 +16,7 @@ The code listens on UART1 for a link-check command (`01 E0 FC 01 00`). The baudr ## Realtek AmebaZ This only works when using [ltchiptool](ltchiptool.md) for flashing. Upon starting UART communication, the tool sends `55 AA 22 E0 D6 FC` (0x55AA followed by the `realtek-ambz` family ID). After detecting that pattern, the chip proceeds to reboot into UART download mode (using [`lt_reboot_download_mode()`](../../../ltapi/lt__device_8h.md)) + +## Realtek AmebaZ2 + +The code listens on UART2 for a `ping\n` command, that is sent by [ltchiptool](ltchiptool.md) (and possibly by the vendor flasher, too). The device is then rebooted to download mode. diff --git a/docs/resources/documents.md b/docs/resources/documents.md index 16dd7d57f..96bf276f4 100644 --- a/docs/resources/documents.md +++ b/docs/resources/documents.md @@ -17,4 +17,4 @@ UM0201 | [Ameba Common BT Application User Manual EN](https://raw.githubusercont   | Found elsewhere AN0400 | [Ameba-D Application Note_v3_watermark](https://files.seeedstudio.com/products/102110419/Basic%20documents/AN0400%20Ameba-D%20Application%20Note_v3_watermark.pdf) AN0500 | [Realtek Ameba-ZII application note](https://www.e-paper-display.com/99IOT/00015797-AN0500-Realtek-Ameba-ZII-application-note.en_233850.pdf) -  | [Realtek Ameba-ZII datasheet v0.8](https://www.e-paper-display.com/Ameba-Z_II_DataSheet_v0r8_RTL8720Cx_20190424%29.pdf) +  | [Realtek Ameba-ZII datasheet v0.8](https://web.archive.org/web/20230523175304if_/https://cetest02.cn-bj.ufileos.com/100001_2110255103/RTL872xZ2%20IC%20Datasheet.pdf) diff --git a/docs/script.js b/docs/script.js new file mode 100644 index 000000000..2845e5938 --- /dev/null +++ b/docs/script.js @@ -0,0 +1,6 @@ +document$.subscribe(function () { + var tables = document.querySelectorAll("article table:not([class])") + tables.forEach(function (table) { + new Tablesort(table) + }) +}) diff --git a/docs/scripts/build_json.py b/docs/scripts/build_json.py index 924ed82ab..f3b1cd652 100644 --- a/docs/scripts/build_json.py +++ b/docs/scripts/build_json.py @@ -1,7 +1,7 @@ import json from ltchiptool import Board -from update_docs import board_obj_sort +from write_boards import board_obj_sort boards = map(Board, Board.get_list()) boards = list(sorted(boards, key=board_obj_sort)) diff --git a/docs/scripts/write_apis.py b/docs/scripts/write_apis.py new file mode 100644 index 000000000..d1bf4de95 --- /dev/null +++ b/docs/scripts/write_apis.py @@ -0,0 +1,129 @@ +# Copyright (c) Kuba SzczodrzyƄski 2023-06-22. + +import re +from glob import glob +from os.path import dirname, join + +import colorama +from colorama import Fore, Style +from markdown import Markdown + +if __name__ == "__main__": + colorama.init() + + api_path = join(dirname(__file__), "..", "..", "cores/common/base/api/lt_*.*") + out_path = join(dirname(__file__), "..", "contrib") + + declaration = "" + implementation = "" + + for file in glob(api_path): + with open(file, "r") as f: + data = f.read() + if file.endswith(".h"): + declaration += data + elif file.endswith(".c"): + implementation += data + + block_comment_regex = r"\/\*[\d\D]+?\*\/" + line_comment_regex = r"\/\/.+?$" + macro_regex = r"#(?:[^\n\\]|\\\n)+$" + line_regex = r"\n+" + declaration = re.sub(block_comment_regex, "", declaration) + declaration = re.sub(line_comment_regex, "", declaration, flags=re.MULTILINE) + declaration = re.sub(macro_regex, "", declaration, flags=re.MULTILINE) + declaration = re.sub(line_regex, "\n", declaration) + implementation = re.sub(block_comment_regex, "", implementation) + implementation = re.sub(line_comment_regex, "", implementation, flags=re.MULTILINE) + implementation = re.sub(macro_regex, "", implementation, flags=re.MULTILINE) + implementation = re.sub(line_regex, "\n", implementation) + + declaration_regex = r"^([^\s][\d\w\s]+ \*?)([\S]+?)\(.*?\);$" + implementation_regex = r"^(__attribute__\(\(weak\)\) )?([\w\d* ]+?)([\w\d]+)\(.+?{" + + function_types = {} + decl_functions = set() + impl_functions = set() + weak_functions = set() + + for match in re.finditer( + pattern=declaration_regex, + string=declaration, + flags=re.DOTALL | re.MULTILINE, + ): + function_type = match[1].strip() + function_name = match[2].strip() + + if function_types.get(function_name, function_type) != function_type: + print( + Fore.YELLOW + + "WARNING: Wrong return type: " + + f"'{function_types[function_name]} {function_name}'" + + f"vs '{function_type} {function_name}'" + + Style.RESET_ALL + ) + + function_types[function_name] = function_type + decl_functions.add(function_name) + + for match in re.finditer( + pattern=implementation_regex, + string=implementation, + flags=re.DOTALL | re.MULTILINE, + ): + is_weak = match[1] + function_type = match[2].strip() + function_name = match[3].strip() + function_types[function_name] = function_type + + if function_types.get(function_name, function_type) != function_type: + print( + Fore.YELLOW + + "WARNING: Wrong return type: " + + f"'{function_types[function_name]} {function_name}'" + + f"vs '{function_type} {function_name}'" + + Style.RESET_ALL + ) + + function_types[function_name] = function_type + if is_weak: + weak_functions.add(function_name) + else: + impl_functions.add(function_name) + +for function in list(impl_functions): + if "static" in function_types[function]: + impl_functions.remove(function) + +common_functions = impl_functions.union(weak_functions) +family_functions = decl_functions - common_functions +undecl_functions = common_functions - decl_functions +if undecl_functions: + print(Fore.RED + "ERROR: Undeclared functions: " + ", ".join(undecl_functions)) + exit(1) + +md = Markdown(out_path, "lt-api-functions") +header = [ + "Type", + "Function", + "Common", + "Weak", + "Family", +] +rows = [] + +for function in ( + sorted(family_functions) + sorted(weak_functions) + sorted(impl_functions) +): + rows.append( + [ + f"`{function_types[function]}`", + f"{function}()", + "✔" if function in impl_functions else "", + "✔" if function in weak_functions else "", + "✔" if function not in common_functions else "", + ] + ) + +md.add_table(header, *rows) +md.write() diff --git a/docs/scripts/update_docs.py b/docs/scripts/write_boards.py similarity index 100% rename from docs/scripts/update_docs.py rename to docs/scripts/write_boards.py diff --git a/external-libs.json b/external-libs.json index 4cb871b7c..264e7ba39 100644 --- a/external-libs.json +++ b/external-libs.json @@ -36,13 +36,13 @@ "PRINTF_INCLUDE_CONFIG_H": "1" } }, - "ltchiptool": { - "package": "tool-ltchiptool", + "uf2ota": { + "package": "library-uf2ota", "sources": [ - "+" + "+" ], "includes": [ - "+<.>" + "+" ] }, "arduino-api": { @@ -121,5 +121,25 @@ "+", "+" ] + }, + "lwip-ambz2": { + "package": "library-lwip", + "sources": [ + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+" + ], + "includes": [ + "+", + "+", + "+", + "+" + ] } } diff --git a/mkdocs.yml b/mkdocs.yml index 0329105e5..39d3ba57a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -39,6 +39,10 @@ plugins: extra_css: - docs/style.css +extra_javascript: + - https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js + - docs/script.js + markdown_extensions: - md_in_html - admonition diff --git a/platform.json b/platform.json index 8d4ff7cf0..cc4639731 100644 --- a/platform.json +++ b/platform.json @@ -43,6 +43,14 @@ "version_prefix": true, "toolchains": { "any": "gccarmnoneeabi@~1.100301.0" + }, + "libraries": { + "freertos": "10.0.1", + "lwip": { + "2.0.3": "2.0.3-ambz2", + "2.1.3": "2.1.3-ambz2", + "default": "2.1.3-ambz2" + } } }, "framework-beken-bdk": { @@ -80,7 +88,7 @@ }, "library-freertos-port": { "type": "framework", - "version": "https://github.com/libretiny-eu/library-freertos-port#2023.03.13" + "version": "https://github.com/libretiny-eu/library-freertos-port#2023.05.23" }, "library-flashdb": { "type": "framework", @@ -90,6 +98,10 @@ "type": "framework", "version": "https://github.com/libretiny-eu/library-printf#6.1.0" }, + "library-uf2ota": { + "type": "framework", + "version": "https://github.com/libretiny-eu/library-uf2ota#5.0.0" + }, "toolchain-gccarmnoneeabi": { "type": "toolchain", "optionalVersions": [ @@ -99,12 +111,6 @@ "~1.100301.0" ] }, - "tool-ltchiptool": { - "type": "uploader", - "version": "https://github.com/libretiny-eu/ltchiptool#v4.0.0", - "version_prefix": true, - "note": "This is used only for C/C++ code from ltchiptool." - }, "tool-openocd": { "type": "uploader", "optional": true, diff --git a/platform.py b/platform.py index 2da5b4a25..b4f27ab16 100644 --- a/platform.py +++ b/platform.py @@ -17,7 +17,7 @@ from platformio.platform.board import PlatformBoardConfig from semantic_version import SimpleSpec, Version -LTCHIPTOOL_VERSION = "^4.0.1" +LTCHIPTOOL_VERSION = "^4.2.3" # Install & import tools