diff --git a/.cargo/config.toml b/.cargo/config.toml index b18ce92..0c68a89 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -11,3 +11,10 @@ rustflags = [ "-C", "link-arg=-Tdefmt.x", "-C", "opt-level=z", ] + +[target.thumbv6m-none-eabi] +rustflags = [ + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "opt-level=z", +] diff --git a/CHANGELOG.md b/CHANGELOG.md index 605efee..98f1212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [0.3.0] - 2026-04-10 + +### Added + +- Waveshare RP2040-LoRa board support (RP2040 + SX1262, no OLED, LED on GPIO25) + - First RP2040 board — third MCU family after ESP32-S3 and nRF52840 + - New `hal/rp2040.rs` module with MCU primitives (SPI, USB, flash unique ID) + - USB-C via FPC ribbon cable to adapter board + - PE4259 antenna switch on GPIO17, 850-930 MHz (HF) + +### Changed + +- `cortex-m-rt/set-vtor` moved from global dependency to per-board feature + (Cortex-M0+ has no VTOR register) +- `build.rs` memory.x selection is now target-aware (thumbv6m vs thumbv7em) + ## [0.2.1] - 2026-04-07 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 4122f6b..f421601 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,30 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ascii-canvas" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" +dependencies = [ + "term", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -35,6 +59,21 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitfield" version = "0.13.2" @@ -79,6 +118,15 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "byte-slice-cast" version = "1.2.3" @@ -103,6 +151,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "cortex-m" version = "0.7.7" @@ -136,6 +194,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -261,6 +337,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + [[package]] name = "defmt" version = "0.3.100" @@ -329,6 +411,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ + "block-buffer", "crypto-common", ] @@ -385,6 +468,7 @@ dependencies = [ "embassy-executor", "embassy-futures", "embassy-nrf", + "embassy-rp", "embassy-sync 0.8.0", "embassy-time", "embassy-usb", @@ -402,11 +486,18 @@ dependencies = [ "minijinja", "owo-colors", "panic-probe", + "portable-atomic", "ssd1306", "static_cell", "ucobs", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "embassy-embedded-hal" version = "0.5.0" @@ -486,6 +577,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" dependencies = [ + "cortex-m", + "critical-section", + "defmt 1.0.1", "num-traits", ] @@ -560,6 +654,45 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "embassy-rp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8d5ac11a8bc209d359ad98bb10a10f786471dd474790f3a4f991c77ae94f6f" +dependencies = [ + "cfg-if", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.0.1", + "document-features", + "embassy-embedded-hal 0.5.0", + "embassy-futures", + "embassy-hal-internal 0.3.0", + "embassy-sync 0.7.2", + "embassy-time", + "embassy-time-driver", + "embassy-time-queue-utils", + "embassy-usb-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io 0.6.1", + "embedded-io-async 0.6.1", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nb 1.1.0", + "pio", + "rand_core 0.6.4", + "rand_core 0.9.5", + "rp-pac", + "rp2040-boot2", + "sha2-const-stable", + "smart-leds", +] + [[package]] name = "embassy-sync" version = "0.6.2" @@ -735,6 +868,16 @@ dependencies = [ "embedded-hal 1.0.0", ] +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "embedded-io" version = "0.6.1" @@ -780,6 +923,15 @@ dependencies = [ "embedded-storage", ] +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1086,6 +1238,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "float-cmp" version = "0.9.0" @@ -1254,18 +1412,83 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + [[package]] name = "litrs" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -1347,6 +1570,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nrf-pac" version = "0.3.0" @@ -1366,6 +1595,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "object" version = "0.37.3" @@ -1397,23 +1647,128 @@ dependencies = [ "defmt 1.0.1", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba4153cee9585abc451271aa437d9e8defdea8b468d48ba6b8f098cbe03d7f" +dependencies = [ + "pio-core", + "pio-proc", +] + +[[package]] +name = "pio-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825266c1eaddf54f636d06eefa4bf3c99d774c14ec46a4a6c6e5128a0f10d205" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio-core", +] + +[[package]] +name = "pio-proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a76571f5fe51af43cc80ac870fe0c79cc0cdd686b9002a6c4c84bfdd0176b" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio-core", + "pio-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "portable-atomic" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +dependencies = [ + "critical-section", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro-crate" @@ -1517,6 +1872,50 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + [[package]] name = "riscv" version = "0.15.0" @@ -1576,6 +1975,25 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1376b15f3ff160e9b1e8ea564ce427f2f6fcf77528cc0a8bf405cb476f9cea7" +[[package]] +name = "rp-pac" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af65855c40b2c35079514c5489abffc0429347fef25d8467ff98ad84b4322d3" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + [[package]] name = "rustc_version" version = "0.2.3" @@ -1597,6 +2015,21 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semihosting" version = "0.1.25" @@ -1661,6 +2094,52 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smart-leds" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df34e571fa9993fa6f99131a374d58ca3d694b75f9baac93458fe0d6057bf0" +dependencies = [ + "smart-leds-trait", +] + +[[package]] +name = "smart-leds-trait" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f4441a131924d58da6b83a7ad765c460e64630cce504376c3a87a2558c487f" +dependencies = [ + "rgb", +] + [[package]] name = "somni-expr" version = "0.2.0" @@ -1716,6 +2195,18 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1765,6 +2256,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -1854,6 +2354,18 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -1932,6 +2444,16 @@ dependencies = [ "vcell", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/Cargo.toml b/Cargo.toml index e86497e..3a9c772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["."] [package] name = "donglora" -version = "0.2.1" +version = "0.3.0" edition = "2021" build = "build.rs" authors = ["Stephen Waits "] @@ -32,6 +32,7 @@ embassy-usb-driver = "0.2" ucobs = { version = "0.3", features = ["legacy-msrv"] } heapless = "0.9" lora-phy = "3.0" +portable-atomic = { version = "1", optional = true, default-features = false, features = ["critical-section"] } static_cell = "2.1" # Display rendering @@ -63,8 +64,10 @@ esp-rtos = { version = "0.2", optional = true, features = [ ] } # nRF52840 (RAK 4631) -cortex-m = { version = "0.7", optional = true, features = ["critical-section-single-core"] } -cortex-m-rt = { version = "0.7", optional = true, features = ["set-vtor"] } +cortex-m = { version = "0.7", optional = true, features = [ + "critical-section-single-core", +] } +cortex-m-rt = { version = "0.7", optional = true } defmt-rtt = { version = "1.0", optional = true } embassy-nrf = { version = "0.10", optional = true, features = [ "nrf52840", @@ -74,6 +77,13 @@ embassy-nrf = { version = "0.10", optional = true, features = [ ] } panic-probe = { version = "1.0", optional = true, features = ["print-defmt"] } +# RP2040 (Waveshare RP2040-LoRa) +embassy-rp = { version = "0.9", optional = true, features = [ + "rp2040", + "time-driver", + "defmt", +] } + [build-dependencies] minijinja = "2.18" owo-colors = "4.3" @@ -100,6 +110,7 @@ heltec_v4 = [ rak_wisblock_4631 = [ "dep:cortex-m", "dep:cortex-m-rt", + "cortex-m-rt/set-vtor", "dep:defmt-rtt", "dep:embassy-nrf", "dep:panic-probe", @@ -109,12 +120,23 @@ rak_wisblock_4631 = [ wio_tracker_l1 = [ "dep:cortex-m", "dep:cortex-m-rt", + "cortex-m-rt/set-vtor", "dep:defmt-rtt", "dep:embassy-nrf", "dep:panic-probe", "embassy-executor/arch-cortex-m", "embassy-executor/executor-thread", ] +waveshare_rp2040_lora = [ + "dep:cortex-m", + "dep:cortex-m-rt", + "dep:defmt-rtt", + "dep:embassy-rp", + "dep:panic-probe", + "dep:portable-atomic", + "embassy-executor/arch-cortex-m", + "embassy-executor/executor-thread", +] [lints.clippy] undocumented_unsafe_blocks = "deny" @@ -122,6 +144,6 @@ undocumented_unsafe_blocks = "deny" [profile.release] codegen-units = 1 lto = "fat" -opt-level = 3 # "s"/"z" break Xtensa (l32r literal range exceeded) +opt-level = 3 # "s"/"z" break Xtensa (l32r literal range exceeded) panic = "abort" strip = true diff --git a/README.md b/README.md index f997b62..c08fa1c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ All commands are run from the `firmware/` directory. | Heltec V3 (UART) | `heltec_v3_uart` | ESP32-S3 | SX1262 | CP2102 bridge | xtensa-esp32s3-none-elf | | Heltec V4 | `heltec_v4` | ESP32-S3 | SX1262 | Native CDC-ACM | xtensa-esp32s3-none-elf | | RAK WisBlock 4631 | `rak_wisblock_4631` | nRF52840 | SX1262 | Native CDC-ACM | thumbv7em-none-eabihf | +| Waveshare RP2040-LoRa | `waveshare_rp2040_lora` | RP2040 | SX1262 | Native CDC-ACM | thumbv6m-none-eabi | | Wio Tracker L1 | `wio_tracker_l1` | nRF52840 | SX1262 | Native CDC-ACM | thumbv7em-none-eabihf | ### Heltec V3 USB Note diff --git a/build.rs b/build.rs index 75f1ce7..d666128 100644 --- a/build.rs +++ b/build.rs @@ -108,14 +108,20 @@ fn main() { println!("cargo:rerun-if-changed=src/board/mod.rs.j2"); println!("cargo:rerun-if-changed={}", board_dir); - // cortex-m-rt's link.x does `INCLUDE memory.x`. Copy our nRF layout into - // OUT_DIR so it's only visible to ARM builds. + // cortex-m-rt's link.x does `INCLUDE memory.x`. Copy the correct layout + // into OUT_DIR based on the target MCU family. let target = std::env::var("TARGET").unwrap_or_default(); - if !target.starts_with("xtensa") { + let memory_x = match target.as_str() { + "thumbv7em-none-eabihf" => Some("ld/nrf52840-memory.x"), + "thumbv6m-none-eabi" => Some("ld/rp2040-memory.x"), + _ => None, + }; + if let Some(src) = memory_x { let out_dir = std::env::var("OUT_DIR").unwrap(); - fs::copy("ld/nrf52840-memory.x", format!("{out_dir}/memory.x")) - .expect("failed to copy ld/nrf52840-memory.x"); + fs::copy(src, format!("{out_dir}/memory.x")) + .expect("failed to copy memory.x"); println!("cargo:rustc-link-search={out_dir}"); } println!("cargo:rerun-if-changed=ld/nrf52840-memory.x"); + println!("cargo:rerun-if-changed=ld/rp2040-memory.x"); } diff --git a/justfile b/justfile index 0ef9689..539c229 100644 --- a/justfile +++ b/justfile @@ -8,12 +8,13 @@ heltec_v3_uart := "heltec_v3_uart xtensa-esp32s3-none-elf esp32s3" heltec_v4 := "heltec_v4 xtensa-esp32s3-none-elf esp32s3" rak_wisblock_4631 := "rak_wisblock_4631 thumbv7em-none-eabihf nRF52840_xxAA" wio_tracker_l1 := "wio_tracker_l1 thumbv7em-none-eabihf nRF52840_xxAA" +waveshare_rp2040_lora := "waveshare_rp2040_lora thumbv6m-none-eabi rp2040" builds_dir := "builds" version := `sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml` # All known boards -boards := "heltec_v3 heltec_v3_uart heltec_v4 rak_wisblock_4631 wio_tracker_l1" +boards := "heltec_v3 heltec_v3_uart heltec_v4 rak_wisblock_4631 waveshare_rp2040_lora wio_tracker_l1" # Install all required tools and toolchains setup: @@ -28,7 +29,8 @@ setup: @if ! rustup component list --toolchain nightly --installed 2>/dev/null | grep -q "^rust-src"; then \ rustup component add rust-src --toolchain nightly; \ fi - @# ARM target + @# ARM targets + @rustup target list --installed | grep -q "^thumbv6m-none-eabi$" || rustup target add thumbv6m-none-eabi @rustup target list --installed | grep -q "^thumbv7em-none-eabihf$" || rustup target add thumbv7em-none-eabihf # Build release firmware for all boards, installing toolchains as needed @@ -180,6 +182,12 @@ _copy_firmware board profile: mise exec -- espflash save-image --chip "$chip" --merge --skip-padding "$src" "$dst.bin"; \ echo "→ $dst.bin"; \ ;; \ + thumbv6m-*) \ + mise exec -- rust-objcopy -O ihex "$src" "$dst.hex"; \ + mise exec -- cargo-hex-to-uf2 hex-to-uf2 --input-path "$dst.hex" --output-path "$dst.uf2" --family rp2040; \ + rm "$dst.hex"; \ + echo "→ $dst.uf2"; \ + ;; \ thumbv7em-*) \ mise exec -- rust-objcopy -O ihex "$src" "$dst.hex"; \ mise exec -- cargo-hex-to-uf2 hex-to-uf2 --input-path "$dst.hex" --output-path "$dst.uf2" --family nrf52840; \ @@ -211,5 +219,6 @@ _info name: elif [ "{{name}}" == "heltec_v3_uart" ]; then echo "{{heltec_v3_uart}}"; \ elif [ "{{name}}" == "heltec_v4" ]; then echo "{{heltec_v4}}"; \ elif [ "{{name}}" == "rak_wisblock_4631" ]; then echo "{{rak_wisblock_4631}}"; \ + elif [ "{{name}}" == "waveshare_rp2040_lora" ]; then echo "{{waveshare_rp2040_lora}}"; \ elif [ "{{name}}" == "wio_tracker_l1" ]; then echo "{{wio_tracker_l1}}"; \ else echo "Unknown board: {{name}}" >&2; exit 1; fi diff --git a/ld/rp2040-memory.x b/ld/rp2040-memory.x new file mode 100644 index 0000000..8cdf9cf --- /dev/null +++ b/ld/rp2040-memory.x @@ -0,0 +1,24 @@ +/* RP2040 memory layout — UF2 bootloader (ROM boot2) + * + * 0x10000000 – 0x10000100 boot2 (second-stage bootloader, 256 bytes) + * 0x10000100 – 0x10200000 Application <- we live here + * + * Flash: 2MB (W25Q16JV on Waveshare RP2040-LoRa) + * RAM: 264KB SRAM (two 256KB banks + 2x 4KB scratch) + */ + +MEMORY +{ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 264K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; diff --git a/src/board/PORTING.md b/src/board/PORTING.md index 7ed5318..be816b9 100644 --- a/src/board/PORTING.md +++ b/src/board/PORTING.md @@ -7,6 +7,7 @@ Create `src/board/your_board.rs`. Use an existing board as a template: - ESP32-S3 boards: copy `heltec_v4.rs` (uses shared helpers from `board/esp32s3.rs` and `hal/esp32s3.rs`) - nRF52840 boards: copy `rak_wisblock_4631.rs` (uses shared helpers from `hal/nrf52840.rs`) +- RP2040 boards: copy `waveshare_rp2040_lora.rs` (uses shared helpers from `hal/rp2040.rs`) - New MCU family: create a new `hal/.rs` with MCU primitives first `build.rs` auto-discovers your board file — any `.rs` file in `src/board/` that diff --git a/src/board/waveshare_rp2040_lora.rs b/src/board/waveshare_rp2040_lora.rs new file mode 100644 index 0000000..b6cc454 --- /dev/null +++ b/src/board/waveshare_rp2040_lora.rs @@ -0,0 +1,141 @@ +use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::spi; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_time::Delay; + +use lora_phy::iv::GenericSx126xInterfaceVariant; +use lora_phy::sx126x::{self, Sx1262, Sx126x}; + +use super::traits::{BoardParts, LoRaBoard}; +use crate::hal::rp2040 as mcu; + +// ── Concrete peripheral types ────────────────────────────────────── + +type Nss = Output<'static>; +type Iv = GenericSx126xInterfaceVariant, Input<'static>>; +type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, mcu::SpiBus, Nss>; +pub type RadioDriver = Sx126x; +pub type UsbDriver = mcu::UsbRpDriver; +pub type LedDriver = crate::driver::simple_led::SimpleLed>; + +// Display types — never instantiated (no OLED on this board), but the +// trait requires concrete associated types that satisfy the bounds. +pub type DisplayI2c = embassy_rp::i2c::I2c<'static, embassy_rp::peripherals::I2C0, embassy_rp::i2c::Async>; +pub type DisplayDriver = ssd1306::Ssd1306Async< + ssd1306::prelude::I2CInterface, + ssd1306::size::DisplaySize128x64, + ssd1306::mode::BufferedGraphicsModeAsync, +>; + +// ── Peripheral bundles ───────────────────────────────────────────── + +pub struct RadioParts { + pub driver: RadioDriver, + pub delay: Delay, +} + +pub struct UsbParts { + pub driver: UsbDriver, +} + +pub struct DisplayParts { + pub i2c: DisplayI2c, +} + +// ── Display init ─────────────────────────────────────────────────── + +pub async fn create_display(_i2c: DisplayI2c) -> Option { + None +} + +// ── Board init ───────────────────────────────────────────────────── + +pub struct Board { + p: embassy_rp::Peripherals, +} + +impl LoRaBoard for Board { + const NAME: &'static str = "Waveshare RP2040-LoRa"; + const TX_POWER_RANGE: (i8, i8) = (-9, 22); + + type RadioParts = RadioParts; + type CommParts = UsbParts; + type DisplayParts = DisplayParts; + type DisplayDriver = DisplayDriver; + type LedDriver = LedDriver; + + fn init() -> Self { + let p = embassy_rp::init(Default::default()); + Self { p } + } + + fn mac_address() -> [u8; 6] { + mcu::mac_address() + } + + fn into_parts(self) -> BoardParts { + let p = self.p; + + // ── MAC address from flash unique ID ─────────────────── + let mut flash = embassy_rp::flash::Flash::<_, _, { 2 * 1024 * 1024 }>::new_blocking(p.FLASH); + mcu::init_mac(&mut flash); + + // ── SPI1 bus for SX1262 ──────────────────────────────── + // + // Pin mapping from Meshtastic & MeshCore firmware (confirmed working): + // SCK = GPIO14, MOSI = GPIO15, MISO = GPIO24, CS = GPIO13 + // + // The Waveshare wiki (https://www.waveshare.com/wiki/RP2040-LoRa) lists + // different pins (MOSI=11, MISO=8, CS=9) but those don't work in practice. + // Both sets are valid SPI1 alternate functions on RP2040. + let mut spi_cfg = spi::Config::default(); + spi_cfg.frequency = 1_000_000; + let spi = spi::Spi::new( + p.SPI1, p.PIN_14, p.PIN_15, p.PIN_24, p.DMA_CH0, p.DMA_CH1, spi_cfg, + ); + let spi_bus = mcu::share_spi_bus(spi); + + let nss = Output::new(p.PIN_13, Level::High); + let spi_device = SpiDevice::new(spi_bus, nss); + + let reset = Output::new(p.PIN_23, Level::High); + let dio1 = Input::new(p.PIN_16, Pull::Down); + let busy = Input::new(p.PIN_18, Pull::Down); + + // PE4259 antenna switch: GPIO17 = !CTRL (rx_enable). + // DIO2 is internally wired as CTRL (tx_enable), managed by the SX1262. + let rx_enable = Output::new(p.PIN_17, Level::Low); + let iv = GenericSx126xInterfaceVariant::new(reset, dio1, busy, Some(rx_enable), None) + .expect("SX1262 interface init"); + + let sx_config = sx126x::Config { + chip: Sx1262, + tcxo_ctrl: None, // No TCXO on this board + use_dcdc: true, + rx_boost: false, + }; + + let radio = RadioParts { + driver: Sx126x::new(spi_device, iv, sx_config), + delay: Delay, + }; + + // ── USB ──────────────────────────────────────────────── + let host = UsbParts { + driver: embassy_rp::usb::Driver::new(p.USB, mcu::Irqs), + }; + + // ── LED on GPIO25 ────────────────────────────────────── + let led_pin = Output::new(p.PIN_25, Level::Low); + let led = crate::driver::simple_led::SimpleLed(led_pin); + + BoardParts { + radio, + host, + display: None, + led, + mac: Self::mac_address(), + } + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 9a0f6a4..72b02eb 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,5 +1,5 @@ #[cfg(feature = "wio_tracker_l1")] pub mod sh1106; -#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4", feature = "wio_tracker_l1"))] +#[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4", feature = "wio_tracker_l1", feature = "waveshare_rp2040_lora"))] pub mod simple_led; diff --git a/src/hal/mod.rs b/src/hal/mod.rs index 372fea3..d90afa1 100644 --- a/src/hal/mod.rs +++ b/src/hal/mod.rs @@ -8,3 +8,6 @@ pub mod esp32s3; #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))] pub mod nrf52840; + +#[cfg(feature = "waveshare_rp2040_lora")] +pub mod rp2040; diff --git a/src/hal/rp2040.rs b/src/hal/rp2040.rs new file mode 100644 index 0000000..18593a9 --- /dev/null +++ b/src/hal/rp2040.rs @@ -0,0 +1,76 @@ +//! RP2040 MCU initialization primitives. +//! +//! Provides shared boilerplate for RP2040 boards: interrupt bindings, +//! type aliases, StaticCell bus wrapping, USB init, and MAC address reading. + +use embassy_rp::bind_interrupts; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use static_cell::StaticCell; + +// ── Interrupts (shared by all RP2040 boards) ────────────────────── + +bind_interrupts!(pub struct Irqs { + USBCTRL_IRQ => embassy_rp::usb::InterruptHandler; +}); + +// ── MCU-level types ──────────────────────────────────────────────── + +pub type SpiBus = embassy_rp::spi::Spi<'static, embassy_rp::peripherals::SPI1, embassy_rp::spi::Async>; +pub type UsbRpDriver = embassy_rp::usb::Driver<'static, embassy_rp::peripherals::USB>; + +// ── SPI bus sharing ──────────────────────────────────────────────── + +/// Wrap an initialized SPI peripheral in a shared bus (StaticCell + Mutex). +pub fn share_spi_bus( + spi: SpiBus, +) -> &'static embassy_sync::mutex::Mutex { + static SPI_BUS: StaticCell> = + StaticCell::new(); + SPI_BUS.init(embassy_sync::mutex::Mutex::new(spi)) +} + +// ── MAC address ──────────────────────────────────────────────────── + +/// Store for the MAC address derived from flash unique ID. +/// Populated once during `init_mac()`, read by `mac_address()`. +static MAC: StaticCell<[u8; 6]> = StaticCell::new(); +static mut MAC_REF: Option<&'static [u8; 6]> = None; + +/// Read the flash chip's unique ID and derive a 6-byte MAC address. +/// +/// Must be called once during board init before `mac_address()`. +pub fn init_mac( + flash: &mut embassy_rp::flash::Flash<'_, embassy_rp::peripherals::FLASH, embassy_rp::flash::Blocking, FLASH_SIZE>, +) { + let mut uid = [0u8; 8]; + flash.blocking_unique_id(&mut uid).expect("flash unique ID"); + + // XOR the first 2 bytes into bytes 2..8 to fold 8 bytes into 6, + // then set the locally-administered bit (byte 0, bit 1). + let mac = [ + uid[2] ^ uid[0] | 0x02, + uid[3] ^ uid[1], + uid[4], + uid[5], + uid[6], + uid[7], + ]; + + let stored = MAC.init(mac); + // SAFETY: Written once during single-threaded init, before the executor + // starts. All subsequent reads are from &'static reference. + unsafe { + MAC_REF = Some(stored); + } +} + +/// Return the MAC address derived from the flash unique ID. +/// +/// Panics if `init_mac()` has not been called. +pub fn mac_address() -> [u8; 6] { + // SAFETY: MAC_REF is set once during init before the executor starts, + // and only read (never written) after that. + unsafe { + *MAC_REF.expect("init_mac() must be called before mac_address()") + } +} diff --git a/src/main.rs b/src/main.rs index 7ebd943..03ceb11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ use crate::channel::{CommandChannel, DisplayCommandChannel, ResponseChannel, Sta #[cfg(not(test))] cfg_if::cfg_if! { - if #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))] { + if #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1", feature = "waveshare_rp2040_lora"))] { use defmt_rtt as _; use panic_probe as _; } else if #[cfg(any(feature = "heltec_v3", feature = "heltec_v3_uart", feature = "heltec_v4"))] { @@ -61,7 +61,7 @@ static DISPLAY_COMMANDS: DisplayCommandChannel = DisplayCommandChannel::new(); #[cfg(not(test))] cfg_if::cfg_if! { - if #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1"))] { + if #[cfg(any(feature = "rak_wisblock_4631", feature = "wio_tracker_l1", feature = "waveshare_rp2040_lora"))] { #[embassy_executor::main] async fn main(spawner: Spawner) { run(spawner).await;