1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Board support for the **M5Stack Cardputer ADV** (ESP32-S3FN8, Stamp-S3A).
//!
//! ```ignore
//! #[esp_rtos::main]
//! async fn main(_spawner: Spawner) -> ! {
//! let mut board = oxstack_cardputer::init(oxstack_cardputer::default_config());
//! let _backlight = board.backlight.init();
//! let i2c = board.sys_i2c.into_bus();
//! let mut display = board.display.init().await.unwrap();
//! let mut keyboard = board.keyboard.init(i2c).await.unwrap();
//! let fb = oxstack_cardputer::display::framebuffer();
//! loop {
//! let ev = keyboard.next_event().await.unwrap();
//! // draw into `fb`, then:
//! display.flush(fb).await.unwrap();
//! }
//! }
//! ```
//!
//! # Model
//!
//! [`Board`] splits the chip's peripherals into one *resource group* per
//! board function: `display`, `keyboard`, `audio`, `spi_bus` and so on. Each
//! group owns exactly the pins and peripherals that function is wired to, so
//! the compiler rules out two drivers fighting over a pin. Groups you never
//! `init` cost nothing.
//!
//! Every group has an `init` that **borrows** the group (`&mut self`) and
//! returns a driver tied to that borrow. Drop the driver and the group is
//! free again, so one firmware can start the same driver twice, or the
//! microphone after the speaker, without rebooting:
//!
//! ```ignore
//! let mut mic = board.audio.recorder(i2c, rate, gain).await?;
//! // ... record ...
//! drop(mic);
//! let mut speaker = board.audio.player(i2c, rate, volume).await?; // same hardware
//! ```
//!
//! Groups are plain structs of `esp-hal` peripherals with a `'d` lifetime.
//! Destructure one to drive the pins yourself, or `reborrow()` it to lend a
//! shorter-lived copy to a function, exactly as with `esp-hal` peripherals.
//!
//! Three things are one-per-boot by nature and keep a consuming API: the
//! shared I²C bus ([`i2c::SysI2cResources::into_bus`]), the framebuffer
//! ([`display::framebuffer`]) and the Wi-Fi station ([`net::start_station`]).
//!
//! # Project setup
//!
//! The quickest start is the template in the repository:
//!
//! ```sh
//! cargo install cargo-generate espup espflash --locked
//! espup install && source ~/export-esp.sh
//! cargo generate --git https://github.com/theashraf/oxstack templates/cardputer-app
//! ```
//!
//! By hand, a firmware crate needs:
//!
//! * **Dependencies** at the versions this crate pins. The ones you use in
//! types are re-exported here ([`esp_hal`], [`esp_rtos`],
//! [`embassy_executor`], [`embassy_time`], [`embassy_sync`],
//! [`embassy_futures`], [`esp_bootloader_esp_idf`]). Add `esp-backtrace`
//! (panic handler) and `esp-println` with `defmt-espflash` + `jtag-serial`
//! (log transport) yourself; a library must not pull in a panic handler.
//! `#[esp_rtos::main]` expands to paths in `esp_rtos` and
//! `embassy_executor`, so list those two as direct dependencies too.
//! * **`rust-toolchain.toml`** selecting the `esp` channel (`espup install`).
//! * **`.cargo/config.toml`**: target `xtensa-esp32s3-none-elf`,
//! `build-std = ["core", "alloc"]`, `rustflags = ["-C", "link-arg=-nostartfiles"]`
//! and an `espflash flash --monitor --log-format defmt` runner.
//! * **`build.rs`** emitting `-Tdefmt.x` and, last, `-Tlinkall.x`.
//! * `esp_bootloader_esp_idf::esp_app_desc!();` once in `main.rs`.
//!
//! API docs are built for the ESP32-S3 and hosted at
//! <https://theashraf.github.io/oxstack/oxstack_cardputer/>; docs.rs cannot
//! build for Xtensa.
extern crate alloc;
/// Implements `reborrow` for a resource group: a copy of the group whose
/// lifetime is that of the borrow, exactly like `esp-hal` peripherals.
/// `(buffer, descriptors)` for a DMA buffer of `$size` bytes split into
/// `$chunk`-byte descriptors, backed by statics in internal RAM.
///
/// Each expansion owns one pair of statics and hands out `'static`
/// references to them, so the caller must guarantee that references from
/// the previous expansion are dead. Resource groups ensure this by borrowing
/// themselves for the lifetime of the driver that holds the buffer.
pub use *;
pub use oxstack_audio as dsp;
pub use oxstack_drivers as drivers;
pub use oxstack_gfx as gfx;
pub use oxstack_input as input;
// The crates whose types appear in this API, at the versions it pins.
pub use ;
/// Initialises the chip, starts the `esp-rtos` scheduler (required for
/// Embassy timers), and returns the board. With the `radio` feature it also
/// adds the reclaimed-RAM heap that `esp-radio` needs.
///
/// Use [`Board::new`] instead to keep control of the timer and software
/// interrupt the scheduler would otherwise claim.
/// The configuration [`init`] should normally be given: CPU at 240 MHz.