Add auto-detection feature to esp-println (#1658)

* Add auto-detection feature to esp-println

* CHANGELOG.md

* Minor README change

* Build `esp-println` in CI
This commit is contained in:
Björn Quentin 2024-06-05 12:44:17 +02:00 committed by GitHub
parent 8a47e4254f
commit f125c20cf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 197 additions and 30 deletions

View File

@ -152,6 +152,53 @@ jobs:
- name: Build esp-riscv-rt (riscv32imac, all features)
run: cd esp-riscv-rt/ && cargo build --target=riscv32imac-unknown-none-elf --features=ci
esp-println:
name: esp-println (${{ matrix.device.soc }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
device: [
# RISC-V devices:
{ soc: "esp32c2", target: "riscv32imc-unknown-none-elf" },
{ soc: "esp32c3", target: "riscv32imc-unknown-none-elf" },
{ soc: "esp32c6", target: "riscv32imac-unknown-none-elf" },
{ soc: "esp32h2", target: "riscv32imac-unknown-none-elf" },
# Xtensa devices:
{ soc: "esp32", target: "xtensa-esp32-none-elf" },
{ soc: "esp32s2", target: "xtensa-esp32s2-none-elf" },
{ soc: "esp32s3", target: "xtensa-esp32s3-none-elf" },
]
steps:
- uses: actions/checkout@v4
# Install the Rust toolchain for RISC-V devices:
- if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.device.soc) }}
uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
toolchain: stable
components: rust-src
# Install the Rust toolchain for Xtensa devices:
- if: contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.device.soc)
uses: esp-rs/xtensa-toolchain@v1.5
with:
buildtargets: ${{ matrix.device.soc }}
default: true
ldproxy: false
- uses: Swatinem/rust-cache@v2
# Make sure we're able to build with the default features and most common features enabled
- name: Build (no features)
run: |
cargo xtask build-package \
--features=${{ matrix.device.soc }},log \
--target=${{ matrix.device.target }} \
esp-println
extras:
runs-on: ubuntu-latest

View File

@ -0,0 +1,2 @@
[unstable]
build-std = ["alloc", "core"]

43
esp-println/CHANGELOG.md Normal file
View File

