Add the esp-println package to the repository (#1575)
* Add the `esp-println` package to the repository * Update esp-println version in README.md Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com> --------- Co-authored-by: Scott Mabin <scott@mabez.dev> Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com>
This commit is contained in:
parent
5facb759ae
commit
cc28c3eb09
@ -10,6 +10,7 @@ exclude = [
|
|||||||
"esp-ieee802154",
|
"esp-ieee802154",
|
||||||
"esp-lp-hal",
|
"esp-lp-hal",
|
||||||
"esp-metadata",
|
"esp-metadata",
|
||||||
|
"esp-println",
|
||||||
"esp-riscv-rt",
|
"esp-riscv-rt",
|
||||||
"examples",
|
"examples",
|
||||||
"hil-test",
|
"hil-test",
|
||||||
|
|||||||
47
esp-println/Cargo.toml
Normal file
47
esp-println/Cargo.toml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[package]
|
||||||
|
name = "esp-println"
|
||||||
|
version = "0.9.1"
|
||||||
|
edition = "2021"
|
||||||
|
description = "Provides `print!` and `println!` implementations various Espressif devices"
|
||||||
|
repository = "https://github.com/esp-rs/esp-hal"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
cargo-args = ["-Z", "build-std=core"]
|
||||||
|
default-target = "riscv32imc-unknown-none-elf"
|
||||||
|
features = ["esp32c3"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
critical-section = { version = "1.1.2", optional = true }
|
||||||
|
defmt = { version = "0.3.7", optional = true }
|
||||||
|
log = { version = "0.4.21", optional = true }
|
||||||
|
portable-atomic = { version = "1.6.0", optional = true, default-features = false }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
esp-build = { version = "0.1.0", path = "../esp-build" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["dep:critical-section", "colors", "uart"]
|
||||||
|
log = ["dep:log"]
|
||||||
|
|
||||||
|
# You must enable exactly 1 of the below features to support the correct chip:
|
||||||
|
esp32 = []
|
||||||
|
esp32c2 = []
|
||||||
|
esp32c3 = []
|
||||||
|
esp32c6 = []
|
||||||
|
esp32h2 = []
|
||||||
|
esp32p4 = []
|
||||||
|
esp32s2 = []
|
||||||
|
esp32s3 = []
|
||||||
|
|
||||||
|
# You must enable exactly 1 of the below features to enable to intended
|
||||||
|
# communication method (note that "uart" is enabled by default):
|
||||||
|
jtag-serial = ["dep:portable-atomic"] # C3, C6, H2, P4, and S3 only!
|
||||||
|
no-op = []
|
||||||
|
uart = []
|
||||||
|
|
||||||
|
# Enables a `defmt` backend usable with espflash. We force rzcobs encoding to simplify implementation
|
||||||
|
defmt-espflash = ["dep:defmt", "defmt?/encoding-rzcobs"]
|
||||||
|
|
||||||
|
# logging sub-features
|
||||||
|
colors = []
|
||||||
117
esp-println/README.md
Normal file
117
esp-println/README.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# esp-println
|
||||||
|
|
||||||
|
A library that provides `print!`, `println!`, `dbg!` implementations and
|
||||||
|
logging capabilities for Espressif devices.
|
||||||
|
|
||||||
|
- Supports all Espressif ESP32 family devices.
|
||||||
|
- Supports different communication methods:
|
||||||
|
- UART (Default)
|
||||||
|
- JTAG-Serial (Only available in ESP32-C3, ESP32-C6, ESP32-H2, ESP32-S3)
|
||||||
|
- No-op: Turns printing into a no-op
|
||||||
|
- Supports [`defmt`] backend
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```toml
|
||||||
|
esp-println = { version = "0.9.1", features = ["esp32c2"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
or `cargo add esp-println --features esp32c2`
|
||||||
|
It's important to specify your target device as feature.
|
||||||
|
|
||||||
|
Then in your program:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use esp_println::println;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now `println!("Hello world")` as usual.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- There is one feature for each supported target: `esp32`, `esp32c2`,
|
||||||
|
`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`.
|
||||||
|
- Only one of these features can be enabled at a time.
|
||||||
|
- `log`: Enables logging using [`log` crate].
|
||||||
|
- `colors` enable colored logging.
|
||||||
|
- Only effective when using the `log` feature.
|
||||||
|
- `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
|
||||||
|
messages will be colored.
|
||||||
|
If we want to use a communication method that is not `uart`, the default
|
||||||
|
one, we need to [disable the default features].
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
With the feature `log` activated you can initialize a simple logger like this
|
||||||
|
|
||||||
|
```rust
|
||||||
|
init_logger(log::LevelFilter::Info);
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a default feature `colors` which enables colored log output.
|
||||||
|
|
||||||
|
Additionally, you can use
|
||||||
|
|
||||||
|
```rust
|
||||||
|
init_logger_from_env();
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case the following environment variables are used:
|
||||||
|
|
||||||
|
- `ESP_LOGLEVEL` sets the log level, use values like `trace`, `info` etc.
|
||||||
|
- `ESP_LOGTARGETS` if set you should provide the crate names of crates (optionally with a path e.g. `esp_wifi::compat::common`) which should get logged, separated by `,` and no additional whitespace between
|
||||||
|
|
||||||
|
If this simple logger implementation isn't sufficient for your needs, you can implement your own logger on top of `esp-println`. See [Implementing a Logger section log documentaion]
|
||||||
|
|
||||||
|
## `defmt`
|
||||||
|
|
||||||
|
Using the `defmt-espflash` feature, `esp-println` will install a `defmt` global logger. The logger will
|
||||||
|
output to the same data stream as `println!()`, and adds framing bytes so it can be used even with
|
||||||
|
other, non-`defmt` output. Using the `defmt-espflash` feature automatically uses the [rzCOBS] encoding and does
|
||||||
|
not allow changing the encoding.
|
||||||
|
|
||||||
|
Follow the [`defmt` book's setup instructions] on how to
|
||||||
|
set up `defmt`. Remember, the global logger is already installed for you by `esp-println`!
|
||||||
|
|
||||||
|
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
|
||||||
|
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
|
||||||
|
|
||||||
|
[`defmt`]: https://github.com/knurling-rs/defmt
|
||||||
|
[`log` crate]: https://github.com/rust-lang/log
|
||||||
|
[rzCOBS]: https://github.com/Dirbaio/rzcobs
|
||||||
|
[`espflash`]: https://github.com/esp-rs/espflash
|
||||||
|
[disable the default features]: https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature
|
||||||
|
[Implementing a Logger section log documentaion]: https://docs.rs/log/0.4.17/log/#implementing-a-logger
|
||||||
|
[`defmt` book's setup instructions]: https://defmt.ferrous-systems.com/setup
|
||||||
|
|
||||||
|
# Troubleshooting linker errors
|
||||||
|
|
||||||
|
If you experience linker errors, make sure you have _some_ reference to `esp_println` in your code.
|
||||||
|
If you don't use `esp_println` directly, you'll need to add e.g. `use esp_println as _;` to your
|
||||||
|
import statements. This ensures that the global logger will not be removed by the compiler.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Licensed under either of:
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
# Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||||
|
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||||
|
any additional terms or conditions.
|
||||||
33
esp-println/build.rs
Normal file
33
esp-println/build.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use esp_build::assert_unique_used_features;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Ensure that only a single chip is specified
|
||||||
|
assert_unique_used_features!(
|
||||||
|
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure that only a single communication method is specified
|
||||||
|
assert_unique_used_features!("jtag-serial", "uart");
|
||||||
|
|
||||||
|
// Ensure that, if the `jtag-serial` communication method feature is enabled,
|
||||||
|
// either the `esp32c3`, `esp32c6`, `esp32h2`, or `esp32s3` chip feature is
|
||||||
|
// enabled.
|
||||||
|
if cfg!(feature = "jtag-serial")
|
||||||
|
&& !(cfg!(feature = "esp32c3")
|
||||||
|
|| cfg!(feature = "esp32c6")
|
||||||
|
|| cfg!(feature = "esp32h2")
|
||||||
|
|| cfg!(feature = "esp32p4")
|
||||||
|
|| cfg!(feature = "esp32s3"))
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"The `jtag-serial` feature is only supported by the ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4, and ESP32-S3"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that, if the `colors` is used with `log`.`
|
||||||
|
if cfg!(feature = "colors") && !cfg!(feature = "log") {
|
||||||
|
println!(
|
||||||
|
"cargo:warning=The `colors` feature is only effective when using the `log` feature"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
89
esp-println/src/defmt.rs
Normal file
89
esp-println/src/defmt.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//! defmt global logger implementation.
|
||||||
|
// Implementation taken from defmt-rtt, with a custom framing prefix
|
||||||
|
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
use critical_section::RestoreState;
|
||||||
|
|
||||||
|
use super::Printer;
|
||||||
|
|
||||||
|
/// Global logger lock.
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
static mut TAKEN: bool = false;
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
static mut CS_RESTORE: RestoreState = RestoreState::invalid();
|
||||||
|
static mut ENCODER: defmt::Encoder = defmt::Encoder::new();
|
||||||
|
|
||||||
|
#[defmt::global_logger]
|
||||||
|
pub struct Logger;
|
||||||
|
unsafe impl defmt::Logger for Logger {
|
||||||
|
fn acquire() {
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
unsafe {
|
||||||
|
// safety: Must be paired with corresponding call to release(), see below
|
||||||
|
let restore = critical_section::acquire();
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
if TAKEN {
|
||||||
|
panic!("defmt logger taken reentrantly")
|
||||||
|
}
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
TAKEN = true;
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
CS_RESTORE = restore;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not disabled, write a non-UTF8 sequence to indicate the start of a defmt
|
||||||
|
// frame. We need this to distinguish defmt frames from other data that
|
||||||
|
// might be written to the printer.
|
||||||
|
do_write(&[0xFF, 0x00]);
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
unsafe { ENCODER.start_frame(do_write) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn release() {
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
ENCODER.end_frame(do_write);
|
||||||
|
|
||||||
|
Printer.flush();
|
||||||
|
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
{
|
||||||
|
// We don't need to write a custom end-of-frame sequence because:
|
||||||
|
// - using `defmt`, the rzcobs encoding already includes a terminating zero
|
||||||
|
// - using `defmt-raw`, we don't add any additional framing data
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
TAKEN = false;
|
||||||
|
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
let restore = CS_RESTORE;
|
||||||
|
|
||||||
|
// safety: Must be paired with corresponding call to acquire(), see above
|
||||||
|
critical_section::release(restore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn flush() {
|
||||||
|
Printer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write(bytes: &[u8]) {
|
||||||
|
// safety: accessing the `static mut` is OK because we have acquired a critical
|
||||||
|
// section.
|
||||||
|
ENCODER.write(bytes, do_write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_write(bytes: &[u8]) {
|
||||||
|
Printer.write_bytes_assume_cs(bytes)
|
||||||
|
}
|
||||||
376
esp-println/src/lib.rs
Normal file
376
esp-println/src/lib.rs
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||||
|
#![allow(rustdoc::bare_urls)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-espflash")]
|
||||||
|
pub mod defmt;
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
pub mod logger;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no-op"))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
{
|
||||||
|
use core::fmt::Write;
|
||||||
|
writeln!($crate::Printer, $($arg)*).ok();
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no-op"))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
{
|
||||||
|
use core::fmt::Write;
|
||||||
|
write!($crate::Printer, $($arg)*).ok();
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no-op")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
($($arg:tt)*) => {{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no-op")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($arg:tt)*) => {{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation adapted from `std::dbg`
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! dbg {
|
||||||
|
// NOTE: We cannot use `concat!` to make a static string as a format argument
|
||||||
|
// of `eprintln!` because `file!` could contain a `{` or
|
||||||
|
// `$val` expression could be a block (`{ .. }`), in which case the `println!`
|
||||||
|
// will be malformed.
|
||||||
|
() => {
|
||||||
|
$crate::println!("[{}:{}]", ::core::file!(), ::core::line!())
|
||||||
|
};
|
||||||
|
($val:expr $(,)?) => {
|
||||||
|
// Use of `match` here is intentional because it affects the lifetimes
|
||||||
|
// of temporaries - https://stackoverflow.com/a/48732525/1063961
|
||||||
|
match $val {
|
||||||
|
tmp => {
|
||||||
|
$crate::println!("[{}:{}] {} = {:#?}",
|
||||||
|
::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($val:expr),+ $(,)?) => {
|
||||||
|
($($crate::dbg!($val)),+,)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printer {
|
||||||
|
pub fn write_bytes(&mut self, bytes: &[u8]) {
|
||||||
|
with(|| {
|
||||||
|
self.write_bytes_assume_cs(bytes);
|
||||||
|
self.flush();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "jtag-serial",
|
||||||
|
any(
|
||||||
|
feature = "esp32c3",
|
||||||
|
feature = "esp32c6",
|
||||||
|
feature = "esp32h2",
|
||||||
|
feature = "esp32p4",
|
||||||
|
feature = "esp32s3"
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
mod serial_jtag_printer {
|
||||||
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32c3")]
|
||||||
|
const SERIAL_JTAG_FIFO_REG: usize = 0x6004_3000;
|
||||||
|
#[cfg(feature = "esp32c3")]
|
||||||
|
const SERIAL_JTAG_CONF_REG: usize = 0x6004_3004;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
|
||||||
|
const SERIAL_JTAG_FIFO_REG: usize = 0x6000_F000;
|
||||||
|
#[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
|
||||||
|
const SERIAL_JTAG_CONF_REG: usize = 0x6000_F004;
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32p4")]
|
||||||
|
const SERIAL_JTAG_FIFO_REG: usize = 0x500D_2000;
|
||||||
|
#[cfg(feature = "esp32p4")]
|
||||||
|
const SERIAL_JTAG_CONF_REG: usize = 0x500D_2004;
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32s3")]
|
||||||
|
const SERIAL_JTAG_FIFO_REG: usize = 0x6003_8000;
|
||||||
|
#[cfg(feature = "esp32s3")]
|
||||||
|
const SERIAL_JTAG_CONF_REG: usize = 0x6003_8004;
|
||||||
|
|
||||||
|
/// A previous wait has timed out. We use this flag to avoid blocking
|
||||||
|
/// forever if there is no host attached.
|
||||||
|
static TIMED_OUT: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
fn fifo_flush() {
|
||||||
|
let conf = SERIAL_JTAG_CONF_REG as *mut u32;
|
||||||
|
unsafe { conf.write_volatile(0b001) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fifo_full() -> bool {
|
||||||
|
let conf = SERIAL_JTAG_CONF_REG as *mut u32;
|
||||||
|
unsafe { conf.read_volatile() & 0b010 == 0b000 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fifo_write(byte: u8) {
|
||||||
|
let fifo = SERIAL_JTAG_FIFO_REG as *mut u32;
|
||||||
|
unsafe { fifo.write_volatile(byte as u32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_flush() -> bool {
|
||||||
|
const TIMEOUT_ITERATIONS: usize = 50_000;
|
||||||
|
|
||||||
|
// Wait for some time for the FIFO to clear.
|
||||||
|
let mut timeout = TIMEOUT_ITERATIONS;
|
||||||
|
while fifo_full() {
|
||||||
|
if timeout == 0 {
|
||||||
|
TIMED_OUT.store(true, Ordering::Relaxed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
timeout -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Printer {
|
||||||
|
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
|
||||||
|
if fifo_full() {
|
||||||
|
// The FIFO is full. Let's see if we can progress.
|
||||||
|
|
||||||
|
if TIMED_OUT.load(Ordering::Relaxed) {
|
||||||
|
// Still wasn't able to drain the FIFO. Let's assume we won't be able to, and
|
||||||
|
// don't queue up more data.
|
||||||
|
// This is important so we don't block forever if there is no host attached.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give the fifo some time to drain.
|
||||||
|
if !wait_for_flush() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reset the flag - we managed to clear our FIFO.
|
||||||
|
TIMED_OUT.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
for &b in bytes {
|
||||||
|
if fifo_full() {
|
||||||
|
fifo_flush();
|
||||||
|
|
||||||
|
// Wait for the FIFO to clear, we have more data to shift out.
|
||||||
|
if !wait_for_flush() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fifo_write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) {
|
||||||
|
fifo_flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "uart", 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]) {
|
||||||
|
for &b in bytes {
|
||||||
|
unsafe {
|
||||||
|
let uart_tx_one_char: unsafe extern "C" fn(u8) -> i32 =
|
||||||
|
core::mem::transmute(UART_TX_ONE_CHAR);
|
||||||
|
uart_tx_one_char(b)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "uart", feature = "esp32s2"))]
|
||||||
|
mod uart_printer {
|
||||||
|
const UART_TX_ONE_CHAR: usize = 0x4000_9200;
|
||||||
|
impl super::Printer {
|
||||||
|
pub fn write_bytes_assume_cs(&mut self, 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 {
|
||||||
|
unsafe {
|
||||||
|
// write FIFO
|
||||||
|
(0x3f400000 as *mut u32).write_volatile(b as u32);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for TX_DONE
|
||||||
|
while unsafe { (0x3f400004 as *const u32).read_volatile() } & (1 << 14) == 0 {}
|
||||||
|
unsafe {
|
||||||
|
// reset TX_DONE
|
||||||
|
(0x3f400010 as *mut u32).write_volatile(1 << 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "uart", not(any(feature = "esp32", feature = "esp32s2"))))]
|
||||||
|
mod uart_printer {
|
||||||
|
trait Functions {
|
||||||
|
const TX_ONE_CHAR: usize;
|
||||||
|
const CHUNK_SIZE: usize = 32;
|
||||||
|
|
||||||
|
fn tx_byte(b: u8) {
|
||||||
|
unsafe {
|
||||||
|
let tx_one_char: unsafe extern "C" fn(u8) -> i32 =
|
||||||
|
core::mem::transmute(Self::TX_ONE_CHAR);
|
||||||
|
tx_one_char(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Device;
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32c2")]
|
||||||
|
impl Functions for Device {
|
||||||
|
const TX_ONE_CHAR: usize = 0x4000_005C;
|
||||||
|
|
||||||
|
fn flush() {
|
||||||
|
// tx_one_char waits for empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32c3")]
|
||||||
|
impl Functions for Device {
|
||||||
|
const TX_ONE_CHAR: usize = 0x4000_0068;
|
||||||
|
|
||||||
|
fn flush() {
|
||||||
|
unsafe {
|
||||||
|
const TX_FLUSH: usize = 0x4000_0080;
|
||||||
|
const GET_CHANNEL: usize = 0x4000_058C;
|
||||||
|
let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
|
||||||
|
let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
|
||||||
|
|
||||||
|
const G_USB_PRINT_ADDR: usize = 0x3FCD_FFD0;
|
||||||
|
let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
|
||||||
|
|
||||||
|
let channel = if *g_usb_print {
|
||||||
|
// Flush USB-JTAG
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
get_channel()
|
||||||
|
};
|
||||||
|
tx_flush(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32s3")]
|
||||||
|
impl Functions for Device {
|
||||||
|
const TX_ONE_CHAR: usize = 0x4000_0648;
|
||||||
|
|
||||||
|
fn flush() {
|
||||||
|
unsafe {
|
||||||
|
const TX_FLUSH: usize = 0x4000_0690;
|
||||||
|
const GET_CHANNEL: usize = 0x4000_1A58;
|
||||||
|
let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
|
||||||
|
let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
|
||||||
|
|
||||||
|
const G_USB_PRINT_ADDR: usize = 0x3FCE_FFB8;
|
||||||
|
let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
|
||||||
|
|
||||||
|
let channel = if *g_usb_print {
|
||||||
|
// Flush USB-JTAG
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
get_channel()
|
||||||
|
};
|
||||||
|
tx_flush(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
|
||||||
|
impl Functions for Device {
|
||||||
|
const TX_ONE_CHAR: usize = 0x4000_0058;
|
||||||
|
|
||||||
|
fn flush() {
|
||||||
|
unsafe {
|
||||||
|
const TX_FLUSH: usize = 0x4000_0074;
|
||||||
|
const GET_CHANNEL: usize = 0x4000_003C;
|
||||||
|
|
||||||
|
let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
|
||||||
|
let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
|
||||||
|
|
||||||
|
tx_flush(get_channel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32p4")]
|
||||||
|
impl Functions for Device {
|
||||||
|
const TX_ONE_CHAR: usize = 0x4FC0_0054;
|
||||||
|
|
||||||
|
fn flush() {
|
||||||
|
unsafe {
|
||||||
|
const TX_FLUSH: usize = 0x4FC0_0074;
|
||||||
|
const GET_CHANNEL: usize = 0x4FC0_0038;
|
||||||
|
|
||||||
|
let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
|
||||||
|
let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
|
||||||
|
|
||||||
|
tx_flush(get_channel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Printer {
|
||||||
|
pub fn write_bytes_assume_cs(&mut self, bytes: &[u8]) {
|
||||||
|
for chunk in bytes.chunks(Device::CHUNK_SIZE) {
|
||||||
|
for &b in chunk {
|
||||||
|
Device::tx_byte(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn with<R>(f: impl FnOnce() -> R) -> R {
|
||||||
|
#[cfg(feature = "critical-section")]
|
||||||
|
return critical_section::with(|_| f());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "critical-section"))]
|
||||||
|
f()
|
||||||
|
}
|
||||||
76
esp-println/src/logger.rs
Normal file
76
esp-println/src/logger.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use core::str::FromStr;
|
||||||
|
|
||||||
|
use log::LevelFilter;
|
||||||
|
|
||||||
|
use super::println;
|
||||||
|
|
||||||
|
const LOG_TARGETS: Option<&'static str> = option_env!("ESP_LOGTARGETS");
|
||||||
|
|
||||||
|
pub fn init_logger(level: log::LevelFilter) {
|
||||||
|
unsafe {
|
||||||
|
log::set_logger_racy(&EspLogger).unwrap();
|
||||||
|
log::set_max_level_racy(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_logger_from_env() {
|
||||||
|
unsafe {
|
||||||
|
log::set_logger_racy(&EspLogger).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
const LEVEL: Option<&'static str> = option_env!("ESP_LOGLEVEL");
|
||||||
|
|
||||||
|
if let Some(lvl) = LEVEL {
|
||||||
|
let level = LevelFilter::from_str(lvl).unwrap_or_else(|_| LevelFilter::Off);
|
||||||
|
unsafe { log::set_max_level_racy(level) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EspLogger;
|
||||||
|
|
||||||
|
impl log::Log for EspLogger {
|
||||||
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
// check enabled log targets if any
|
||||||
|
if let Some(targets) = LOG_TARGETS {
|
||||||
|
if targets
|
||||||
|
.split(",")
|
||||||
|
.find(|v| record.target().starts_with(v))
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RESET: &str = "\u{001B}[0m";
|
||||||
|
const RED: &str = "\u{001B}[31m";
|
||||||
|
const GREEN: &str = "\u{001B}[32m";
|
||||||
|
const YELLOW: &str = "\u{001B}[33m";
|
||||||
|
const BLUE: &str = "\u{001B}[34m";
|
||||||
|
const CYAN: &str = "\u{001B}[35m";
|
||||||
|
|
||||||
|
#[cfg(feature = "colors")]
|
||||||
|
let color = match record.level() {
|
||||||
|
log::Level::Error => RED,
|
||||||
|
log::Level::Warn => YELLOW,
|
||||||
|
log::Level::Info => GREEN,
|
||||||
|
log::Level::Debug => BLUE,
|
||||||
|
log::Level::Trace => CYAN,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "colors")]
|
||||||
|
let reset = RESET;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "colors"))]
|
||||||
|
let color = "";
|
||||||
|
#[cfg(not(feature = "colors"))]
|
||||||
|
let reset = "";
|
||||||
|
|
||||||
|
println!("{}{} - {}{}", color, record.level(), record.args(), reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ esp-backtrace = { version = "0.11.1", features = ["exception-handler", "pa
|
|||||||
esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] }
|
esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] }
|
||||||
esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true }
|
esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true }
|
||||||
esp-ieee802154 = { version = "0.1.0", path = "../esp-ieee802154", optional = true }
|
esp-ieee802154 = { version = "0.1.0", path = "../esp-ieee802154", optional = true }
|
||||||
esp-println = { version = "0.9.1", features = ["log"] }
|
esp-println = { version = "0.9.1", path = "../esp-println", features = ["log"] }
|
||||||
fugit = "0.3.7"
|
fugit = "0.3.7"
|
||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
@ -79,3 +79,7 @@ incremental = false
|
|||||||
opt-level = 3
|
opt-level = 3
|
||||||
lto = 'fat'
|
lto = 'fat'
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
|
|
||||||
|
# TODO: Remove patch once `esp-backtrace` package has been added to repository
|
||||||
|
[patch.crates-io]
|
||||||
|
esp-println = { version = "0.9.1", path = "../esp-println" }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user