aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci.sh1
-rw-r--r--docs/pages/faq.adoc7
-rw-r--r--docs/pages/sharing_peripherals.adoc2
-rw-r--r--embassy-boot/src/boot_loader.rs11
-rw-r--r--embassy-net-wiznet/src/chip/mod.rs7
-rw-r--r--embassy-net-wiznet/src/chip/w5100s.rs3
-rw-r--r--embassy-net-wiznet/src/chip/w5500.rs3
-rw-r--r--embassy-net-wiznet/src/device.rs62
-rw-r--r--embassy-net-wiznet/src/lib.rs10
-rw-r--r--embassy-net/src/tcp.rs6
-rw-r--r--embassy-nrf/CHANGELOG.md1
-rw-r--r--embassy-nrf/src/pwm.rs58
-rw-r--r--embassy-nrf/src/radio/ble.rs9
-rw-r--r--embassy-rp/src/uart/buffered.rs18
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs29
-rw-r--r--embassy-stm32/src/adc/g4.rs2
-rw-r--r--embassy-stm32/src/adc/mod.rs2
-rw-r--r--embassy-stm32/src/adc/ringbuffered_v2.rs437
-rw-r--r--embassy-stm32/src/adc/v2.rs5
-rw-r--r--embassy-stm32/src/adc/v3.rs238
-rw-r--r--embassy-stm32/src/adc/v4.rs205
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs1
-rw-r--r--embassy-stm32/src/i2c/v2.rs5
-rw-r--r--embassy-stm32/src/ltdc.rs510
-rw-r--r--embassy-stm32/src/tsc/mod.rs271
-rw-r--r--embassy-stm32/src/usart/mod.rs45
-rw-r--r--examples/rp/Cargo.toml3
-rw-r--r--examples/rp/src/bin/assign_resources.rs79
-rw-r--r--examples/rp/src/bin/ethernet_w5500_multisocket.rs3
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_client.rs3
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_server.rs3
-rw-r--r--examples/rp/src/bin/ethernet_w5500_udp.rs3
-rw-r--r--examples/rp/src/bin/sharing.rs150
-rw-r--r--examples/stm32f4/src/bin/adc.rs8
-rw-r--r--examples/stm32f4/src/bin/adc_dma.rs83
-rw-r--r--examples/stm32f4/src/bin/eth_w5500.rs4
-rw-r--r--examples/stm32f7/src/bin/adc.rs4
-rw-r--r--examples/stm32g0/src/bin/adc.rs4
-rw-r--r--examples/stm32g0/src/bin/adc_dma.rs44
-rw-r--r--examples/stm32g0/src/bin/adc_oversampling.rs43
-rw-r--r--examples/stm32g4/src/bin/adc.rs2
-rw-r--r--examples/stm32h7/Cargo.toml1
-rw-r--r--examples/stm32h7/src/bin/adc.rs4
-rw-r--r--examples/stm32h7/src/bin/adc_dma.rs76
-rw-r--r--examples/stm32h7/src/bin/sai.rs186
-rw-r--r--examples/stm32h7/src/bin/spi_bdma.rs12
-rw-r--r--examples/stm32h735/.cargo/config.toml8
-rw-r--r--examples/stm32h735/Cargo.toml61
-rw-r--r--examples/stm32h735/build.rs35
-rw-r--r--examples/stm32h735/memory.x5
-rw-r--r--examples/stm32h735/src/bin/ferris.bmpbin0 -> 6794 bytes
-rw-r--r--examples/stm32h735/src/bin/ltdc.rs467
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/dds.rs116
-rw-r--r--examples/stm32l4/src/bin/adc.rs2
-rw-r--r--examples/stm32u0/src/bin/adc.rs2
-rw-r--r--examples/stm32u5/src/bin/tsc.rs10
-rw-r--r--tests/rp/src/bin/ethernet_w5100s_perf.rs3
-rw-r--r--tests/stm32/src/bin/dac.rs4
60 files changed, 3136 insertions, 246 deletions
diff --git a/ci.sh b/ci.sh
index 04877dcd3..8ac9e1ccd 100755
--- a/ci.sh
+++ b/ci.sh
@@ -201,6 +201,7 @@ cargo batch \
201 --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ 201 --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \
202 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \ 202 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \
203 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ 203 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \
204 --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h735 \
204 --- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7rs \ 205 --- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7rs \
205 --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ 206 --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
206 --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ 207 --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc
index a2f56a539..4ab04e2a1 100644
--- a/docs/pages/faq.adoc
+++ b/docs/pages/faq.adoc
@@ -352,8 +352,13 @@ There are two main ways to handle concurrency in Embassy:
352 352
353In general, either of these approaches will work. The main differences of these approaches are: 353In general, either of these approaches will work. The main differences of these approaches are:
354 354
355When using **separate tasks**, each task needs its own RAM allocation, so there's a little overhead for each task, so one task that does three things will likely be a little bit smaller than three tasks that do one thing (not a lot, probably a couple dozen bytes). In contrast, with **multiple futures in one task**, you don't need multiple task allocations, and it will generally be easier to share data, or use borrowed resources, inside of a single task. 355When using **separate tasks**, each task needs its own RAM allocation, so there's a little overhead for each task, so one task that does three things will likely be a little bit smaller than three tasks that do one thing (not a lot, probably a couple dozen bytes). In contrast, with **multiple futures in one task**, you don't need multiple task allocations, and it will generally be easier to share data, or use borrowed resources, inside of a single task.
356An example showcasing some methods for sharing things between tasks link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here].
356 357
357But when it comes to "waking" tasks, for example when a data transfer is complete or a button is pressed, it's faster to wake a dedicated task, because that task does not need to check which future is actually ready. `join` and `select` must check ALL of the futures they are managing to see which one (or which ones) are ready to do more work. This is because all Rust executors (like Embassy or Tokio) only have the ability to wake tasks, not specific futures. This means you will use slightly less CPU time juggling futures when using dedicated tasks. 358But when it comes to "waking" tasks, for example when a data transfer is complete or a button is pressed, it's faster to wake a dedicated task, because that task does not need to check which future is actually ready. `join` and `select` must check ALL of the futures they are managing to see which one (or which ones) are ready to do more work. This is because all Rust executors (like Embassy or Tokio) only have the ability to wake tasks, not specific futures. This means you will use slightly less CPU time juggling futures when using dedicated tasks.
358 359
359Practically, there's not a LOT of difference either way - so go with what makes it easier for you and your code first, but there will be some details that are slightly different in each case. 360Practically, there's not a LOT of difference either way - so go with what makes it easier for you and your code first, but there will be some details that are slightly different in each case.
361
362== splitting peripherals resources between tasks
363
364There are two ways to split resources between tasks, either manually assigned or by a convenient macro. See link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/assign_resources.rs[this example] \ No newline at end of file
diff --git a/docs/pages/sharing_peripherals.adoc b/docs/pages/sharing_peripherals.adoc
index 6bcd56b01..ebd899c4e 100644
--- a/docs/pages/sharing_peripherals.adoc
+++ b/docs/pages/sharing_peripherals.adoc
@@ -126,3 +126,5 @@ async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>,
126 126
127This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure 127This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure
128that the operation is completed before continuing to do other work in your task. 128that the operation is completed before continuing to do other work in your task.
129
130An example showcasing more methods for sharing link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here]. \ No newline at end of file
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs
index a38558056..789fa34c1 100644
--- a/embassy-boot/src/boot_loader.rs
+++ b/embassy-boot/src/boot_loader.rs
@@ -235,12 +235,15 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
235 /// | DFU | 3 | 4 | 5 | 6 | 3 | 235 /// | DFU | 3 | 4 | 5 | 6 | 3 |
236 /// 236 ///
237 pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> { 237 pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
238 const {
239 assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0);
240 assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0);
241 assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0);
242 assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0);
243 }
244
238 // Ensure we have enough progress pages to store copy progress 245 // Ensure we have enough progress pages to store copy progress
239 assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); 246 assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32);
240 assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32);
241 assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32);
242 assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32);
243 assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32);
244 assert!(aligned_buf.len() >= STATE::WRITE_SIZE); 247 assert!(aligned_buf.len() >= STATE::WRITE_SIZE);
245 assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); 248 assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);
246 assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); 249 assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);
diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs
index e1f963d95..2e7a9ed6c 100644
--- a/embassy-net-wiznet/src/chip/mod.rs
+++ b/embassy-net-wiznet/src/chip/mod.rs
@@ -8,10 +8,17 @@ pub use w5100s::W5100S;
8pub(crate) trait SealedChip { 8pub(crate) trait SealedChip {
9 type Address; 9 type Address;
10 10
11 /// The version of the chip as reported by the VERSIONR register.
12 /// This is used to verify that the chip is supported by the driver,
13 /// and that SPI communication is working.
14 const CHIP_VERSION: u8;
15
11 const COMMON_MODE: Self::Address; 16 const COMMON_MODE: Self::Address;
12 const COMMON_MAC: Self::Address; 17 const COMMON_MAC: Self::Address;
13 const COMMON_SOCKET_INTR: Self::Address; 18 const COMMON_SOCKET_INTR: Self::Address;
14 const COMMON_PHY_CFG: Self::Address; 19 const COMMON_PHY_CFG: Self::Address;
20 const COMMON_VERSION: Self::Address;
21
15 const SOCKET_MODE: Self::Address; 22 const SOCKET_MODE: Self::Address;
16 const SOCKET_COMMAND: Self::Address; 23 const SOCKET_COMMAND: Self::Address;
17 const SOCKET_RXBUF_SIZE: Self::Address; 24 const SOCKET_RXBUF_SIZE: Self::Address;
diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs
index 23ce3ed83..4c4b7ab16 100644
--- a/embassy-net-wiznet/src/chip/w5100s.rs
+++ b/embassy-net-wiznet/src/chip/w5100s.rs
@@ -11,10 +11,13 @@ impl super::Chip for W5100S {}
11impl super::SealedChip for W5100S { 11impl super::SealedChip for W5100S {
12 type Address = u16; 12 type Address = u16;
13 13
14 const CHIP_VERSION: u8 = 0x51;
15
14 const COMMON_MODE: Self::Address = 0x00; 16 const COMMON_MODE: Self::Address = 0x00;
15 const COMMON_MAC: Self::Address = 0x09; 17 const COMMON_MAC: Self::Address = 0x09;
16 const COMMON_SOCKET_INTR: Self::Address = 0x16; 18 const COMMON_SOCKET_INTR: Self::Address = 0x16;
17 const COMMON_PHY_CFG: Self::Address = 0x3c; 19 const COMMON_PHY_CFG: Self::Address = 0x3c;
20 const COMMON_VERSION: Self::Address = 0x80;
18 21
19 const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00; 22 const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00;
20 const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01; 23 const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01;
diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs
index 12e610ea2..5cfcb94e4 100644
--- a/embassy-net-wiznet/src/chip/w5500.rs
+++ b/embassy-net-wiznet/src/chip/w5500.rs
@@ -15,10 +15,13 @@ impl super::Chip for W5500 {}
15impl super::SealedChip for W5500 { 15impl super::SealedChip for W5500 {
16 type Address = (RegisterBlock, u16); 16 type Address = (RegisterBlock, u16);
17 17
18 const CHIP_VERSION: u8 = 0x04;
19
18 const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); 20 const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00);
19 const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09); 21 const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09);
20 const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18); 22 const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18);
21 const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E); 23 const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E);
24 const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x39);
22 25
23 const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00); 26 const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00);
24 const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01); 27 const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01);
diff --git a/embassy-net-wiznet/src/device.rs b/embassy-net-wiznet/src/device.rs
index 43f9512a3..d2b6bb0c3 100644
--- a/embassy-net-wiznet/src/device.rs
+++ b/embassy-net-wiznet/src/device.rs
@@ -24,9 +24,57 @@ pub(crate) struct WiznetDevice<C, SPI> {
24 _phantom: PhantomData<C>, 24 _phantom: PhantomData<C>,
25} 25}
26 26
27/// Error type when initializing a new Wiznet device
28pub enum InitError<SE> {
29 /// Error occurred when sending or receiving SPI data
30 SpiError(SE),
31 /// The chip returned a version that isn't expected or supported
32 InvalidChipVersion {
33 /// The version that is supported
34 expected: u8,
35 /// The version that was returned by the chip
36 actual: u8,
37 },
38}
39
40impl<SE> From<SE> for InitError<SE> {
41 fn from(e: SE) -> Self {
42 InitError::SpiError(e)
43 }
44}
45
46impl<SE> core::fmt::Debug for InitError<SE>
47where
48 SE: core::fmt::Debug,
49{
50 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51 match self {
52 InitError::SpiError(e) => write!(f, "SpiError({:?})", e),
53 InitError::InvalidChipVersion { expected, actual } => {
54 write!(f, "InvalidChipVersion {{ expected: {}, actual: {} }}", expected, actual)
55 }
56 }
57 }
58}
59
60#[cfg(feature = "defmt")]
61impl<SE> defmt::Format for InitError<SE>
62where
63 SE: defmt::Format,
64{
65 fn format(&self, f: defmt::Formatter) {
66 match self {
67 InitError::SpiError(e) => defmt::write!(f, "SpiError({})", e),
68 InitError::InvalidChipVersion { expected, actual } => {
69 defmt::write!(f, "InvalidChipVersion {{ expected: {}, actual: {} }}", expected, actual)
70 }
71 }
72 }
73}
74
27impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> { 75impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> {
28 /// Create and initialize the driver 76 /// Create and initialize the driver
29 pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<Self, SPI::Error> { 77 pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<Self, InitError<SPI::Error>> {
30 let mut this = Self { 78 let mut this = Self {
31 spi, 79 spi,
32 _phantom: PhantomData, 80 _phantom: PhantomData,
@@ -35,6 +83,18 @@ impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> {
35 // Reset device 83 // Reset device
36 this.bus_write(C::COMMON_MODE, &[0x80]).await?; 84 this.bus_write(C::COMMON_MODE, &[0x80]).await?;
37 85
86 // Check the version of the chip
87 let mut version = [0];
88 this.bus_read(C::COMMON_VERSION, &mut version).await?;
89 if version[0] != C::CHIP_VERSION {
90 #[cfg(feature = "defmt")]
91 defmt::error!("invalid chip version: {} (expected {})", version[0], C::CHIP_VERSION);
92 return Err(InitError::InvalidChipVersion {
93 actual: version[0],
94 expected: C::CHIP_VERSION,
95 });
96 }
97
38 // Enable interrupt pin 98 // Enable interrupt pin
39 this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?; 99 this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?;
40 // Enable receive interrupt 100 // Enable receive interrupt
diff --git a/embassy-net-wiznet/src/lib.rs b/embassy-net-wiznet/src/lib.rs
index 90102196a..3fbd4c741 100644
--- a/embassy-net-wiznet/src/lib.rs
+++ b/embassy-net-wiznet/src/lib.rs
@@ -15,6 +15,7 @@ use embedded_hal_async::digital::Wait;
15use embedded_hal_async::spi::SpiDevice; 15use embedded_hal_async::spi::SpiDevice;
16 16
17use crate::chip::Chip; 17use crate::chip::Chip;
18pub use crate::device::InitError;
18use crate::device::WiznetDevice; 19use crate::device::WiznetDevice;
19 20
20// If you change this update the docs of State 21// If you change this update the docs of State
@@ -105,7 +106,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi
105 spi_dev: SPI, 106 spi_dev: SPI,
106 int: INT, 107 int: INT,
107 mut reset: RST, 108 mut reset: RST,
108) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { 109) -> Result<(Device<'a>, Runner<'a, C, SPI, INT, RST>), InitError<SPI::Error>> {
109 // Reset the chip. 110 // Reset the chip.
110 reset.set_low().ok(); 111 reset.set_low().ok();
111 // Ensure the reset is registered. 112 // Ensure the reset is registered.
@@ -116,10 +117,11 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi
116 // Slowest is w5100s which is 100ms, so let's just wait that. 117 // Slowest is w5100s which is 100ms, so let's just wait that.
117 Timer::after_millis(100).await; 118 Timer::after_millis(100).await;
118 119
119 let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); 120 let mac = WiznetDevice::new(spi_dev, mac_addr).await?;
120 121
121 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); 122 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
122 ( 123
124 Ok((
123 device, 125 device,
124 Runner { 126 Runner {
125 ch: runner, 127 ch: runner,
@@ -127,5 +129,5 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi
127 int, 129 int,
128 _reset: reset, 130 _reset: reset,
129 }, 131 },
130 ) 132 ))
131} 133}
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index 4d6dc92de..74eff9dae 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -603,7 +603,7 @@ mod embedded_io_impls {
603 603
604 impl<'d> embedded_io_async::WriteReady for TcpSocket<'d> { 604 impl<'d> embedded_io_async::WriteReady for TcpSocket<'d> {
605 fn write_ready(&mut self) -> Result<bool, Self::Error> { 605 fn write_ready(&mut self) -> Result<bool, Self::Error> {
606 Ok(self.io.with(|s, _| s.may_send())) 606 Ok(self.io.with(|s, _| s.can_send()))
607 } 607 }
608 } 608 }
609 609
@@ -619,7 +619,7 @@ mod embedded_io_impls {
619 619
620 impl<'d> embedded_io_async::ReadReady for TcpReader<'d> { 620 impl<'d> embedded_io_async::ReadReady for TcpReader<'d> {
621 fn read_ready(&mut self) -> Result<bool, Self::Error> { 621 fn read_ready(&mut self) -> Result<bool, Self::Error> {
622 Ok(self.io.with(|s, _| s.can_recv())) 622 Ok(self.io.with(|s, _| s.can_recv() || !s.may_recv()))
623 } 623 }
624 } 624 }
625 625
@@ -639,7 +639,7 @@ mod embedded_io_impls {
639 639
640 impl<'d> embedded_io_async::WriteReady for TcpWriter<'d> { 640 impl<'d> embedded_io_async::WriteReady for TcpWriter<'d> {
641 fn write_ready(&mut self) -> Result<bool, Self::Error> { 641 fn write_ready(&mut self) -> Result<bool, Self::Error> {
642 Ok(self.io.with(|s, _| s.may_send())) 642 Ok(self.io.with(|s, _| s.can_send()))
643 } 643 }
644 } 644 }
645} 645}
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 773a1a108..6f07a8c6d 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
24- spi: Add support for configuring bit order for bus 24- spi: Add support for configuring bit order for bus
25- pwm: Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method 25- pwm: Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method
26- gpio: Drop GPIO Pin generics (API break) 26- gpio: Drop GPIO Pin generics (API break)
27- pwm: Allow specifying OutputDrive for PWM channels
27 28
28## 0.1.0 - 2024-01-12 29## 0.1.0 - 2024-01-12
29 30
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index 12057f7dd..8e8f166d7 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -6,7 +6,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
6 6
7use embassy_hal_internal::{into_ref, PeripheralRef}; 7use embassy_hal_internal::{into_ref, PeripheralRef};
8 8
9use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin as _}; 9use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _};
10use crate::ppi::{Event, Task}; 10use crate::ppi::{Event, Task};
11use crate::util::slice_in_ram_or; 11use crate::util::slice_in_ram_or;
12use crate::{interrupt, pac, Peripheral}; 12use crate::{interrupt, pac, Peripheral};
@@ -128,19 +128,23 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
128 128
129 if let Some(pin) = &ch0 { 129 if let Some(pin) = &ch0 {
130 pin.set_low(); 130 pin.set_low();
131 pin.conf().write(|w| w.dir().output()); 131 pin.conf()
132 .write(|w| w.dir().output().drive().variant(convert_drive(config.ch0_drive)));
132 } 133 }
133 if let Some(pin) = &ch1 { 134 if let Some(pin) = &ch1 {
134 pin.set_low(); 135 pin.set_low();
135 pin.conf().write(|w| w.dir().output()); 136 pin.conf()
137 .write(|w| w.dir().output().drive().variant(convert_drive(config.ch1_drive)));
136 } 138 }
137 if let Some(pin) = &ch2 { 139 if let Some(pin) = &ch2 {
138 pin.set_low(); 140 pin.set_low();
139 pin.conf().write(|w| w.dir().output()); 141 pin.conf()
142 .write(|w| w.dir().output().drive().variant(convert_drive(config.ch2_drive)));
140 } 143 }
141 if let Some(pin) = &ch3 { 144 if let Some(pin) = &ch3 {
142 pin.set_low(); 145 pin.set_low();
143 pin.conf().write(|w| w.dir().output()); 146 pin.conf()
147 .write(|w| w.dir().output().drive().variant(convert_drive(config.ch3_drive)));
144 } 148 }
145 149
146 r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) }); 150 r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) });
@@ -319,6 +323,14 @@ pub struct Config {
319 pub prescaler: Prescaler, 323 pub prescaler: Prescaler,
320 /// How a sequence is read from RAM and is spread to the compare register 324 /// How a sequence is read from RAM and is spread to the compare register
321 pub sequence_load: SequenceLoad, 325 pub sequence_load: SequenceLoad,
326 /// Drive strength for the channel 0 line.
327 pub ch0_drive: OutputDrive,
328 /// Drive strength for the channel 1 line.
329 pub ch1_drive: OutputDrive,
330 /// Drive strength for the channel 2 line.
331 pub ch2_drive: OutputDrive,
332 /// Drive strength for the channel 3 line.
333 pub ch3_drive: OutputDrive,
322} 334}
323 335
324impl Default for Config { 336impl Default for Config {
@@ -328,6 +340,10 @@ impl Default for Config {
328 max_duty: 1000, 340 max_duty: 1000,
329 prescaler: Prescaler::Div16, 341 prescaler: Prescaler::Div16,
330 sequence_load: SequenceLoad::Common, 342 sequence_load: SequenceLoad::Common,
343 ch0_drive: OutputDrive::Standard,
344 ch1_drive: OutputDrive::Standard,
345 ch2_drive: OutputDrive::Standard,
346 ch3_drive: OutputDrive::Standard,
331 } 347 }
332 } 348 }
333} 349}
@@ -815,6 +831,38 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
815 let max_duty = self.max_duty() as u32; 831 let max_duty = self.max_duty() as u32;
816 clk / max_duty 832 clk / max_duty
817 } 833 }
834
835 /// Sets the PWM-Channel0 output drive strength
836 #[inline(always)]
837 pub fn set_ch0_drive(&self, drive: OutputDrive) {
838 if let Some(pin) = &self.ch0 {
839 pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive)));
840 }
841 }
842
843 /// Sets the PWM-Channel1 output drive strength
844 #[inline(always)]
845 pub fn set_ch1_drive(&self, drive: OutputDrive) {
846 if let Some(pin) = &self.ch1 {
847 pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive)));
848 }
849 }
850
851 /// Sets the PWM-Channel2 output drive strength
852 #[inline(always)]
853 pub fn set_ch2_drive(&self, drive: OutputDrive) {
854 if let Some(pin) = &self.ch2 {
855 pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive)));
856 }
857 }
858
859 /// Sets the PWM-Channel3 output drive strength
860 #[inline(always)]
861 pub fn set_ch3_drive(&self, drive: OutputDrive) {
862 if let Some(pin) = &self.ch3 {
863 pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive)));
864 }
865 }
818} 866}
819 867
820impl<'a, T: Instance> Drop for SimplePwm<'a, T> { 868impl<'a, T: Instance> Drop for SimplePwm<'a, T> {
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 93003fb19..4f0b0641f 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -335,8 +335,6 @@ impl<'d, T: Instance> Radio<'d, T> {
335 } 335 }
336 336
337 async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { 337 async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
338 //self.trace_state();
339
340 let r = T::regs(); 338 let r = T::regs();
341 let s = T::state(); 339 let s = T::state();
342 340
@@ -347,12 +345,10 @@ impl<'d, T: Instance> Radio<'d, T> {
347 trace!("radio drop: stopping"); 345 trace!("radio drop: stopping");
348 346
349 r.intenclr.write(|w| w.end().clear()); 347 r.intenclr.write(|w| w.end().clear());
350 r.events_end.reset();
351 348
352 r.tasks_stop.write(|w| unsafe { w.bits(1) }); 349 r.tasks_stop.write(|w| unsafe { w.bits(1) });
353 350
354 // The docs don't explicitly mention any event to acknowledge the stop task 351 r.events_end.reset();
355 while r.events_end.read().bits() == 0 {}
356 352
357 trace!("radio drop: stopped"); 353 trace!("radio drop: stopped");
358 }); 354 });
@@ -368,7 +364,6 @@ impl<'d, T: Instance> Radio<'d, T> {
368 364
369 // Trigger the transmission 365 // Trigger the transmission
370 trigger(); 366 trigger();
371 // self.trace_state();
372 367
373 // On poll check if interrupt happen 368 // On poll check if interrupt happen
374 poll_fn(|cx| { 369 poll_fn(|cx| {
@@ -382,7 +377,7 @@ impl<'d, T: Instance> Radio<'d, T> {
382 .await; 377 .await;
383 378
384 compiler_fence(Ordering::SeqCst); 379 compiler_fence(Ordering::SeqCst);
385 r.events_disabled.reset(); // ACK 380 r.events_end.reset(); // ACK
386 381
387 // Everthing ends fine, so it disable the drop 382 // Everthing ends fine, so it disable the drop
388 drop.defuse(); 383 drop.defuse();
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs
index c94164040..cfbd82ccf 100644
--- a/embassy-rp/src/uart/buffered.rs
+++ b/embassy-rp/src/uart/buffered.rs
@@ -315,6 +315,12 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
315 w.set_rtim(true); 315 w.set_rtim(true);
316 }); 316 });
317 } 317 }
318
319 /// we are ready to read if there is data in the buffer
320 fn read_ready() -> Result<bool, Error> {
321 let state = T::buffered_state();
322 Ok(!state.rx_buf.is_empty())
323 }
318} 324}
319 325
320impl<'d, T: Instance> BufferedUartTx<'d, T> { 326impl<'d, T: Instance> BufferedUartTx<'d, T> {
@@ -621,6 +627,18 @@ impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> {
621 } 627 }
622} 628}
623 629
630impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUart<'d, T> {
631 fn read_ready(&mut self) -> Result<bool, Self::Error> {
632 BufferedUartRx::<'d, T>::read_ready()
633 }
634}
635
636impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUartRx<'d, T> {
637 fn read_ready(&mut self) -> Result<bool, Self::Error> {
638 Self::read_ready()
639 }
640}
641
624impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> { 642impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> {
625 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 643 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
626 BufferedUartRx::<'d, T>::fill_buf().await 644 BufferedUartRx::<'d, T>::fill_buf().await
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 77b517dba..523bacb11 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -72,7 +72,7 @@ rand_core = "0.6.3"
72sdio-host = "0.5.0" 72sdio-host = "0.5.0"
73critical-section = "1.1" 73critical-section = "1.1"
74#stm32-metapac = { version = "15" } 74#stm32-metapac = { version = "15" }
75stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a8ab0a3421ed0ca4b282f54028a0a2decacd8631" } 75stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645" }
76 76
77vcell = "0.1.3" 77vcell = "0.1.3"
78nb = "1.0.0" 78nb = "1.0.0"
@@ -97,7 +97,7 @@ proc-macro2 = "1.0.36"
97quote = "1.0.15" 97quote = "1.0.15"
98 98
99#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 99#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
100stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a8ab0a3421ed0ca4b282f54028a0a2decacd8631", default-features = false, features = ["metadata"] } 100stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645", default-features = false, features = ["metadata"] }
101 101
102[features] 102[features]
103default = ["rt"] 103default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 6aedcc228..8457e3a13 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -475,13 +475,21 @@ fn main() {
475 } 475 }
476 476
477 impl<'a> ClockGen<'a> { 477 impl<'a> ClockGen<'a> {
478 fn gen_clock(&mut self, name: &str) -> TokenStream { 478 fn gen_clock(&mut self, peripheral: &str, name: &str) -> TokenStream {
479 let clock_name = format_ident!("{}", name.to_ascii_lowercase()); 479 let clock_name = format_ident!("{}", name.to_ascii_lowercase());
480 self.clock_names.insert(name.to_ascii_lowercase()); 480 self.clock_names.insert(name.to_ascii_lowercase());
481 quote!( unsafe { crate::rcc::get_freqs().#clock_name.unwrap() } ) 481 quote!(unsafe {
482 unwrap!(
483 crate::rcc::get_freqs().#clock_name,
484 "peripheral '{}' is configured to use the '{}' clock, which is not running. \
485 Either enable it in 'config.rcc' or change 'config.rcc.mux' to use another clock",
486 #peripheral,
487 #name
488 )
489 })
482 } 490 }
483 491
484 fn gen_mux(&mut self, mux: &PeripheralRccRegister) -> TokenStream { 492 fn gen_mux(&mut self, peripheral: &str, mux: &PeripheralRccRegister) -> TokenStream {
485 let ir = &self.rcc_registers.ir; 493 let ir = &self.rcc_registers.ir;
486 let fieldset_name = mux.register.to_ascii_lowercase(); 494 let fieldset_name = mux.register.to_ascii_lowercase();
487 let fieldset = ir 495 let fieldset = ir
@@ -506,9 +514,9 @@ fn main() {
506 for v in enumm.variants.iter().filter(|v| v.name != "DISABLE") { 514 for v in enumm.variants.iter().filter(|v| v.name != "DISABLE") {
507 let variant_name = format_ident!("{}", v.name); 515 let variant_name = format_ident!("{}", v.name);
508 let expr = if let Some(mux) = self.chained_muxes.get(&v.name) { 516 let expr = if let Some(mux) = self.chained_muxes.get(&v.name) {
509 self.gen_mux(mux) 517 self.gen_mux(peripheral, mux)
510 } else { 518 } else {
511 self.gen_clock(v.name) 519 self.gen_clock(peripheral, v.name)
512 }; 520 };
513 match_arms.extend(quote! { 521 match_arms.extend(quote! {
514 crate::pac::rcc::vals::#enum_name::#variant_name => #expr, 522 crate::pac::rcc::vals::#enum_name::#variant_name => #expr,
@@ -586,8 +594,8 @@ fn main() {
586 }; 594 };
587 595
588 let clock_frequency = match &rcc.kernel_clock { 596 let clock_frequency = match &rcc.kernel_clock {
589 PeripheralRccKernelClock::Mux(mux) => clock_gen.gen_mux(mux), 597 PeripheralRccKernelClock::Mux(mux) => clock_gen.gen_mux(p.name, mux),
590 PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(clock), 598 PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock),
591 }; 599 };
592 600
593 // A refcount leak can result if the same field is shared by peripherals with different stop modes 601 // A refcount leak can result if the same field is shared by peripherals with different stop modes
@@ -764,7 +772,7 @@ fn main() {
764 772
765 #[rustfmt::skip] 773 #[rustfmt::skip]
766 let signals: HashMap<_, _> = [ 774 let signals: HashMap<_, _> = [
767 // (kind, signal) => trait 775 // (kind, signal) => trait
768 (("ucpd", "CC1"), quote!(crate::ucpd::Cc1Pin)), 776 (("ucpd", "CC1"), quote!(crate::ucpd::Cc1Pin)),
769 (("ucpd", "CC2"), quote!(crate::ucpd::Cc2Pin)), 777 (("ucpd", "CC2"), quote!(crate::ucpd::Cc2Pin)),
770 (("usart", "TX"), quote!(crate::usart::TxPin)), 778 (("usart", "TX"), quote!(crate::usart::TxPin)),
@@ -1178,6 +1186,11 @@ fn main() {
1178 1186
1179 let signals: HashMap<_, _> = [ 1187 let signals: HashMap<_, _> = [
1180 // (kind, signal) => trait 1188 // (kind, signal) => trait
1189 (("adc", "ADC"), quote!(crate::adc::RxDma)),
1190 (("adc", "ADC1"), quote!(crate::adc::RxDma)),
1191 (("adc", "ADC2"), quote!(crate::adc::RxDma)),
1192 (("adc", "ADC3"), quote!(crate::adc::RxDma)),
1193 (("adc", "ADC4"), quote!(crate::adc::RxDma)),
1181 (("ucpd", "RX"), quote!(crate::ucpd::RxDma)), 1194 (("ucpd", "RX"), quote!(crate::ucpd::RxDma)),
1182 (("ucpd", "TX"), quote!(crate::ucpd::TxDma)), 1195 (("ucpd", "TX"), quote!(crate::ucpd::TxDma)),
1183 (("usart", "RX"), quote!(crate::usart::RxDma)), 1196 (("usart", "RX"), quote!(crate::usart::RxDma)),
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 6569361fe..c1e584f59 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -258,7 +258,7 @@ impl<'d, T: Instance> Adc<'d, T> {
258 } 258 }
259 259
260 /// Read an ADC pin. 260 /// Read an ADC pin.
261 pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { 261 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
262 channel.setup(); 262 channel.setup();
263 263
264 self.read_channel(channel.channel()) 264 self.read_channel(channel.channel())
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 0c22a7dae..7a7d7cd8e 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -28,6 +28,8 @@ pub use crate::pac::adc::vals::Res as Resolution;
28pub use crate::pac::adc::vals::SampleTime; 28pub use crate::pac::adc::vals::SampleTime;
29use crate::peripherals; 29use crate::peripherals;
30 30
31dma_trait!(RxDma, Instance);
32
31/// Analog to Digital driver. 33/// Analog to Digital driver.
32pub struct Adc<'d, T: Instance> { 34pub struct Adc<'d, T: Instance> {
33 #[allow(unused)] 35 #[allow(unused)]
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
new file mode 100644
index 000000000..3b064044e
--- /dev/null
+++ b/embassy-stm32/src/adc/ringbuffered_v2.rs
@@ -0,0 +1,437 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::sync::atomic::{compiler_fence, Ordering};
4
5use embassy_hal_internal::{into_ref, Peripheral};
6use stm32_metapac::adc::vals::SampleTime;
7
8use crate::adc::{Adc, AdcChannel, Instance, RxDma};
9use crate::dma::ringbuffer::OverrunError;
10use crate::dma::{Priority, ReadableRingBuffer, TransferOptions};
11use crate::pac::adc::vals;
12use crate::rcc;
13
14fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
15 r.sr().modify(|regs| {
16 regs.set_eoc(false);
17 regs.set_ovr(false);
18 });
19}
20
21#[derive(PartialOrd, PartialEq, Debug, Clone, Copy)]
22pub enum Sequence {
23 One,
24 Two,
25 Three,
26 Four,
27 Five,
28 Six,
29 Seven,
30 Eight,
31 Nine,
32 Ten,
33 Eleven,
34 Twelve,
35 Thirteen,
36 Fourteen,
37 Fifteen,
38 Sixteen,
39}
40
41impl From<Sequence> for u8 {
42 fn from(s: Sequence) -> u8 {
43 match s {
44 Sequence::One => 0,
45 Sequence::Two => 1,
46 Sequence::Three => 2,
47 Sequence::Four => 3,
48 Sequence::Five => 4,
49 Sequence::Six => 5,
50 Sequence::Seven => 6,
51 Sequence::Eight => 7,
52 Sequence::Nine => 8,
53 Sequence::Ten => 9,
54 Sequence::Eleven => 10,
55 Sequence::Twelve => 11,
56 Sequence::Thirteen => 12,
57 Sequence::Fourteen => 13,
58 Sequence::Fifteen => 14,
59 Sequence::Sixteen => 15,
60 }
61 }
62}
63
64impl From<u8> for Sequence {
65 fn from(val: u8) -> Self {
66 match val {
67 0 => Sequence::One,
68 1 => Sequence::Two,
69 2 => Sequence::Three,
70 3 => Sequence::Four,
71 4 => Sequence::Five,
72 5 => Sequence::Six,
73 6 => Sequence::Seven,
74 7 => Sequence::Eight,
75 8 => Sequence::Nine,
76 9 => Sequence::Ten,
77 10 => Sequence::Eleven,
78 11 => Sequence::Twelve,
79 12 => Sequence::Thirteen,
80 13 => Sequence::Fourteen,
81 14 => Sequence::Fifteen,
82 15 => Sequence::Sixteen,
83 _ => panic!("Invalid sequence number"),
84 }
85 }
86}
87
88pub struct RingBufferedAdc<'d, T: Instance> {
89 _phantom: PhantomData<T>,
90 ring_buf: ReadableRingBuffer<'d, u16>,
91}
92
93impl<'d, T: Instance> Adc<'d, T> {
94 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
95 ///
96 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
97 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
98 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
99 ///
100 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
101 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
102 ///
103 /// [`read`]: #method.read
104 pub fn into_ring_buffered(
105 self,
106 dma: impl Peripheral<P = impl RxDma<T>> + 'd,
107 dma_buf: &'d mut [u16],
108 ) -> RingBufferedAdc<'d, T> {
109 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
110 into_ref!(dma);
111
112 let opts: crate::dma::TransferOptions = TransferOptions {
113 half_transfer_ir: true,
114 priority: Priority::VeryHigh,
115 ..Default::default()
116 };
117
118 // Safety: we forget the struct before this function returns.
119 let rx_src = T::regs().dr().as_ptr() as *mut u16;
120 let request = dma.request();
121
122 let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) };
123
124 // Don't disable the clock
125 mem::forget(self);
126
127 RingBufferedAdc {
128 _phantom: PhantomData,
129 ring_buf,
130 }
131 }
132}
133
134impl<'d, T: Instance> RingBufferedAdc<'d, T> {
135 fn is_on() -> bool {
136 T::regs().cr2().read().adon()
137 }
138
139 fn stop_adc() {
140 T::regs().cr2().modify(|reg| {
141 reg.set_adon(false);
142 });
143 }
144
145 fn start_adc() {
146 T::regs().cr2().modify(|reg| {
147 reg.set_adon(true);
148 });
149 }
150
151 /// Sets the channel sample time
152 ///
153 /// ## SAFETY:
154 /// - ADON == 0 i.e ADC must not be enabled when this is called.
155 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
156 if ch <= 9 {
157 T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
158 } else {
159 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
160 }
161 }
162
163 fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) {
164 let ch_iter = ch.iter();
165 for idx in ch_iter {
166 unsafe {
167 Self::set_channel_sample_time(*idx, sample_time);
168 }
169 }
170 }
171
172 pub fn set_sample_sequence(
173 &mut self,
174 sequence: Sequence,
175 channel: &mut impl AdcChannel<T>,
176 sample_time: SampleTime,
177 ) {
178 let was_on = Self::is_on();
179 if !was_on {
180 Self::start_adc();
181 }
182
183 // Check the sequence is long enough
184 T::regs().sqr1().modify(|r| {
185 let prev: Sequence = r.l().into();
186 if prev < sequence {
187 let new_l: Sequence = sequence;
188 trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8);
189 r.set_l(sequence.into())
190 } else {
191 r.set_l(prev.into())
192 }
193 });
194
195 // Set this GPIO as an analog input.
196 channel.setup();
197
198 // Set the channel in the right sequence field.
199 match sequence {
200 Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
201 Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
202 Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())),
203 Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())),
204 Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())),
205 Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())),
206 Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(6, channel.channel())),
207 Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(7, channel.channel())),
208 Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(8, channel.channel())),
209 Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(9, channel.channel())),
210 Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(10, channel.channel())),
211 Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(11, channel.channel())),
212 Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(12, channel.channel())),
213 Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(13, channel.channel())),
214 Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(14, channel.channel())),
215 Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(15, channel.channel())),
216 };
217
218 if !was_on {
219 Self::stop_adc();
220 }
221
222 self.set_channels_sample_time(&[channel.channel()], sample_time);
223
224 Self::start_adc();
225 }
226
227 /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
228 pub fn start(&mut self) -> Result<(), OverrunError> {
229 self.ring_buf.clear();
230
231 self.setup_adc();
232
233 Ok(())
234 }
235
236 fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> {
237 self.teardown_adc();
238 Err(err)
239 }
240
241 /// Stops DMA transfer.
242 /// It does not turn off ADC.
243 /// Calling `start` restarts continuous DMA transfer.
244 ///
245 /// [`start`]: #method.start
246 pub fn teardown_adc(&mut self) {
247 // Stop the DMA transfer
248 self.ring_buf.request_stop();
249
250 let r = T::regs();
251
252 // Stop ADC
253 r.cr2().modify(|reg| {
254 // Stop ADC
255 reg.set_swstart(false);
256 // Stop DMA
257 reg.set_dma(false);
258 });
259
260 r.cr1().modify(|w| {
261 // Disable interrupt for end of conversion
262 w.set_eocie(false);
263 // Disable interrupt for overrun
264 w.set_ovrie(false);
265 });
266
267 clear_interrupt_flags(r);
268
269 compiler_fence(Ordering::SeqCst);
270 }
271
272 fn setup_adc(&mut self) {
273 compiler_fence(Ordering::SeqCst);
274
275 self.ring_buf.start();
276
277 let r = T::regs();
278
279 // Enable ADC
280 let was_on = Self::is_on();
281 if !was_on {
282 r.cr2().modify(|reg| {
283 reg.set_adon(false);
284 reg.set_swstart(false);
285 });
286 }
287
288 // Clear all interrupts
289 r.sr().modify(|regs| {
290 regs.set_eoc(false);
291 regs.set_ovr(false);
292 regs.set_strt(false);
293 });
294
295 r.cr1().modify(|w| {
296 // Enable interrupt for end of conversion
297 w.set_eocie(true);
298 // Enable interrupt for overrun
299 w.set_ovrie(true);
300 // Scanning converisons of multiple channels
301 w.set_scan(true);
302 // Continuous conversion mode
303 w.set_discen(false);
304 });
305
306 r.cr2().modify(|w| {
307 // Enable DMA mode
308 w.set_dma(true);
309 // Enable continuous conversions
310 w.set_cont(true);
311 // DMA requests are issues as long as DMA=1 and data are converted.
312 w.set_dds(vals::Dds::CONTINUOUS);
313 // EOC flag is set at the end of each conversion.
314 w.set_eocs(vals::Eocs::EACHCONVERSION);
315 });
316
317 // Begin ADC conversions
318 T::regs().cr2().modify(|reg| {
319 reg.set_adon(true);
320 reg.set_swstart(true);
321 });
322
323 super::blocking_delay_us(3);
324 }
325
326 /// Read bytes that are readily available in the ring buffer.
327 /// If no bytes are currently available in the buffer the call waits until the some
328 /// bytes are available (at least one byte and at most half the buffer size)
329 ///
330 /// Background receive is started if `start()` has not been previously called.
331 ///
332 /// Receive in the background is terminated if an error is returned.
333 /// It must then manually be started again by calling `start()` or by re-calling `read()`.
334 pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
335 let r = T::regs();
336
337 // Start background receive if it was not already started
338 if !r.cr2().read().dma() {
339 self.start()?;
340 }
341
342 // Clear overrun flag if set.
343 if r.sr().read().ovr() {
344 return self.stop(OverrunError);
345 }
346
347 loop {
348 match self.ring_buf.read(buf) {
349 Ok((0, _)) => {}
350 Ok((len, _)) => {
351 return Ok(len);
352 }
353 Err(_) => {
354 return self.stop(OverrunError);
355 }
356 }
357 }
358 }
359
360 /// Reads measurements from the DMA ring buffer.
361 ///
362 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
363 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
364 ///
365 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`.
366 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
367 /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`.
368 ///
369 /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again.
370 ///
371 /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval.
372 /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected.
373 ///
374 /// Example:
375 /// ```rust,ignore
376 /// const DMA_BUF_LEN: usize = 120;
377 /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
378 /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf);
379 ///
380 /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
381 /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112);
382 /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112);
383 ///
384 /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
385 /// loop {
386 /// match adc.read(&mut measurements).await {
387 /// Ok(_) => {
388 /// defmt::info!("adc1: {}", measurements);
389 /// // Only needed to manually control sample rate.
390 /// adc.teardown_adc();
391 /// }
392 /// Err(e) => {
393 /// defmt::warn!("Error: {:?}", e);
394 /// // DMA overrun, next call to `read` restarts ADC.
395 /// }
396 /// }
397 ///
398 /// // Manually control sample rate.
399 /// Timer::after_millis(100).await;
400 /// }
401 /// ```
402 ///
403 ///
404 /// [`set_sample_sequence`]: #method.set_sample_sequence
405 /// [`teardown_adc`]: #method.teardown_adc
406 /// [`start`]: #method.start
407 pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> {
408 assert_eq!(
409 self.ring_buf.capacity() / 2,
410 N,
411 "Buffer size must be half the size of the ring buffer"
412 );
413
414 let r = T::regs();
415
416 // Start background receive if it was not already started
417 if !r.cr2().read().dma() {
418 self.start()?;
419 }
420
421 // Clear overrun flag if set.
422 if r.sr().read().ovr() {
423 return self.stop(OverrunError);
424 }
425 match self.ring_buf.read_exact(measurements).await {
426 Ok(len) => Ok(len),
427 Err(_) => self.stop(OverrunError),
428 }
429 }
430}
431
432impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
433 fn drop(&mut self) {
434 self.teardown_adc();
435 rcc::disable::<T>();
436 }
437}
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index e3175dff5..842a5ee6d 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -6,6 +6,9 @@ use crate::peripherals::ADC1;
6use crate::time::Hertz; 6use crate::time::Hertz;
7use crate::{rcc, Peripheral}; 7use crate::{rcc, Peripheral};
8 8
9mod ringbuffered_v2;
10pub use ringbuffered_v2::{RingBufferedAdc, Sequence};
11
9/// Default VREF voltage used for sample conversion to millivolts. 12/// Default VREF voltage used for sample conversion to millivolts.
10pub const VREF_DEFAULT_MV: u32 = 3300; 13pub const VREF_DEFAULT_MV: u32 = 3300;
11/// VREF voltage used for factory calibration of VREFINTCAL register. 14/// VREF voltage used for factory calibration of VREFINTCAL register.
@@ -175,7 +178,7 @@ where
175 T::regs().dr().read().0 as u16 178 T::regs().dr().read().0 as u16
176 } 179 }
177 180
178 pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { 181 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
179 channel.setup(); 182 channel.setup();
180 183
181 // Configure ADC 184 // Configure ADC
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 398c57a92..9441e42ff 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -1,9 +1,12 @@
1use cfg_if::cfg_if; 1use cfg_if::cfg_if;
2use embassy_hal_internal::into_ref; 2use embassy_hal_internal::into_ref;
3use pac::adc::vals::Dmacfg;
3 4
4use super::blocking_delay_us; 5use super::{
5use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; 6 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel,
6use crate::{rcc, Peripheral}; 7};
8use crate::dma::Transfer;
9use crate::{pac, rcc, Peripheral};
7 10
8/// Default VREF voltage used for sample conversion to millivolts. 11/// Default VREF voltage used for sample conversion to millivolts.
9pub const VREF_DEFAULT_MV: u32 = 3300; 12pub const VREF_DEFAULT_MV: u32 = 3300;
@@ -12,7 +15,7 @@ pub const VREF_CALIB_MV: u32 = 3000;
12 15
13pub struct VrefInt; 16pub struct VrefInt;
14impl<T: Instance> AdcChannel<T> for VrefInt {} 17impl<T: Instance> AdcChannel<T> for VrefInt {}
15impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { 18impl<T: Instance> SealedAdcChannel<T> for VrefInt {
16 fn channel(&self) -> u8 { 19 fn channel(&self) -> u8 {
17 cfg_if! { 20 cfg_if! {
18 if #[cfg(adc_g0)] { 21 if #[cfg(adc_g0)] {
@@ -31,7 +34,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
31 34
32pub struct Temperature; 35pub struct Temperature;
33impl<T: Instance> AdcChannel<T> for Temperature {} 36impl<T: Instance> AdcChannel<T> for Temperature {}
34impl<T: Instance> super::SealedAdcChannel<T> for Temperature { 37impl<T: Instance> SealedAdcChannel<T> for Temperature {
35 fn channel(&self) -> u8 { 38 fn channel(&self) -> u8 {
36 cfg_if! { 39 cfg_if! {
37 if #[cfg(adc_g0)] { 40 if #[cfg(adc_g0)] {
@@ -50,7 +53,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
50 53
51pub struct Vbat; 54pub struct Vbat;
52impl<T: Instance> AdcChannel<T> for Vbat {} 55impl<T: Instance> AdcChannel<T> for Vbat {}
53impl<T: Instance> super::SealedAdcChannel<T> for Vbat { 56impl<T: Instance> SealedAdcChannel<T> for Vbat {
54 fn channel(&self) -> u8 { 57 fn channel(&self) -> u8 {
55 cfg_if! { 58 cfg_if! {
56 if #[cfg(adc_g0)] { 59 if #[cfg(adc_g0)] {
@@ -101,6 +104,7 @@ impl<'d, T: Instance> Adc<'d, T> {
101 reg.set_advregen(true); 104 reg.set_advregen(true);
102 }); 105 });
103 106
107 // If this is false then each ADC_CHSELR bit enables an input channel.
104 #[cfg(any(adc_g0, adc_u0))] 108 #[cfg(any(adc_g0, adc_u0))]
105 T::regs().cfgr1().modify(|reg| { 109 T::regs().cfgr1().modify(|reg| {
106 reg.set_chselrmod(false); 110 reg.set_chselrmod(false);
@@ -124,6 +128,28 @@ impl<'d, T: Instance> Adc<'d, T> {
124 } 128 }
125 } 129 }
126 130
131 // Enable ADC only when it is not already running.
132 fn enable(&mut self) {
133 // Make sure bits are off
134 while T::regs().cr().read().addis() {
135 // spin
136 }
137
138 if !T::regs().cr().read().aden() {
139 // Enable ADC
140 T::regs().isr().modify(|reg| {
141 reg.set_adrdy(true);
142 });
143 T::regs().cr().modify(|reg| {
144 reg.set_aden(true);
145 });
146
147 while !T::regs().isr().read().adrdy() {
148 // spin
149 }
150 }
151 }
152
127 pub fn enable_vrefint(&self) -> VrefInt { 153 pub fn enable_vrefint(&self) -> VrefInt {
128 #[cfg(not(any(adc_g0, adc_u0)))] 154 #[cfg(not(any(adc_g0, adc_u0)))]
129 T::common_regs().ccr().modify(|reg| { 155 T::common_regs().ccr().modify(|reg| {
@@ -181,10 +207,17 @@ impl<'d, T: Instance> Adc<'d, T> {
181 Vbat {} 207 Vbat {}
182 } 208 }
183 209
210 /// Set the ADC sample time.
184 pub fn set_sample_time(&mut self, sample_time: SampleTime) { 211 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
185 self.sample_time = sample_time; 212 self.sample_time = sample_time;
186 } 213 }
187 214
215 /// Get the ADC sample time.
216 pub fn sample_time(&self) -> SampleTime {
217 self.sample_time
218 }
219
220 /// Set the ADC resolution.
188 pub fn set_resolution(&mut self, resolution: Resolution) { 221 pub fn set_resolution(&mut self, resolution: Resolution) {
189 #[cfg(not(any(adc_g0, adc_u0)))] 222 #[cfg(not(any(adc_g0, adc_u0)))]
190 T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); 223 T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
@@ -220,24 +253,164 @@ impl<'d, T: Instance> Adc<'d, T> {
220 T::regs().dr().read().0 as u16 253 T::regs().dr().read().0 as u16
221 } 254 }
222 255
223 pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { 256 /// Read an ADC channel.
224 // Make sure bits are off 257 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
225 while T::regs().cr().read().addis() { 258 self.read_channel(channel)
226 // spin 259 }
260
261 /// Read one or multiple ADC channels using DMA.
262 ///
263 /// `sequence` iterator and `readings` must have the same length.
264 ///
265 /// Example
266 /// ```rust,ignore
267 /// use embassy_stm32::adc::{Adc, AdcChannel}
268 ///
269 /// let mut adc = Adc::new(p.ADC1);
270 /// let mut adc_pin0 = p.PA0.degrade_adc();
271 /// let mut adc_pin1 = p.PA1.degrade_adc();
272 /// let mut measurements = [0u16; 2];
273 ///
274 /// adc.read_async(
275 /// p.DMA1_CH2,
276 /// [
277 /// (&mut *adc_pin0, SampleTime::CYCLES160_5),
278 /// (&mut *adc_pin1, SampleTime::CYCLES160_5),
279 /// ]
280 /// .into_iter(),
281 /// &mut measurements,
282 /// )
283 /// .await;
284 /// defmt::info!("measurements: {}", measurements);
285 /// ```
286 pub async fn read(
287 &mut self,
288 rx_dma: &mut impl RxDma<T>,
289 sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
290 readings: &mut [u16],
291 ) {
292 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
293 assert!(
294 sequence.len() == readings.len(),
295 "Sequence length must be equal to readings length"
296 );
297 assert!(
298 sequence.len() <= 16,
299 "Asynchronous read sequence cannot be more than 16 in length"
300 );
301
302 // Ensure no conversions are ongoing and ADC is enabled.
303 Self::cancel_conversions();
304 self.enable();
305
306 // Set sequence length
307 #[cfg(not(any(adc_g0, adc_u0)))]
308 T::regs().sqr1().modify(|w| {
309 w.set_l(sequence.len() as u8 - 1);
310 });
311
312 #[cfg(any(adc_g0, adc_u0))]
313 let mut channel_mask = 0;
314
315 // Configure channels and ranks
316 for (_i, (channel, sample_time)) in sequence.enumerate() {
317 Self::configure_channel(channel, sample_time);
318
319 // Each channel is sampled according to sequence
320 #[cfg(not(any(adc_g0, adc_u0)))]
321 match _i {
322 0..=3 => {
323 T::regs().sqr1().modify(|w| {
324 w.set_sq(_i, channel.channel());
325 });
326 }
327 4..=8 => {
328 T::regs().sqr2().modify(|w| {
329 w.set_sq(_i - 4, channel.channel());
330 });
331 }
332 9..=13 => {
333 T::regs().sqr3().modify(|w| {
334 w.set_sq(_i - 9, channel.channel());
335 });
336 }
337 14..=15 => {
338 T::regs().sqr4().modify(|w| {
339 w.set_sq(_i - 14, channel.channel());
340 });
341 }
342 _ => unreachable!(),
343 }
344
345 #[cfg(any(adc_g0, adc_u0))]
346 {
347 channel_mask |= 1 << channel.channel();
348 }
227 } 349 }
228 350
229 // Enable ADC 351 // On G0 and U0 enabled channels are sampled from 0 to last channel.
352 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
353 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
354 #[cfg(any(adc_g0, adc_u0))]
355 T::regs().chselr().modify(|reg| {
356 reg.set_chsel(channel_mask);
357 });
358
359 // Set continuous mode with oneshot dma.
360 // Clear overrun flag before starting transfer.
230 T::regs().isr().modify(|reg| { 361 T::regs().isr().modify(|reg| {
231 reg.set_adrdy(true); 362 reg.set_ovr(true);
232 }); 363 });
364
365 #[cfg(not(any(adc_g0, adc_u0)))]
366 T::regs().cfgr().modify(|reg| {
367 reg.set_discen(false);
368 reg.set_cont(true);
369 reg.set_dmacfg(Dmacfg::ONESHOT);
370 reg.set_dmaen(true);
371 });
372 #[cfg(any(adc_g0, adc_u0))]
373 T::regs().cfgr1().modify(|reg| {
374 reg.set_discen(false);
375 reg.set_cont(true);
376 reg.set_dmacfg(Dmacfg::ONESHOT);
377 reg.set_dmaen(true);
378 });
379
380 let request = rx_dma.request();
381 let transfer = unsafe {
382 Transfer::new_read(
383 rx_dma,
384 request,
385 T::regs().dr().as_ptr() as *mut u16,
386 readings,
387 Default::default(),
388 )
389 };
390
391 // Start conversion
233 T::regs().cr().modify(|reg| { 392 T::regs().cr().modify(|reg| {
234 reg.set_aden(true); 393 reg.set_adstart(true);
235 }); 394 });
236 395
237 while !T::regs().isr().read().adrdy() { 396 // Wait for conversion sequence to finish.
238 // spin 397 transfer.await;
239 } 398
399 // Ensure conversions are finished.
400 Self::cancel_conversions();
401
402 // Reset configuration.
403 #[cfg(not(any(adc_g0, adc_u0)))]
404 T::regs().cfgr().modify(|reg| {
405 reg.set_cont(false);
406 });
407 #[cfg(any(adc_g0, adc_u0))]
408 T::regs().cfgr1().modify(|reg| {
409 reg.set_cont(false);
410 });
411 }
240 412
413 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
241 // RM0492, RM0481, etc. 414 // RM0492, RM0481, etc.
242 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." 415 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
243 #[cfg(adc_h5)] 416 #[cfg(adc_h5)]
@@ -246,7 +419,12 @@ impl<'d, T: Instance> Adc<'d, T> {
246 } 419 }
247 420
248 // Configure channel 421 // Configure channel
249 Self::set_channel_sample_time(channel.channel(), self.sample_time); 422 Self::set_channel_sample_time(channel.channel(), sample_time);
423 }
424
425 fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
426 self.enable();
427 Self::configure_channel(channel, self.sample_time);
250 428
251 // Select channel 429 // Select channel
252 #[cfg(not(any(adc_g0, adc_u0)))] 430 #[cfg(not(any(adc_g0, adc_u0)))]
@@ -262,7 +440,6 @@ impl<'d, T: Instance> Adc<'d, T> {
262 // STM32G4: Section 2.7.3 440 // STM32G4: Section 2.7.3
263 #[cfg(any(rcc_l4, rcc_g4))] 441 #[cfg(any(rcc_l4, rcc_g4))]
264 let _ = self.convert(); 442 let _ = self.convert();
265
266 let val = self.convert(); 443 let val = self.convert();
267 444
268 T::regs().cr().modify(|reg| reg.set_addis(true)); 445 T::regs().cr().modify(|reg| reg.set_addis(true));
@@ -277,9 +454,25 @@ impl<'d, T: Instance> Adc<'d, T> {
277 val 454 val
278 } 455 }
279 456
457 #[cfg(any(adc_g0, adc_u0))]
458 pub fn set_oversampling_shift(&mut self, shift: u8) {
459 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
460 }
461
462 #[cfg(any(adc_g0, adc_u0))]
463 pub fn set_oversampling_ratio(&mut self, ratio: u8) {
464 T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));
465 }
466
467 #[cfg(any(adc_g0, adc_u0))]
468 pub fn oversampling_enable(&mut self, enable: bool) {
469 T::regs().cfgr2().modify(|reg| reg.set_ovse(enable));
470 }
471
280 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { 472 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
281 cfg_if! { 473 cfg_if! {
282 if #[cfg(any(adc_g0, adc_u0))] { 474 if #[cfg(any(adc_g0, adc_u0))] {
475 // On G0 and U6 all channels use the same sampling time.
283 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); 476 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
284 } else if #[cfg(adc_h5)] { 477 } else if #[cfg(adc_h5)] {
285 match _ch { 478 match _ch {
@@ -294,4 +487,13 @@ impl<'d, T: Instance> Adc<'d, T> {
294 } 487 }
295 } 488 }
296 } 489 }
490
491 fn cancel_conversions() {
492 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
493 T::regs().cr().modify(|reg| {
494 reg.set_adstp(true);
495 });
496 while T::regs().cr().read().adstart() {}
497 }
498 }
297} 499}
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 50db646fe..63b5b58ea 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -1,8 +1,11 @@
1#[allow(unused)] 1#[allow(unused)]
2use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; 2use pac::adc::vals::{Adcaldif, Adstp, Boost, Difsel, Dmngt, Exten, Pcsel};
3use pac::adccommon::vals::Presc; 3use pac::adccommon::vals::Presc;
4 4
5use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; 5use super::{
6 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel,
7};
8use crate::dma::Transfer;
6use crate::time::Hertz; 9use crate::time::Hertz;
7use crate::{pac, rcc, Peripheral}; 10use crate::{pac, rcc, Peripheral};
8 11
@@ -34,7 +37,7 @@ const VBAT_CHANNEL: u8 = 17;
34/// Internal voltage reference channel. 37/// Internal voltage reference channel.
35pub struct VrefInt; 38pub struct VrefInt;
36impl<T: Instance> AdcChannel<T> for VrefInt {} 39impl<T: Instance> AdcChannel<T> for VrefInt {}
37impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { 40impl<T: Instance> SealedAdcChannel<T> for VrefInt {
38 fn channel(&self) -> u8 { 41 fn channel(&self) -> u8 {
39 VREF_CHANNEL 42 VREF_CHANNEL
40 } 43 }
@@ -43,7 +46,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
43/// Internal temperature channel. 46/// Internal temperature channel.
44pub struct Temperature; 47pub struct Temperature;
45impl<T: Instance> AdcChannel<T> for Temperature {} 48impl<T: Instance> AdcChannel<T> for Temperature {}
46impl<T: Instance> super::SealedAdcChannel<T> for Temperature { 49impl<T: Instance> SealedAdcChannel<T> for Temperature {
47 fn channel(&self) -> u8 { 50 fn channel(&self) -> u8 {
48 TEMP_CHANNEL 51 TEMP_CHANNEL
49 } 52 }
@@ -52,7 +55,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
52/// Internal battery voltage channel. 55/// Internal battery voltage channel.
53pub struct Vbat; 56pub struct Vbat;
54impl<T: Instance> AdcChannel<T> for Vbat {} 57impl<T: Instance> AdcChannel<T> for Vbat {}
55impl<T: Instance> super::SealedAdcChannel<T> for Vbat { 58impl<T: Instance> SealedAdcChannel<T> for Vbat {
56 fn channel(&self) -> u8 { 59 fn channel(&self) -> u8 {
57 VBAT_CHANNEL 60 VBAT_CHANNEL
58 } 61 }
@@ -126,6 +129,21 @@ impl Prescaler {
126 } 129 }
127} 130}
128 131
132/// Number of samples used for averaging.
133pub enum Averaging {
134 Disabled,
135 Samples2,
136 Samples4,
137 Samples8,
138 Samples16,
139 Samples32,
140 Samples64,
141 Samples128,
142 Samples256,
143 Samples512,
144 Samples1024,
145}
146
129impl<'d, T: Instance> Adc<'d, T> { 147impl<'d, T: Instance> Adc<'d, T> {
130 /// Create a new ADC driver. 148 /// Create a new ADC driver.
131 pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { 149 pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
@@ -247,11 +265,39 @@ impl<'d, T: Instance> Adc<'d, T> {
247 self.sample_time = sample_time; 265 self.sample_time = sample_time;
248 } 266 }
249 267
268 /// Get the ADC sample time.
269 pub fn sample_time(&self) -> SampleTime {
270 self.sample_time
271 }
272
250 /// Set the ADC resolution. 273 /// Set the ADC resolution.
251 pub fn set_resolution(&mut self, resolution: Resolution) { 274 pub fn set_resolution(&mut self, resolution: Resolution) {
252 T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); 275 T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
253 } 276 }
254 277
278 /// Set hardware averaging.
279 pub fn set_averaging(&mut self, averaging: Averaging) {
280 let (enable, samples, right_shift) = match averaging {
281 Averaging::Disabled => (false, 0, 0),
282 Averaging::Samples2 => (true, 1, 1),
283 Averaging::Samples4 => (true, 3, 2),
284 Averaging::Samples8 => (true, 7, 3),
285 Averaging::Samples16 => (true, 15, 4),
286 Averaging::Samples32 => (true, 31, 5),
287 Averaging::Samples64 => (true, 63, 6),
288 Averaging::Samples128 => (true, 127, 7),
289 Averaging::Samples256 => (true, 255, 8),
290 Averaging::Samples512 => (true, 511, 9),
291 Averaging::Samples1024 => (true, 1023, 10),
292 };
293
294 T::regs().cfgr2().modify(|reg| {
295 reg.set_rovse(enable);
296 reg.set_osvr(samples);
297 reg.set_ovss(right_shift);
298 })
299 }
300
255 /// Perform a single conversion. 301 /// Perform a single conversion.
256 fn convert(&mut self) -> u16 { 302 fn convert(&mut self) -> u16 {
257 T::regs().isr().modify(|reg| { 303 T::regs().isr().modify(|reg| {
@@ -272,26 +318,148 @@ impl<'d, T: Instance> Adc<'d, T> {
272 } 318 }
273 319
274 /// Read an ADC channel. 320 /// Read an ADC channel.
275 pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { 321 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
276 channel.setup(); 322 self.read_channel(channel)
323 }
324
325 /// Read one or multiple ADC channels using DMA.
326 ///
327 /// `sequence` iterator and `readings` must have the same length.
328 ///
329 /// Example
330 /// ```rust,ignore
331 /// use embassy_stm32::adc::{Adc, AdcChannel}
332 ///
333 /// let mut adc = Adc::new(p.ADC1);
334 /// let mut adc_pin0 = p.PA0.degrade_adc();
335 /// let mut adc_pin2 = p.PA2.degrade_adc();
336 /// let mut measurements = [0u16; 2];
337 ///
338 /// adc.read_async(
339 /// p.DMA2_CH0,
340 /// [
341 /// (&mut *adc_pin0, SampleTime::CYCLES112),
342 /// (&mut *adc_pin2, SampleTime::CYCLES112),
343 /// ]
344 /// .into_iter(),
345 /// &mut measurements,
346 /// )
347 /// .await;
348 /// defmt::info!("measurements: {}", measurements);
349 /// ```
350 pub async fn read(
351 &mut self,
352 rx_dma: &mut impl RxDma<T>,
353 sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>,
354 readings: &mut [u16],
355 ) {
356 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
357 assert!(
358 sequence.len() == readings.len(),
359 "Sequence length must be equal to readings length"
360 );
361 assert!(
362 sequence.len() <= 16,
363 "Asynchronous read sequence cannot be more than 16 in length"
364 );
365
366 // Ensure no conversions are ongoing
367 Self::cancel_conversions();
368
369 // Set sequence length
370 T::regs().sqr1().modify(|w| {
371 w.set_l(sequence.len() as u8 - 1);
372 });
373
374 // Configure channels and ranks
375 for (i, (channel, sample_time)) in sequence.enumerate() {
376 Self::configure_channel(channel, sample_time);
377 match i {
378 0..=3 => {
379 T::regs().sqr1().modify(|w| {
380 w.set_sq(i, channel.channel());
381 });
382 }
383 4..=8 => {
384 T::regs().sqr2().modify(|w| {
385 w.set_sq(i - 4, channel.channel());
386 });
387 }
388 9..=13 => {
389 T::regs().sqr3().modify(|w| {
390 w.set_sq(i - 9, channel.channel());
391 });
392 }
393 14..=15 => {
394 T::regs().sqr4().modify(|w| {
395 w.set_sq(i - 14, channel.channel());
396 });
397 }
398 _ => unreachable!(),
399 }
400 }
401
402 // Set continuous mode with oneshot dma.
403 // Clear overrun flag before starting transfer.
404
405 T::regs().isr().modify(|reg| {
406 reg.set_ovr(true);
407 });
408 T::regs().cfgr().modify(|reg| {
409 reg.set_cont(true);
410 reg.set_dmngt(Dmngt::DMA_ONESHOT);
411 });
412
413 let request = rx_dma.request();
414 let transfer = unsafe {
415 Transfer::new_read(
416 rx_dma,
417 request,
418 T::regs().dr().as_ptr() as *mut u16,
419 readings,
420 Default::default(),
421 )
422 };
423
424 // Start conversion
425 T::regs().cr().modify(|reg| {
426 reg.set_adstart(true);
427 });
428
429 // Wait for conversion sequence to finish.
430 transfer.await;
431
432 // Ensure conversions are finished.
433 Self::cancel_conversions();
277 434
278 self.read_channel(channel.channel()) 435 // Reset configuration.
436 T::regs().cfgr().modify(|reg| {
437 reg.set_cont(false);
438 reg.set_dmngt(Dmngt::from_bits(0));
439 });
279 } 440 }
280 441
281 fn read_channel(&mut self, channel: u8) -> u16 { 442 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
282 // Configure channel 443 channel.setup();
283 Self::set_channel_sample_time(channel, self.sample_time); 444
445 let channel = channel.channel();
446
447 Self::set_channel_sample_time(channel, sample_time);
284 448
285 #[cfg(stm32h7)] 449 #[cfg(stm32h7)]
286 { 450 {
287 T::regs().cfgr2().modify(|w| w.set_lshift(0)); 451 T::regs().cfgr2().modify(|w| w.set_lshift(0));
288 T::regs() 452 T::regs()
289 .pcsel() 453 .pcsel()
290 .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); 454 .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
291 } 455 }
456 }
292 457
293 T::regs().sqr1().write(|reg| { 458 fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
294 reg.set_sq(0, channel); 459 Self::configure_channel(channel, self.sample_time);
460
461 T::regs().sqr1().modify(|reg| {
462 reg.set_sq(0, channel.channel());
295 reg.set_l(0); 463 reg.set_l(0);
296 }); 464 });
297 465
@@ -306,4 +474,13 @@ impl<'d, T: Instance> Adc<'d, T> {
306 T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); 474 T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
307 } 475 }
308 } 476 }
477
478 fn cancel_conversions() {
479 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
480 T::regs().cr().modify(|reg| {
481 reg.set_adstp(Adstp::STOP);
482 });
483 while T::regs().cr().read().adstart() {}
484 }
485 }
309} 486}
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index a462e317f..8a6aa53a0 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -904,6 +904,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
904 let data_size = W::size(); 904 let data_size = W::size();
905 let buffer_ptr = buffer.as_mut_ptr(); 905 let buffer_ptr = buffer.as_mut_ptr();
906 906
907 options.half_transfer_ir = true;
907 options.complete_transfer_ir = true; 908 options.complete_transfer_ir = true;
908 options.circular = true; 909 options.circular = true;
909 910
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 80163c287..8c8df79dd 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -109,6 +109,11 @@ impl<'d, M: Mode> I2c<'d, M> {
109 timeout.check()?; 109 timeout.check()?;
110 } 110 }
111 111
112 // Wait for the bus to be free
113 while info.regs.isr().read().busy() {
114 timeout.check()?;
115 }
116
112 let reload = if reload { 117 let reload = if reload {
113 i2c::vals::Reload::NOTCOMPLETED 118 i2c::vals::Reload::NOTCOMPLETED
114 } else { 119 } else {
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs
index 481d77843..4c5239971 100644
--- a/embassy-stm32/src/ltdc.rs
+++ b/embassy-stm32/src/ltdc.rs
@@ -1,19 +1,194 @@
1//! LTDC 1//! LTDC - LCD-TFT Display Controller
2//! See ST application note AN4861: Introduction to LCD-TFT display controller (LTDC) on STM32 MCUs for high level details
3//! This module was tested against the stm32h735g-dk using the RM0468 ST reference manual for detailed register information
4
5use core::future::poll_fn;
2use core::marker::PhantomData; 6use core::marker::PhantomData;
7use core::task::Poll;
8
9use embassy_hal_internal::{into_ref, PeripheralRef};
10use embassy_sync::waitqueue::AtomicWaker;
11use stm32_metapac::ltdc::regs::Dccr;
12use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr};
13
14use crate::gpio::{AfType, OutputType, Speed};
15use crate::interrupt::typelevel::Interrupt;
16use crate::interrupt::{self};
17use crate::{peripherals, rcc, Peripheral};
18
19static LTDC_WAKER: AtomicWaker = AtomicWaker::new();
20
21/// LTDC error
22#[derive(Debug, PartialEq, Eq, Clone, Copy)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub enum Error {
25 /// FIFO underrun. Generated when a pixel is requested while the FIFO is empty
26 FifoUnderrun,
27 /// Transfer error. Generated when a bus error occurs
28 TransferError,
29}
30
31/// Display configuration parameters
32#[derive(Clone, Copy, Debug, PartialEq)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct LtdcConfiguration {
35 /// Active width in pixels
36 pub active_width: u16,
37 /// Active height in pixels
38 pub active_height: u16,
39
40 /// Horizontal back porch (in units of pixel clock period)
41 pub h_back_porch: u16,
42 /// Horizontal front porch (in units of pixel clock period)
43 pub h_front_porch: u16,
44 /// Vertical back porch (in units of horizontal scan line)
45 pub v_back_porch: u16,
46 /// Vertical front porch (in units of horizontal scan line)
47 pub v_front_porch: u16,
3 48
4use crate::rcc::{self, RccPeripheral}; 49 /// Horizontal synchronization width (in units of pixel clock period)
5use crate::{peripherals, Peripheral}; 50 pub h_sync: u16,
51 /// Vertical synchronization height (in units of horizontal scan line)
52 pub v_sync: u16,
53
54 /// Horizontal synchronization polarity: `false`: active low, `true`: active high
55 pub h_sync_polarity: PolarityActive,
56 /// Vertical synchronization polarity: `false`: active low, `true`: active high
57 pub v_sync_polarity: PolarityActive,
58 /// Data enable polarity: `false`: active low, `true`: active high
59 pub data_enable_polarity: PolarityActive,
60 /// Pixel clock polarity: `false`: falling edge, `true`: rising edge
61 pub pixel_clock_polarity: PolarityEdge,
62}
63
64/// Edge polarity
65#[derive(Clone, Copy, Debug, PartialEq)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum PolarityEdge {
68 /// Falling edge
69 FallingEdge,
70 /// Rising edge
71 RisingEdge,
72}
73
74/// Active polarity
75#[derive(Clone, Copy, Debug, PartialEq)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub enum PolarityActive {
78 /// Active low
79 ActiveLow,
80 /// Active high
81 ActiveHigh,
82}
6 83
7/// LTDC driver. 84/// LTDC driver.
8pub struct Ltdc<'d, T: Instance> { 85pub struct Ltdc<'d, T: Instance> {
9 _peri: PhantomData<&'d mut T>, 86 _peri: PeripheralRef<'d, T>,
87}
88
89/// LTDC interrupt handler.
90pub struct InterruptHandler<T: Instance> {
91 _phantom: PhantomData<T>,
92}
93
94/// 24 bit color
95#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub struct RgbColor {
98 /// Red
99 pub red: u8,
100 /// Green
101 pub green: u8,
102 /// Blue
103 pub blue: u8,
104}
105
106/// Layer
107#[derive(Debug, PartialEq, Eq, Clone, Copy)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109pub struct LtdcLayerConfig {
110 /// Layer number
111 pub layer: LtdcLayer,
112 /// Pixel format
113 pub pixel_format: PixelFormat,
114 /// Window left x in pixels
115 pub window_x0: u16,
116 /// Window right x in pixels
117 pub window_x1: u16,
118 /// Window top y in pixels
119 pub window_y0: u16,
120 /// Window bottom y in pixels
121 pub window_y1: u16,
122}
123
124/// Pixel format
125#[repr(u8)]
126#[derive(Debug, PartialEq, Eq, Clone, Copy)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub enum PixelFormat {
129 /// ARGB8888
130 ARGB8888 = Pf::ARGB8888 as u8,
131 /// RGB888
132 RGB888 = Pf::RGB888 as u8,
133 /// RGB565
134 RGB565 = Pf::RGB565 as u8,
135 /// ARGB1555
136 ARGB1555 = Pf::ARGB1555 as u8,
137 /// ARGB4444
138 ARGB4444 = Pf::ARGB4444 as u8,
139 /// L8 (8-bit luminance)
140 L8 = Pf::L8 as u8,
141 /// AL44 (4-bit alpha, 4-bit luminance
142 AL44 = Pf::AL44 as u8,
143 /// AL88 (8-bit alpha, 8-bit luminance)
144 AL88 = Pf::AL88 as u8,
145}
146
147impl PixelFormat {
148 /// Number of bytes per pixel
149 pub fn bytes_per_pixel(&self) -> usize {
150 match self {
151 PixelFormat::ARGB8888 => 4,
152 PixelFormat::RGB888 => 3,
153 PixelFormat::RGB565 | PixelFormat::ARGB4444 | PixelFormat::ARGB1555 | PixelFormat::AL88 => 2,
154 PixelFormat::AL44 | PixelFormat::L8 => 1,
155 }
156 }
157}
158
159/// Ltdc Blending Layer
160#[repr(usize)]
161#[derive(Debug, PartialEq, Eq, Clone, Copy)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163pub enum LtdcLayer {
164 /// Bottom layer
165 Layer1 = 0,
166 /// Top layer
167 Layer2 = 1,
168}
169
170impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
171 unsafe fn on_interrupt() {
172 cortex_m::asm::dsb();
173 Ltdc::<T>::enable_interrupts(false);
174 LTDC_WAKER.wake();
175 }
10} 176}
11 177
12impl<'d, T: Instance> Ltdc<'d, T> { 178impl<'d, T: Instance> Ltdc<'d, T> {
179 // Create a new LTDC driver without specifying color and control pins. This is typically used if you want to drive a display though a DsiHost
13 /// Note: Full-Duplex modes are not supported at this time 180 /// Note: Full-Duplex modes are not supported at this time
14 pub fn new( 181 pub fn new(peri: impl Peripheral<P = T> + 'd) -> Self {
15 _peri: impl Peripheral<P = T> + 'd, 182 Self::setup_clocks();
16 /* 183 into_ref!(peri);
184 Self { _peri: peri }
185 }
186
187 /// Create a new LTDC driver. 8 pins per color channel for blue, green and red
188 #[allow(clippy::too_many_arguments)]
189 pub fn new_with_pins(
190 peri: impl Peripheral<P = T> + 'd,
191 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
17 clk: impl Peripheral<P = impl ClkPin<T>> + 'd, 192 clk: impl Peripheral<P = impl ClkPin<T>> + 'd,
18 hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd, 193 hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd,
19 vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd, 194 vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd,
@@ -41,40 +216,112 @@ impl<'d, T: Instance> Ltdc<'d, T> {
41 r5: impl Peripheral<P = impl R5Pin<T>> + 'd, 216 r5: impl Peripheral<P = impl R5Pin<T>> + 'd,
42 r6: impl Peripheral<P = impl R6Pin<T>> + 'd, 217 r6: impl Peripheral<P = impl R6Pin<T>> + 'd,
43 r7: impl Peripheral<P = impl R7Pin<T>> + 'd, 218 r7: impl Peripheral<P = impl R7Pin<T>> + 'd,
44 */
45 ) -> Self { 219 ) -> Self {
46 //into_ref!(clk); 220 Self::setup_clocks();
221 into_ref!(peri);
222 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh));
223 new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
224 new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
225 new_pin!(b0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
226 new_pin!(b1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
227 new_pin!(b2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
228 new_pin!(b3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
229 new_pin!(b4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
230 new_pin!(b5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
231 new_pin!(b6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
232 new_pin!(b7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
233 new_pin!(g0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
234 new_pin!(g1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
235 new_pin!(g2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
236 new_pin!(g3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
237 new_pin!(g4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
238 new_pin!(g5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
239 new_pin!(g6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
240 new_pin!(g7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
241 new_pin!(r0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
242 new_pin!(r1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
243 new_pin!(r2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
244 new_pin!(r3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
245 new_pin!(r4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
246 new_pin!(r5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
247 new_pin!(r6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
248 new_pin!(r7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
47 249
48 critical_section::with(|_cs| { 250 Self { _peri: peri }
49 // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this. 251 }
50 // According to the debugger, this bit gets set, anyway.
51 #[cfg(stm32f7)]
52 stm32_metapac::RCC
53 .dckcfgr1()
54 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
55 252
56 // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO. 253 /// Initialise and enable the display
57 #[cfg(not(any(stm32f7, stm32u5)))] 254 pub fn init(&mut self, config: &LtdcConfiguration) {
58 stm32_metapac::RCC 255 use stm32_metapac::ltdc::vals::{Depol, Hspol, Pcpol, Vspol};
59 .dckcfgr() 256 let ltdc = T::regs();
60 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); 257
258 // check bus access
259 assert!(ltdc.gcr().read().0 == 0x2220); // reset value
260
261 // configure the HS, VS, DE and PC polarity
262 ltdc.gcr().modify(|w| {
263 w.set_hspol(match config.h_sync_polarity {
264 PolarityActive::ActiveHigh => Hspol::ACTIVEHIGH,
265 PolarityActive::ActiveLow => Hspol::ACTIVELOW,
266 });
267
268 w.set_vspol(match config.v_sync_polarity {
269 PolarityActive::ActiveHigh => Vspol::ACTIVEHIGH,
270 PolarityActive::ActiveLow => Vspol::ACTIVELOW,
271 });
272
273 w.set_depol(match config.data_enable_polarity {
274 PolarityActive::ActiveHigh => Depol::ACTIVEHIGH,
275 PolarityActive::ActiveLow => Depol::ACTIVELOW,
276 });
277
278 w.set_pcpol(match config.pixel_clock_polarity {
279 PolarityEdge::RisingEdge => Pcpol::RISINGEDGE,
280 PolarityEdge::FallingEdge => Pcpol::FALLINGEDGE,
281 });
61 }); 282 });
62 283
63 rcc::enable_and_reset::<T>(); 284 // set synchronization pulse width
285 ltdc.sscr().modify(|w| {
286 w.set_vsh(config.v_sync - 1);
287 w.set_hsw(config.h_sync - 1);
288 });
64 289
65 //new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)); 290 // set accumulated back porch
291 ltdc.bpcr().modify(|w| {
292 w.set_avbp(config.v_sync + config.v_back_porch - 1);
293 w.set_ahbp(config.h_sync + config.h_back_porch - 1);
294 });
66 295
67 // Set Tearing Enable pin according to CubeMx example 296 // set accumulated active width
68 //te.set_as_af_pull(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low)); 297 let aa_height = config.v_sync + config.v_back_porch + config.active_height - 1;
69 /* 298 let aa_width = config.h_sync + config.h_back_porch + config.active_width - 1;
70 T::regs().wcr().modify(|w| { 299 ltdc.awcr().modify(|w| {
71 w.set_dsien(true); 300 w.set_aah(aa_height);
72 }); 301 w.set_aaw(aa_width);
73 */ 302 });
74 Self { _peri: PhantomData } 303
304 // set total width and height
305 let total_height: u16 = config.v_sync + config.v_back_porch + config.active_height + config.v_front_porch - 1;
306 let total_width: u16 = config.h_sync + config.h_back_porch + config.active_width + config.h_front_porch - 1;
307 ltdc.twcr().modify(|w| {
308 w.set_totalh(total_height);
309 w.set_totalw(total_width)
310 });
311
312 // set the background color value to black
313 ltdc.bccr().modify(|w| {
314 w.set_bcred(0);
315 w.set_bcgreen(0);
316 w.set_bcblue(0);
317 });
318
319 self.enable();
75 } 320 }
76 321
77 /// Set the enable bit in the control register and assert that it has been enabled 322 /// Set the enable bit in the control register and assert that it has been enabled
323 ///
324 /// This does need to be called if init has already been called
78 pub fn enable(&mut self) { 325 pub fn enable(&mut self) {
79 T::regs().gcr().modify(|w| w.set_ltdcen(true)); 326 T::regs().gcr().modify(|w| w.set_ltdcen(true));
80 assert!(T::regs().gcr().read().ltdcen()) 327 assert!(T::regs().gcr().read().ltdcen())
@@ -85,6 +332,188 @@ impl<'d, T: Instance> Ltdc<'d, T> {
85 T::regs().gcr().modify(|w| w.set_ltdcen(false)); 332 T::regs().gcr().modify(|w| w.set_ltdcen(false));
86 assert!(!T::regs().gcr().read().ltdcen()) 333 assert!(!T::regs().gcr().read().ltdcen())
87 } 334 }
335
336 /// Initialise and enable the layer
337 ///
338 /// clut - a 256 length color look-up table applies to L8, AL44 and AL88 pixel format and will default to greyscale if `None` supplied and these pixel formats are used
339 pub fn init_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {
340 let ltdc = T::regs();
341 let layer = ltdc.layer(layer_config.layer as usize);
342
343 // 256 color look-up table for L8, AL88 and AL88 pixel formats
344 if let Some(clut) = clut {
345 assert_eq!(clut.len(), 256, "Color lookup table must be exactly 256 in length");
346 for (index, color) in clut.iter().enumerate() {
347 layer.clutwr().write(|w| {
348 w.set_clutadd(index as u8);
349 w.set_red(color.red);
350 w.set_green(color.green);
351 w.set_blue(color.blue);
352 });
353 }
354 }
355
356 // configure the horizontal start and stop position
357 let h_win_start = layer_config.window_x0 + ltdc.bpcr().read().ahbp() + 1;
358 let h_win_stop = layer_config.window_x1 + ltdc.bpcr().read().ahbp();
359 layer.whpcr().write(|w| {
360 w.set_whstpos(h_win_start);
361 w.set_whsppos(h_win_stop);
362 });
363
364 // configure the vertical start and stop position
365 let v_win_start = layer_config.window_y0 + ltdc.bpcr().read().avbp() + 1;
366 let v_win_stop = layer_config.window_y1 + ltdc.bpcr().read().avbp();
367 layer.wvpcr().write(|w| {
368 w.set_wvstpos(v_win_start);
369 w.set_wvsppos(v_win_stop)
370 });
371
372 // set the pixel format
373 layer
374 .pfcr()
375 .write(|w| w.set_pf(Pf::from_bits(layer_config.pixel_format as u8)));
376
377 // set the default color value to transparent black
378 layer.dccr().write_value(Dccr::default());
379
380 // set the global constant alpha value
381 let alpha = 0xFF;
382 layer.cacr().write(|w| w.set_consta(alpha));
383
384 // set the blending factors.
385 layer.bfcr().modify(|w| {
386 w.set_bf1(Bf1::PIXEL);
387 w.set_bf2(Bf2::PIXEL);
388 });
389
390 // calculate framebuffer pixel size in bytes
391 let bytes_per_pixel = layer_config.pixel_format.bytes_per_pixel() as u16;
392 let width = layer_config.window_x1 - layer_config.window_x0;
393 let height = layer_config.window_y1 - layer_config.window_y0;
394
395 // framebuffer pitch and line length
396 layer.cfblr().modify(|w| {
397 w.set_cfbp(width * bytes_per_pixel);
398 w.set_cfbll(width * bytes_per_pixel + 7);
399 });
400
401 // framebuffer line number
402 layer.cfblnr().modify(|w| w.set_cfblnbr(height));
403
404 // enable LTDC_Layer by setting LEN bit
405 layer.cr().modify(|w| {
406 if clut.is_some() {
407 w.set_cluten(true);
408 }
409 w.set_len(true);
410 });
411 }
412
413 /// Set the current buffer. The async function will return when buffer has been completely copied to the LCD screen
414 /// frame_buffer_addr is a pointer to memory that should not move (best to make it static)
415 pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {
416 let mut bits = T::regs().isr().read();
417
418 // if all clear
419 if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {
420 // wait for interrupt
421 poll_fn(|cx| {
422 // quick check to avoid registration if already done.
423 let bits = T::regs().isr().read();
424 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
425 return Poll::Ready(());
426 }
427
428 LTDC_WAKER.register(cx.waker());
429 Self::clear_interrupt_flags(); // don't poison the request with old flags
430 Self::enable_interrupts(true);
431
432 // set the new frame buffer address
433 let layer = T::regs().layer(layer as usize);
434 layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));
435
436 // configure a shadow reload for the next blanking period
437 T::regs().srcr().write(|w| {
438 w.set_vbr(Vbr::RELOAD);
439 });
440
441 // need to check condition after register to avoid a race
442 // condition that would result in lost notifications.
443 let bits = T::regs().isr().read();
444 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
445 Poll::Ready(())
446 } else {
447 Poll::Pending
448 }
449 })
450 .await;
451
452 // re-read the status register after wait.
453 bits = T::regs().isr().read();
454 }
455
456 let result = if bits.fuif() {
457 Err(Error::FifoUnderrun)
458 } else if bits.terrif() {
459 Err(Error::TransferError)
460 } else if bits.lif() {
461 panic!("line interrupt event is disabled")
462 } else if bits.rrif() {
463 // register reload flag is expected
464 Ok(())
465 } else {
466 unreachable!("all interrupt status values checked")
467 };
468
469 Self::clear_interrupt_flags();
470 result
471 }
472
473 fn setup_clocks() {
474 critical_section::with(|_cs| {
475 // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this.
476 // According to the debugger, this bit gets set, anyway.
477 #[cfg(stm32f7)]
478 crate::pac::RCC
479 .dckcfgr1()
480 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
481
482 // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO.
483 #[cfg(stm32f4)]
484 crate::pac::RCC
485 .dckcfgr()
486 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
487 });
488
489 rcc::enable_and_reset::<T>();
490 }
491
492 fn clear_interrupt_flags() {
493 T::regs().icr().write(|w| {
494 w.set_cfuif(Cfuif::CLEAR);
495 w.set_clif(Clif::CLEAR);
496 w.set_crrif(Crrif::CLEAR);
497 w.set_cterrif(Cterrif::CLEAR);
498 });
499 }
500
501 fn enable_interrupts(enable: bool) {
502 T::regs().ier().write(|w| {
503 w.set_fuie(enable);
504 w.set_lie(false); // we are not interested in the line interrupt enable event
505 w.set_rrie(enable);
506 w.set_terrie(enable)
507 });
508
509 // enable interrupts for LTDC peripheral
510 T::Interrupt::unpend();
511 if enable {
512 unsafe { T::Interrupt::enable() };
513 } else {
514 T::Interrupt::disable()
515 }
516 }
88} 517}
89 518
90impl<'d, T: Instance> Drop for Ltdc<'d, T> { 519impl<'d, T: Instance> Drop for Ltdc<'d, T> {
@@ -95,9 +524,12 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral {
95 fn regs() -> crate::pac::ltdc::Ltdc; 524 fn regs() -> crate::pac::ltdc::Ltdc;
96} 525}
97 526
98/// DSI instance trait. 527/// LTDC instance trait.
99#[allow(private_bounds)] 528#[allow(private_bounds)]
100pub trait Instance: SealedInstance + RccPeripheral + 'static {} 529pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
530 /// Interrupt for this LTDC instance.
531 type Interrupt: interrupt::typelevel::Interrupt;
532}
101 533
102pin_trait!(ClkPin, Instance); 534pin_trait!(ClkPin, Instance);
103pin_trait!(HsyncPin, Instance); 535pin_trait!(HsyncPin, Instance);
@@ -128,14 +560,16 @@ pin_trait!(B5Pin, Instance);
128pin_trait!(B6Pin, Instance); 560pin_trait!(B6Pin, Instance);
129pin_trait!(B7Pin, Instance); 561pin_trait!(B7Pin, Instance);
130 562
131foreach_peripheral!( 563foreach_interrupt!(
132 (ltdc, $inst:ident) => { 564 ($inst:ident, ltdc, LTDC, GLOBAL, $irq:ident) => {
133 impl crate::ltdc::SealedInstance for peripherals::$inst { 565 impl Instance for peripherals::$inst {
566 type Interrupt = crate::interrupt::typelevel::$irq;
567 }
568
569 impl SealedInstance for peripherals::$inst {
134 fn regs() -> crate::pac::ltdc::Ltdc { 570 fn regs() -> crate::pac::ltdc::Ltdc {
135 crate::pac::$inst 571 crate::pac::$inst
136 } 572 }
137 } 573 }
138
139 impl crate::ltdc::Instance for peripherals::$inst {}
140 }; 574 };
141); 575);
diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs
index 8ba208ce7..5cb58e918 100644
--- a/embassy-stm32/src/tsc/mod.rs
+++ b/embassy-stm32/src/tsc/mod.rs
@@ -65,15 +65,19 @@
65/// Enums defined for peripheral parameters 65/// Enums defined for peripheral parameters
66pub mod enums; 66pub mod enums;
67 67
68use core::future::poll_fn;
68use core::marker::PhantomData; 69use core::marker::PhantomData;
70use core::task::Poll;
69 71
70use embassy_hal_internal::{into_ref, PeripheralRef}; 72use embassy_hal_internal::{into_ref, PeripheralRef};
73use embassy_sync::waitqueue::AtomicWaker;
71pub use enums::*; 74pub use enums::*;
72 75
73use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 76use crate::gpio::{AfType, AnyPin, OutputType, Speed};
74use crate::pac::tsc::Tsc as Regs; 77use crate::interrupt::typelevel::Interrupt;
78use crate::mode::{Async, Blocking, Mode as PeriMode};
75use crate::rcc::{self, RccPeripheral}; 79use crate::rcc::{self, RccPeripheral};
76use crate::{peripherals, Peripheral}; 80use crate::{interrupt, peripherals, Peripheral};
77 81
78#[cfg(tsc_v1)] 82#[cfg(tsc_v1)]
79const TSC_NUM_GROUPS: u32 = 6; 83const TSC_NUM_GROUPS: u32 = 6;
@@ -90,6 +94,18 @@ pub enum Error {
90 Test, 94 Test,
91} 95}
92 96
97/// TSC interrupt handler.
98pub struct InterruptHandler<T: Instance> {
99 _phantom: PhantomData<T>,
100}
101
102impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
103 unsafe fn on_interrupt() {
104 T::regs().ier().write(|w| w.set_eoaie(false));
105 T::waker().wake();
106 }
107}
108
93/// Pin type definition to control IO parameters 109/// Pin type definition to control IO parameters
94pub enum PinType { 110pub enum PinType {
95 /// Sensing channel pin connected to an electrode 111 /// Sensing channel pin connected to an electrode
@@ -490,7 +506,7 @@ pub enum G7 {}
490pub enum G8 {} 506pub enum G8 {}
491 507
492/// TSC driver 508/// TSC driver
493pub struct Tsc<'d, T: Instance> { 509pub struct Tsc<'d, T: Instance, K: PeriMode> {
494 _peri: PeripheralRef<'d, T>, 510 _peri: PeripheralRef<'d, T>,
495 _g1: Option<PinGroup<'d, T, G1>>, 511 _g1: Option<PinGroup<'d, T, G1>>,
496 _g2: Option<PinGroup<'d, T, G2>>, 512 _g2: Option<PinGroup<'d, T, G2>>,
@@ -504,11 +520,102 @@ pub struct Tsc<'d, T: Instance> {
504 _g8: Option<PinGroup<'d, T, G8>>, 520 _g8: Option<PinGroup<'d, T, G8>>,
505 state: State, 521 state: State,
506 config: Config, 522 config: Config,
523 _kind: PhantomData<K>,
507} 524}
508 525
509impl<'d, T: Instance> Tsc<'d, T> { 526impl<'d, T: Instance> Tsc<'d, T, Async> {
510 /// Create new TSC driver 527 /// Create a Tsc instance that can be awaited for completion
511 pub fn new( 528 pub fn new_async(
529 peri: impl Peripheral<P = T> + 'd,
530 g1: Option<PinGroup<'d, T, G1>>,
531 g2: Option<PinGroup<'d, T, G2>>,
532 g3: Option<PinGroup<'d, T, G3>>,
533 g4: Option<PinGroup<'d, T, G4>>,
534 g5: Option<PinGroup<'d, T, G5>>,
535 g6: Option<PinGroup<'d, T, G6>>,
536 #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
537 #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
538 config: Config,
539 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
540 ) -> Self {
541 // Need to check valid pin configuration input
542 let g1 = g1.filter(|b| b.check_group().is_ok());
543 let g2 = g2.filter(|b| b.check_group().is_ok());
544 let g3 = g3.filter(|b| b.check_group().is_ok());
545 let g4 = g4.filter(|b| b.check_group().is_ok());
546 let g5 = g5.filter(|b| b.check_group().is_ok());
547 let g6 = g6.filter(|b| b.check_group().is_ok());
548 #[cfg(any(tsc_v2, tsc_v3))]
549 let g7 = g7.filter(|b| b.check_group().is_ok());
550 #[cfg(tsc_v3)]
551 let g8 = g8.filter(|b| b.check_group().is_ok());
552
553 match Self::check_shields(
554 &g1,
555 &g2,
556 &g3,
557 &g4,
558 &g5,
559 &g6,
560 #[cfg(any(tsc_v2, tsc_v3))]
561 &g7,
562 #[cfg(tsc_v3)]
563 &g8,
564 ) {
565 Ok(()) => Self::new_inner(
566 peri,
567 g1,
568 g2,
569 g3,
570 g4,
571 g5,
572 g6,
573 #[cfg(any(tsc_v2, tsc_v3))]
574 g7,
575 #[cfg(tsc_v3)]
576 g8,
577 config,
578 ),
579 Err(_) => Self::new_inner(
580 peri,
581 None,
582 None,
583 None,
584 None,
585 None,
586 None,
587 #[cfg(any(tsc_v2, tsc_v3))]
588 None,
589 #[cfg(tsc_v3)]
590 None,
591 config,
592 ),
593 }
594 }
595 /// Asyncronously wait for the end of an acquisition
596 pub async fn pend_for_acquisition(&mut self) {
597 poll_fn(|cx| match self.get_state() {
598 State::Busy => {
599 T::waker().register(cx.waker());
600 T::regs().ier().write(|w| w.set_eoaie(true));
601 if self.get_state() != State::Busy {
602 T::regs().ier().write(|w| w.set_eoaie(false));
603 return Poll::Ready(());
604 }
605 Poll::Pending
606 }
607 _ => {
608 T::regs().ier().write(|w| w.set_eoaie(false));
609 Poll::Ready(())
610 }
611 })
612 .await;
613 }
614}
615
616impl<'d, T: Instance> Tsc<'d, T, Blocking> {
617 /// Create a Tsc instance that must be polled for completion
618 pub fn new_blocking(
512 peri: impl Peripheral<P = T> + 'd, 619 peri: impl Peripheral<P = T> + 'd,
513 g1: Option<PinGroup<'d, T, G1>>, 620 g1: Option<PinGroup<'d, T, G1>>,
514 g2: Option<PinGroup<'d, T, G2>>, 621 g2: Option<PinGroup<'d, T, G2>>,
@@ -574,7 +681,14 @@ impl<'d, T: Instance> Tsc<'d, T> {
574 ), 681 ),
575 } 682 }
576 } 683 }
684 /// Wait for end of acquisition
685 pub fn poll_for_acquisition(&mut self) {
686 while self.get_state() == State::Busy {}
687 }
688}
577 689
690impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> {
691 /// Create new TSC driver
578 fn check_shields( 692 fn check_shields(
579 g1: &Option<PinGroup<'d, T, G1>>, 693 g1: &Option<PinGroup<'d, T, G1>>,
580 g2: &Option<PinGroup<'d, T, G2>>, 694 g2: &Option<PinGroup<'d, T, G2>>,
@@ -663,7 +777,7 @@ impl<'d, T: Instance> Tsc<'d, T> {
663 777
664 rcc::enable_and_reset::<T>(); 778 rcc::enable_and_reset::<T>();
665 779
666 T::REGS.cr().modify(|w| { 780 T::regs().cr().modify(|w| {
667 w.set_tsce(true); 781 w.set_tsce(true);
668 w.set_ctph(config.ct_pulse_high_length.into()); 782 w.set_ctph(config.ct_pulse_high_length.into());
669 w.set_ctpl(config.ct_pulse_low_length.into()); 783 w.set_ctpl(config.ct_pulse_low_length.into());
@@ -691,33 +805,39 @@ impl<'d, T: Instance> Tsc<'d, T> {
691 805
692 // Set IO configuration 806 // Set IO configuration
693 // Disable Schmitt trigger hysteresis on all used TSC IOs 807 // Disable Schmitt trigger hysteresis on all used TSC IOs
694 T::REGS 808 T::regs()
695 .iohcr() 809 .iohcr()
696 .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios)); 810 .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios));
697 811
698 // Set channel and shield IOs 812 // Set channel and shield IOs
699 T::REGS.ioccr().write(|w| w.0 = config.channel_ios | config.shield_ios); 813 T::regs()
814 .ioccr()
815 .write(|w| w.0 = config.channel_ios | config.shield_ios);
700 816
701 // Set sampling IOs 817 // Set sampling IOs
702 T::REGS.ioscr().write(|w| w.0 = config.sampling_ios); 818 T::regs().ioscr().write(|w| w.0 = config.sampling_ios);
703 819
704 // Set the groups to be acquired 820 // Set the groups to be acquired
705 T::REGS 821 T::regs()
706 .iogcsr() 822 .iogcsr()
707 .write(|w| w.0 = Self::extract_groups(config.channel_ios)); 823 .write(|w| w.0 = Self::extract_groups(config.channel_ios));
708 824
709 // Disable interrupts 825 // Disable interrupts
710 T::REGS.ier().modify(|w| { 826 T::regs().ier().modify(|w| {
711 w.set_eoaie(false); 827 w.set_eoaie(false);
712 w.set_mceie(false); 828 w.set_mceie(false);
713 }); 829 });
714 830
715 // Clear flags 831 // Clear flags
716 T::REGS.icr().modify(|w| { 832 T::regs().icr().modify(|w| {
717 w.set_eoaic(true); 833 w.set_eoaic(true);
718 w.set_mceic(true); 834 w.set_mceic(true);
719 }); 835 });
720 836
837 unsafe {
838 T::Interrupt::enable();
839 }
840
721 Self { 841 Self {
722 _peri: peri, 842 _peri: peri,
723 _g1: g1, 843 _g1: g1,
@@ -732,6 +852,7 @@ impl<'d, T: Instance> Tsc<'d, T> {
732 _g8: g8, 852 _g8: g8,
733 state: State::Ready, 853 state: State::Ready,
734 config, 854 config,
855 _kind: PhantomData,
735 } 856 }
736 } 857 }
737 858
@@ -740,68 +861,41 @@ impl<'d, T: Instance> Tsc<'d, T> {
740 self.state = State::Busy; 861 self.state = State::Busy;
741 862
742 // Disable interrupts 863 // Disable interrupts
743 T::REGS.ier().modify(|w| { 864 T::regs().ier().modify(|w| {
744 w.set_eoaie(false); 865 w.set_eoaie(false);
745 w.set_mceie(false); 866 w.set_mceie(false);
746 }); 867 });
747 868
748 // Clear flags 869 // Clear flags
749 T::REGS.icr().modify(|w| { 870 T::regs().icr().modify(|w| {
750 w.set_eoaic(true); 871 w.set_eoaic(true);
751 w.set_mceic(true); 872 w.set_mceic(true);
752 }); 873 });
753 874
754 // Set the touch sensing IOs not acquired to the default mode 875 // Set the touch sensing IOs not acquired to the default mode
755 T::REGS.cr().modify(|w| { 876 T::regs().cr().modify(|w| {
756 w.set_iodef(self.config.io_default_mode); 877 w.set_iodef(self.config.io_default_mode);
757 }); 878 });
758 879
759 // Start the acquisition 880 // Start the acquisition
760 T::REGS.cr().modify(|w| { 881 T::regs().cr().modify(|w| {
761 w.set_start(true);
762 });
763 }
764
765 /// Start charge transfer acquisition with interrupts enabled
766 pub fn start_it(&mut self) {
767 self.state = State::Busy;
768
769 // Enable interrupts
770 T::REGS.ier().modify(|w| {
771 w.set_eoaie(true);
772 w.set_mceie(self.config.max_count_interrupt);
773 });
774
775 // Clear flags
776 T::REGS.icr().modify(|w| {
777 w.set_eoaic(true);
778 w.set_mceic(true);
779 });
780
781 // Set the touch sensing IOs not acquired to the default mode
782 T::REGS.cr().modify(|w| {
783 w.set_iodef(self.config.io_default_mode);
784 });
785
786 // Start the acquisition
787 T::REGS.cr().modify(|w| {
788 w.set_start(true); 882 w.set_start(true);
789 }); 883 });
790 } 884 }
791 885
792 /// Stop charge transfer acquisition 886 /// Stop charge transfer acquisition
793 pub fn stop(&mut self) { 887 pub fn stop(&mut self) {
794 T::REGS.cr().modify(|w| { 888 T::regs().cr().modify(|w| {
795 w.set_start(false); 889 w.set_start(false);
796 }); 890 });
797 891
798 // Set the touch sensing IOs in low power mode 892 // Set the touch sensing IOs in low power mode
799 T::REGS.cr().modify(|w| { 893 T::regs().cr().modify(|w| {
800 w.set_iodef(false); 894 w.set_iodef(false);
801 }); 895 });
802 896
803 // Clear flags 897 // Clear flags
804 T::REGS.icr().modify(|w| { 898 T::regs().icr().modify(|w| {
805 w.set_eoaic(true); 899 w.set_eoaic(true);
806 w.set_mceic(true); 900 w.set_mceic(true);
807 }); 901 });
@@ -809,42 +903,11 @@ impl<'d, T: Instance> Tsc<'d, T> {
809 self.state = State::Ready; 903 self.state = State::Ready;
810 } 904 }
811 905
812 /// Stop charge transfer acquisition and clear interrupts
813 pub fn stop_it(&mut self) {
814 T::REGS.cr().modify(|w| {
815 w.set_start(false);
816 });
817
818 // Set the touch sensing IOs in low power mode
819 T::REGS.cr().modify(|w| {
820 w.set_iodef(false);
821 });
822
823 // Disable interrupts
824 T::REGS.ier().modify(|w| {
825 w.set_eoaie(false);
826 w.set_mceie(false);
827 });
828
829 // Clear flags
830 T::REGS.icr().modify(|w| {
831 w.set_eoaic(true);
832 w.set_mceic(true);
833 });
834
835 self.state = State::Ready;
836 }
837
838 /// Wait for end of acquisition
839 pub fn poll_for_acquisition(&mut self) {
840 while self.get_state() == State::Busy {}
841 }
842
843 /// Get current state of acquisition 906 /// Get current state of acquisition
844 pub fn get_state(&mut self) -> State { 907 pub fn get_state(&mut self) -> State {
845 if self.state == State::Busy { 908 if self.state == State::Busy {
846 if T::REGS.isr().read().eoaf() { 909 if T::regs().isr().read().eoaf() {
847 if T::REGS.isr().read().mcef() { 910 if T::regs().isr().read().mcef() {
848 self.state = State::Error 911 self.state = State::Error
849 } else { 912 } else {
850 self.state = State::Ready 913 self.state = State::Ready
@@ -859,16 +922,16 @@ impl<'d, T: Instance> Tsc<'d, T> {
859 // Status bits are set by hardware when the acquisition on the corresponding 922 // Status bits are set by hardware when the acquisition on the corresponding
860 // enabled analog IO group is complete, cleared when new acquisition is started 923 // enabled analog IO group is complete, cleared when new acquisition is started
861 let status = match index { 924 let status = match index {
862 Group::One => T::REGS.iogcsr().read().g1s(), 925 Group::One => T::regs().iogcsr().read().g1s(),
863 Group::Two => T::REGS.iogcsr().read().g2s(), 926 Group::Two => T::regs().iogcsr().read().g2s(),
864 Group::Three => T::REGS.iogcsr().read().g3s(), 927 Group::Three => T::regs().iogcsr().read().g3s(),
865 Group::Four => T::REGS.iogcsr().read().g4s(), 928 Group::Four => T::regs().iogcsr().read().g4s(),
866 Group::Five => T::REGS.iogcsr().read().g5s(), 929 Group::Five => T::regs().iogcsr().read().g5s(),
867 Group::Six => T::REGS.iogcsr().read().g6s(), 930 Group::Six => T::regs().iogcsr().read().g6s(),
868 #[cfg(any(tsc_v2, tsc_v3))] 931 #[cfg(any(tsc_v2, tsc_v3))]
869 Group::Seven => T::REGS.iogcsr().read().g7s(), 932 Group::Seven => T::regs().iogcsr().read().g7s(),
870 #[cfg(tsc_v3)] 933 #[cfg(tsc_v3)]
871 Group::Eight => T::REGS.iogcsr().read().g8s(), 934 Group::Eight => T::regs().iogcsr().read().g8s(),
872 }; 935 };
873 match status { 936 match status {
874 true => GroupStatus::Complete, 937 true => GroupStatus::Complete,
@@ -878,39 +941,51 @@ impl<'d, T: Instance> Tsc<'d, T> {
878 941
879 /// Get the count for the acquisiton, valid once group status is set 942 /// Get the count for the acquisiton, valid once group status is set
880 pub fn group_get_value(&mut self, index: Group) -> u16 { 943 pub fn group_get_value(&mut self, index: Group) -> u16 {
881 T::REGS.iogcr(index.into()).read().cnt() 944 T::regs().iogcr(index.into()).read().cnt()
882 } 945 }
883 946
884 /// Discharge the IOs for subsequent acquisition 947 /// Discharge the IOs for subsequent acquisition
885 pub fn discharge_io(&mut self, status: bool) { 948 pub fn discharge_io(&mut self, status: bool) {
886 // Set the touch sensing IOs in low power mode 949 // Set the touch sensing IOs in low power mode
887 T::REGS.cr().modify(|w| { 950 T::regs().cr().modify(|w| {
888 w.set_iodef(!status); 951 w.set_iodef(!status);
889 }); 952 });
890 } 953 }
891} 954}
892 955
893impl<'d, T: Instance> Drop for Tsc<'d, T> { 956impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> {
894 fn drop(&mut self) { 957 fn drop(&mut self) {
895 rcc::disable::<T>(); 958 rcc::disable::<T>();
896 } 959 }
897} 960}
898 961
899pub(crate) trait SealedInstance { 962pub(crate) trait SealedInstance {
900 const REGS: Regs; 963 fn regs() -> crate::pac::tsc::Tsc;
964 fn waker() -> &'static AtomicWaker;
901} 965}
902 966
903/// TSC instance trait 967/// TSC instance trait
904#[allow(private_bounds)] 968#[allow(private_bounds)]
905pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} 969pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {
970 /// Interrupt for this TSC instance
971 type Interrupt: interrupt::typelevel::Interrupt;
972}
906 973
907foreach_peripheral!( 974foreach_interrupt!(
908 (tsc, $inst:ident) => { 975 ($inst:ident, tsc, TSC, GLOBAL, $irq:ident) => {
909 impl SealedInstance for peripherals::$inst { 976 impl Instance for peripherals::$inst {
910 const REGS: Regs = crate::pac::$inst; 977 type Interrupt = crate::interrupt::typelevel::$irq;
911 } 978 }
912 979
913 impl Instance for peripherals::$inst {} 980 impl SealedInstance for peripherals::$inst {
981 fn regs() -> crate::pac::tsc::Tsc {
982 crate::pac::$inst
983 }
984 fn waker() -> &'static AtomicWaker {
985 static WAKER: AtomicWaker = AtomicWaker::new();
986 &WAKER
987 }
988 }
914 }; 989 };
915); 990);
916 991
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 5754f783e..7ed3793a1 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -371,9 +371,12 @@ impl<'d> UartTx<'d, Async> {
371 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { 371 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
372 let r = self.info.regs; 372 let r = self.info.regs;
373 373
374 // Disable Receiver for Half-Duplex mode 374 // Enable Transmitter and disable Receiver for Half-Duplex mode
375 if r.cr3().read().hdsel() { 375 let mut cr1 = r.cr1().read();
376 r.cr1().modify(|reg| reg.set_re(false)); 376 if r.cr3().read().hdsel() && !cr1.te() {
377 cr1.set_te(true);
378 cr1.set_re(false);
379 r.cr1().write_value(cr1);
377 } 380 }
378 381
379 let ch = self.tx_dma.as_mut().unwrap(); 382 let ch = self.tx_dma.as_mut().unwrap();
@@ -474,9 +477,12 @@ impl<'d, M: Mode> UartTx<'d, M> {
474 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 477 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
475 let r = self.info.regs; 478 let r = self.info.regs;
476 479
477 // Disable Receiver for Half-Duplex mode 480 // Enable Transmitter and disable Receiver for Half-Duplex mode
478 if r.cr3().read().hdsel() { 481 let mut cr1 = r.cr1().read();
479 r.cr1().modify(|reg| reg.set_re(false)); 482 if r.cr3().read().hdsel() && !cr1.te() {
483 cr1.set_te(true);
484 cr1.set_re(false);
485 r.cr1().write_value(cr1);
480 } 486 }
481 487
482 for &b in buffer { 488 for &b in buffer {
@@ -561,8 +567,9 @@ impl<'d> UartRx<'d, Async> {
561 ) -> Result<ReadCompletionEvent, Error> { 567 ) -> Result<ReadCompletionEvent, Error> {
562 let r = self.info.regs; 568 let r = self.info.regs;
563 569
564 // Call flush for Half-Duplex mode. It prevents reading of bytes which have just been written. 570 // Call flush for Half-Duplex mode if some bytes were written and flush was not called.
565 if r.cr3().read().hdsel() { 571 // It prevents reading of bytes which have just been written.
572 if r.cr3().read().hdsel() && r.cr1().read().te() {
566 blocking_flush(self.info)?; 573 blocking_flush(self.info)?;
567 } 574 }
568 575
@@ -898,8 +905,9 @@ impl<'d, M: Mode> UartRx<'d, M> {
898 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 905 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
899 let r = self.info.regs; 906 let r = self.info.regs;
900 907
901 // Call flush for Half-Duplex mode. It prevents reading of bytes which have just been written. 908 // Call flush for Half-Duplex mode if some bytes were written and flush was not called.
902 if r.cr3().read().hdsel() { 909 // It prevents reading of bytes which have just been written.
910 if r.cr3().read().hdsel() && r.cr1().read().te() {
903 blocking_flush(self.info)?; 911 blocking_flush(self.info)?;
904 } 912 }
905 913
@@ -1481,10 +1489,19 @@ fn configure(
1481 r.cr1().write(|w| { 1489 r.cr1().write(|w| {
1482 // enable uart 1490 // enable uart
1483 w.set_ue(true); 1491 w.set_ue(true);
1484 // enable transceiver 1492
1485 w.set_te(enable_tx); 1493 if config.half_duplex {
1486 // enable receiver 1494 // The te and re bits will be set by write, read and flush methods.
1487 w.set_re(enable_rx); 1495 // Receiver should be enabled by default for Half-Duplex.
1496 w.set_te(false);
1497 w.set_re(true);
1498 } else {
1499 // enable transceiver
1500 w.set_te(enable_tx);
1501 // enable receiver
1502 w.set_re(enable_rx);
1503 }
1504
1488 // configure word size 1505 // configure word size
1489 // if using odd or even parity it must be configured to 9bits 1506 // if using odd or even parity it must be configured to 9bits
1490 w.set_m0(if config.parity != Parity::ParityNone { 1507 w.set_m0(if config.parity != Parity::ParityNone {
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 9bd403f02..d06ab5e5a 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -29,6 +29,9 @@ reqwless = { version = "0.12.0", features = ["defmt",]}
29serde = { version = "1.0.203", default-features = false, features = ["derive"] } 29serde = { version = "1.0.203", default-features = false, features = ["derive"] }
30serde-json-core = "0.5.1" 30serde-json-core = "0.5.1"
31 31
32# for assign resources example
33assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
34
32#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 35#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
33cortex-m = { version = "0.7.6", features = ["inline-asm"] } 36cortex-m = { version = "0.7.6", features = ["inline-asm"] }
34cortex-m-rt = "0.7.0" 37cortex-m-rt = "0.7.0"
diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs
new file mode 100644
index 000000000..ff6eff4a2
--- /dev/null
+++ b/examples/rp/src/bin/assign_resources.rs
@@ -0,0 +1,79 @@
1//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
2//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)
3//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
4//!
5//! There are basically two ways we demonstrate here:
6//! 1) Assigning resources to a task by passing parts of the peripherals
7//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
8//!
9//! using four LEDs on Pins 10, 11, 20 and 21
10
11#![no_std]
12#![no_main]
13
14use assign_resources::assign_resources;
15use defmt::*;
16use embassy_executor::Spawner;
17use embassy_rp::gpio::{Level, Output};
18use embassy_rp::peripherals::{self, PIN_20, PIN_21};
19use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _};
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 // initialize the peripherals
25 let p = embassy_rp::init(Default::default());
26
27 // 1) Assigning a resource to a task by passing parts of the peripherals.
28 spawner
29 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
30 .unwrap();
31
32 // 2) Using the assign-resources macro to assign resources to a task.
33 // we perform the split, see further below for the definition of the resources struct
34 let r = split_resources!(p);
35 // and then we can use them
36 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
37}
38
39// 1) Assigning a resource to a task by passing parts of the peripherals.
40#[embassy_executor::task]
41async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
42 let mut led_20 = Output::new(pin_20, Level::Low);
43 let mut led_21 = Output::new(pin_21, Level::High);
44
45 loop {
46 info!("toggling leds");
47 led_20.toggle();
48 led_21.toggle();
49 Timer::after_secs(1).await;
50 }
51}
52
53// 2) Using the assign-resources macro to assign resources to a task.
54// first we define the resources we want to assign to the task using the assign_resources! macro
55// basically this will split up the peripherals struct into smaller structs, that we define here
56// naming is up to you, make sure your future self understands what you did here
57assign_resources! {
58 leds: Leds{
59 led_10: PIN_10,
60 led_11: PIN_11,
61 }
62 // add more resources to more structs if needed, for example defining one struct for each task
63}
64// this could be done in another file and imported here, but for the sake of simplicity we do it here
65// see https://github.com/adamgreig/assign-resources for more information
66
67// 2) Using the split resources in a task
68#[embassy_executor::task]
69async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
70 let mut led_10 = Output::new(r.led_10, Level::Low);
71 let mut led_11 = Output::new(r.led_11, Level::High);
72
73 loop {
74 info!("toggling leds");
75 led_10.toggle();
76 led_11.toggle();
77 Timer::after_secs(1).await;
78 }
79}
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
index bd52cadca..def26b53d 100644
--- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs
+++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
@@ -63,7 +63,8 @@ async fn main(spawner: Spawner) {
63 w5500_int, 63 w5500_int,
64 w5500_reset, 64 w5500_reset,
65 ) 65 )
66 .await; 66 .await
67 .unwrap();
67 unwrap!(spawner.spawn(ethernet_task(runner))); 68 unwrap!(spawner.spawn(ethernet_task(runner)));
68 69
69 // Generate random seed 70 // Generate random seed
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
index 3e4fbd2e6..6c4a78361 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
@@ -66,7 +66,8 @@ async fn main(spawner: Spawner) {
66 w5500_int, 66 w5500_int,
67 w5500_reset, 67 w5500_reset,
68 ) 68 )
69 .await; 69 .await
70 .unwrap();
70 unwrap!(spawner.spawn(ethernet_task(runner))); 71 unwrap!(spawner.spawn(ethernet_task(runner)));
71 72
72 // Generate random seed 73 // Generate random seed
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
index 5532851f3..30a3a7463 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
@@ -65,7 +65,8 @@ async fn main(spawner: Spawner) {
65 w5500_int, 65 w5500_int,
66 w5500_reset, 66 w5500_reset,
67 ) 67 )
68 .await; 68 .await
69 .unwrap();
69 unwrap!(spawner.spawn(ethernet_task(runner))); 70 unwrap!(spawner.spawn(ethernet_task(runner)));
70 71
71 // Generate random seed 72 // Generate random seed
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs
index adb1d8941..1613ed887 100644
--- a/examples/rp/src/bin/ethernet_w5500_udp.rs
+++ b/examples/rp/src/bin/ethernet_w5500_udp.rs
@@ -63,7 +63,8 @@ async fn main(spawner: Spawner) {
63 w5500_int, 63 w5500_int,
64 w5500_reset, 64 w5500_reset,
65 ) 65 )
66 .await; 66 .await
67 .unwrap();
67 unwrap!(spawner.spawn(ethernet_task(runner))); 68 unwrap!(spawner.spawn(ethernet_task(runner)));
68 69
69 // Generate random seed 70 // Generate random seed
diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs
new file mode 100644
index 000000000..5416e20ce
--- /dev/null
+++ b/examples/rp/src/bin/sharing.rs
@@ -0,0 +1,150 @@
1//! This example shows some common strategies for sharing resources between tasks.
2//!
3//! We demonstrate five different ways of sharing, covering different use cases:
4//! - Atomics: This method is used for simple values, such as bool and u8..u32
5//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
6//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
7//! The async Mutex has interior mutability built-in, so no RefCell is needed.
8//! - Cell: For sharing Copy types between tasks running on the same executor.
9//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
10//!
11//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
12
13#![no_std]
14#![no_main]
15
16use core::cell::{Cell, RefCell};
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use cortex_m_rt::entry;
20use defmt::info;
21use embassy_executor::{Executor, InterruptExecutor};
22use embassy_rp::clocks::RoscRng;
23use embassy_rp::interrupt::{InterruptExt, Priority};
24use embassy_rp::peripherals::UART0;
25use embassy_rp::uart::{self, InterruptHandler, UartTx};
26use embassy_rp::{bind_interrupts, interrupt};
27use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
28use embassy_sync::{blocking_mutex, mutex};
29use embassy_time::{Duration, Ticker};
30use rand::RngCore;
31use static_cell::{ConstStaticCell, StaticCell};
32use {defmt_rtt as _, panic_probe as _};
33
34type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
35
36struct MyType {
37 inner: u32,
38}
39
40static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
41static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
42
43// Use Atomics for simple values
44static ATOMIC: AtomicU32 = AtomicU32::new(0);
45
46// Use blocking Mutex with Cell/RefCell for sharing non-async things
47static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
48 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
49
50bind_interrupts!(struct Irqs {
51 UART0_IRQ => InterruptHandler<UART0>;
52});
53
54#[interrupt]
55unsafe fn SWI_IRQ_0() {
56 EXECUTOR_HI.on_interrupt()
57}
58
59#[entry]
60fn main() -> ! {
61 let p = embassy_rp::init(Default::default());
62 info!("Here we go!");
63
64 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
65 // Use the async Mutex for sharing async things (built-in interior mutability)
66 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
67 let uart = UART.init(mutex::Mutex::new(uart));
68
69 // High-priority executor: runs in interrupt mode
70 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
71 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
72 spawner.must_spawn(task_a(uart));
73
74 // Low priority executor: runs in thread mode
75 let executor = EXECUTOR_LOW.init(Executor::new());
76 executor.run(|spawner| {
77 // No Mutex needed when sharing between tasks running on the same executor
78
79 // Use Cell for Copy-types
80 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
81 let cell = CELL.take();
82
83 // Use RefCell for &mut access
84 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
85 let ref_cell = REF_CELL.take();
86
87 spawner.must_spawn(task_b(uart, cell, ref_cell));
88 spawner.must_spawn(task_c(cell, ref_cell));
89 });
90}
91
92#[embassy_executor::task]
93async fn task_a(uart: &'static UartAsyncMutex) {
94 let mut ticker = Ticker::every(Duration::from_secs(1));
95 loop {
96 let random = RoscRng.next_u32();
97
98 {
99 let mut uart = uart.lock().await;
100 uart.write(b"task a").await.unwrap();
101 // The uart lock is released when it goes out of scope
102 }
103
104 ATOMIC.store(random, Ordering::Relaxed);
105
106 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
107
108 ticker.next().await;
109 }
110}
111
112#[embassy_executor::task]
113async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
114 let mut ticker = Ticker::every(Duration::from_secs(1));
115 loop {
116 let random = RoscRng.next_u32();
117
118 uart.lock().await.write(b"task b").await.unwrap();
119
120 cell.set(random.to_be_bytes());
121
122 ref_cell.borrow_mut().inner = random;
123
124 ticker.next().await;
125 }
126}
127
128#[embassy_executor::task]
129async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
130 let mut ticker = Ticker::every(Duration::from_secs(1));
131 loop {
132 info!("=======================");
133
134 let atomic_val = ATOMIC.load(Ordering::Relaxed);
135 info!("atomic: {}", atomic_val);
136
137 MUTEX_BLOCKING.lock(|x| {
138 let val = x.borrow().inner;
139 info!("blocking mutex: {}", val);
140 });
141
142 let cell_val = cell.get();
143 info!("cell: {:?}", cell_val);
144
145 let ref_cell_val = ref_cell.borrow().inner;
146 info!("ref_cell: {:?}", ref_cell_val);
147
148 ticker.next().await;
149 }
150}
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs
index 9473b7b7f..423d29225 100644
--- a/examples/stm32f4/src/bin/adc.rs
+++ b/examples/stm32f4/src/bin/adc.rs
@@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) {
23 // Startup delay can be combined to the maximum of either 23 // Startup delay can be combined to the maximum of either
24 delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); 24 delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));
25 25
26 let vrefint_sample = adc.read(&mut vrefint); 26 let vrefint_sample = adc.blocking_read(&mut vrefint);
27 27
28 let convert_to_millivolts = |sample| { 28 let convert_to_millivolts = |sample| {
29 // From http://www.st.com/resource/en/datasheet/DM00071990.pdf 29 // From http://www.st.com/resource/en/datasheet/DM00071990.pdf
@@ -50,16 +50,16 @@ async fn main(_spawner: Spawner) {
50 50
51 loop { 51 loop {
52 // Read pin 52 // Read pin
53 let v = adc.read(&mut pin); 53 let v = adc.blocking_read(&mut pin);
54 info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); 54 info!("PC1: {} ({} mV)", v, convert_to_millivolts(v));
55 55
56 // Read internal temperature 56 // Read internal temperature
57 let v = adc.read(&mut temp); 57 let v = adc.blocking_read(&mut temp);
58 let celcius = convert_to_celcius(v); 58 let celcius = convert_to_celcius(v);
59 info!("Internal temp: {} ({} C)", v, celcius); 59 info!("Internal temp: {} ({} C)", v, celcius);
60 60
61 // Read internal voltage reference 61 // Read internal voltage reference
62 let v = adc.read(&mut vrefint); 62 let v = adc.blocking_read(&mut vrefint);
63 info!("VrefInt: {}", v); 63 info!("VrefInt: {}", v);
64 64
65 Timer::after_millis(100).await; 65 Timer::after_millis(100).await;
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
new file mode 100644
index 000000000..43a761e6d
--- /dev/null
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -0,0 +1,83 @@
1#![no_std]
2#![no_main]
3use cortex_m::singleton;
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence};
7use embassy_stm32::Peripherals;
8use embassy_time::Instant;
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default());
14 spawner.must_spawn(adc_task(p));
15}
16
17#[embassy_executor::task]
18async fn adc_task(mut p: Peripherals) {
19 const ADC_BUF_SIZE: usize = 1024;
20 let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
21 let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
22
23 let adc = Adc::new(p.ADC1);
24 let adc2 = Adc::new(p.ADC2);
25
26 let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data);
27 let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2);
28
29 adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
30 adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112);
31 adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112);
32 adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112);
33
34 // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
35 // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of
36 // what channel is at what index is lost. The buffer must be cleared and reset. This *is* handled here, but allowing this to happen will cause
37 // a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted.
38
39 // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most
40 // frequently.
41 let mut tic = Instant::now();
42 let mut buffer1 = [0u16; 512];
43 let mut buffer2 = [0u16; 512];
44 let _ = adc.start();
45 let _ = adc2.start();
46 loop {
47 match adc.read(&mut buffer1).await {
48 Ok(_data) => {
49 let toc = Instant::now();
50 info!(
51 "\n adc1: {} dt = {}, n = {}",
52 buffer1[0..16],
53 (toc - tic).as_micros(),
54 _data
55 );
56 tic = toc;
57 }
58 Err(e) => {
59 warn!("Error: {:?}", e);
60 buffer1 = [0u16; 512];
61 let _ = adc.start();
62 }
63 }
64
65 match adc2.read(&mut buffer2).await {
66 Ok(_data) => {
67 let toc = Instant::now();
68 info!(
69 "\n adc2: {} dt = {}, n = {}",
70 buffer2[0..16],
71 (toc - tic).as_micros(),
72 _data
73 );
74 tic = toc;
75 }
76 Err(e) => {
77 warn!("Error: {:?}", e);
78 buffer2 = [0u16; 512];
79 let _ = adc2.start();
80 }
81 }
82 }
83}
diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs
index c51111110..3c770a873 100644
--- a/examples/stm32f4/src/bin/eth_w5500.rs
+++ b/examples/stm32f4/src/bin/eth_w5500.rs
@@ -80,7 +80,9 @@ async fn main(spawner: Spawner) -> ! {
80 let mac_addr = [0x02, 234, 3, 4, 82, 231]; 80 let mac_addr = [0x02, 234, 3, 4, 82, 231];
81 static STATE: StaticCell<State<2, 2>> = StaticCell::new(); 81 static STATE: StaticCell<State<2, 2>> = StaticCell::new();
82 let state = STATE.init(State::<2, 2>::new()); 82 let state = STATE.init(State::<2, 2>::new());
83 let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset).await; 83 let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset)
84 .await
85 .unwrap();
84 unwrap!(spawner.spawn(ethernet_task(runner))); 86 unwrap!(spawner.spawn(ethernet_task(runner)));
85 87
86 let config = embassy_net::Config::dhcpv4(Default::default()); 88 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs
index 641157960..6689e3b5d 100644
--- a/examples/stm32f7/src/bin/adc.rs
+++ b/examples/stm32f7/src/bin/adc.rs
@@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
16 let mut pin = p.PA3; 16 let mut pin = p.PA3;
17 17
18 let mut vrefint = adc.enable_vrefint(); 18 let mut vrefint = adc.enable_vrefint();
19 let vrefint_sample = adc.read(&mut vrefint); 19 let vrefint_sample = adc.blocking_read(&mut vrefint);
20 let convert_to_millivolts = |sample| { 20 let convert_to_millivolts = |sample| {
21 // From http://www.st.com/resource/en/datasheet/DM00273119.pdf 21 // From http://www.st.com/resource/en/datasheet/DM00273119.pdf
22 // 6.3.27 Reference voltage 22 // 6.3.27 Reference voltage
@@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) {
26 }; 26 };
27 27
28 loop { 28 loop {
29 let v = adc.read(&mut pin); 29 let v = adc.blocking_read(&mut pin);
30 info!("--> {} - {} mV", v, convert_to_millivolts(v)); 30 info!("--> {} - {} mV", v, convert_to_millivolts(v));
31 Timer::after_millis(100).await; 31 Timer::after_millis(100).await;
32 } 32 }
diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs
index a35119e3d..6c7f3b48a 100644
--- a/examples/stm32g0/src/bin/adc.rs
+++ b/examples/stm32g0/src/bin/adc.rs
@@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
17 let mut pin = p.PA1; 17 let mut pin = p.PA1;
18 18
19 let mut vrefint = adc.enable_vrefint(); 19 let mut vrefint = adc.enable_vrefint();
20 let vrefint_sample = adc.read(&mut vrefint); 20 let vrefint_sample = adc.blocking_read(&mut vrefint);
21 let convert_to_millivolts = |sample| { 21 let convert_to_millivolts = |sample| {
22 // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf 22 // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
23 // 6.3.3 Embedded internal reference voltage 23 // 6.3.3 Embedded internal reference voltage
@@ -27,7 +27,7 @@ async fn main(_spawner: Spawner) {
27 }; 27 };
28 28
29 loop { 29 loop {
30 let v = adc.read(&mut pin); 30 let v = adc.blocking_read(&mut pin);
31 info!("--> {} - {} mV", v, convert_to_millivolts(v)); 31 info!("--> {} - {} mV", v, convert_to_millivolts(v));
32 Timer::after_millis(100).await; 32 Timer::after_millis(100).await;
33 } 33 }
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs
new file mode 100644
index 000000000..3713e5a21
--- /dev/null
+++ b/examples/stm32g0/src/bin/adc_dma.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};
7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _};
9
10static mut DMA_BUF: [u16; 2] = [0; 2];
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let mut read_buffer = unsafe { &mut DMA_BUF[..] };
15
16 let p = embassy_stm32::init(Default::default());
17
18 info!("Hello World!");
19
20 let mut adc = Adc::new(p.ADC1);
21
22 let mut dma = p.DMA1_CH1;
23 let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
24 let mut pa0 = p.PA0.degrade_adc();
25
26 loop {
27 adc.read(
28 &mut dma,
29 [
30 (&mut vrefint_channel, SampleTime::CYCLES160_5),
31 (&mut pa0, SampleTime::CYCLES160_5),
32 ]
33 .into_iter(),
34 &mut read_buffer,
35 )
36 .await;
37
38 let vrefint = read_buffer[0];
39 let measured = read_buffer[1];
40 info!("vrefint: {}", vrefint);
41 info!("measured: {}", measured);
42 Timer::after_millis(500).await;
43 }
44}
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs
new file mode 100644
index 000000000..9c5dd872a
--- /dev/null
+++ b/examples/stm32g0/src/bin/adc_oversampling.rs
@@ -0,0 +1,43 @@
1//! adc oversampling example
2//!
3//! This example uses adc oversampling to achieve 16bit data
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_stm32::adc::{Adc, SampleTime};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let p = embassy_stm32::init(Default::default());
17 info!("Adc oversample test");
18
19 let mut adc = Adc::new(p.ADC1);
20 adc.set_sample_time(SampleTime::CYCLES1_5);
21 let mut pin = p.PA1;
22
23 // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
24 // page373 15.8 Oversampler
25 // Table 76. Maximum output results vs N and M. Grayed values indicates truncation
26 // 0x00 oversampling ratio X2
27 // 0x01 oversampling ratio X4
28 // 0x02 oversampling ratio X8
29 // 0x03 oversampling ratio X16
30 // 0x04 oversampling ratio X32
31 // 0x05 oversampling ratio X64
32 // 0x06 oversampling ratio X128
33 // 0x07 oversampling ratio X256
34 adc.set_oversampling_ratio(0x03);
35 adc.set_oversampling_shift(0b0000);
36 adc.oversampling_enable(true);
37
38 loop {
39 let v = adc.blocking_read(&mut pin);
40 info!("--> {} ", v); //max 65520 = 0xFFF0
41 Timer::after_millis(100).await;
42 }
43}
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs
index 3de38cbd6..adca846d8 100644
--- a/examples/stm32g4/src/bin/adc.rs
+++ b/examples/stm32g4/src/bin/adc.rs
@@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) {
32 adc.set_sample_time(SampleTime::CYCLES24_5); 32 adc.set_sample_time(SampleTime::CYCLES24_5);
33 33
34 loop { 34 loop {
35 let measured = adc.read(&mut p.PA7); 35 let measured = adc.blocking_read(&mut p.PA7);
36 info!("measured: {}", measured); 36 info!("measured: {}", measured);
37 Timer::after_millis(500).await; 37 Timer::after_millis(500).await;
38 } 38 }
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 0584f3916..78343b74f 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -34,6 +34,7 @@ stm32-fmc = "0.3.0"
34embedded-storage = "0.3.1" 34embedded-storage = "0.3.1"
35static_cell = "2" 35static_cell = "2"
36chrono = { version = "^0.4", default-features = false } 36chrono = { version = "^0.4", default-features = false }
37grounded = "0.2.0"
37 38
38# cargo build/run 39# cargo build/run
39[profile.dev] 40[profile.dev]
diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs
index e9a857a74..98504ddf6 100644
--- a/examples/stm32h7/src/bin/adc.rs
+++ b/examples/stm32h7/src/bin/adc.rs
@@ -51,9 +51,9 @@ async fn main(_spawner: Spawner) {
51 let mut vrefint_channel = adc.enable_vrefint(); 51 let mut vrefint_channel = adc.enable_vrefint();
52 52
53 loop { 53 loop {
54 let vrefint = adc.read(&mut vrefint_channel); 54 let vrefint = adc.blocking_read(&mut vrefint_channel);
55 info!("vrefint: {}", vrefint); 55 info!("vrefint: {}", vrefint);
56 let measured = adc.read(&mut p.PC0); 56 let measured = adc.blocking_read(&mut p.PC0);
57 info!("measured: {}", measured); 57 info!("measured: {}", measured);
58 Timer::after_millis(500).await; 58 Timer::after_millis(500).await;
59 } 59 }
diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs
new file mode 100644
index 000000000..0b905d227
--- /dev/null
+++ b/examples/stm32h7/src/bin/adc_dma.rs
@@ -0,0 +1,76 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};
7use embassy_stm32::Config;
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _};
10
11#[link_section = ".ram_d3"]
12static mut DMA_BUF: [u16; 2] = [0; 2];
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let mut read_buffer = unsafe { &mut DMA_BUF[..] };
17
18 let mut config = Config::default();
19 {
20 use embassy_stm32::rcc::*;
21 config.rcc.hsi = Some(HSIPrescaler::DIV1);
22 config.rcc.csi = true;
23 config.rcc.pll1 = Some(Pll {
24 source: PllSource::HSI,
25 prediv: PllPreDiv::DIV4,
26 mul: PllMul::MUL50,
27 divp: Some(PllDiv::DIV2),
28 divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q
29 divr: None,
30 });
31 config.rcc.pll2 = Some(Pll {
32 source: PllSource::HSI,
33 prediv: PllPreDiv::DIV4,
34 mul: PllMul::MUL50,
35 divp: Some(PllDiv::DIV8), // 100mhz
36 divq: None,
37 divr: None,
38 });
39 config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
40 config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
41 config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
42 config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
43 config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
44 config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
45 config.rcc.voltage_scale = VoltageScale::Scale1;
46 config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;
47 }
48 let p = embassy_stm32::init(config);
49
50 info!("Hello World!");
51
52 let mut adc = Adc::new(p.ADC3);
53
54 let mut dma = p.DMA1_CH1;
55 let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
56 let mut pc0 = p.PC0.degrade_adc();
57
58 loop {
59 adc.read(
60 &mut dma,
61 [
62 (&mut vrefint_channel, SampleTime::CYCLES387_5),
63 (&mut pc0, SampleTime::CYCLES810_5),
64 ]
65 .into_iter(),
66 &mut read_buffer,
67 )
68 .await;
69
70 let vrefint = read_buffer[0];
71 let measured = read_buffer[1];
72 info!("vrefint: {}", vrefint);
73 info!("measured: {}", measured);
74 Timer::after_millis(500).await;
75 }
76}
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs
new file mode 100644
index 000000000..f6735e235
--- /dev/null
+++ b/examples/stm32h7/src/bin/sai.rs
@@ -0,0 +1,186 @@
1//! Daisy Seed rev.7(with PCM3060 codec)
2//! https://electro-smith.com/products/daisy-seed
3#![no_std]
4#![no_main]
5
6use embassy_executor::Spawner;
7use grounded::uninit::GroundedArrayCell;
8use hal::rcc::*;
9use hal::sai::*;
10use hal::time::Hertz;
11use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};
12
13const BLOCK_LENGTH: usize = 32; // 32 samples
14const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * 2; // 2 channels
15const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks
16const SAMPLE_RATE: u32 = 48000;
17
18//DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions
19#[link_section = ".sram1_bss"]
20static mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
21#[link_section = ".sram1_bss"]
22static mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let mut config = hal::Config::default();
27 config.rcc.pll1 = Some(Pll {
28 source: PllSource::HSE,
29 prediv: PllPreDiv::DIV4,
30 mul: PllMul::MUL200,
31 divp: Some(PllDiv::DIV2),
32 divq: Some(PllDiv::DIV5),
33 divr: Some(PllDiv::DIV2),
34 });
35 config.rcc.pll3 = Some(Pll {
36 source: PllSource::HSE,
37 prediv: PllPreDiv::DIV6,
38 mul: PllMul::MUL295,
39 divp: Some(PllDiv::DIV16),
40 divq: Some(PllDiv::DIV4),
41 divr: Some(PllDiv::DIV32),
42 });
43 config.rcc.sys = Sysclk::PLL1_P;
44 config.rcc.mux.sai1sel = hal::pac::rcc::vals::Saisel::PLL3_P;
45 config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
46 config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
47 config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
48 config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
49 config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
50 config.rcc.hse = Some(Hse {
51 freq: Hertz::mhz(16),
52 mode: HseMode::Oscillator,
53 });
54
55 let p = hal::init(config);
56
57 let (sub_block_tx, sub_block_rx) = hal::sai::split_subblocks(p.SAI1);
58 let kernel_clock = hal::rcc::frequency::<hal::peripherals::SAI1>().0;
59 let mclk_div = mclk_div_from_u8((kernel_clock / (SAMPLE_RATE * 256)) as u8);
60
61 let mut tx_config = hal::sai::Config::default();
62 tx_config.mode = Mode::Master;
63 tx_config.tx_rx = TxRx::Transmitter;
64 tx_config.sync_output = true;
65 tx_config.clock_strobe = ClockStrobe::Falling;
66 tx_config.master_clock_divider = mclk_div;
67 tx_config.stereo_mono = StereoMono::Stereo;
68 tx_config.data_size = DataSize::Data24;
69 tx_config.bit_order = BitOrder::MsbFirst;
70 tx_config.frame_sync_polarity = FrameSyncPolarity::ActiveHigh;
71 tx_config.frame_sync_offset = FrameSyncOffset::OnFirstBit;
72 tx_config.frame_length = 64;
73 tx_config.frame_sync_active_level_length = embassy_stm32::sai::word::U7(32);
74 tx_config.fifo_threshold = FifoThreshold::Quarter;
75
76 let mut rx_config = tx_config.clone();
77 rx_config.mode = Mode::Slave;
78 rx_config.tx_rx = TxRx::Receiver;
79 rx_config.sync_input = SyncInput::Internal;
80 rx_config.clock_strobe = ClockStrobe::Rising;
81 rx_config.sync_output = false;
82
83 let tx_buffer: &mut [u32] = unsafe {
84 TX_BUFFER.initialize_all_copied(0);
85 let (ptr, len) = TX_BUFFER.get_ptr_len();
86 core::slice::from_raw_parts_mut(ptr, len)
87 };
88
89 let mut sai_transmitter = Sai::new_asynchronous_with_mclk(
90 sub_block_tx,
91 p.PE5,
92 p.PE6,
93 p.PE4,
94 p.PE2,
95 p.DMA1_CH0,
96 tx_buffer,
97 tx_config,
98 );
99
100 let rx_buffer: &mut [u32] = unsafe {
101 RX_BUFFER.initialize_all_copied(0);
102 let (ptr, len) = RX_BUFFER.get_ptr_len();
103 core::slice::from_raw_parts_mut(ptr, len)
104 };
105
106 let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config);
107
108 sai_receiver.start();
109 sai_transmitter.start();
110
111 let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];
112
113 loop {
114 sai_receiver.read(&mut buf).await.unwrap();
115 sai_transmitter.write(&buf).await.unwrap();
116 }
117}
118
119const fn mclk_div_from_u8(v: u8) -> MasterClockDivider {
120 match v {
121 1 => MasterClockDivider::Div1,
122 2 => MasterClockDivider::Div2,
123 3 => MasterClockDivider::Div3,
124 4 => MasterClockDivider::Div4,
125 5 => MasterClockDivider::Div5,
126 6 => MasterClockDivider::Div6,
127 7 => MasterClockDivider::Div7,
128 8 => MasterClockDivider::Div8,
129 9 => MasterClockDivider::Div9,
130 10 => MasterClockDivider::Div10,
131 11 => MasterClockDivider::Div11,
132 12 => MasterClockDivider::Div12,
133 13 => MasterClockDivider::Div13,
134 14 => MasterClockDivider::Div14,
135 15 => MasterClockDivider::Div15,
136 16 => MasterClockDivider::Div16,
137 17 => MasterClockDivider::Div17,
138 18 => MasterClockDivider::Div18,
139 19 => MasterClockDivider::Div19,
140 20 => MasterClockDivider::Div20,
141 21 => MasterClockDivider::Div21,
142 22 => MasterClockDivider::Div22,
143 23 => MasterClockDivider::Div23,
144 24 => MasterClockDivider::Div24,
145 25 => MasterClockDivider::Div25,
146 26 => MasterClockDivider::Div26,
147 27 => MasterClockDivider::Div27,
148 28 => MasterClockDivider::Div28,
149 29 => MasterClockDivider::Div29,
150 30 => MasterClockDivider::Div30,
151 31 => MasterClockDivider::Div31,
152 32 => MasterClockDivider::Div32,
153 33 => MasterClockDivider::Div33,
154 34 => MasterClockDivider::Div34,
155 35 => MasterClockDivider::Div35,
156 36 => MasterClockDivider::Div36,
157 37 => MasterClockDivider::Div37,
158 38 => MasterClockDivider::Div38,
159 39 => MasterClockDivider::Div39,
160 40 => MasterClockDivider::Div40,
161 41 => MasterClockDivider::Div41,
162 42 => MasterClockDivider::Div42,
163 43 => MasterClockDivider::Div43,
164 44 => MasterClockDivider::Div44,
165 45 => MasterClockDivider::Div45,
166 46 => MasterClockDivider::Div46,
167 47 => MasterClockDivider::Div47,
168 48 => MasterClockDivider::Div48,
169 49 => MasterClockDivider::Div49,
170 50 => MasterClockDivider::Div50,
171 51 => MasterClockDivider::Div51,
172 52 => MasterClockDivider::Div52,
173 53 => MasterClockDivider::Div53,
174 54 => MasterClockDivider::Div54,
175 55 => MasterClockDivider::Div55,
176 56 => MasterClockDivider::Div56,
177 57 => MasterClockDivider::Div57,
178 58 => MasterClockDivider::Div58,
179 59 => MasterClockDivider::Div59,
180 60 => MasterClockDivider::Div60,
181 61 => MasterClockDivider::Div61,
182 62 => MasterClockDivider::Div62,
183 63 => MasterClockDivider::Div63,
184 _ => panic!(),
185 }
186}
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs
index b2e941078..43fb6b41c 100644
--- a/examples/stm32h7/src/bin/spi_bdma.rs
+++ b/examples/stm32h7/src/bin/spi_bdma.rs
@@ -10,18 +10,24 @@ use embassy_executor::Executor;
10use embassy_stm32::mode::Async; 10use embassy_stm32::mode::Async;
11use embassy_stm32::time::mhz; 11use embassy_stm32::time::mhz;
12use embassy_stm32::{spi, Config}; 12use embassy_stm32::{spi, Config};
13use grounded::uninit::GroundedArrayCell;
13use heapless::String; 14use heapless::String;
14use static_cell::StaticCell; 15use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
16 17
17// Defined in memory.x 18// Defined in memory.x
18#[link_section = ".ram_d3"] 19#[link_section = ".ram_d3"]
19static mut RAM_D3: [u8; 64 * 1024] = [0u8; 64 * 1024]; 20static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit();
20 21
21#[embassy_executor::task] 22#[embassy_executor::task]
22async fn main_task(mut spi: spi::Spi<'static, Async>) { 23async fn main_task(mut spi: spi::Spi<'static, Async>) {
23 let read_buffer = unsafe { &mut RAM_D3[0..128] }; 24 let (read_buffer, write_buffer) = unsafe {
24 let write_buffer = unsafe { &mut RAM_D3[128..256] }; 25 RAM_D3.initialize_all_copied(0);
26 (
27 RAM_D3.get_subslice_mut_unchecked(0, 128),
28 RAM_D3.get_subslice_mut_unchecked(128, 128),
29 )
30 };
25 31
26 for n in 0u32.. { 32 for n in 0u32.. {
27 let mut write: String<128> = String::new(); 33 let mut write: String<128> = String::new();
diff --git a/examples/stm32h735/.cargo/config.toml b/examples/stm32h735/.cargo/config.toml
new file mode 100644
index 000000000..95536c6a8
--- /dev/null
+++ b/examples/stm32h735/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip STM32H735IGKx'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml
new file mode 100644
index 000000000..fc21cc894
--- /dev/null
+++ b/examples/stm32h735/Cargo.toml
@@ -0,0 +1,61 @@
1[package]
2edition = "2021"
3name = "embassy-stm32h735-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
9embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14
15defmt = "0.3"
16defmt-rtt = "0.4"
17
18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0"
20panic-probe = { version = "0.3", features = ["print-defmt"] }
21heapless = { version = "0.8", default-features = false }
22embedded-graphics = { version = "0.8.1" }
23tinybmp = { version = "0.5" }
24
25# cargo build/run
26[profile.dev]
27codegen-units = 1
28debug = 2
29debug-assertions = true # <-
30incremental = false
31opt-level = 3 # <-
32overflow-checks = true # <-
33
34# cargo test
35[profile.test]
36codegen-units = 1
37debug = 2
38debug-assertions = true # <-
39incremental = false
40opt-level = 3 # <-
41overflow-checks = true # <-
42
43# cargo build/run --release
44[profile.release]
45codegen-units = 1
46debug = 2
47debug-assertions = false # <-
48incremental = false
49lto = 'fat'
50opt-level = 3 # <-
51overflow-checks = false # <-
52
53# cargo test --release
54[profile.bench]
55codegen-units = 1
56debug = 2
57debug-assertions = false # <-
58incremental = false
59lto = 'fat'
60opt-level = 3 # <-
61overflow-checks = false # <-
diff --git a/examples/stm32h735/build.rs b/examples/stm32h735/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/stm32h735/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/stm32h735/memory.x b/examples/stm32h735/memory.x
new file mode 100644
index 000000000..3a70d24d2
--- /dev/null
+++ b/examples/stm32h735/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x08000000, LENGTH = 1024K
4 RAM : ORIGIN = 0x24000000, LENGTH = 320K
5} \ No newline at end of file
diff --git a/examples/stm32h735/src/bin/ferris.bmp b/examples/stm32h735/src/bin/ferris.bmp
new file mode 100644
index 000000000..7a222ab84
--- /dev/null
+++ b/examples/stm32h735/src/bin/ferris.bmp
Binary files differ
diff --git a/examples/stm32h735/src/bin/ltdc.rs b/examples/stm32h735/src/bin/ltdc.rs
new file mode 100644
index 000000000..a36fdef2c
--- /dev/null
+++ b/examples/stm32h735/src/bin/ltdc.rs
@@ -0,0 +1,467 @@
1#![no_std]
2#![no_main]
3#![macro_use]
4#![allow(static_mut_refs)]
5
6/// This example demonstrates the LTDC lcd display peripheral and was tested to run on an stm32h735g-dk (embassy-stm32 feature "stm32h735ig" and probe-rs chip "STM32H735IGKx")
7/// Even though the dev kit has 16MB of attached PSRAM this example uses the 320KB of internal AXIS RAM found on the mcu itself to make the example more standalone and portable.
8/// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level.
9/// The example bounces a ferris crab bitmap around the screen while blinking an led on another task
10///
11use bouncy_box::BouncyBox;
12use defmt::{info, unwrap};
13use embassy_executor::Spawner;
14use embassy_stm32::gpio::{Level, Output, Speed};
15use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};
16use embassy_stm32::{bind_interrupts, peripherals};
17use embassy_time::{Duration, Timer};
18use embedded_graphics::draw_target::DrawTarget;
19use embedded_graphics::geometry::{OriginDimensions, Point, Size};
20use embedded_graphics::image::Image;
21use embedded_graphics::pixelcolor::raw::RawU24;
22use embedded_graphics::pixelcolor::Rgb888;
23use embedded_graphics::prelude::*;
24use embedded_graphics::primitives::Rectangle;
25use embedded_graphics::Pixel;
26use heapless::{Entry, FnvIndexMap};
27use tinybmp::Bmp;
28use {defmt_rtt as _, panic_probe as _};
29
30const DISPLAY_WIDTH: usize = 480;
31const DISPLAY_HEIGHT: usize = 272;
32const MY_TASK_POOL_SIZE: usize = 2;
33
34// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu
35pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
36pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
37
38bind_interrupts!(struct Irqs {
39 LTDC => ltdc::InterruptHandler<peripherals::LTDC>;
40});
41
42const NUM_COLORS: usize = 256;
43
44#[embassy_executor::main]
45async fn main(spawner: Spawner) {
46 let p = rcc_setup::stm32h735g_init();
47
48 // blink the led on another task
49 let led = Output::new(p.PC3, Level::High, Speed::Low);
50 unwrap!(spawner.spawn(led_task(led)));
51
52 // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example
53 const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization
54 const RK043FN48H_HBP: u16 = 13; // Horizontal back porch
55 const RK043FN48H_HFP: u16 = 32; // Horizontal front porch
56 const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization
57 const RK043FN48H_VBP: u16 = 2; // Vertical back porch
58 const RK043FN48H_VFP: u16 = 2; // Vertical front porch
59
60 let ltdc_config = LtdcConfiguration {
61 active_width: DISPLAY_WIDTH as _,
62 active_height: DISPLAY_HEIGHT as _,
63 h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init
64 h_front_porch: RK043FN48H_HFP,
65 v_back_porch: RK043FN48H_VBP,
66 v_front_porch: RK043FN48H_VFP,
67 h_sync: RK043FN48H_HSYNC,
68 v_sync: RK043FN48H_VSYNC,
69 h_sync_polarity: PolarityActive::ActiveLow,
70 v_sync_polarity: PolarityActive::ActiveLow,
71 data_enable_polarity: PolarityActive::ActiveHigh,
72 pixel_clock_polarity: PolarityEdge::FallingEdge,
73 };
74
75 info!("init ltdc");
76 let mut ltdc = Ltdc::new_with_pins(
77 p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0,
78 p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15,
79 );
80 ltdc.init(&ltdc_config);
81
82 // we only need to draw on one layer for this example (not to be confused with the double buffer)
83 info!("enable bottom layer");
84 let layer_config = LtdcLayerConfig {
85 pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel
86 layer: LtdcLayer::Layer1,
87 window_x0: 0,
88 window_x1: DISPLAY_WIDTH as _,
89 window_y0: 0,
90 window_y1: DISPLAY_HEIGHT as _,
91 };
92
93 let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap();
94 let color_map = build_color_lookup_map(&ferris_bmp);
95 let clut = build_clut(&color_map);
96
97 // enable the bottom layer with a 256 color lookup table
98 ltdc.init_layer(&layer_config, Some(&clut));
99
100 // Safety: the DoubleBuffer controls access to the statically allocated frame buffers
101 // and it is the only thing that mutates their content
102 let mut double_buffer = DoubleBuffer::new(
103 unsafe { FB1.as_mut() },
104 unsafe { FB2.as_mut() },
105 layer_config,
106 color_map,
107 );
108
109 // this allows us to perform some simple animation for every frame
110 let mut bouncy_box = BouncyBox::new(
111 ferris_bmp.bounding_box(),
112 Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),
113 2,
114 );
115
116 loop {
117 // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen
118 double_buffer.clear();
119 let position = bouncy_box.next_point();
120 let ferris = Image::new(&ferris_bmp, position);
121 unwrap!(ferris.draw(&mut double_buffer));
122
123 // perform async dma data transfer to the lcd screen
124 unwrap!(double_buffer.swap(&mut ltdc).await);
125 }
126}
127
128/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.
129fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {
130 let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new();
131 let mut counter: u8 = 0;
132
133 // add black to position 0
134 color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();
135 counter += 1;
136
137 for Pixel(_point, color) in bmp.pixels() {
138 let raw = color.into_storage();
139 if let Entry::Vacant(v) = color_map.entry(raw) {
140 v.insert(counter).expect("more than 256 colors detected");
141 counter += 1;
142 }
143 }
144 color_map
145}
146
147/// builds the color look-up table from the color map provided
148fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {
149 let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];
150 for (color, index) in color_map.iter() {
151 let color = Rgb888::from(RawU24::new(*color));
152 clut[*index as usize] = ltdc::RgbColor {
153 red: color.r(),
154 green: color.g(),
155 blue: color.b(),
156 };
157 }
158
159 clut
160}
161
162#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]
163async fn led_task(mut led: Output<'static>) {
164 let mut counter = 0;
165 loop {
166 info!("blink: {}", counter);
167 counter += 1;
168
169 // on
170 led.set_low();
171 Timer::after(Duration::from_millis(50)).await;
172
173 // off
174 led.set_high();
175 Timer::after(Duration::from_millis(450)).await;
176 }
177}
178
179pub type TargetPixelType = u8;
180
181// A simple double buffer
182pub struct DoubleBuffer {
183 buf0: &'static mut [TargetPixelType],
184 buf1: &'static mut [TargetPixelType],
185 is_buf0: bool,
186 layer_config: LtdcLayerConfig,
187 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
188}
189
190impl DoubleBuffer {
191 pub fn new(
192 buf0: &'static mut [TargetPixelType],
193 buf1: &'static mut [TargetPixelType],
194 layer_config: LtdcLayerConfig,
195 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
196 ) -> Self {
197 Self {
198 buf0,
199 buf1,
200 is_buf0: true,
201 layer_config,
202 color_map,
203 }
204 }
205
206 pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {
207 if self.is_buf0 {
208 (&self.color_map, self.buf0)
209 } else {
210 (&self.color_map, self.buf1)
211 }
212 }
213
214 pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {
215 let (_, buf) = self.current();
216 let frame_buffer = buf.as_ptr();
217 self.is_buf0 = !self.is_buf0;
218 ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await
219 }
220
221 /// Clears the buffer
222 pub fn clear(&mut self) {
223 let (color_map, buf) = self.current();
224 let black = Rgb888::new(0, 0, 0).into_storage();
225 let color_index = color_map.get(&black).expect("no black found in the color map");
226
227 for a in buf.iter_mut() {
228 *a = *color_index; // solid black
229 }
230 }
231}
232
233// Implement DrawTarget for
234impl DrawTarget for DoubleBuffer {
235 type Color = Rgb888;
236 type Error = ();
237
238 /// Draw a pixel
239 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
240 where
241 I: IntoIterator<Item = Pixel<Self::Color>>,
242 {
243 let size = self.size();
244 let width = size.width as i32;
245 let height = size.height as i32;
246 let (color_map, buf) = self.current();
247
248 for pixel in pixels {
249 let Pixel(point, color) = pixel;
250
251 if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {
252 let index = point.y * width + point.x;
253 let raw_color = color.into_storage();
254
255 match color_map.get(&raw_color) {
256 Some(x) => {
257 buf[index as usize] = *x;
258 }
259 None => panic!("color not found in color map: {}", raw_color),
260 };
261 } else {
262 // Ignore invalid points
263 }
264 }
265
266 Ok(())
267 }
268}
269
270impl OriginDimensions for DoubleBuffer {
271 /// Return the size of the display
272 fn size(&self) -> Size {
273 Size::new(
274 (self.layer_config.window_x1 - self.layer_config.window_x0) as _,
275 (self.layer_config.window_y1 - self.layer_config.window_y0) as _,
276 )
277 }
278}
279
280mod rcc_setup {
281
282 use embassy_stm32::rcc::{Hse, HseMode, *};
283 use embassy_stm32::time::Hertz;
284 use embassy_stm32::{Config, Peripherals};
285
286 /// Sets up clocks for the stm32h735g mcu
287 /// change this if you plan to use a different microcontroller
288 pub fn stm32h735g_init() -> Peripherals {
289 /*
290 https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c
291 @brief System Clock Configuration
292 The system Clock is configured as follow :
293 System Clock source = PLL (HSE)
294 SYSCLK(Hz) = 520000000 (CPU Clock)
295 HCLK(Hz) = 260000000 (AXI and AHBs Clock)
296 AHB Prescaler = 2
297 D1 APB3 Prescaler = 2 (APB3 Clock 130MHz)
298 D2 APB1 Prescaler = 2 (APB1 Clock 130MHz)
299 D2 APB2 Prescaler = 2 (APB2 Clock 130MHz)
300 D3 APB4 Prescaler = 2 (APB4 Clock 130MHz)
301 HSE Frequency(Hz) = 25000000
302 PLL_M = 5
303 PLL_N = 104
304 PLL_P = 1
305 PLL_Q = 4
306 PLL_R = 2
307 VDD(V) = 3.3
308 Flash Latency(WS) = 3
309 */
310
311 // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator
312 let mut config = Config::default();
313 config.rcc.hse = Some(Hse {
314 freq: Hertz::mhz(25),
315 mode: HseMode::Oscillator,
316 });
317 config.rcc.hsi = None;
318 config.rcc.csi = false;
319 config.rcc.pll1 = Some(Pll {
320 source: PllSource::HSE,
321 prediv: PllPreDiv::DIV5, // PLL_M
322 mul: PllMul::MUL104, // PLL_N
323 divp: Some(PllDiv::DIV1),
324 divq: Some(PllDiv::DIV4),
325 divr: Some(PllDiv::DIV2),
326 });
327 // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c
328 // MX_OSPI_ClockConfig
329 config.rcc.pll2 = Some(Pll {
330 source: PllSource::HSE,
331 prediv: PllPreDiv::DIV5, // PLL_M
332 mul: PllMul::MUL80, // PLL_N
333 divp: Some(PllDiv::DIV5),
334 divq: Some(PllDiv::DIV2),
335 divr: Some(PllDiv::DIV2),
336 });
337 // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c
338 // MX_LTDC_ClockConfig
339 config.rcc.pll3 = Some(Pll {
340 source: PllSource::HSE,
341 prediv: PllPreDiv::DIV5, // PLL_M
342 mul: PllMul::MUL160, // PLL_N
343 divp: Some(PllDiv::DIV2),
344 divq: Some(PllDiv::DIV2),
345 divr: Some(PllDiv::DIV83),
346 });
347 config.rcc.voltage_scale = VoltageScale::Scale0;
348 config.rcc.supply_config = SupplyConfig::DirectSMPS;
349 config.rcc.sys = Sysclk::PLL1_P;
350 config.rcc.ahb_pre = AHBPrescaler::DIV2;
351 config.rcc.apb1_pre = APBPrescaler::DIV2;
352 config.rcc.apb2_pre = APBPrescaler::DIV2;
353 config.rcc.apb3_pre = APBPrescaler::DIV2;
354 config.rcc.apb4_pre = APBPrescaler::DIV2;
355 embassy_stm32::init(config)
356 }
357}
358
359mod bouncy_box {
360 use embedded_graphics::geometry::Point;
361 use embedded_graphics::primitives::Rectangle;
362
363 enum Direction {
364 DownLeft,
365 DownRight,
366 UpLeft,
367 UpRight,
368 }
369
370 pub struct BouncyBox {
371 direction: Direction,
372 child_rect: Rectangle,
373 parent_rect: Rectangle,
374 current_point: Point,
375 move_by: usize,
376 }
377
378 // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box
379 impl BouncyBox {
380 pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {
381 let center_box = parent_rect.center();
382 let center_img = child_rect.center();
383 let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);
384 Self {
385 direction: Direction::DownRight,
386 child_rect,
387 parent_rect,
388 current_point,
389 move_by,
390 }
391 }
392
393 pub fn next_point(&mut self) -> Point {
394 let direction = &self.direction;
395 let img_height = self.child_rect.size.height as i32;
396 let box_height = self.parent_rect.size.height as i32;
397 let img_width = self.child_rect.size.width as i32;
398 let box_width = self.parent_rect.size.width as i32;
399 let move_by = self.move_by as i32;
400
401 match direction {
402 Direction::DownLeft => {
403 self.current_point.x -= move_by;
404 self.current_point.y += move_by;
405
406 let x_out_of_bounds = self.current_point.x < 0;
407 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
408
409 if x_out_of_bounds && y_out_of_bounds {
410 self.direction = Direction::UpRight
411 } else if x_out_of_bounds && !y_out_of_bounds {
412 self.direction = Direction::DownRight
413 } else if !x_out_of_bounds && y_out_of_bounds {
414 self.direction = Direction::UpLeft
415 }
416 }
417 Direction::DownRight => {
418 self.current_point.x += move_by;
419 self.current_point.y += move_by;
420
421 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
422 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
423
424 if x_out_of_bounds && y_out_of_bounds {
425 self.direction = Direction::UpLeft
426 } else if x_out_of_bounds && !y_out_of_bounds {
427 self.direction = Direction::DownLeft
428 } else if !x_out_of_bounds && y_out_of_bounds {
429 self.direction = Direction::UpRight
430 }
431 }
432 Direction::UpLeft => {
433 self.current_point.x -= move_by;
434 self.current_point.y -= move_by;
435
436 let x_out_of_bounds = self.current_point.x < 0;
437 let y_out_of_bounds = self.current_point.y < 0;
438
439 if x_out_of_bounds && y_out_of_bounds {
440 self.direction = Direction::DownRight
441 } else if x_out_of_bounds && !y_out_of_bounds {
442 self.direction = Direction::UpRight
443 } else if !x_out_of_bounds && y_out_of_bounds {
444 self.direction = Direction::DownLeft
445 }
446 }
447 Direction::UpRight => {
448 self.current_point.x += move_by;
449 self.current_point.y -= move_by;
450
451 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
452 let y_out_of_bounds = self.current_point.y < 0;
453
454 if x_out_of_bounds && y_out_of_bounds {
455 self.direction = Direction::DownLeft
456 } else if x_out_of_bounds && !y_out_of_bounds {
457 self.direction = Direction::UpLeft
458 } else if !x_out_of_bounds && y_out_of_bounds {
459 self.direction = Direction::DownRight
460 }
461 }
462 }
463
464 self.current_point
465 }
466 }
467}
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 2c599e7a3..5b0519ac4 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l072cz to your chip name, if necessary. 8# Change stm32l072cz to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs
new file mode 100644
index 000000000..a54b28a93
--- /dev/null
+++ b/examples/stm32l0/src/bin/dds.rs
@@ -0,0 +1,116 @@
1#![no_std]
2#![no_main]
3
4use core::option::Option::Some;
5
6use defmt::info;
7use defmt_rtt as _; // global logger
8use embassy_executor::Spawner;
9use embassy_stm32::gpio::OutputType;
10use embassy_stm32::rcc::*;
11use embassy_stm32::time::hz;
12use embassy_stm32::timer::low_level::{Timer as LLTimer, *};
13use embassy_stm32::timer::simple_pwm::PwmPin;
14use embassy_stm32::timer::Channel;
15use embassy_stm32::{interrupt, pac, Config};
16use panic_probe as _;
17
18const DDS_SINE_DATA: [u8; 256] = [
19 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae, 0xb0, 0xb3, 0xb6,
20 0xb9, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4,
21 0xe6, 0xe8, 0xea, 0xec, 0xed, 0xef, 0xf0, 0xf2, 0xf3, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd,
22 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb,
23 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde,
24 0xdc, 0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3, 0xb0, 0xae,
25 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83, 0x80, 0x7c, 0x79, 0x76, 0x73,
26 0x70, 0x6d, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x5a, 0x57, 0x54, 0x51, 0x4f, 0x4c, 0x49, 0x46, 0x43, 0x40, 0x3e, 0x3b,
27 0x38, 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x2a, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12,
28 0x10, 0x0f, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
29 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
30 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x2a, 0x2c,
31 0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3e, 0x40, 0x43, 0x46, 0x49, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x60,
32 0x63, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c,
33];
34
35// frequency: 15625/(256/(DDS_INCR/2**24)) = 999,99999Hz
36static mut DDS_INCR: u32 = 0x10624DD2;
37
38// fractional phase accumulator
39static mut DDS_AKKU: u32 = 0x00000000;
40
41#[interrupt]
42fn TIM2() {
43 unsafe {
44 // get next value of DDS
45 DDS_AKKU = DDS_AKKU.wrapping_add(DDS_INCR);
46 let value = (DDS_SINE_DATA[(DDS_AKKU >> 24) as usize] as u16) << 3;
47
48 // set new output compare value
49 pac::TIM2.ccr(2).modify(|w| w.set_ccr(value));
50
51 // reset interrupt flag
52 pac::TIM2.sr().modify(|r| r.set_uif(false));
53 }
54}
55
56#[embassy_executor::main]
57async fn main(_spawner: Spawner) {
58 info!("Hello World!");
59
60 // configure for 32MHz (HSI16 * 6 / 3)
61 let mut config = Config::default();
62 config.rcc.sys = Sysclk::PLL1_R;
63 config.rcc.hsi = true;
64 config.rcc.pll = Some(Pll {
65 source: PllSource::HSI,
66 div: PllDiv::DIV3,
67 mul: PllMul::MUL6,
68 });
69
70 let p = embassy_stm32::init(config);
71
72 // setup PWM pin in AF mode
73 let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull);
74
75 // initialize timer
76 // we cannot use SimplePWM here because the Time is privately encapsulated
77 let timer = LLTimer::new(p.TIM2);
78
79 // set counting mode
80 timer.set_counting_mode(CountingMode::EdgeAlignedUp);
81
82 // set pwm sample frequency
83 timer.set_frequency(hz(15625));
84
85 // enable outputs
86 timer.enable_outputs();
87
88 // start timer
89 timer.start();
90
91 // set output compare mode
92 timer.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
93
94 // set output compare preload
95 timer.set_output_compare_preload(Channel::Ch3, true);
96
97 // set output polarity
98 timer.set_output_polarity(Channel::Ch3, OutputPolarity::ActiveHigh);
99
100 // set compare value
101 timer.set_compare_value(Channel::Ch3, timer.get_max_compare_value() / 2);
102
103 // enable pwm channel
104 timer.enable_channel(Channel::Ch3, true);
105
106 // enable timer interrupts
107 timer.enable_update_interrupt(true);
108 unsafe { cortex_m::peripheral::NVIC::unmask(interrupt::TIM2) };
109
110 async {
111 loop {
112 embassy_time::Timer::after_millis(5000).await;
113 }
114 }
115 .await;
116}
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs
index 7a89334e0..c557ac6d7 100644
--- a/examples/stm32l4/src/bin/adc.rs
+++ b/examples/stm32l4/src/bin/adc.rs
@@ -23,7 +23,7 @@ fn main() -> ! {
23 let mut channel = p.PC0; 23 let mut channel = p.PC0;
24 24
25 loop { 25 loop {
26 let v = adc.read(&mut channel); 26 let v = adc.blocking_read(&mut channel);
27 info!("--> {}", v); 27 info!("--> {}", v);
28 } 28 }
29} 29}
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs
index 4410448f1..c8252e4e1 100644
--- a/examples/stm32u0/src/bin/adc.rs
+++ b/examples/stm32u0/src/bin/adc.rs
@@ -23,7 +23,7 @@ fn main() -> ! {
23 let mut channel = p.PC0; 23 let mut channel = p.PC0;
24 24
25 loop { 25 loop {
26 let v = adc.read(&mut channel); 26 let v = adc.blocking_read(&mut channel);
27 info!("--> {}", v); 27 info!("--> {}", v);
28 embassy_time::block_for(Duration::from_millis(200)); 28 embassy_time::block_for(Duration::from_millis(200));
29 } 29 }
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs
index f5593d1c4..eb15d275a 100644
--- a/examples/stm32u5/src/bin/tsc.rs
+++ b/examples/stm32u5/src/bin/tsc.rs
@@ -2,10 +2,15 @@
2#![no_main] 2#![no_main]
3 3
4use defmt::*; 4use defmt::*;
5use embassy_stm32::bind_interrupts;
5use embassy_stm32::tsc::{self, *}; 6use embassy_stm32::tsc::{self, *};
6use embassy_time::Timer; 7use embassy_time::Timer;
7use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
8 9
10bind_interrupts!(struct Irqs {
11 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
12});
13
9#[cortex_m_rt::exception] 14#[cortex_m_rt::exception]
10unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { 15unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! {
11 cortex_m::peripheral::SCB::sys_reset(); 16 cortex_m::peripheral::SCB::sys_reset();
@@ -45,7 +50,7 @@ async fn main(_spawner: embassy_executor::Spawner) {
45 g7.set_io2(context.PE3, PinType::Sample); 50 g7.set_io2(context.PE3, PinType::Sample);
46 g7.set_io3(context.PE4, PinType::Channel); 51 g7.set_io3(context.PE4, PinType::Channel);
47 52
48 let mut touch_controller = tsc::Tsc::new( 53 let mut touch_controller = tsc::Tsc::new_async(
49 context.TSC, 54 context.TSC,
50 Some(g1), 55 Some(g1),
51 Some(g2), 56 Some(g2),
@@ -56,6 +61,7 @@ async fn main(_spawner: embassy_executor::Spawner) {
56 Some(g7), 61 Some(g7),
57 None, 62 None,
58 config, 63 config,
64 Irqs,
59 ); 65 );
60 66
61 touch_controller.discharge_io(true); 67 touch_controller.discharge_io(true);
@@ -67,7 +73,7 @@ async fn main(_spawner: embassy_executor::Spawner) {
67 let mut group_seven_val = 0; 73 let mut group_seven_val = 0;
68 info!("Starting touch_controller interface"); 74 info!("Starting touch_controller interface");
69 loop { 75 loop {
70 touch_controller.poll_for_acquisition(); 76 touch_controller.pend_for_acquisition().await;
71 touch_controller.discharge_io(true); 77 touch_controller.discharge_io(true);
72 Timer::after_millis(1).await; 78 Timer::after_millis(1).await;
73 79
diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs
index 5d5547773..4b04571bd 100644
--- a/tests/rp/src/bin/ethernet_w5100s_perf.rs
+++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs
@@ -59,7 +59,8 @@ async fn main(spawner: Spawner) {
59 w5500_int, 59 w5500_int,
60 w5500_reset, 60 w5500_reset,
61 ) 61 )
62 .await; 62 .await
63 .unwrap();
63 unwrap!(spawner.spawn(ethernet_task(runner))); 64 unwrap!(spawner.spawn(ethernet_task(runner)));
64 65
65 // Generate random seed 66 // Generate random seed
diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs
index 06501ab14..86a68c530 100644
--- a/tests/stm32/src/bin/dac.rs
+++ b/tests/stm32/src/bin/dac.rs
@@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) {
38 dac.set(Value::Bit8(0)); 38 dac.set(Value::Bit8(0));
39 // Now wait a little to obtain a stable value 39 // Now wait a little to obtain a stable value
40 Timer::after_millis(30).await; 40 Timer::after_millis(30).await;
41 let offset = adc.read(&mut adc_pin); 41 let offset = adc.blocking_read(&mut adc_pin);
42 42
43 for v in 0..=255 { 43 for v in 0..=255 {
44 // First set the DAC output value 44 // First set the DAC output value
@@ -49,7 +49,7 @@ async fn main(_spawner: Spawner) {
49 Timer::after_millis(30).await; 49 Timer::after_millis(30).await;
50 50
51 // Need to steal the peripherals here because PA4 is obviously in use already 51 // Need to steal the peripherals here because PA4 is obviously in use already
52 let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); 52 let measured = adc.blocking_read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
53 // Calibrate and normalize the measurement to get close to the dac_output_val 53 // Calibrate and normalize the measurement to get close to the dac_output_val
54 let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; 54 let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;
55 55