Skip to content

Commit

Permalink
book First refactor-through
Browse files Browse the repository at this point in the history
* `setup`: Deduplicate setup
* `setup-app`: name `cargo.toml`; add `defmt-itm`
* `macros`: Language
* `primitives`: Table-ize and drop `u24` (got dropped in (#521))
* `str`: Add links to doc-rs
* ...
  • Loading branch information
Urhengulas committed Jul 13, 2021
1 parent 6344d62 commit 37d05b9
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 185 deletions.
1 change: 0 additions & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[Introduction](./introduction.md)
- [User guide](./user-guide.md)
- [Setup](./setup.md)
- [Libraries](./setup-library.md)
- [Applications](./setup-app.md)
- [Logging macros](./macros.md)
- [Primitives](./primitives.md)
Expand Down
32 changes: 17 additions & 15 deletions book/src/bitfields.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Bitfields

`:m..n` is the bitfield formatting parameter.
When paired with a positional parameter it can be used to display the bitfields of a register.
> `:M..N` is the bitfield formatting parameter.
The bitfield argument is expected to be a *unsigned* integer that's large enough to contain the bitfields.
For example, if bitfield ranges only cover up to bit `11` (e.g. `=8..12`) then the argument must be at least `u16`.

Bit indices are little-endian: the 0th bit is the rightmost bit.

When paired with a positional parameter it can be used to display the bitfields of a register.
``` rust
# extern crate defmt;
# let pcnf1 = 0u32;
Expand All @@ -14,20 +19,17 @@ defmt::trace!(
);
```

The bitfield argument is expected to be a *fully typed, unsigned* integer that's large enough to contain the bitfields.
For example, if bitfield ranges only cover up to bit `11` (e.g. `=8..12`) then the argument must be `u16`.

Bit indices are little-endian: the 0th bit is the rightmost bit.

Bitfields are not range inclusive, e.g.
Bitfields are not range inclusive, e.g. following statement will evaluate to `5` (`0b110`):
``` rust
# extern crate defmt;
defmt::trace!("first two bits: {0=0..3}", 254u32);
// -> TRACE: first three bits: 110
defmt::trace!("first three bits: {0=0..3}", 254u32);
```
will evaluate to `2` (`0b10`).

⚠️ You can not reuse the same argument in a bitfield- and a non bitfield parameter. This will not compile:
``` rust,compile_fail
# extern crate defmt;
defmt::trace!("{0=5..13} {0=u16}", 256u16);
```
> ⚠️ You can not reuse the same argument in a bitfield- and a non bitfield parameter.
>
> This will not compile:
> ``` rust,compile_fail
> # extern crate defmt;
> defmt::trace!("{0=5..13} {0=u16}", 256u16);
> ```
22 changes: 11 additions & 11 deletions book/src/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

`defmt` supports 5 different logging levels listed below from lowest severity to highest severity:

- TRACE
- DEBUG
- INFO
- WARN
- ERROR
- `TRACE`
- `DEBUG`
- `INFO`
- `WARN`
- `ERROR`

By default all logging is *disabled*.
The amount of logging to perform can be controlled at the *crate* level using the following Cargo features:

- `defmt-default`, log at INFO, or TRACE, level and up
- `defmt-trace`, log at TRACE level and up
- `defmt-debug`, log at DEBUG level and up
- `defmt-info`, log at INFO level and up
- `defmt-warn`, log at WARN level and up
- `defmt-error`, log at ERROR level
- `defmt-trace`, log at `TRACE` level and up
- `defmt-debug`, log at `DEBUG` level and up
- `defmt-info`, log at `INFO` level and up
- `defmt-warn`, log at `WARN` level and up
- `defmt-error`, log at `ERROR` level
- `defmt-default`, log at `INFO`, or `TRACE`, level and up

These features must only be enabled by the top level *application* crate as shown below.

Expand Down
31 changes: 20 additions & 11 deletions book/src/format-slices.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
# Format slices / arrays

The `{=[?]}` parameter can be used to log a slices of values that implement the `Format` trait.
The expected argument is a slice.
> The `{=[?]}` parameter can be used to log a slice of values that implement the `Format` trait.
``` rust
# extern crate defmt;
# use defmt::{Format, info};
# use defmt::Format;
#
#[derive(Format)]
struct X {
y: u16,
z: u8,
}
let xs: &[X] = &[/* .. */];
info!("xs={=[?]}", xs);

let xs: &[X] = &[ /* .. */ ];
defmt::info!("xs={=[?]}", xs);
```

Note that for slices of bytes `{=[u8]}` should be preferred as it's better compressed.
`[T] where T: Format` also implements the `Format` trait so it's possible to format `[T]` with `{=?}` but `{=[?]}` uses slightly less bandwidth.
* The expected argument is a slice.

* Note that for slices of bytes, `{=[u8]}` should be preferred as it's better compressed.

* `[T] where T: Format` also implements the `Format` trait so it's possible to format `[T]` with `{=?}` but `{=[?]}` uses slightly less bandwidth.

If you have an array of types that implement the `Format` trait, instead of a slice. You should use
the `{=[?; N]}` parameter (where `N` is a number); this saves bandwidth compared to `{=[?]}`.

## Arrays

If you have an array of types that implement the `Format` trait, you should use
the `{=[?; N]}` parameter (where `N` is the number of elements); this saves bandwidth compared to `{=[?]}`.

``` rust
# extern crate defmt;
# use defmt::{Format, info};
# use defmt::Format;
#
#[derive(Format)]
struct X {
y: u16,
z: u8,
}

let xs: [X; 2] = [
# X { y: 1, z: 2 },
# X { y: 3, z: 4 },
// ..
];
info!("xs={=[?; 2]}", xs);
defmt::info!("xs={=[?; 2]}", xs);
```
18 changes: 11 additions & 7 deletions book/src/format.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Implementing `Format`

- [`#[derive(Format)]`](#deriveformat)
- [Manual implementation with `write!`](#manual-implementation-with-write)
- [Newtypes](#newtypes)
- [Uncompressed adapters](#uncompressed-adapters)

## `#[derive(Format)]`

The easiest way to implement the `Format` trait for a struct or enum is to use the `derive` attribute.
The easiest way to implement the `Format` trait for a `struct` or `enum` is to use the `derive` attribute.

``` rust
# extern crate defmt;
Expand All @@ -17,20 +22,19 @@ struct Header {

# #[derive(Format)]
# struct Descriptor;
#
#[derive(Format)]
enum Request {
GetDescriptor { descriptor: Descriptor, length: u16 },
SetAddress { address: u8 },
}
```

NOTE: Like built-in derives like `#[derive(Debug)]`, `#[derive(Format)]` will add `Format` bounds to the generic type parameters of the struct.

NOTE: Do *not* use the API used by the expansion of the `derive(Format)` macro; it is *unstable*.
Like built-in derives (e.g. `#[derive(Debug)]`), `#[derive(Format)]` will add `Format` bounds to the generic type parameters of the struct.

## `write!`
> ⚠️ Do *not* use the API used by the expansion of the `derive(Format)` macro; it is *unstable*.
> NOTE `write!` is available in `defmt` v0.1.**2**+
## Manual implementation with `write!`

It is also possible to implement the `Format` trait manually.
This trait has a single required method: `format`.
Expand Down Expand Up @@ -76,7 +80,7 @@ impl defmt::Format for MyU8 {

If you quickly want to get some code running and do not care about it being efficient you can use the two adapter types [`Display2Format`] and [`Debug2Format`].

These adapters disable compression and use the `core::fmt` code on-device! You should always prefer `defmt::Format` over `Debug` whenever possible.
> ⚠️ These adapters disable compression and use the `core::fmt` code on-device! You should always prefer `defmt::Format` over `Debug` whenever possible!
Note that this always uses `{:?}` to format the contained value, meaning that any provided defmt display hints will be ignored.

Expand Down
23 changes: 22 additions & 1 deletion book/src/global-logger.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# #[global_logger]

*Applications* that, directly or transitively, use any of `defmt` logging macros need to define a `#[global_logger]` or include one in their dependency graph.
> *Applications* that, directly or transitively, use any of `defmt` logging macros need to define a `#[global_logger]` or include one in their dependency graph.
This is similar to how the `alloc` crate depends on a `#[global_allocator]`.

The `global_logger` defines how data is moved from the *device*, where the application runs, to the host, where logs will be formatted and displayed.
Expand All @@ -20,6 +21,26 @@ Finally, `#[global_logger]` specifies which `Logger` implementation will be used
`#[global_logger]` must be used on a *unit* struct, a struct with no fields.
This struct must implement the `Logger` trait.
It's recommended that this struct is kept private.

```rust
# extern crate defmt;
#
#[defmt::global_logger]
struct Logger;

unsafe impl defmt::Logger for Logger {
fn acquire() {
// ...
}
unsafe fn release() {
// ...
}
unsafe fn write(bytes: &[u8]) {
// ...
}
}
```

Only a single `#[global_logger]` struct can appear in the dependency graph of an application.
The `global_logger` should be selected *at the top* of the dependency graph, that is in the application crate.

Expand Down
79 changes: 45 additions & 34 deletions book/src/hints.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,71 @@
# Display hints

A hint can be applied to each formatting parameter to change how it's printed on the host side.
The hint follows the syntax `:x` and must come after the type within the braces.
Examples: typed `{=u8:x}`, untyped `{:b}`
> A hint can be applied to each formatting parameter to change how it's printed on the host side.
The hint follows the syntax `:Display` and must come after the type within the braces, for example:
* typed `{=u8:x}`
* untyped `{:b}`

The following display hints are currently supported:

- `x`, lowercase hexadecimal
- `X`, uppercase hexadecimal
- `?`, `core::fmt::Debug`-like
- `b`, binary
- `a`, ASCII
- `us`, microseconds (formats integers as time stamps)
| hint | name |
| :---- | :--------------------------------------------- |
| `:x` | lowercase hexadecimal |
| `:X` | uppercase hexadecimal |
| `:?` | `core::fmt::Debug`-like |
| `:b` | binary |
| `:a` | ASCII |
| `:us` | microseconds (formats integers as time stamps) |

The first 4 display hints resemble what's supported in `core::fmt`. Examples below:
The first 4 display hints resemble what's supported in `core::fmt`, for example:

``` rust
# extern crate defmt;
defmt::info!("{=u8:x}", 42); // -> INFO 2a
defmt::info!("{=u8:X}", 42); // -> INFO 2A
defmt::info!("{=u8:x}", 42); // -> INFO 2a
defmt::info!("{=u8:X}", 42); // -> INFO 2A
defmt::info!("{=u8:#x}", 42); // -> INFO 0x2a
defmt::info!("{=u8:b}", 42); // -> INFO 101010
defmt::info!("{=u8:b}", 42); // -> INFO 101010

defmt::info!("{=str}", "hello\tworld"); // -> INFO hello world
defmt::info!("{=str:?}", "hello\tworld"); // -> INFO "hello\tworld"
```

Leading zeros are supported, for example
The ASCII display hint formats byte slices (and arrays) using Rust's byte string syntax.

``` rust
# extern crate defmt;
defmt::info!("{=u8:03}", 42); // -> INFO 042
defmt::info!("{=u8:08X}", 42); // -> INFO 0000002A
defmt::info!("{=u8:#010X}", 42); // -> INFO 0x0000002A
let bytes = [104, 101, 255, 108, 108, 111];

defmt::info!("{=[u8]:a}", bytes); // -> INFO b"he\xffllo"
```

When the alternate form is used for hex and binary, the `0x`/`0b` length is subtracted from the
leading zeros. This matches [`core::fmt` behavior](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b11809759f975e266251f7968e542756). No further
customization is supported (at the moment).
## Alternate printing

The ASCII display hint formats byte slices (and arrays) using Rust's byte string syntax.
Adding `#` in front of a binary and hexadecimal display hints, precedes these numbers with a base indicator.

``` rust
# extern crate defmt;
let bytes = [104, 101, 255, 108, 108, 111];
defmt::info!("{=u8:b}", 42); // -> INFO 101010
defmt::info!("{=u8:#b}", 42); // -> INFO 0b101010
defmt::info!("{=u8:x}", 42); // -> INFO 2a
defmt::info!("{=u8:#x}", 42); // -> INFO 0x2a
defmt::info!("{=u8:X}", 42); // -> INFO 2A
defmt::info!("{=u8:#X}", 42); // -> INFO 0x2A
```

defmt::info!("{=[u8]:a}", bytes);
// -> INFO b"he\xffllo"
## Zero padding

Padding numbers with leading zeros is supported, for example:

``` rust
# extern crate defmt;
defmt::info!("{=u8:03}", 42); // -> INFO 042
defmt::info!("{=u8:08X}", 42); // -> INFO 0000002A
defmt::info!("{=u8:#010X}", 42); // -> INFO 0x0000002A
```

When the alternate form is used for hex and binary, the `0x`/`0b` length is subtracted from the leading zeros. This matches [`core::fmt` behavior](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b11809759f975e266251f7968e542756).

## Propagation

Display hints "propagate downwards" and apply to formatting parameters that specify no display hint.
Expand All @@ -59,11 +76,8 @@ Display hints "propagate downwards" and apply to formatting parameters that spec
struct S { x: u8 }

let x = S { x: 42 };
defmt::info!("{}", x);
// -> INFO S { x: 42 }

defmt::info!("{:#x}", x);
// -> INFO S { x: 0x2a }
defmt::info!("{}", x); // -> INFO S { x: 42 }
defmt::info!("{:#x}", x); // -> INFO S { x: 0x2a }
```

``` rust
Expand All @@ -78,9 +92,6 @@ impl defmt::Format for S {
}

let x = S { x: 42, y: 42 };
defmt::info!("{}", x);
// -> INFO S { x: 42, y: 2a }

defmt::info!("{:b}", x);
// -> INFO S { x: 101010, y: 2a }
defmt::info!("{}", x); // -> INFO S { x: 42, y: 2a }
defmt::info!("{:b}", x); // -> INFO S { x: 101010, y: 2a }
```
28 changes: 16 additions & 12 deletions book/src/istr.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Interned strings

The `{=istr}` formatting parameter is used for *interned* strings.
> The `{=istr}` formatting parameter is used for *interned* strings.
Compared to the `{=str}` parameter, which transmits a complete string, `{=istr}` saves bandwidth by sending only a string index.
The `{=istr}` parameter expects an argument with type `defmt::Str`.
A `Str` value is created using the `intern!` macro; the argument to this macro must be a string literal.

The `{=istr}` parameter expects an argument with type [`defmt::Str`].
A `Str` value is created using the [`intern!`] macro; the argument to this macro must be a string literal.

``` rust
# extern crate defmt;
Expand All @@ -14,15 +16,17 @@ defmt::info!("{=str}", s);
# use defmt::Str;
let interned: Str = defmt::intern!("The quick brown fox jumps over the lazy dog");
defmt::info!("{=istr}", interned);
// ^^^^^^^^ bandwidth-use <= 2 bytes
// ^^^^^^^^^ bandwidth-use <= 2 bytes
```

This was a contrived example to show the difference in bandwidth use.
In practice you should use:

``` rust
# extern crate defmt;
defmt::info!("The quick brown fox jumps over the lazy dog");
```
> ⚠️ This was a contrived example to show the difference in bandwidth use.
>
> In practice you should following, which also interns the log string and uses as little bandwidth as the `{=istr}` version:
>
> ``` rust
> # extern crate defmt;
> defmt::info!("The quick brown fox jumps over the lazy dog");
> ```
which also interns the log string and uses as little bandwidth as the `{=istr}` version.
[`defmt::Str`]: https://docs.rs/defmt/*/defmt/struct.Str.html
[`intern!`]: https://docs.rs/defmt/*/defmt/macro.intern.html
Loading

0 comments on commit 37d05b9

Please sign in to comment.