aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsodo <[email protected]>2023-12-10 01:45:55 +0900
committersodo <[email protected]>2023-12-10 01:45:55 +0900
commit7956ef9247e5fb1eb957843af1230132d1766815 (patch)
tree7b78a0f650dbc079a492a77a964ad4ba08ed7e3f
parent58d503a77da73204f097fae1f1a2a1ce2cf90600 (diff)
parente99649e37d668cba0e58134ab76e81e5440db6c1 (diff)
Merge remote-tracking branch 'origin/main'
-rw-r--r--docs/modules/ROOT/nav.adoc7
-rw-r--r--docs/modules/ROOT/pages/best_practices.adoc53
-rw-r--r--docs/modules/ROOT/pages/embassy_in_the_wild.adoc9
-rw-r--r--docs/modules/ROOT/pages/faq.adoc20
-rw-r--r--docs/modules/ROOT/pages/new_project.adoc178
-rw-r--r--embassy-hal-internal/src/atomic_ring_buffer.rs14
-rw-r--r--embassy-hal-internal/src/drop.rs7
-rw-r--r--embassy-hal-internal/src/lib.rs1
-rw-r--r--embassy-hal-internal/src/macros.rs5
-rw-r--r--embassy-hal-internal/src/peripheral.rs1
-rw-r--r--embassy-hal-internal/src/ratio.rs1
-rw-r--r--embassy-nrf/src/lib.rs22
-rw-r--r--embassy-rp/src/lib.rs11
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs4
-rw-r--r--embassy-stm32/src/adc/f1.rs2
-rw-r--r--embassy-stm32/src/adc/f3_v1_1.rs413
-rw-r--r--embassy-stm32/src/adc/mod.rs21
-rw-r--r--embassy-stm32/src/adc/resolution.rs10
-rw-r--r--embassy-stm32/src/adc/sample_time.rs25
-rw-r--r--embassy-stm32/src/can/mod.rs2
-rw-r--r--embassy-stm32/src/lib.rs23
-rw-r--r--embassy-stm32/src/rcc/g4.rs3
-rw-r--r--embassy-stm32/src/rcc/mod.rs5
-rw-r--r--embassy-stm32/src/usb/usb.rs2
-rw-r--r--tests/stm32/Cargo.toml17
-rw-r--r--tests/stm32/src/bin/dac.rs5
-rw-r--r--tests/stm32/src/bin/dac_l1.rs86
-rw-r--r--tests/stm32/src/common.rs9
29 files changed, 922 insertions, 38 deletions
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 8d7f6f411..fabb80e31 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -1,6 +1,8 @@
1* xref:getting_started.adoc[Getting started] 1* xref:getting_started.adoc[Getting started]
2** xref:basic_application.adoc[Basic application] 2** xref:basic_application.adoc[Basic application]
3** xref:project_structure.adoc[Project Structure] 3** xref:project_structure.adoc[Project Structure]
4** xref:new_project.adoc[Starting a new Embassy project]
5** xref:best_practices.adoc[Best Practices]
4* xref:layer_by_layer.adoc[Bare metal to async] 6* xref:layer_by_layer.adoc[Bare metal to async]
5* xref:runtime.adoc[Executor] 7* xref:runtime.adoc[Executor]
6* xref:delaying_a_task.adoc[Delaying a Task] 8* xref:delaying_a_task.adoc[Delaying a Task]
@@ -10,6 +12,7 @@
10* xref:bootloader.adoc[Bootloader] 12* xref:bootloader.adoc[Bootloader]
11 13
12* xref:examples.adoc[Examples] 14* xref:examples.adoc[Examples]
13* xref:developer.adoc[Developer] 15* xref:developer.adoc[Developer Docs]
14** xref:developer_stm32.adoc[Developer: STM32] 16** xref:developer_stm32.adoc[Developer Docs: STM32]
17* xref:embassy_in_the_wild.adoc[Embassy in the wild]
15* xref:faq.adoc[Frequently Asked Questions] 18* xref:faq.adoc[Frequently Asked Questions]
diff --git a/docs/modules/ROOT/pages/best_practices.adoc b/docs/modules/ROOT/pages/best_practices.adoc
new file mode 100644
index 000000000..1e02f9ba9
--- /dev/null
+++ b/docs/modules/ROOT/pages/best_practices.adoc
@@ -0,0 +1,53 @@
1= Best Practices
2
3Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.
4
5== Passing Buffers by Reference
6It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack.
7This, however, can easily blow up your stack if you are not careful.
8
9Consider the following example:
10[,rust]
11----
12fn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {
13 // do stuff and return new buffer
14 for elem in buf.iter_mut() {
15 *elem = 0;
16 }
17 buf
18}
19
20pub fn main() -> () {
21 let buf = [1u8; 1024];
22 let buf_new = process_buffer(buf);
23 // do stuff with buf_new
24 ()
25}
26----
27When calling `process_buffer` in your program, a copy of the buffer you pass to the function will be created,
28consuming another 1024 bytes.
29After the processing, another 1024 byte buffer will be placed on the stack to be returned to the caller.
30(You can check the assembly, there will be two memcopy operations, e.g., `bl __aeabi_memcpy` when compiling for a Cortex-M processor.)
31
32*Possible Solution:*
33
34Pass the data by reference and not by value on both, the way in and the way out.
35For example, you could return a slice of the input buffer as the output.
36Requiring the lifetime of the input slice and the output slice to be the same, the memory safetly of this procedure will be enforced by the compiler.
37
38[,rust]
39----
40fn process_buffer<'a>(buf: &'a mut [u8]) -> &'a mut[u8] {
41 for elem in buf.iter_mut() {
42 *elem = 0;
43 }
44 buf
45}
46
47pub fn main() -> () {
48 let mut buf = [1u8; 1024];
49 let buf_new = process_buffer(&mut buf);
50 // do stuff with buf_new
51 ()
52}
53----
diff --git a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc
new file mode 100644
index 000000000..a1c31bfc7
--- /dev/null
+++ b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc
@@ -0,0 +1,9 @@
1= Embassy in the wild!
2
3Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/modules/ROOT/pages/embassy_in_the_wild.adoc[add more]!
4
5* link:https://github.com/cbruiz/printhor/[Printhor: The highly reliable but not necessarily functional 3D printer firmware]
6** Targets some STM32 MCUs
7* link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device
8** Targets the ESP32-S3 or ESP32-C6 MCU
9* The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL
diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc
index df3f66658..d1a012978 100644
--- a/docs/modules/ROOT/pages/faq.adoc
+++ b/docs/modules/ROOT/pages/faq.adoc
@@ -47,7 +47,8 @@ The first step to managing your binary size is to set up your link:https://doc.r
47debug = false 47debug = false
48lto = true 48lto = true
49opt-level = "s" 49opt-level = "s"
50incremental = true 50incremental = false
51codegen-units = 1
51---- 52----
52 53
53All of these flags are elaborated on in the Rust Book page linked above. 54All of these flags are elaborated on in the Rust Book page linked above.
@@ -135,3 +136,20 @@ embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd
135---- 136----
136 137
137Note that the git revision should match any other embassy patches or git dependencies that you are using! 138Note that the git revision should match any other embassy patches or git dependencies that you are using!
139
140== How can I optimize the speed of my embassy-stm32 program?
141
142* Make sure RCC is set up to go as fast as possible
143* Make sure link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html[flash cache] is enabled
144* build with `--release`
145* Set the following keys for the release profile in your `Cargo.toml`:
146 ** `opt-level = "s"`
147 ** `lto = "fat"`
148* Set the following keys in the `[unstable]` section of your `.cargo/config.toml`
149 ** `build-std = ["core"]`
150 ** `build-std-features = ["panic_immediate_abort"]`
151* Enable feature `embassy-time/generic-queue`, disable feature `embassy-executor/integrated-timers`
152* When using `InterruptExecutor`:
153 ** disable `executor-thread`
154 ** make `main`` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }`
155 ** *Note:* If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor. \ No newline at end of file
diff --git a/docs/modules/ROOT/pages/new_project.adoc b/docs/modules/ROOT/pages/new_project.adoc
new file mode 100644
index 000000000..ce139ed8d
--- /dev/null
+++ b/docs/modules/ROOT/pages/new_project.adoc
@@ -0,0 +1,178 @@
1= Starting a new Embassy project
2
3Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one you’re targeting.
4
5As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.
6
7Run:
8
9[source,bash]
10----
11cargo new stm32g474-example
12cd stm32g474-example
13----
14
15to create an empty rust project:
16
17[source]
18----
19stm32g474-example
20├── Cargo.toml
21└── src
22 └── main.rs
23----
24
25Looking in link:https://github.com/embassy-rs/embassy/tree/main/examples[the Embassy examples], we can see there’s a `stm32g4` folder. Find `src/blinky.rs` and copy its contents into our `src/main.rs`.
26
27== .cargo/config.toml
28
29Currently, we’d need to provide cargo with a target triple every time we run `cargo build` or `cargo run`. Let’s spare ourselves that work by copying `.cargo/config.toml` from `examples/stm32g4` into our project.
30
31[source]
32----
33stm32g474-example
34├── .cargo
35│   └── config.toml
36├── Cargo.toml
37└── src
38 └── main.rs
39----
40
41In addition to a target triple, `.cargo/config.toml` contains a `runner` key which allows us to conveniently run our project on hardware with `cargo run` via probe-rs. In order for this to work, we need to provide the correct chip ID. We can do this by checking `probe-rs chip list`:
42
43[source,bash]
44----
45$ probe-rs chip list | grep -i stm32g474re
46 STM32G474RETx
47----
48
49and copying `STM32G474RETx` into `.cargo/config.toml` as so:
50
51[source,toml]
52----
53[target.'cfg(all(target_arch = "arm", target_os = "none"))']
54# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
55runner = "probe-rs run --chip STM32G474RETx"
56----
57
58== Cargo.toml
59
60Now that cargo knows what target to compile for (and probe-rs knows what chip to run it on), we’re ready to add some dependencies.
61
62Looking in `examples/stm32g4/Cargo.toml`, we can see that the examples require a number of embassy crates. For blinky, we’ll only need three of them: `embassy-stm32`, `embassy-executor` and `embassy-time`.
63
64At the time of writing, the latest version of embassy isn‘t available on crates.io, so we need to install it straight from the git repository. The recommended way of doing so is as follows:
65
66* Copy the required `embassy-*` lines from the example `Cargo.toml`
67* Make any necessary changes to `features`, e.g. requiring the `stm32g474re` feature of `embassy-stm32`
68* Remove the `path = ""` keys in the `embassy-*` entries
69* Create a `[patch.crates-io]` section, with entries for each embassy crate we need. These should all contain identical values: a link to the git repository, and a reference to the commit we’re checking out. Assuming you want the latest commit, you can find it by running `git ls-remote https://github.com/embassy-rs/embassy.git HEAD`
70
71NOTE: When using this method, it’s necessary that the `version` keys in `[dependencies]` match up with the versions defined in each crate’s `Cargo.toml` in the specificed `rev` under `[patch.crates.io]`. This means that when updating, you have to a pick a new revision, change everything in `[patch.crates.io]` to match it, and then correct any versions under `[dependencies]` which have changed. Hopefully this will no longer be necessary once embassy is released on crates.io!
72
73At the time of writing, this method produces the following results:
74
75[source,toml]
76----
77[dependencies]
78embassy-stm32 = {version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"]}
79embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
80embassy-time = { version = "0.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
81
82[patch.crates-io]
83embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
84embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
85embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
86----
87
88There are a few other dependencies we need to build the project, but fortunately they’re much simpler to install. Copy their lines from the example `Cargo.toml` to the the `[dependencies]` section in the new `Cargo.toml`:
89
90[source,toml]
91----
92defmt = "0.3.5"
93defmt-rtt = "0.4.0"
94cortex-m = {version = "0.7.7", features = ["critical-section-single-core"]}
95cortex-m-rt = "0.7.3"
96panic-probe = "0.3.1"
97----
98
99These are the bare minimum dependencies required to run `blinky.rs`, but it’s worth taking a look at the other dependencies specified in the example `Cargo.toml`, and noting what features are required for use with embassy – for example `futures = { version = "0.3.17", default-features = false, features = ["async-await"] }`.
100
101Finally, copy the `[profile.release]` section from the example `Cargo.toml` into ours.
102
103[source,toml]
104----
105[profile.release]
106debug = 2
107----
108
109== rust-toolchain.toml
110
111Before we can build our project, we need to add an additional file to tell cargo to use the nightly toolchain. Copy the `rust-toolchain.toml` from the embassy repo to ours, and trim the list of targets down to only the target triple relevent for our project — in this case, `thumbv7em-none-eabi`:
112
113[source]
114----
115stm32g474-example
116├── .cargo
117│   └── config.toml
118├── Cargo.toml
119├── rust-toolchain.toml
120└── src
121 └── main.rs
122----
123
124[source,toml]
125----
126# Before upgrading check that everything is available on all tier1 targets here:
127# https://rust-lang.github.io/rustup-components-history
128[toolchain]
129channel = "nightly-2023-11-01"
130components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
131targets = ["thumbv7em-none-eabi"]
132----
133
134== build.rs
135
136In order to produce a working binary for our target, cargo requires a custom build script. Copy `build.rs` from the example to our project:
137
138[source]
139----
140stm32g474-example
141├── build.rs
142├── .cargo
143│ └── config.toml
144├── Cargo.toml
145├── rust-toolchain.toml
146└── src
147 └── main.rs
148----
149
150== Building and running
151
152At this point, we‘re finally ready to build and run our project! Connect your board via a debug probe and run:
153
154[source,bash]
155----
156cargo run --release
157----
158
159should result in a blinking LED (if there’s one attached to the pin in `src/main.rs` – change it if not!) and the following output:
160
161[source]
162----
163 Compiling stm32g474-example v0.1.0 (/home/you/stm32g474-example)
164 Finished release [optimized + debuginfo] target(s) in 0.22s
165 Running `probe-rs run --chip STM32G474RETx target/thumbv7em-none-eabi/release/stm32g474-example`
166 Erasing sectors ✔ [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s )
167 Programming pages ✔ [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s
1680.000000 TRACE BDCR configured: 00008200
169└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117
1700.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) }
171└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130
1720.000000 INFO Hello World!
173└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14
1740.000091 INFO high
175└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19
1760.300201 INFO low
177└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
178---- \ No newline at end of file
diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs
index ea84925c4..b4f2cec28 100644
--- a/embassy-hal-internal/src/atomic_ring_buffer.rs
+++ b/embassy-hal-internal/src/atomic_ring_buffer.rs
@@ -1,3 +1,4 @@
1//! Atomic reusable ringbuffer.
1use core::slice; 2use core::slice;
2use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; 3use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
3 4
@@ -14,8 +15,9 @@ use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
14/// One concurrent writer and one concurrent reader are supported, even at 15/// One concurrent writer and one concurrent reader are supported, even at
15/// different execution priorities (like main and irq). 16/// different execution priorities (like main and irq).
16pub struct RingBuffer { 17pub struct RingBuffer {
18 #[doc(hidden)]
17 pub buf: AtomicPtr<u8>, 19 pub buf: AtomicPtr<u8>,
18 pub len: AtomicUsize, 20 len: AtomicUsize,
19 21
20 // start and end wrap at len*2, not at len. 22 // start and end wrap at len*2, not at len.
21 // This allows distinguishing "full" and "empty". 23 // This allows distinguishing "full" and "empty".
@@ -24,11 +26,16 @@ pub struct RingBuffer {
24 // 26 //
25 // This avoids having to consider the ringbuffer "full" at len-1 instead of len. 27 // This avoids having to consider the ringbuffer "full" at len-1 instead of len.
26 // The usual solution is adding a "full" flag, but that can't be made atomic 28 // The usual solution is adding a "full" flag, but that can't be made atomic
29 #[doc(hidden)]
27 pub start: AtomicUsize, 30 pub start: AtomicUsize,
31 #[doc(hidden)]
28 pub end: AtomicUsize, 32 pub end: AtomicUsize,
29} 33}
30 34
35/// A type which can only read from a ring buffer.
31pub struct Reader<'a>(&'a RingBuffer); 36pub struct Reader<'a>(&'a RingBuffer);
37
38/// A type which can only write to a ring buffer.
32pub struct Writer<'a>(&'a RingBuffer); 39pub struct Writer<'a>(&'a RingBuffer);
33 40
34impl RingBuffer { 41impl RingBuffer {
@@ -89,10 +96,12 @@ impl RingBuffer {
89 Writer(self) 96 Writer(self)
90 } 97 }
91 98
99 /// Return length of buffer.
92 pub fn len(&self) -> usize { 100 pub fn len(&self) -> usize {
93 self.len.load(Ordering::Relaxed) 101 self.len.load(Ordering::Relaxed)
94 } 102 }
95 103
104 /// Check if buffer is full.
96 pub fn is_full(&self) -> bool { 105 pub fn is_full(&self) -> bool {
97 let len = self.len.load(Ordering::Relaxed); 106 let len = self.len.load(Ordering::Relaxed);
98 let start = self.start.load(Ordering::Relaxed); 107 let start = self.start.load(Ordering::Relaxed);
@@ -101,6 +110,7 @@ impl RingBuffer {
101 self.wrap(start + len) == end 110 self.wrap(start + len) == end
102 } 111 }
103 112
113 /// Check if buffer is empty.
104 pub fn is_empty(&self) -> bool { 114 pub fn is_empty(&self) -> bool {
105 let start = self.start.load(Ordering::Relaxed); 115 let start = self.start.load(Ordering::Relaxed);
106 let end = self.end.load(Ordering::Relaxed); 116 let end = self.end.load(Ordering::Relaxed);
@@ -238,6 +248,7 @@ impl<'a> Writer<'a> {
238 [(unsafe { buf.add(end) }, n0), (buf, n1)] 248 [(unsafe { buf.add(end) }, n0), (buf, n1)]
239 } 249 }
240 250
251 /// Mark n bytes as written and advance the write index.
241 pub fn push_done(&mut self, n: usize) { 252 pub fn push_done(&mut self, n: usize) {
242 trace!(" ringbuf: push {:?}", n); 253 trace!(" ringbuf: push {:?}", n);
243 let end = self.0.end.load(Ordering::Relaxed); 254 let end = self.0.end.load(Ordering::Relaxed);
@@ -323,6 +334,7 @@ impl<'a> Reader<'a> {
323 (unsafe { buf.add(start) }, n) 334 (unsafe { buf.add(start) }, n)
324 } 335 }
325 336
337 /// Mark n bytes as read and allow advance the read index.
326 pub fn pop_done(&mut self, n: usize) { 338 pub fn pop_done(&mut self, n: usize) {
327 trace!(" ringbuf: pop {:?}", n); 339 trace!(" ringbuf: pop {:?}", n);
328 340
diff --git a/embassy-hal-internal/src/drop.rs b/embassy-hal-internal/src/drop.rs
index 7cd16aaec..8383fcc16 100644
--- a/embassy-hal-internal/src/drop.rs
+++ b/embassy-hal-internal/src/drop.rs
@@ -1,16 +1,20 @@
1//! Types for controlling when drop is invoked.
1use core::mem; 2use core::mem;
2use core::mem::MaybeUninit; 3use core::mem::MaybeUninit;
3 4
4#[must_use = "to delay the drop handler invokation to the end of the scope"] 5/// A type to delay the drop handler invocation.
6#[must_use = "to delay the drop handler invocation to the end of the scope"]
5pub struct OnDrop<F: FnOnce()> { 7pub struct OnDrop<F: FnOnce()> {
6 f: MaybeUninit<F>, 8 f: MaybeUninit<F>,
7} 9}
8 10
9impl<F: FnOnce()> OnDrop<F> { 11impl<F: FnOnce()> OnDrop<F> {
12 /// Create a new instance.
10 pub fn new(f: F) -> Self { 13 pub fn new(f: F) -> Self {
11 Self { f: MaybeUninit::new(f) } 14 Self { f: MaybeUninit::new(f) }
12 } 15 }
13 16
17 /// Prevent drop handler from running.
14 pub fn defuse(self) { 18 pub fn defuse(self) {
15 mem::forget(self) 19 mem::forget(self)
16 } 20 }
@@ -34,6 +38,7 @@ pub struct DropBomb {
34} 38}
35 39
36impl DropBomb { 40impl DropBomb {
41 /// Create a new instance.
37 pub fn new() -> Self { 42 pub fn new() -> Self {
38 Self { _private: () } 43 Self { _private: () }
39 } 44 }
diff --git a/embassy-hal-internal/src/lib.rs b/embassy-hal-internal/src/lib.rs
index f3d59e588..89f20e993 100644
--- a/embassy-hal-internal/src/lib.rs
+++ b/embassy-hal-internal/src/lib.rs
@@ -1,6 +1,7 @@
1#![no_std] 1#![no_std]
2#![allow(clippy::new_without_default)] 2#![allow(clippy::new_without_default)]
3#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
4 5
5// This mod MUST go first, so that the others see its macros. 6// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt; 7pub(crate) mod fmt;
diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs
index 97df38954..07cd89487 100644
--- a/embassy-hal-internal/src/macros.rs
+++ b/embassy-hal-internal/src/macros.rs
@@ -1,3 +1,4 @@
1/// Types for the peripheral singletons.
1#[macro_export] 2#[macro_export]
2macro_rules! peripherals_definition { 3macro_rules! peripherals_definition {
3 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { 4 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
@@ -29,6 +30,7 @@ macro_rules! peripherals_definition {
29 }; 30 };
30} 31}
31 32
33/// Define the peripherals struct.
32#[macro_export] 34#[macro_export]
33macro_rules! peripherals_struct { 35macro_rules! peripherals_struct {
34 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { 36 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
@@ -87,6 +89,7 @@ macro_rules! peripherals_struct {
87 }; 89 };
88} 90}
89 91
92/// Defining peripheral type.
90#[macro_export] 93#[macro_export]
91macro_rules! peripherals { 94macro_rules! peripherals {
92 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { 95 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
@@ -105,6 +108,7 @@ macro_rules! peripherals {
105 }; 108 };
106} 109}
107 110
111/// Convenience converting into reference.
108#[macro_export] 112#[macro_export]
109macro_rules! into_ref { 113macro_rules! into_ref {
110 ($($name:ident),*) => { 114 ($($name:ident),*) => {
@@ -114,6 +118,7 @@ macro_rules! into_ref {
114 } 118 }
115} 119}
116 120
121/// Implement the peripheral trait.
117#[macro_export] 122#[macro_export]
118macro_rules! impl_peripheral { 123macro_rules! impl_peripheral {
119 ($type:ident) => { 124 ($type:ident) => {
diff --git a/embassy-hal-internal/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs
index 38b4c452e..16d49edfb 100644
--- a/embassy-hal-internal/src/peripheral.rs
+++ b/embassy-hal-internal/src/peripheral.rs
@@ -20,6 +20,7 @@ pub struct PeripheralRef<'a, T> {
20} 20}
21 21
22impl<'a, T> PeripheralRef<'a, T> { 22impl<'a, T> PeripheralRef<'a, T> {
23 /// Create a new reference to a peripheral.
23 #[inline] 24 #[inline]
24 pub fn new(inner: T) -> Self { 25 pub fn new(inner: T) -> Self {
25 Self { 26 Self {
diff --git a/embassy-hal-internal/src/ratio.rs b/embassy-hal-internal/src/ratio.rs
index 9a8808a33..91dcfd47c 100644
--- a/embassy-hal-internal/src/ratio.rs
+++ b/embassy-hal-internal/src/ratio.rs
@@ -1,3 +1,4 @@
1//! Types for dealing with rational numbers.
1use core::ops::{Add, Div, Mul}; 2use core::ops::{Add, Div, Mul};
2 3
3use num_traits::{CheckedAdd, CheckedDiv, CheckedMul}; 4use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 3274dafb1..9093ad919 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -97,6 +97,28 @@ mod chip;
97/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) 97/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
98/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to 98/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
99/// prove at compile-time that the right interrupts have been bound. 99/// prove at compile-time that the right interrupts have been bound.
100///
101/// Example of how to bind one interrupt:
102///
103/// ```rust,ignore
104/// use embassy_nrf::{bind_interrupts, spim, peripherals};
105///
106/// bind_interrupts!(struct Irqs {
107/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
108/// });
109/// ```
110///
111/// Example of how to bind multiple interrupts in a single macro invocation:
112///
113/// ```rust,ignore
114/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals};
115///
116/// bind_interrupts!(struct Irqs {
117/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
118/// SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>;
119/// });
120/// ```
121
100// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. 122// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
101#[macro_export] 123#[macro_export]
102macro_rules! bind_interrupts { 124macro_rules! bind_interrupts {
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 66e4cfdcf..5151323a9 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -86,6 +86,17 @@ embassy_hal_internal::interrupt_mod!(
86/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) 86/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
87/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to 87/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
88/// prove at compile-time that the right interrupts have been bound. 88/// prove at compile-time that the right interrupts have been bound.
89///
90/// Example of how to bind one interrupt:
91///
92/// ```rust,ignore
93/// use embassy_rp::{bind_interrupts, usb, peripherals};
94///
95/// bind_interrupts!(struct Irqs {
96/// USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
97/// });
98/// ```
99///
89// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. 100// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
90#[macro_export] 101#[macro_export]
91macro_rules! bind_interrupts { 102macro_rules! bind_interrupts {
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index a8790ce63..f8c4313e5 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -58,7 +58,7 @@ rand_core = "0.6.3"
58sdio-host = "0.5.0" 58sdio-host = "0.5.0"
59embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } 59embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
60critical-section = "1.1" 60critical-section = "1.1"
61stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" } 61stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8f5fcae8c289c1ad481cc3a2bb37db023a61599c" }
62vcell = "0.1.3" 62vcell = "0.1.3"
63bxcan = "0.7.0" 63bxcan = "0.7.0"
64nb = "1.0.0" 64nb = "1.0.0"
@@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
76[build-dependencies] 76[build-dependencies]
77proc-macro2 = "1.0.36" 77proc-macro2 = "1.0.36"
78quote = "1.0.15" 78quote = "1.0.15"
79stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]} 79stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8f5fcae8c289c1ad481cc3a2bb37db023a61599c", default-features = false, features = ["metadata"]}
80 80
81 81
82[features] 82[features]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 7bfd290d2..0eef43ac4 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -930,6 +930,10 @@ fn main() {
930 } else if pin.signal.starts_with("INN") { 930 } else if pin.signal.starts_with("INN") {
931 // TODO handle in the future when embassy supports differential measurements 931 // TODO handle in the future when embassy supports differential measurements
932 None 932 None
933 } else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") {
934 // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
935 let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap();
936 Some(32u8 + signal.parse::<u8>().unwrap())
933 } else if pin.signal.starts_with("IN") { 937 } else if pin.signal.starts_with("IN") {
934 Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) 938 Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
935 } else { 939 } else {
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs
index ad0f13826..fb27bb87b 100644
--- a/embassy-stm32/src/adc/f1.rs
+++ b/embassy-stm32/src/adc/f1.rs
@@ -148,7 +148,7 @@ impl<'d, T: Instance> Adc<'d, T> {
148 reg.set_cont(false); 148 reg.set_cont(false);
149 reg.set_exttrig(true); 149 reg.set_exttrig(true);
150 reg.set_swstart(false); 150 reg.set_swstart(false);
151 reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART); 151 reg.set_extsel(7); // SWSTART
152 }); 152 });
153 153
154 // Configure the channel to sample 154 // Configure the channel to sample
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs
new file mode 100644
index 000000000..6915a8f1c
--- /dev/null
+++ b/embassy-stm32/src/adc/f3_v1_1.rs
@@ -0,0 +1,413 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::task::Poll;
4
5use embassy_futures::yield_now;
6use embassy_hal_internal::into_ref;
7use embassy_time::Instant;
8
9use super::Resolution;
10use crate::adc::{Adc, AdcPin, Instance, SampleTime};
11use crate::interrupt::typelevel::Interrupt;
12use crate::time::Hertz;
13use crate::{interrupt, Peripheral};
14
15const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ;
16
17pub const VDDA_CALIB_MV: u32 = 3300;
18pub const ADC_MAX: u32 = (1 << 12) - 1;
19pub const VREF_INT: u32 = 1230;
20
21pub enum AdcPowerMode {
22 AlwaysOn,
23 DelayOff,
24 IdleOff,
25 DelayIdleOff,
26}
27
28pub enum Prescaler {
29 Div1,
30 Div2,
31 Div3,
32 Div4,
33}
34
35/// Interrupt handler.
36pub struct InterruptHandler<T: Instance> {
37 _phantom: PhantomData<T>,
38}
39
40impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
41 unsafe fn on_interrupt() {
42 if T::regs().sr().read().eoc() {
43 T::regs().cr1().modify(|w| w.set_eocie(false));
44 } else {
45 return;
46 }
47
48 T::state().waker.wake();
49 }
50}
51
52fn update_vref<T: Instance>(op: i8) {
53 static VREF_STATUS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
54
55 if op > 0 {
56 if VREF_STATUS.fetch_add(1, core::sync::atomic::Ordering::SeqCst) == 0 {
57 T::regs().ccr().modify(|w| w.set_tsvrefe(true));
58 }
59 } else {
60 if VREF_STATUS.fetch_sub(1, core::sync::atomic::Ordering::SeqCst) == 1 {
61 T::regs().ccr().modify(|w| w.set_tsvrefe(false));
62 }
63 }
64}
65
66pub struct Vref<T: Instance>(core::marker::PhantomData<T>);
67impl<T: Instance> AdcPin<T> for Vref<T> {}
68impl<T: Instance> super::sealed::AdcPin<T> for Vref<T> {
69 fn channel(&self) -> u8 {
70 17
71 }
72}
73
74impl<T: Instance> Vref<T> {
75 /// The value that vref would be if vdda was at 3000mv
76 pub fn calibrated_value(&self) -> u16 {
77 crate::pac::VREFINTCAL.data().read().value()
78 }
79
80 pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration {
81 let vref_val = adc.read(self).await;
82 Calibration {
83 vref_cal: self.calibrated_value(),
84 vref_val,
85 }
86 }
87}
88
89pub struct Calibration {
90 vref_cal: u16,
91 vref_val: u16,
92}
93
94impl Calibration {
95 /// The millivolts that the calibration value was measured at
96 pub const CALIBRATION_UV: u32 = 3_000_000;
97
98 /// Returns the measured VddA in microvolts (uV)
99 pub fn vdda_uv(&self) -> u32 {
100 (Self::CALIBRATION_UV * self.vref_cal as u32) / self.vref_val as u32
101 }
102
103 /// Returns the measured VddA as an f32
104 pub fn vdda_f32(&self) -> f32 {
105 (Self::CALIBRATION_UV as f32 / 1_000.0) * (self.vref_cal as f32 / self.vref_val as f32)
106 }
107
108 /// Returns a calibrated voltage value as in microvolts (uV)
109 pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 {
110 (self.vdda_uv() / resolution.to_max_count()) * raw as u32
111 }
112
113 /// Returns a calibrated voltage value as an f32
114 pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 {
115 raw as f32 * self.vdda_f32() / resolution.to_max_count() as f32
116 }
117}
118
119impl<T: Instance> Drop for Vref<T> {
120 fn drop(&mut self) {
121 update_vref::<T>(-1)
122 }
123}
124
125pub struct Temperature<T: Instance>(core::marker::PhantomData<T>);
126impl<T: Instance> AdcPin<T> for Temperature<T> {}
127impl<T: Instance> super::sealed::AdcPin<T> for Temperature<T> {
128 fn channel(&self) -> u8 {
129 16
130 }
131}
132
133impl<T: Instance> Drop for Temperature<T> {
134 fn drop(&mut self) {
135 update_vref::<T>(-1)
136 }
137}
138
139impl<'d, T: Instance> Adc<'d, T> {
140 pub fn new(
141 adc: impl Peripheral<P = T> + 'd,
142 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
143 ) -> Self {
144 into_ref!(adc);
145
146 T::enable_and_reset();
147
148 //let r = T::regs();
149 //r.cr2().write(|w| w.set_align(true));
150
151 T::Interrupt::unpend();
152 unsafe {
153 T::Interrupt::enable();
154 }
155
156 Self { adc }
157 }
158
159 fn freq() -> Hertz {
160 let div = T::regs().ccr().read().adcpre() + 1;
161 ADC_FREQ / div as u32
162 }
163
164 pub async fn set_resolution(&mut self, res: Resolution) {
165 let was_on = Self::is_on();
166 if was_on {
167 self.stop_adc().await;
168 }
169
170 T::regs().cr1().modify(|w| w.set_res(res.into()));
171
172 if was_on {
173 self.start_adc().await;
174 }
175 }
176
177 pub fn resolution(&self) -> Resolution {
178 match T::regs().cr1().read().res() {
179 crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit,
180 crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit,
181 crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit,
182 crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit,
183 }
184 }
185
186 pub fn enable_vref(&self) -> Vref<T> {
187 update_vref::<T>(1);
188
189 Vref(core::marker::PhantomData)
190 }
191
192 pub fn enable_temperature(&self) -> Temperature<T> {
193 T::regs().ccr().modify(|w| w.set_tsvrefe(true));
194
195 Temperature::<T>(core::marker::PhantomData)
196 }
197
198 /// Perform a single conversion.
199 async fn convert(&mut self) -> u16 {
200 let was_on = Self::is_on();
201
202 if !was_on {
203 self.start_adc().await;
204 }
205
206 self.wait_sample_ready().await;
207
208 T::regs().sr().write(|_| {});
209 T::regs().cr1().modify(|w| {
210 w.set_eocie(true);
211 w.set_scan(false);
212 });
213 T::regs().cr2().modify(|w| {
214 w.set_swstart(true);
215 w.set_cont(false);
216 }); // swstart cleared by HW
217
218 let res = poll_fn(|cx| {
219 T::state().waker.register(cx.waker());
220
221 if T::regs().sr().read().eoc() {
222 let res = T::regs().dr().read().rdata();
223 Poll::Ready(res)
224 } else {
225 Poll::Pending
226 }
227 })
228 .await;
229
230 if !was_on {
231 self.stop_adc().await;
232 }
233
234 res
235 }
236
237 #[inline(always)]
238 fn is_on() -> bool {
239 T::regs().sr().read().adons() || T::regs().cr2().read().adon()
240 }
241
242 pub async fn start_adc(&self) {
243 //defmt::trace!("Turn ADC on");
244 T::regs().cr2().modify(|w| w.set_adon(true));
245 //defmt::trace!("Waiting for ADC to turn on");
246
247 let mut t = Instant::now();
248
249 while !T::regs().sr().read().adons() {
250 yield_now().await;
251 if t.elapsed() > embassy_time::Duration::from_millis(1000) {
252 t = Instant::now();
253 //defmt::trace!("ADC still not on");
254 }
255 }
256
257 //defmt::trace!("ADC on");
258 }
259
260 pub async fn stop_adc(&self) {
261 if T::regs().cr2().read().adon() {
262 //defmt::trace!("ADC should be on, wait for it to start");
263 while !T::regs().csr().read().adons1() {
264 yield_now().await;
265 }
266 }
267
268 //defmt::trace!("Turn ADC off");
269
270 T::regs().cr2().modify(|w| w.set_adon(false));
271
272 //defmt::trace!("Waiting for ADC to turn off");
273
274 while T::regs().csr().read().adons1() {
275 yield_now().await;
276 }
277 }
278
279 pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
280 self.set_sample_sequence(&[pin.channel()]).await;
281 self.convert().await
282 }
283
284 async fn wait_sample_ready(&self) {
285 //trace!("Waiting for sample channel to be ready");
286 while T::regs().sr().read().rcnr() {
287 yield_now().await;
288 }
289 }
290
291 pub async fn set_sample_time(&mut self, pin: &mut impl AdcPin<T>, sample_time: SampleTime) {
292 if Self::get_channel_sample_time(pin.channel()) != sample_time {
293 self.stop_adc().await;
294 unsafe {
295 Self::set_channel_sample_time(pin.channel(), sample_time);
296 }
297 self.start_adc().await;
298 }
299 }
300
301 pub fn get_sample_time(&self, pin: &impl AdcPin<T>) -> SampleTime {
302 Self::get_channel_sample_time(pin.channel())
303 }
304
305 /// Sets the channel sample time
306 ///
307 /// ## SAFETY:
308 /// - ADON == 0 i.e ADC must not be enabled when this is called.
309 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
310 let sample_time = sample_time.into();
311
312 match ch {
313 0..=9 => T::regs().smpr3().modify(|reg| reg.set_smp(ch as _, sample_time)),
314 10..=19 => T::regs()
315 .smpr2()
316 .modify(|reg| reg.set_smp(ch as usize - 10, sample_time)),
317 20..=29 => T::regs()
318 .smpr1()
319 .modify(|reg| reg.set_smp(ch as usize - 20, sample_time)),
320 30..=31 => T::regs()
321 .smpr0()
322 .modify(|reg| reg.set_smp(ch as usize - 30, sample_time)),
323 _ => panic!("Invalid channel to sample"),
324 }
325 }
326
327 fn get_channel_sample_time(ch: u8) -> SampleTime {
328 match ch {
329 0..=9 => T::regs().smpr3().read().smp(ch as _),
330 10..=19 => T::regs().smpr2().read().smp(ch as usize - 10),
331 20..=29 => T::regs().smpr1().read().smp(ch as usize - 20),
332 30..=31 => T::regs().smpr0().read().smp(ch as usize - 30),
333 _ => panic!("Invalid channel to sample"),
334 }
335 .into()
336 }
337
338 /// Sets the sequence to sample the ADC. Must be less than 28 elements.
339 async fn set_sample_sequence(&self, sequence: &[u8]) {
340 assert!(sequence.len() <= 28);
341 let mut iter = sequence.iter();
342 T::regs().sqr1().modify(|w| w.set_l((sequence.len() - 1) as _));
343 for (idx, ch) in iter.by_ref().take(6).enumerate() {
344 T::regs().sqr5().modify(|w| w.set_sq(idx, *ch));
345 }
346 for (idx, ch) in iter.by_ref().take(6).enumerate() {
347 T::regs().sqr4().modify(|w| w.set_sq(idx, *ch));
348 }
349 for (idx, ch) in iter.by_ref().take(6).enumerate() {
350 T::regs().sqr3().modify(|w| w.set_sq(idx, *ch));
351 }
352 for (idx, ch) in iter.by_ref().take(6).enumerate() {
353 T::regs().sqr2().modify(|w| w.set_sq(idx, *ch));
354 }
355 for (idx, ch) in iter.by_ref().take(4).enumerate() {
356 T::regs().sqr1().modify(|w| w.set_sq(idx, *ch));
357 }
358 }
359
360 fn get_res_clks(res: Resolution) -> u32 {
361 match res {
362 Resolution::TwelveBit => 12,
363 Resolution::TenBit => 11,
364 Resolution::EightBit => 9,
365 Resolution::SixBit => 7,
366 }
367 }
368
369 fn get_sample_time_clks(sample_time: SampleTime) -> u32 {
370 match sample_time {
371 SampleTime::Cycles4 => 4,
372 SampleTime::Cycles9 => 9,
373 SampleTime::Cycles16 => 16,
374 SampleTime::Cycles24 => 24,
375 SampleTime::Cycles48 => 48,
376 SampleTime::Cycles96 => 96,
377 SampleTime::Cycles192 => 192,
378 SampleTime::Cycles384 => 384,
379 }
380 }
381
382 pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
383 let res_clks = Self::get_res_clks(self.resolution());
384 let us_clks = us * Self::freq().0 / 1_000_000;
385 let clks = us_clks.saturating_sub(res_clks);
386 match clks {
387 0..=4 => SampleTime::Cycles4,
388 5..=9 => SampleTime::Cycles9,
389 10..=16 => SampleTime::Cycles16,
390 17..=24 => SampleTime::Cycles24,
391 25..=48 => SampleTime::Cycles48,
392 49..=96 => SampleTime::Cycles96,
393 97..=192 => SampleTime::Cycles192,
394 193.. => SampleTime::Cycles384,
395 }
396 }
397
398 pub fn us_for_cfg(&self, res: Resolution, sample_time: SampleTime) -> u32 {
399 let res_clks = Self::get_res_clks(res);
400 let sample_clks = Self::get_sample_time_clks(sample_time);
401 (res_clks + sample_clks) * 1_000_000 / Self::freq().0
402 }
403}
404
405impl<'d, T: Instance> Drop for Adc<'d, T> {
406 fn drop(&mut self) {
407 while !T::regs().sr().read().adons() {}
408
409 T::regs().cr2().modify(|w| w.set_adon(false));
410
411 T::disable();
412 }
413}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 3e2980bf4..dbe53c807 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -3,6 +3,7 @@
3#[cfg(not(adc_f3_v2))] 3#[cfg(not(adc_f3_v2))]
4#[cfg_attr(adc_f1, path = "f1.rs")] 4#[cfg_attr(adc_f1, path = "f1.rs")]
5#[cfg_attr(adc_f3, path = "f3.rs")] 5#[cfg_attr(adc_f3, path = "f3.rs")]
6#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
6#[cfg_attr(adc_v1, path = "v1.rs")] 7#[cfg_attr(adc_v1, path = "v1.rs")]
7#[cfg_attr(adc_v2, path = "v2.rs")] 8#[cfg_attr(adc_v2, path = "v2.rs")]
8#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] 9#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
@@ -26,20 +27,20 @@ use crate::peripherals;
26pub struct Adc<'d, T: Instance> { 27pub struct Adc<'d, T: Instance> {
27 #[allow(unused)] 28 #[allow(unused)]
28 adc: crate::PeripheralRef<'d, T>, 29 adc: crate::PeripheralRef<'d, T>,
29 #[cfg(not(adc_f3_v2))] 30 #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))]
30 sample_time: SampleTime, 31 sample_time: SampleTime,
31} 32}
32 33
33pub(crate) mod sealed { 34pub(crate) mod sealed {
34 #[cfg(any(adc_f1, adc_f3, adc_v1))] 35 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
35 use embassy_sync::waitqueue::AtomicWaker; 36 use embassy_sync::waitqueue::AtomicWaker;
36 37
37 #[cfg(any(adc_f1, adc_f3, adc_v1))] 38 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
38 pub struct State { 39 pub struct State {
39 pub waker: AtomicWaker, 40 pub waker: AtomicWaker,
40 } 41 }
41 42
42 #[cfg(any(adc_f1, adc_f3, adc_v1))] 43 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
43 impl State { 44 impl State {
44 pub const fn new() -> Self { 45 pub const fn new() -> Self {
45 Self { 46 Self {
@@ -54,11 +55,11 @@ pub(crate) mod sealed {
54 55
55 pub trait Instance: InterruptableInstance { 56 pub trait Instance: InterruptableInstance {
56 fn regs() -> crate::pac::adc::Adc; 57 fn regs() -> crate::pac::adc::Adc;
57 #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] 58 #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
58 fn common_regs() -> crate::pac::adccommon::AdcCommon; 59 fn common_regs() -> crate::pac::adccommon::AdcCommon;
59 #[cfg(adc_f3)] 60 #[cfg(adc_f3)]
60 fn frequency() -> crate::time::Hertz; 61 fn frequency() -> crate::time::Hertz;
61 #[cfg(any(adc_f1, adc_f3, adc_v1))] 62 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
62 fn state() -> &'static State; 63 fn state() -> &'static State;
63 } 64 }
64 65
@@ -74,9 +75,9 @@ pub(crate) mod sealed {
74 } 75 }
75} 76}
76 77
77#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0)))] 78#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
78pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} 79pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
79#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0))] 80#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
80pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} 81pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
81 82
82pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} 83pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
@@ -89,7 +90,7 @@ foreach_adc!(
89 crate::pac::$inst 90 crate::pac::$inst
90 } 91 }
91 92
92 #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] 93 #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
93 fn common_regs() -> crate::pac::adccommon::AdcCommon { 94 fn common_regs() -> crate::pac::adccommon::AdcCommon {
94 return crate::pac::$common_inst 95 return crate::pac::$common_inst
95 } 96 }
@@ -99,7 +100,7 @@ foreach_adc!(
99 unsafe { crate::rcc::get_freqs() }.$clock.unwrap() 100 unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
100 } 101 }
101 102
102 #[cfg(any(adc_f1, adc_f3, adc_v1))] 103 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
103 fn state() -> &'static sealed::State { 104 fn state() -> &'static sealed::State {
104 static STATE: sealed::State = sealed::State::new(); 105 static STATE: sealed::State = sealed::State::new();
105 &STATE 106 &STATE
diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs
index 5668137b5..383980b5a 100644
--- a/embassy-stm32/src/adc/resolution.rs
+++ b/embassy-stm32/src/adc/resolution.rs
@@ -1,5 +1,6 @@
1#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] 1#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
2#[derive(Clone, Copy, Debug, Eq, PartialEq)] 2#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3pub enum Resolution { 4pub enum Resolution {
4 TwelveBit, 5 TwelveBit,
5 TenBit, 6 TenBit,
@@ -9,6 +10,7 @@ pub enum Resolution {
9 10
10#[cfg(adc_v4)] 11#[cfg(adc_v4)]
11#[derive(Clone, Copy, Debug, Eq, PartialEq)] 12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub enum Resolution { 14pub enum Resolution {
13 SixteenBit, 15 SixteenBit,
14 FourteenBit, 16 FourteenBit,
@@ -19,7 +21,7 @@ pub enum Resolution {
19 21
20impl Default for Resolution { 22impl Default for Resolution {
21 fn default() -> Self { 23 fn default() -> Self {
22 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] 24 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
23 { 25 {
24 Self::TwelveBit 26 Self::TwelveBit
25 } 27 }
@@ -40,7 +42,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
40 Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, 42 Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
41 Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, 43 Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
42 Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, 44 Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
43 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] 45 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
44 Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, 46 Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
45 } 47 }
46 } 48 }
@@ -56,7 +58,7 @@ impl Resolution {
56 Resolution::TwelveBit => (1 << 12) - 1, 58 Resolution::TwelveBit => (1 << 12) - 1,
57 Resolution::TenBit => (1 << 10) - 1, 59 Resolution::TenBit => (1 << 10) - 1,
58 Resolution::EightBit => (1 << 8) - 1, 60 Resolution::EightBit => (1 << 8) - 1,
59 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] 61 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
60 Resolution::SixBit => (1 << 6) - 1, 62 Resolution::SixBit => (1 << 6) - 1,
61 } 63 }
62 } 64 }
diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs
index 6a6619299..5a06f1a5a 100644
--- a/embassy-stm32/src/adc/sample_time.rs
+++ b/embassy-stm32/src/adc/sample_time.rs
@@ -3,6 +3,7 @@ macro_rules! impl_sample_time {
3 ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { 3 ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
4 #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] 4 #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
5 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] 5 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
6 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
6 pub enum SampleTime { 7 pub enum SampleTime {
7 $( 8 $(
8 #[doc = concat!($doc, " ADC clock cycles.")] 9 #[doc = concat!($doc, " ADC clock cycles.")]
@@ -18,6 +19,14 @@ macro_rules! impl_sample_time {
18 } 19 }
19 } 20 }
20 21
22 impl From<crate::pac::adc::vals::SampleTime> for SampleTime {
23 fn from(sample_time: crate::pac::adc::vals::SampleTime) -> SampleTime {
24 match sample_time {
25 $(crate::pac::adc::vals::SampleTime::$pac_variant => SampleTime::$variant),*
26 }
27 }
28 }
29
21 impl Default for SampleTime { 30 impl Default for SampleTime {
22 fn default() -> Self { 31 fn default() -> Self {
23 Self::$default 32 Self::$default
@@ -121,3 +130,19 @@ impl_sample_time!(
121 ("601.5", Cycles601_5, CYCLES601_5) 130 ("601.5", Cycles601_5, CYCLES601_5)
122 ) 131 )
123); 132);
133
134#[cfg(any(adc_f3_v1_1))]
135impl_sample_time!(
136 "4",
137 Cycles4,
138 (
139 ("4", Cycles4, CYCLES4),
140 ("9", Cycles9, CYCLES9),
141 ("16", Cycles16, CYCLES16),
142 ("24", Cycles24, CYCLES24),
143 ("48", Cycles48, CYCLES48),
144 ("96", Cycles96, CYCLES96),
145 ("192", Cycles192, CYCLES192),
146 ("384", Cycles384, CYCLES384)
147 )
148);
diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs
index 4ff5aa0de..425f9ac2e 100644
--- a/embassy-stm32/src/can/mod.rs
+++ b/embassy-stm32/src/can/mod.rs
@@ -1,6 +1,6 @@
1#![macro_use] 1#![macro_use]
2 2
3#[cfg_attr(can_bxcan, path = "bxcan.rs")] 3#[cfg_attr(can_bxcan, path = "bxcan.rs")]
4#[cfg_attr(can_fdcan, path = "fdcan.rs")] 4#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")]
5mod _version; 5mod _version;
6pub use _version::*; 6pub use _version::*;
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 13e189da6..5d9b4e6a0 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -90,6 +90,29 @@ pub use crate::_generated::interrupt;
90/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) 90/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
91/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to 91/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
92/// prove at compile-time that the right interrupts have been bound. 92/// prove at compile-time that the right interrupts have been bound.
93///
94/// Example of how to bind one interrupt:
95///
96/// ```rust,ignore
97/// use embassy_stm32::{bind_interrupts, usb_otg, peripherals};
98///
99/// bind_interrupts!(struct Irqs {
100/// OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
101/// });
102/// ```
103///
104/// Example of how to bind multiple interrupts, and multiple handlers to each interrupt, in a single macro invocation:
105///
106/// ```rust,ignore
107/// use embassy_stm32::{bind_interrupts, i2c, peripherals};
108///
109/// bind_interrupts!(struct Irqs {
110/// I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
111/// I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,
112/// i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;
113/// });
114/// ```
115
93// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. 116// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
94#[macro_export] 117#[macro_export]
95macro_rules! bind_interrupts { 118macro_rules! bind_interrupts {
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index 48b27255d..fca364c21 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -308,6 +308,7 @@ pub(crate) unsafe fn init(config: Config) {
308 sys: sys_clk, 308 sys: sys_clk,
309 hclk1: ahb_freq, 309 hclk1: ahb_freq,
310 hclk2: ahb_freq, 310 hclk2: ahb_freq,
311 hclk3: ahb_freq,
311 pclk1: apb1_freq, 312 pclk1: apb1_freq,
312 pclk1_tim: apb1_tim_freq, 313 pclk1_tim: apb1_tim_freq,
313 pclk2: apb2_freq, 314 pclk2: apb2_freq,
@@ -315,6 +316,8 @@ pub(crate) unsafe fn init(config: Config) {
315 adc: adc12_ck, 316 adc: adc12_ck,
316 adc34: adc345_ck, 317 adc34: adc345_ck,
317 pll1_p: None, 318 pll1_p: None,
319 pll1_q: None, // TODO
320 hse: None, // TODO
318 rtc, 321 rtc,
319 }); 322 });
320} 323}
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 2e144dc77..dc829a9ad 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -95,6 +95,7 @@ pub struct Clocks {
95 rcc_h7rm0433, 95 rcc_h7rm0433,
96 rcc_h7ab, 96 rcc_h7ab,
97 rcc_u5, 97 rcc_u5,
98 rcc_g4,
98 rcc_wb, 99 rcc_wb,
99 rcc_wl5, 100 rcc_wl5,
100 rcc_wle 101 rcc_wle
@@ -119,7 +120,7 @@ pub struct Clocks {
119 120
120 #[cfg(any(stm32g4, rcc_l4))] 121 #[cfg(any(stm32g4, rcc_l4))]
121 pub pll1_p: Option<Hertz>, 122 pub pll1_p: Option<Hertz>,
122 #[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))] 123 #[cfg(any(stm32h5, stm32h7, stm32f2, stm32f4, stm32f7, rcc_l4, stm32g4))]
123 pub pll1_q: Option<Hertz>, 124 pub pll1_q: Option<Hertz>,
124 #[cfg(any(stm32h5, stm32h7))] 125 #[cfg(any(stm32h5, stm32h7))]
125 pub pll2_p: Option<Hertz>, 126 pub pll2_p: Option<Hertz>,
@@ -167,7 +168,7 @@ pub struct Clocks {
167 168
168 #[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))] 169 #[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
169 pub lse: Option<Hertz>, 170 pub lse: Option<Hertz>,
170 #[cfg(any(stm32h5, stm32h7))] 171 #[cfg(any(stm32h5, stm32h7, stm32g4))]
171 pub hse: Option<Hertz>, 172 pub hse: Option<Hertz>,
172 173
173 #[cfg(stm32h5)] 174 #[cfg(stm32h5)]
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 9269ddd88..295dc9198 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -263,7 +263,7 @@ impl<'d, T: Instance> Driver<'d, T> {
263 263
264 let regs = T::regs(); 264 let regs = T::regs();
265 265
266 #[cfg(stm32l5)] 266 #[cfg(any(stm32l5, stm32wb))]
267 crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); 267 crate::pac::PWR.cr2().modify(|w| w.set_usv(true));
268 268
269 #[cfg(pwr_h5)] 269 #[cfg(pwr_h5)]
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index c37d89a40..4f53e84f0 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -10,14 +10,14 @@ stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
10stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] 10stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]
11stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"] 11stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
12stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] 12stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
13stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"] 13stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"]
14stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin", "sdmmc"] 14stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"]
15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] 15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"] 16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"] 17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"]
18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] 18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] 19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"]
20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"] 20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng"]
21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"] 21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]
22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] 22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] 23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
@@ -41,7 +41,7 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"]
41mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] 41mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"]
42embassy-stm32-wpan = [] 42embassy-stm32-wpan = []
43not-gpdma = [] 43not-gpdma = []
44dac-adc-pin = [] 44dac = []
45 45
46cm0 = ["portable-atomic/unsafe-assume-single-core"] 46cm0 = ["portable-atomic/unsafe-assume-single-core"]
47 47
@@ -84,7 +84,12 @@ required-features = [ "can",]
84[[bin]] 84[[bin]]
85name = "dac" 85name = "dac"
86path = "src/bin/dac.rs" 86path = "src/bin/dac.rs"
87required-features = [ "dac-adc-pin",] 87required-features = [ "dac",]
88
89[[bin]]
90name = "dac_l1"
91path = "src/bin/dac_l1.rs"
92required-features = [ "stm32l152re",]
88 93
89[[bin]] 94[[bin]]
90name = "eth" 95name = "eth"
diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs
index cf43106b3..9d64742df 100644
--- a/tests/stm32/src/bin/dac.rs
+++ b/tests/stm32/src/bin/dac.rs
@@ -1,7 +1,7 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4// required-features: dac-adc-pin 4// required-features: dac
5 5
6#[path = "../common.rs"] 6#[path = "../common.rs"]
7mod common; 7mod common;
@@ -22,12 +22,13 @@ async fn main(_spawner: Spawner) {
22 // Initialize the board and obtain a Peripherals instance 22 // Initialize the board and obtain a Peripherals instance
23 let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); 23 let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
24 24
25 let adc = peri!(p, ADC);
25 let dac = peri!(p, DAC); 26 let dac = peri!(p, DAC);
26 let dac_pin = peri!(p, DAC_PIN); 27 let dac_pin = peri!(p, DAC_PIN);
27 let mut adc_pin = unsafe { core::ptr::read(&dac_pin) }; 28 let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
28 29
29 let mut dac = DacCh1::new(dac, NoDma, dac_pin); 30 let mut dac = DacCh1::new(dac, NoDma, dac_pin);
30 let mut adc = Adc::new(p.ADC1, &mut Delay); 31 let mut adc = Adc::new(adc, &mut Delay);
31 32
32 #[cfg(feature = "stm32h755zi")] 33 #[cfg(feature = "stm32h755zi")]
33 let normalization_factor = 256; 34 let normalization_factor = 256;
diff --git a/tests/stm32/src/bin/dac_l1.rs b/tests/stm32/src/bin/dac_l1.rs
new file mode 100644
index 000000000..f8b00aaef
--- /dev/null
+++ b/tests/stm32/src/bin/dac_l1.rs
@@ -0,0 +1,86 @@
1#![no_std]
2#![no_main]
3
4// required-features: stm32l152re
5
6#[path = "../common.rs"]
7mod common;
8use core::f32::consts::PI;
9
10use common::*;
11use defmt::assert;
12use embassy_executor::Spawner;
13use embassy_stm32::adc::Adc;
14use embassy_stm32::dac::{DacCh1, Value};
15use embassy_stm32::dma::NoDma;
16use embassy_stm32::{bind_interrupts, peripherals};
17use embassy_time::Timer;
18use micromath::F32Ext;
19use {defmt_rtt as _, panic_probe as _};
20
21bind_interrupts!(struct Irqs {
22 ADC1 => embassy_stm32::adc::InterruptHandler<peripherals::ADC>;
23});
24
25#[embassy_executor::main]
26async fn main(_spawner: Spawner) {
27 // Initialize the board and obtain a Peripherals instance
28 let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
29
30 let adc = peri!(p, ADC);
31 let dac = peri!(p, DAC);
32 let dac_pin = peri!(p, DAC_PIN);
33 let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
34
35 let mut dac = DacCh1::new(dac, NoDma, dac_pin);
36 let mut adc = Adc::new(adc, Irqs);
37
38 #[cfg(feature = "stm32h755zi")]
39 let normalization_factor = 256;
40 #[cfg(any(
41 feature = "stm32f429zi",
42 feature = "stm32f446re",
43 feature = "stm32g071rb",
44 feature = "stm32l152re",
45 ))]
46 let normalization_factor: i32 = 16;
47
48 dac.set(Value::Bit8(0));
49 // Now wait a little to obtain a stable value
50 Timer::after_millis(30).await;
51 let offset = adc.read(&mut adc_pin).await;
52
53 for v in 0..=255 {
54 // First set the DAC output value
55 let dac_output_val = to_sine_wave(v);
56 dac.set(Value::Bit8(dac_output_val));
57
58 // Now wait a little to obtain a stable value
59 Timer::after_millis(30).await;
60
61 // Need to steal the peripherals here because PA4 is obviously in use already
62 let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4).await;
63 // Calibrate and normalize the measurement to get close to the dac_output_val
64 let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;
65
66 info!("value / measured: {} / {}", dac_output_val, measured_normalized);
67
68 // The deviations are quite enormous but that does not matter since this is only a quick test
69 assert!((dac_output_val as i16 - measured_normalized).abs() < 15);
70 }
71
72 info!("Test OK");
73 cortex_m::asm::bkpt();
74}
75
76fn to_sine_wave(v: u8) -> u8 {
77 if v >= 128 {
78 // top half
79 let r = PI * ((v - 128) as f32 / 128.0);
80 (r.sin() * 128.0 + 127.0) as u8
81 } else {
82 // bottom half
83 let r = PI + PI * (v as f32 / 128.0);
84 (r.sin() * 128.0 + 127.0) as u8
85 }
86}
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs
index 155e1d9df..313380b35 100644
--- a/tests/stm32/src/common.rs
+++ b/tests/stm32/src/common.rs
@@ -101,14 +101,14 @@ define_peris!(
101define_peris!( 101define_peris!(
102 UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, 102 UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
103 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, 103 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
104 DAC = DAC1, DAC_PIN = PA4, 104 ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,
105 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, 105 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
106); 106);
107#[cfg(feature = "stm32f429zi")] 107#[cfg(feature = "stm32f429zi")]
108define_peris!( 108define_peris!(
109 UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1, 109 UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,
110 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2, 110 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
111 DAC = DAC, DAC_PIN = PA4, 111 ADC = ADC1, DAC = DAC, DAC_PIN = PA4,
112 CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1, 112 CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1,
113 @irq UART = {USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;}, 113 @irq UART = {USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;},
114); 114);
@@ -116,7 +116,7 @@ define_peris!(
116define_peris!( 116define_peris!(
117 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5, 117 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5,
118 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2, 118 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
119 DAC = DAC, DAC_PIN = PA4, 119 ADC = ADC1, DAC = DAC, DAC_PIN = PA4,
120 CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12, 120 CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12,
121 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, 121 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
122); 122);
@@ -130,7 +130,7 @@ define_peris!(
130define_peris!( 130define_peris!(
131 UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, 131 UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,
132 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, 132 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,
133 DAC = DAC1, DAC_PIN = PA4, 133 ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,
134 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, 134 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
135); 135);
136#[cfg(feature = "stm32h7a3zi")] 136#[cfg(feature = "stm32h7a3zi")]
@@ -191,6 +191,7 @@ define_peris!(
191define_peris!( 191define_peris!(
192 UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, 192 UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,
193 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, 193 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
194 ADC = ADC, DAC = DAC, DAC_PIN = PA4,
194 @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;}, 195 @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;},
195); 196);
196#[cfg(feature = "stm32l552ze")] 197#[cfg(feature = "stm32l552ze")]