@ -0,0 +1,43 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Add `auto` feature to auto-detect Serial-JTAG/UART communication (#1658)
### Fixed
### Changed
- `auto` is the default communication method (#1658)
### Removed
## [0.9.1] - 2024-03-11
### Changed
- Un-pinned the defmt package's version number
## [0.9.0] - 2024-02-07
### Added
- Add support for ESP32-P4
### Removed
- Remove ESP 8266 support
## [0.8.0] - 2023-12-21
### Removed
- Remove RTT and defmt-raw support

View File

@ -22,7 +22,7 @@ portable-atomic = { version = "1.6.0", optional = true, default-features = fal
esp-build = { version = "0.1.0", path = "../esp-build" }
[features]
default = ["dep:critical-section", "colors", "uart"]
default = ["dep:critical-section", "colors", "auto"]
log = ["dep:log"]
# You must enable exactly 1 of the below features to support the correct chip:
@ -36,10 +36,13 @@ esp32s2 = []
esp32s3 = []
# You must enable exactly 1 of the below features to enable to intended
# communication method (note that "uart" is enabled by default):
# communication method (note that "auto" is enabled by default):
jtag-serial = ["dep:portable-atomic"] # C3, C6, H2, P4, and S3 only!
no-op = []
uart = []
auto = ["dep:portable-atomic"]
# Don't print anything
no-op = []
# Enables a `defmt` backend usable with espflash. We force rzcobs encoding to simplify implementation
defmt-espflash = ["dep:defmt", "defmt?/encoding-rzcobs"]

View File

@ -39,22 +39,23 @@ You can now `println!("Hello world")` as usual.
`esp32c3`, `esp32c6`, `esp32h2`, `esp32s2`, and `esp32s3`.
- One of these features must be enabled.
- Only one of these features can be enabled at a time.
- There is one feature for each supported communication method: `uart`, `jtag-serial` and `no-op`.
- There is one feature for each supported communication method: `uart`, `jtag-serial` and `auto`.
- Only one of these features can be enabled at a time.
- `no-op`: Don't print anything.
- `log`: Enables logging using [`log` crate].
- `colors` enable colored logging.
- `colors`: Enable colored logging.
- Only effective when using the `log` feature.
- `critical-section` enables critical sections.
- `critical-section`: Enables critical sections.
- `defmt-espflash`: This is intended to be used with [`espflash`], see `-L/--log-format` argument
of `flash` or `monitor` subcommands of `espflash` and `cargo-espflash`. Uses [rzCOBS] encoding
and adds framing.
## Default Features
By default, we use the `uart`, `critial-section` and `colors` features.
Which means that it will print to the UART, use critical sections and output
By default, we use the `auto`, `critial-section` and `colors` features.
Which means that it will auto-detect if it needs to print to the UART or JTAG-Serial, use critical sections and output
messages will be colored.
If we want to use a communication method that is not `uart`, the default
If we want to use a communication method that is not `auto`, the default
one, we need to [disable the default features].
## Logging

View File

@ -7,7 +7,7 @@ fn main() {
);
// Ensure that only a single communication method is specified
assert_unique_used_features!("jtag-serial", "uart");
assert_unique_used_features!("jtag-serial", "uart", "auto");
// Ensure that, if the `jtag-serial` communication method feature is enabled,
// either the `esp32c3`, `esp32c6`, `esp32h2`, or `esp32s3` chip feature is

View File

@ -68,26 +68,94 @@ macro_rules! dbg {
};
}
#[cfg(any(feature = "jtag-serial", feature = "auto"))]
pub struct PrinterSerialJtag;
#[cfg(any(feature = "uart", feature = "auto"))]
pub struct PrinterUart;
#[cfg(feature = "jtag-serial")]
pub type PrinterImpl = PrinterSerialJtag;
#[cfg(feature = "uart")]
pub type PrinterImpl = PrinterUart;
pub struct Printer;
impl core::fmt::Write for Printer {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
Printer.write_bytes(s.as_bytes());
Printer::write_bytes(s.as_bytes());
Ok(())
}
}
impl Printer {
pub fn write_bytes(&mut self, bytes: &[u8]) {
#[cfg(not(feature = "auto"))]
pub fn write_bytes(bytes: &[u8]) {
with(|| {
self.write_bytes_assume_cs(bytes);
self.flush();
PrinterImpl::write_bytes_assume_cs(bytes);
PrinterImpl::flush();
})
}
#[cfg(feature = "auto")]
pub fn write_bytes(bytes: &[u8]) {
#[cfg(any(
feature = "esp32c3",
feature = "esp32c6",
feature = "esp32h2",
feature = "esp32s3"
))]
{
// Decide if serial-jtag is used by checking SOF interrupt flag.
// SOF packet is sent by the HOST every 1ms on a full speed bus.
// Between two consecutive ticks, there will be at least 1ms (selectable tick
// rate range is 1 - 1000Hz).
// We don't reset the flag - if it was ever connected we assume serial-jtag is
// used
#[cfg(feature = "esp32c3")]
const USB_DEVICE_INT_RAW: *const u32 = 0x60043008 as *const u32;
#[cfg(feature = "esp32c6")]
const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
#[cfg(feature = "esp32h2")]
const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
#[cfg(feature = "esp32s3")]
const USB_DEVICE_INT_RAW: *const u32 = 0x60038000 as *const u32;
const SOF_INT_MASK: u32 = 0b10;
let is_serial_jtag =
unsafe { (USB_DEVICE_INT_RAW.read_volatile() & SOF_INT_MASK) != 0 };
if is_serial_jtag {
with(|| {
PrinterSerialJtag::write_bytes_assume_cs(bytes);
PrinterSerialJtag::flush();
})
} else {
with(|| {
PrinterUart::write_bytes_assume_cs(bytes);
PrinterUart::flush();
})
}
}
#[cfg(not(any(
feature = "esp32c3",
feature = "esp32c6",
feature = "esp32h2",
feature = "esp32s3"
)))]
with(|| {
PrinterUart::write_bytes_assume_cs(bytes);
PrinterUart::flush();
})
}
}
#[cfg(all(
feature = "jtag-serial",
any(feature = "jtag-serial", feature = "auto"),
any(
feature = "esp32c3",
feature = "esp32c6",
@ -154,8 +222,8 @@ mod serial_jtag_printer {
true
}
impl super::Printer {
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
impl super::PrinterSerialJtag {
pub fn write_bytes_assume_cs(bytes: &[u8]) {
if fifo_full() {
// The FIFO is full. Let's see if we can progress.
@ -188,17 +256,17 @@ mod serial_jtag_printer {
}
}
pub fn flush(&mut self) {
pub fn flush() {
fifo_flush();
}
}
}
#[cfg(all(feature = "uart", feature = "esp32"))]
#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32"))]
mod uart_printer {
const UART_TX_ONE_CHAR: usize = 0x4000_9200;
impl super::Printer {
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
impl super::PrinterUart {
pub fn write_bytes_assume_cs(bytes: &[u8]) {
for &b in bytes {
unsafe {
let uart_tx_one_char: unsafe extern "C" fn(u8) -> i32 =
@ -208,14 +276,14 @@ mod uart_printer {
}
}
pub fn flush(&mut self) {}
pub fn flush() {}
}
}
#[cfg(all(feature = "uart", feature = "esp32s2"))]
#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32s2"))]
mod uart_printer {
impl super::Printer {
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
impl super::PrinterUart {
pub fn write_bytes_assume_cs(bytes: &[u8]) {
// On ESP32-S2 the UART_TX_ONE_CHAR ROM-function seems to have some issues.
for chunk in bytes.chunks(64) {
for &b in chunk {
@ -234,11 +302,14 @@ mod uart_printer {
}
}
pub fn flush(&mut self) {}
pub fn flush() {}
}
}
#[cfg(all(feature = "uart", not(any(feature = "esp32", feature = "esp32s2"))))]
#[cfg(all(
any(feature = "uart", feature = "auto"),
not(any(feature = "esp32", feature = "esp32s2"))
))]
mod uart_printer {
trait Functions {
const TX_ONE_CHAR: usize;
@ -350,8 +421,8 @@ mod uart_printer {
}
}
impl super::Printer {
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
impl super::PrinterUart {
pub fn write_bytes_assume_cs(bytes: &[u8]) {
for chunk in bytes.chunks(Device::CHUNK_SIZE) {
for &b in chunk {
Device::tx_byte(b);
@ -361,7 +432,7 @@ mod uart_printer {
}
}
pub fn flush(&mut self) {}
pub fn flush() {}
}
}