Compare commits
	
		
			171 Commits
		
	
	
		
			embassy-ex
			...
			boot-hil
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9010bfe79b | ||
|  | 2a437d31c4 | ||
|  | 2d78aa763a | ||
|  | 1133cbf90e | ||
|  | 77c357e744 | ||
|  | 521970535e | ||
|  | 93d4cfe7c1 | ||
|  | 8413a89752 | ||
|  | db717d9c81 | ||
|  | 808fa9dce6 | ||
|  | ceb13def19 | ||
|  | 5cf494113f | ||
|  | 8edb7bb012 | ||
|  | 8900f5f52b | ||
|  | 8201979d71 | ||
|  | 2d9f50addc | ||
|  | 18da91e252 | ||
|  | 28e2570533 | ||
|  | 26e0823c35 | ||
|  | ceca8b4336 | ||
|  | e308286f3c | ||
|  | 268da2fcde | ||
|  | a992d4895d | ||
|  | 1bae34d37c | ||
|  | aac42e3209 | ||
|  | 08415e001e | ||
|  | d6a1b567ee | ||
|  | a47fb42962 | ||
|  | 70a4a193c5 | ||
|  | 2132afb48b | ||
|  | 0e9131fd14 | ||
|  | 40a18b075d | ||
|  | f17f09057d | ||
|  | 11a78fb1e4 | ||
|  | 48154e18bf | ||
|  | cf2d4eca7c | ||
|  | 3e0b752bef | ||
|  | 6070d61d8c | ||
|  | a4f8d82ef5 | ||
|  | c27b0296fe | ||
|  | 336ae54a56 | ||
|  | d6a1118406 | ||
|  | 9de08d56a0 | ||
|  | 26740bb3ef | ||
|  | 4550452f43 | ||
|  | 08410432b5 | ||
|  | 3cf3caa3ab | ||
|  | c21ad04c2e | ||
|  | d097c99719 | ||
|  | af7c93abba | ||
|  | c356585a59 | ||
|  | 0d3ff34d80 | ||
|  | bb2d6c8542 | ||
|  | a05afc5426 | ||
|  | 50116e5b27 | ||
|  | a80acf686e | ||
|  | 6770d8e8a6 | ||
|  | 7307098780 | ||
|  | f4601af2a4 | ||
|  | fd22f4fac5 | ||
|  | 7622d2eb61 | ||
|  | 7573160077 | ||
|  | 7e18e5e0c1 | ||
|  | 9d76a6e933 | ||
|  | f502271940 | ||
|  | e2f8bf19ea | ||
|  | 1180e1770d | ||
|  | 49ba9c3da2 | ||
|  | ce662766be | ||
|  | a03b6be693 | ||
|  | 87d3086533 | ||
|  | d19e1c1dd1 | ||
|  | 8a55adbfd8 | ||
|  | 6dc56d2b35 | ||
|  | 394503ab69 | ||
|  | bfb4cf775a | ||
|  | 274f63a879 | ||
|  | 615882ebd6 | ||
|  | 6e38b07642 | ||
|  | 1eb03dc41a | ||
|  | a2656f402b | ||
|  | 3466c9cfa9 | ||
|  | 3295ec94e5 | ||
|  | c5732ce285 | ||
|  | 2e6f4237f2 | ||
|  | ac11635b0b | ||
|  | 9dd58660c3 | ||
|  | 9baa3bafb0 | ||
|  | 360286e67c | ||
|  | 0c66636d00 | ||
|  | 9d8c527308 | ||
|  | 1d87ec9cc3 | ||
|  | b74645e259 | ||
|  | 56351cedcb | ||
|  | 0823d9dc93 | ||
|  | c10fb7c1c4 | ||
|  | a2ce3aa1a1 | ||
|  | 3769447382 | ||
|  | 2c6b428ed4 | ||
|  | 83a976cf4b | ||
|  | a76ff53fb3 | ||
|  | f69e8459c9 | ||
|  | 891f1758bc | ||
|  | ae174fd0e0 | ||
|  | 5c936d33d4 | ||
|  | 36ec9bcc1d | ||
|  | 5adc80f6d9 | ||
|  | 98f55fa54d | ||
|  | 416ecc73d8 | ||
|  | 27dfced285 | ||
|  | 21681d8b4e | ||
|  | 989c98f316 | ||
|  | fdb2c4946a | ||
|  | 5e613d9abb | ||
|  | 40b576584e | ||
|  | aa7752020e | ||
|  | 6c165f8dc0 | ||
|  | 975f2f23c0 | ||
|  | 0eeefd3dbf | ||
|  | a4d78a6552 | ||
|  | f503417f4c | ||
|  | 6b8b145266 | ||
|  | e07f943562 | ||
|  | 70a5221b2e | ||
|  | b315c28d4e | ||
|  | e025693914 | ||
|  | 05ee02b593 | ||
|  | 4098a61ef0 | ||
|  | 1db00f5439 | ||
|  | 7fc17bc150 | ||
|  | 6e616a6fe6 | ||
|  | d02886786e | ||
|  | 2db4d01198 | ||
|  | a36ee75d19 | ||
|  | 1f63bf4153 | ||
|  | fd739250ea | ||
|  | 71c4e7e4d2 | ||
|  | 2c80784fe6 | ||
|  | 538001a4bc | ||
|  | e981cd4968 | ||
|  | 91bb3aae3f | ||
|  | e08dbcd027 | ||
|  | 5c27265a21 | ||
|  | 2c36199dea | ||
|  | 13a0be6289 | ||
|  | 9f928010a8 | ||
|  | 88146eb53e | ||
|  | 326e78757b | ||
|  | f77a7fe4bf | ||
|  | cbc92dce05 | ||
|  | 531f51d0eb | ||
|  | f28ab18d7b | ||
|  | 3bf6081eb5 | ||
|  | fb942e6675 | ||
|  | 10ea068027 | ||
|  | 4caa8497fc | ||
|  | 48085939e7 | ||
|  | 7f7256050c | ||
|  | 4b6538c8a8 | ||
|  | db6f9afa2e | ||
|  | 59a5e84df5 | ||
|  | 13f0501673 | ||
|  | 94de1a5353 | ||
|  | db71887817 | ||
|  | 1e430f7413 | ||
|  | 2897670f24 | ||
|  | ca738d6c99 | ||
|  | d33246b072 | ||
|  | 54e2e17520 | ||
|  | 3023e70ccf | ||
|  | cda4047310 | 
							
								
								
									
										4
									
								
								.github/bors.toml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/bors.toml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| status = [ | ||||
|     "all", | ||||
| ] | ||||
| delete_merged_branches = true | ||||
							
								
								
									
										1
									
								
								.github/ci/build.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ci/build.sh
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then | ||||
|     echo Got teleprobe token! | ||||
|     export TELEPROBE_HOST=https://teleprobe.embassy.dev | ||||
|     export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) | ||||
|     export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json | ||||
| fi | ||||
|  | ||||
| hashtime restore /ci/cache/filetime.json || true | ||||
|   | ||||
							
								
								
									
										16
									
								
								ci.sh
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								ci.sh
									
									
									
									
									
								
							| @@ -19,6 +19,19 @@ cargo batch  \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ | ||||
|     --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \ | ||||
|     --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ | ||||
|     --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ | ||||
|     --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ | ||||
| @@ -142,7 +155,7 @@ cargo batch  \ | ||||
|     --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \ | ||||
|     --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \ | ||||
|     --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \ | ||||
|     --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ | ||||
|     --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 --out-dir out/examples/bootloader/nrf \ | ||||
|     --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ | ||||
|     --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ | ||||
|     --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ | ||||
| @@ -158,6 +171,7 @@ cargo batch  \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \ | ||||
|     --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ | ||||
|     --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ | ||||
|     --- build --release --manifest-path tests/boot/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/boot/nrf52840-dk \ | ||||
|     --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ | ||||
|     $BUILD_EXTRA | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ log = ["dep:log"] | ||||
| firmware-logs = [] | ||||
|  | ||||
| [dependencies] | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time"} | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time"} | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync"} | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | ||||
|   | ||||
| @@ -96,6 +96,7 @@ pub(crate) const IOCTL_CMD_UP: u32 = 2; | ||||
| pub(crate) const IOCTL_CMD_DOWN: u32 = 3; | ||||
| pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; | ||||
| pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30; | ||||
| pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52; | ||||
| pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; | ||||
| pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; | ||||
| pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; | ||||
|   | ||||
| @@ -124,7 +124,7 @@ impl<'a> Control<'a> { | ||||
|         Timer::after(Duration::from_millis(100)).await; | ||||
|  | ||||
|         // set wifi up | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||||
|         self.up().await; | ||||
|  | ||||
|         Timer::after(Duration::from_millis(100)).await; | ||||
|  | ||||
| @@ -138,6 +138,16 @@ impl<'a> Control<'a> { | ||||
|         debug!("INIT DONE"); | ||||
|     } | ||||
|  | ||||
|     /// Set the WiFi interface up. | ||||
|     async fn up(&mut self) { | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||||
|     } | ||||
|  | ||||
|     /// Set the interface down. | ||||
|     async fn down(&mut self) { | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; | ||||
|     } | ||||
|  | ||||
|     pub async fn set_power_management(&mut self, mode: PowerManagementMode) { | ||||
|         // power save mode | ||||
|         let mode_num = mode.mode(); | ||||
| @@ -256,13 +266,13 @@ impl<'a> Control<'a> { | ||||
|         } | ||||
|  | ||||
|         // Temporarily set wifi down | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; | ||||
|         self.down().await; | ||||
|  | ||||
|         // Turn off APSTA mode | ||||
|         self.set_iovar_u32("apsta", 0).await; | ||||
|  | ||||
|         // Set wifi up again | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||||
|         self.up().await; | ||||
|  | ||||
|         // Turn on AP mode | ||||
|         self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; | ||||
| @@ -423,6 +433,11 @@ impl<'a> Control<'a> { | ||||
|             events: &self.events, | ||||
|         } | ||||
|     } | ||||
|     /// Leave the wifi, with which we are currently associated. | ||||
|     pub async fn leave(&mut self) { | ||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await; | ||||
|         info!("Disassociated") | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Scanner<'a> { | ||||
|   | ||||
| @@ -83,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -226,7 +229,8 @@ impl<T, E> Try for Result<T, E> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Bytes<'a>(pub &'a [u8]); | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ use ioctl::IoctlState; | ||||
|  | ||||
| use crate::bus::Bus; | ||||
| pub use crate::bus::SpiBusCyw43; | ||||
| pub use crate::control::{Control, Error as ControlError}; | ||||
| pub use crate::control::{Control, Error as ControlError, Scanner}; | ||||
| pub use crate::runner::Runner; | ||||
| pub use crate::structs::BssInfo; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ default = ["time"] | ||||
| [dependencies] | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ | ||||
|     "unproven", | ||||
| ] } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ rtos-trace = { version = "0.1.2", optional = true } | ||||
|  | ||||
| futures-util = { version = "0.3.17", default-features = false } | ||||
| embassy-macros = { version = "0.2.1", path = "../embassy-macros" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true} | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true} | ||||
| atomic-polyfill = "1.0.1" | ||||
| critical-section = "1.1" | ||||
| static_cell = "1.1" | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| const THREAD_PENDER: usize = usize::MAX; | ||||
|  | ||||
| #[export_name = "__pender"] | ||||
| #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] | ||||
| fn __pender(context: *mut ()) { | ||||
| @@ -48,13 +46,14 @@ fn __pender(context: *mut ()) { | ||||
| pub use thread::*; | ||||
| #[cfg(feature = "executor-thread")] | ||||
| mod thread { | ||||
|     pub(super) const THREAD_PENDER: usize = usize::MAX; | ||||
|  | ||||
|     use core::arch::asm; | ||||
|     use core::marker::PhantomData; | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     pub use embassy_macros::main_cortex_m as main; | ||||
|  | ||||
|     use crate::arch::THREAD_PENDER; | ||||
|     use crate::{raw, Spawner}; | ||||
|  | ||||
|     /// Thread mode executor, using WFE/SEV. | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,7 +20,7 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|  | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "embassy-net-adin1110" | ||||
| version = "0.1.0" | ||||
| version = "0.2.0" | ||||
| description = "embassy-net driver for the ADIN1110 ethernet chip" | ||||
| keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"] | ||||
| categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] | ||||
| @@ -12,30 +12,31 @@ edition = "2021" | ||||
| [dependencies] | ||||
| heapless = "0.7.16" | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.4", default-features = false, optional = true } | ||||
| log = { version = "0.4", default-features = false, optional = true } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } | ||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-time = { version = "0.1.0" } | ||||
| embassy-time = { version = "0.1.3" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| bitfield = "0.14.0" | ||||
|  | ||||
|  | ||||
| [dev-dependencies] | ||||
| # reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged. | ||||
| #embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] } | ||||
| embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] } | ||||
| crc = "3.0.1" | ||||
| env_logger = "0.10" | ||||
| critical-section = { version = "1.1.1", features = ["std"] } | ||||
| futures-test = "0.3.17" | ||||
| critical-section = { version = "1.1.2", features = ["std"] } | ||||
| futures-test = "0.3.28" | ||||
|  | ||||
| [features] | ||||
| default = [ ] | ||||
| defmt = [ "dep:defmt" ] | ||||
| defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ] | ||||
| log = ["dep:log"] | ||||
|  | ||||
| [package.metadata.embassy_docs] | ||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/" | ||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/" | ||||
| target = "thumbv7em-none-eabi" | ||||
| features = ["defmt"] | ||||
|   | ||||
| @@ -30,8 +30,8 @@ Currently only `Generic` SPI with or without CRC is supported. | ||||
|  | ||||
| ## Hardware | ||||
|  | ||||
| - Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) dor an example. | ||||
| - [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** | ||||
| - Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) for an example. | ||||
| - [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** | ||||
|  | ||||
| ## Other SPE chips | ||||
|  | ||||
| @@ -44,6 +44,39 @@ ADIN1110 library can tested on the host with a mock SPI driver. | ||||
|  | ||||
| $ `cargo test --target x86_64-unknown-linux-gnu` | ||||
|  | ||||
| ## Benchmark | ||||
|  | ||||
| - Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example. | ||||
|  | ||||
| Basic `ping` benchmark | ||||
| ```rust,ignore | ||||
| # ping <IP> -c 60 | ||||
|  | ||||
| 60 packets transmitted, 60 received, 0% packet loss, time 59066ms | ||||
| rtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms | ||||
|  | ||||
| # ping <IP> -s 1472 -M do -c 60 | ||||
|  | ||||
| 60 packets transmitted, 60 received, 0% packet loss, time 59066ms | ||||
| rtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms | ||||
| ``` | ||||
|  | ||||
| HTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha) | ||||
| ```rust,ignore | ||||
| # oha -c 1 http://<IP> -z 60s | ||||
| Summary: | ||||
|   Success rate: 50.00% | ||||
|   Total:        60.0005 secs | ||||
|   Slowest:      0.0055 secs | ||||
|   Fastest:      0.0033 secs | ||||
|   Average:      0.0034 secs | ||||
|   Requests/sec: 362.1971 | ||||
|  | ||||
|   Total data:   2.99 MiB | ||||
|   Size/request: 289 B | ||||
|   Size/sec:     51.11 KiB | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
| This work is licensed under either of | ||||
|   | ||||
| @@ -257,29 +257,30 @@ pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [ | ||||
|     0x2D02_EF8D, | ||||
| ]; | ||||
|  | ||||
| /// Generate Ethernet Frame Check Sequence | ||||
| #[allow(non_camel_case_types)] | ||||
| #[derive(Debug)] | ||||
| pub struct ETH_FSC(pub u32); | ||||
| pub struct ETH_FCS(pub u32); | ||||
|  | ||||
| impl ETH_FSC { | ||||
| impl ETH_FCS { | ||||
|     pub const CRC32_OK: u32 = 0x2144_df1c; | ||||
|  | ||||
|     #[must_use] | ||||
|     pub fn new(data: &[u8]) -> Self { | ||||
|         let fsc = data.iter().fold(u32::MAX, |crc, byte| { | ||||
|         let fcs = data.iter().fold(u32::MAX, |crc, byte| { | ||||
|             let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; | ||||
|             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) | ||||
|         }) ^ u32::MAX; | ||||
|         Self(fsc) | ||||
|         Self(fcs) | ||||
|     } | ||||
|  | ||||
|     #[must_use] | ||||
|     pub fn update(self, data: &[u8]) -> Self { | ||||
|         let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| { | ||||
|         let fcs = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| { | ||||
|             let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; | ||||
|             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) | ||||
|         }) ^ u32::MAX; | ||||
|         Self(fsc) | ||||
|         Self(fcs) | ||||
|     } | ||||
|  | ||||
|     #[must_use] | ||||
| @@ -319,24 +320,24 @@ mod tests { | ||||
|         ]; | ||||
|  | ||||
|         // Packet A | ||||
|         let own_crc = ETH_FSC::new(&packet_a[0..60]); | ||||
|         let own_crc = ETH_FCS::new(&packet_a[0..60]); | ||||
|         let crc_bytes = own_crc.hton_bytes(); | ||||
|         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); | ||||
|         assert_eq!(&crc_bytes, &packet_a[60..64]); | ||||
|  | ||||
|         let own_crc = ETH_FSC::new(packet_a); | ||||
|         let own_crc = ETH_FCS::new(packet_a); | ||||
|         println!("{:08x}", own_crc.0); | ||||
|         assert_eq!(own_crc.0, ETH_FSC::CRC32_OK); | ||||
|         assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); | ||||
|  | ||||
|         // Packet B | ||||
|         let own_crc = ETH_FSC::new(&packet_b[0..60]); | ||||
|         let own_crc = ETH_FCS::new(&packet_b[0..60]); | ||||
|         let crc_bytes = own_crc.hton_bytes(); | ||||
|         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); | ||||
|         assert_eq!(&crc_bytes, &packet_b[60..64]); | ||||
|  | ||||
|         let own_crc = ETH_FSC::new(packet_b); | ||||
|         let own_crc = ETH_FCS::new(packet_b); | ||||
|         println!("{:08x}", own_crc.0); | ||||
|         assert_eq!(own_crc.0, ETH_FSC::CRC32_OK); | ||||
|         assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -349,9 +350,9 @@ mod tests { | ||||
|         ]; | ||||
|  | ||||
|         let (part_a, part_b) = full_data.split_at(16); | ||||
|         let crc_partially = ETH_FSC::new(part_a).update(part_b); | ||||
|         let crc_partially = ETH_FCS::new(part_a).update(part_b); | ||||
|  | ||||
|         let crc_full = ETH_FSC::new(full_data); | ||||
|         let crc_full = ETH_FCS::new(full_data); | ||||
|  | ||||
|         assert_eq!(crc_full.0, crc_partially.0); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										254
									
								
								embassy-net-adin1110/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								embassy-net-adin1110/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,254 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| macro_rules! assert { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! assert_eq { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert_eq!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert_eq!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! assert_ne { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert_ne!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert_ne!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert_eq { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert_eq!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert_eq!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert_ne { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert_ne!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert_ne!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! todo { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::todo!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::todo!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! panic { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::panic!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::panic!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! trace { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::trace!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::trace!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ignored = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::debug!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ignored = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! info { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::info!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::info!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ignored = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! warn { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::warn!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::warn!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ignored = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! error { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::error!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::error!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ignored = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unwrap { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unwrap!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unwrap { | ||||
|     ($arg:expr) => { | ||||
|         match $crate::fmt::Try::into_result($arg) { | ||||
|             ::core::result::Result::Ok(t) => t, | ||||
|             ::core::result::Result::Err(e) => { | ||||
|                 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||||
|         match $crate::fmt::Try::into_result($arg) { | ||||
|             ::core::result::Result::Ok(t) => t, | ||||
|             ::core::result::Result::Err(e) => { | ||||
|                 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| pub struct NoneError; | ||||
|  | ||||
| pub trait Try { | ||||
|     type Ok; | ||||
|     type Error; | ||||
|     fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||||
| } | ||||
|  | ||||
| impl<T> Try for Option<T> { | ||||
|     type Ok = T; | ||||
|     type Error = NoneError; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Result<T, NoneError> { | ||||
|         self.ok_or(NoneError) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, E> Try for Result<T, E> { | ||||
|     type Ok = T; | ||||
|     type Error = E; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Self { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -32,11 +32,12 @@ enum Reg13Op { | ||||
|     PostReadIncAddr = 0b10 << 14, | ||||
|     Read = 0b11 << 14, | ||||
| } | ||||
|  | ||||
| /// `MdioBus` trait | ||||
| /// Driver needs to implement the Clause 22 | ||||
| /// Optional Clause 45 is the device supports this. | ||||
| /// | ||||
| /// Claus 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf> | ||||
| /// Clause 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf> | ||||
| pub trait MdioBus { | ||||
|     type Error; | ||||
|  | ||||
| @@ -87,89 +88,89 @@ pub trait MdioBus { | ||||
|     } | ||||
| } | ||||
|  | ||||
| // #[cfg(test)] | ||||
| // mod tests { | ||||
| //     use core::convert::Infallible; | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use core::convert::Infallible; | ||||
|  | ||||
| //     use super::{MdioBus, PhyAddr, RegC22, RegVal}; | ||||
|     use super::{MdioBus, PhyAddr, RegC22, RegVal}; | ||||
|  | ||||
| //     #[derive(Debug, PartialEq, Eq)] | ||||
| //     enum A { | ||||
| //         Read(PhyAddr, RegC22), | ||||
| //         Write(PhyAddr, RegC22, RegVal), | ||||
| //     } | ||||
|     #[derive(Debug, PartialEq, Eq)] | ||||
|     enum A { | ||||
|         Read(PhyAddr, RegC22), | ||||
|         Write(PhyAddr, RegC22, RegVal), | ||||
|     } | ||||
|  | ||||
| //     struct MockMdioBus(Vec<A>); | ||||
|     struct MockMdioBus(Vec<A>); | ||||
|  | ||||
| //     impl MockMdioBus { | ||||
| //         pub fn clear(&mut self) { | ||||
| //             self.0.clear(); | ||||
| //         } | ||||
| //     } | ||||
|     impl MockMdioBus { | ||||
|         pub fn clear(&mut self) { | ||||
|             self.0.clear(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| //     impl MdioBus for MockMdioBus { | ||||
| //         type Error = Infallible; | ||||
|     impl MdioBus for MockMdioBus { | ||||
|         type Error = Infallible; | ||||
|  | ||||
| //         fn write_cl22( | ||||
| //             &mut self, | ||||
| //             phy_id: super::PhyAddr, | ||||
| //             reg: super::RegC22, | ||||
| //             reg_val: super::RegVal, | ||||
| //         ) -> Result<(), Self::Error> { | ||||
| //             self.0.push(A::Write(phy_id, reg, reg_val)); | ||||
| //             Ok(()) | ||||
| //         } | ||||
|         async fn write_cl22( | ||||
|             &mut self, | ||||
|             phy_id: super::PhyAddr, | ||||
|             reg: super::RegC22, | ||||
|             reg_val: super::RegVal, | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             self.0.push(A::Write(phy_id, reg, reg_val)); | ||||
|             Ok(()) | ||||
|         } | ||||
|  | ||||
| //         fn read_cl22( | ||||
| //             &mut self, | ||||
| //             phy_id: super::PhyAddr, | ||||
| //             reg: super::RegC22, | ||||
| //         ) -> Result<super::RegVal, Self::Error> { | ||||
| //             self.0.push(A::Read(phy_id, reg)); | ||||
| //             Ok(0) | ||||
| //         } | ||||
| //     } | ||||
|         async fn read_cl22( | ||||
|             &mut self, | ||||
|             phy_id: super::PhyAddr, | ||||
|             reg: super::RegC22, | ||||
|         ) -> Result<super::RegVal, Self::Error> { | ||||
|             self.0.push(A::Read(phy_id, reg)); | ||||
|             Ok(0) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| //     #[test] | ||||
| //     fn read_test() { | ||||
| //         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||
|     #[futures_test::test] | ||||
|     async fn read_test() { | ||||
|         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||
|  | ||||
| //         mdiobus.clear(); | ||||
| //         mdiobus.read_cl22(0x01, 0x00).unwrap(); | ||||
| //         assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]); | ||||
|         mdiobus.clear(); | ||||
|         mdiobus.read_cl22(0x01, 0x00).await.unwrap(); | ||||
|         assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]); | ||||
|  | ||||
| //         mdiobus.clear(); | ||||
| //         mdiobus.read_cl45(0x01, (0xBB, 0x1234)).unwrap(); | ||||
| //         assert_eq!( | ||||
| //             mdiobus.0, | ||||
| //             vec![ | ||||
| //                 #[allow(clippy::identity_op)] | ||||
| //                 A::Write(0x01, 13, (0b00 << 14) | 27), | ||||
| //                 A::Write(0x01, 14, 0x1234), | ||||
| //                 A::Write(0x01, 13, (0b11 << 14) | 27), | ||||
| //                 A::Read(0x01, 14) | ||||
| //             ] | ||||
| //         ); | ||||
| //     } | ||||
|         mdiobus.clear(); | ||||
|         mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.unwrap(); | ||||
|         assert_eq!( | ||||
|             mdiobus.0, | ||||
|             vec![ | ||||
|                 #[allow(clippy::identity_op)] | ||||
|                 A::Write(0x01, 13, (0b00 << 14) | 27), | ||||
|                 A::Write(0x01, 14, 0x1234), | ||||
|                 A::Write(0x01, 13, (0b11 << 14) | 27), | ||||
|                 A::Read(0x01, 14) | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| //     #[test] | ||||
| //     fn write_test() { | ||||
| //         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||
|     #[futures_test::test] | ||||
|     async fn write_test() { | ||||
|         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||
|  | ||||
| //         mdiobus.clear(); | ||||
| //         mdiobus.write_cl22(0x01, 0x00, 0xABCD).unwrap(); | ||||
| //         assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]); | ||||
|         mdiobus.clear(); | ||||
|         mdiobus.write_cl22(0x01, 0x00, 0xABCD).await.unwrap(); | ||||
|         assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]); | ||||
|  | ||||
| //         mdiobus.clear(); | ||||
| //         mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).unwrap(); | ||||
| //         assert_eq!( | ||||
| //             mdiobus.0, | ||||
| //             vec![ | ||||
| //                 A::Write(0x01, 13, 27), | ||||
| //                 A::Write(0x01, 14, 0x1234), | ||||
| //                 A::Write(0x01, 13, (0b01 << 14) | 27), | ||||
| //                 A::Write(0x01, 14, 0xABCD) | ||||
| //             ] | ||||
| //         ); | ||||
| //     } | ||||
| // } | ||||
|         mdiobus.clear(); | ||||
|         mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.unwrap(); | ||||
|         assert_eq!( | ||||
|             mdiobus.0, | ||||
|             vec![ | ||||
|                 A::Write(0x01, 13, 27), | ||||
|                 A::Write(0x01, 14, 0x1234), | ||||
|                 A::Write(0x01, 13, (0b01 << 14) | 27), | ||||
|                 A::Write(0x01, 14, 0xABCD) | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -111,6 +111,7 @@ pub mod RegsC45 { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// 10-BASE-T1x PHY functions. | ||||
| pub struct Phy10BaseT1x(u8); | ||||
|  | ||||
| impl Default for Phy10BaseT1x { | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| use core::fmt::{Debug, Display}; | ||||
|  | ||||
| use bitfield::{bitfield, bitfield_bitrange, bitfield_fields}; | ||||
|  | ||||
| #[allow(non_camel_case_types)] | ||||
| @@ -34,6 +36,12 @@ pub enum SpiRegisters { | ||||
|     RX = 0x91, | ||||
| } | ||||
|  | ||||
| impl Display for SpiRegisters { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{self:?}") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<SpiRegisters> for u16 { | ||||
|     fn from(val: SpiRegisters) -> Self { | ||||
|         val as u16 | ||||
| @@ -68,7 +76,7 @@ impl From<u16> for SpiRegisters { | ||||
|             0x73 => Self::ADDR_MSK_UPR1, | ||||
|             0x90 => Self::RX_FSIZE, | ||||
|             0x91 => Self::RX, | ||||
|             e => panic!("Unknown value {e}"), | ||||
|             e => panic!("Unknown value {}", e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -174,7 +182,7 @@ bitfield! { | ||||
|     pub sdf_detect_src, set_sdf_detect_src : 7; | ||||
|     /// Statistics Clear on Reading | ||||
|     pub stats_clr_on_rd, set_stats_clr_on_rd : 6; | ||||
|     /// Enable CRC Append | ||||
|     /// Enable SPI CRC | ||||
|     pub crc_append, set_crc_append : 5; | ||||
|     /// Admit Frames with IFG Errors on Port 1 (P1) | ||||
|     pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4; | ||||
| @@ -313,7 +321,7 @@ impl From<u8> for LedFunc { | ||||
|             26 => LedFunc::Clk25Ref, | ||||
|             27 => LedFunc::TxTCLK, | ||||
|             28 => LedFunc::Clk120MHz, | ||||
|             e => panic!("Invalid value {e}"), | ||||
|             e => panic!("Invalid value {}", e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -369,7 +377,7 @@ impl From<u8> for LedPol { | ||||
|             0 => LedPol::AutoSense, | ||||
|             1 => LedPol::ActiveHigh, | ||||
|             2 => LedPol::ActiveLow, | ||||
|             e => panic!("Invalid value {e}"), | ||||
|             e => panic!("Invalid value {}", e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ use embassy_net_driver::{Capabilities, LinkState, Medium}; | ||||
| use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||||
| use embassy_sync::blocking_mutex::Mutex; | ||||
| use embassy_sync::waitqueue::WakerRegistration; | ||||
| use embassy_sync::zerocopy_channel; | ||||
|  | ||||
| pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||||
|     rx: [PacketBuf<MTU>; N_RX], | ||||
| @@ -130,24 +131,24 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { | ||||
|     } | ||||
|  | ||||
|     pub async fn tx_buf(&mut self) -> &mut [u8] { | ||||
|         let p = self.tx_chan.recv().await; | ||||
|         let p = self.tx_chan.receive().await; | ||||
|         &mut p.buf[..p.len] | ||||
|     } | ||||
|  | ||||
|     pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { | ||||
|         let p = self.tx_chan.try_recv()?; | ||||
|         let p = self.tx_chan.try_receive()?; | ||||
|         Some(&mut p.buf[..p.len]) | ||||
|     } | ||||
|  | ||||
|     pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { | ||||
|         match self.tx_chan.poll_recv(cx) { | ||||
|         match self.tx_chan.poll_receive(cx) { | ||||
|             Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), | ||||
|             Poll::Pending => Poll::Pending, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn tx_done(&mut self) { | ||||
|         self.tx_chan.recv_done(); | ||||
|         self.tx_chan.receive_done(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -204,24 +205,24 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> { | ||||
|  | ||||
| impl<'d, const MTU: usize> TxRunner<'d, MTU> { | ||||
|     pub async fn tx_buf(&mut self) -> &mut [u8] { | ||||
|         let p = self.tx_chan.recv().await; | ||||
|         let p = self.tx_chan.receive().await; | ||||
|         &mut p.buf[..p.len] | ||||
|     } | ||||
|  | ||||
|     pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { | ||||
|         let p = self.tx_chan.try_recv()?; | ||||
|         let p = self.tx_chan.try_receive()?; | ||||
|         Some(&mut p.buf[..p.len]) | ||||
|     } | ||||
|  | ||||
|     pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { | ||||
|         match self.tx_chan.poll_recv(cx) { | ||||
|         match self.tx_chan.poll_receive(cx) { | ||||
|             Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), | ||||
|             Poll::Pending => Poll::Pending, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn tx_done(&mut self) { | ||||
|         self.tx_chan.recv_done(); | ||||
|         self.tx_chan.receive_done(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -293,7 +294,7 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { | ||||
|     type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; | ||||
|  | ||||
|     fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||||
|         if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { | ||||
|         if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() { | ||||
|             Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) | ||||
|         } else { | ||||
|             None | ||||
| @@ -337,9 +338,9 @@ impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { | ||||
|         F: FnOnce(&mut [u8]) -> R, | ||||
|     { | ||||
|         // NOTE(unwrap): we checked the queue wasn't full when creating the token. | ||||
|         let pkt = unwrap!(self.rx.try_recv()); | ||||
|         let pkt = unwrap!(self.rx.try_receive()); | ||||
|         let r = f(&mut pkt.buf[..pkt.len]); | ||||
|         self.rx.recv_done(); | ||||
|         self.rx.receive_done(); | ||||
|         r | ||||
|     } | ||||
| } | ||||
| @@ -361,215 +362,3 @@ impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { | ||||
|         r | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod zerocopy_channel { | ||||
|     use core::cell::RefCell; | ||||
|     use core::future::poll_fn; | ||||
|     use core::marker::PhantomData; | ||||
|     use core::task::{Context, Poll}; | ||||
|  | ||||
|     use embassy_sync::blocking_mutex::raw::RawMutex; | ||||
|     use embassy_sync::blocking_mutex::Mutex; | ||||
|     use embassy_sync::waitqueue::WakerRegistration; | ||||
|  | ||||
|     pub struct Channel<'a, M: RawMutex, T> { | ||||
|         buf: *mut T, | ||||
|         phantom: PhantomData<&'a mut T>, | ||||
|         state: Mutex<M, RefCell<State>>, | ||||
|     } | ||||
|  | ||||
|     impl<'a, M: RawMutex, T> Channel<'a, M, T> { | ||||
|         pub fn new(buf: &'a mut [T]) -> Self { | ||||
|             let len = buf.len(); | ||||
|             assert!(len != 0); | ||||
|  | ||||
|             Self { | ||||
|                 buf: buf.as_mut_ptr(), | ||||
|                 phantom: PhantomData, | ||||
|                 state: Mutex::new(RefCell::new(State { | ||||
|                     len, | ||||
|                     front: 0, | ||||
|                     back: 0, | ||||
|                     full: false, | ||||
|                     send_waker: WakerRegistration::new(), | ||||
|                     recv_waker: WakerRegistration::new(), | ||||
|                 })), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { | ||||
|             (Sender { channel: self }, Receiver { channel: self }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub struct Sender<'a, M: RawMutex, T> { | ||||
|         channel: &'a Channel<'a, M, T>, | ||||
|     } | ||||
|  | ||||
|     impl<'a, M: RawMutex, T> Sender<'a, M, T> { | ||||
|         pub fn borrow(&mut self) -> Sender<'_, M, T> { | ||||
|             Sender { channel: self.channel } | ||||
|         } | ||||
|  | ||||
|         pub fn try_send(&mut self) -> Option<&mut T> { | ||||
|             self.channel.state.lock(|s| { | ||||
|                 let s = &mut *s.borrow_mut(); | ||||
|                 match s.push_index() { | ||||
|                     Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), | ||||
|                     None => None, | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { | ||||
|             self.channel.state.lock(|s| { | ||||
|                 let s = &mut *s.borrow_mut(); | ||||
|                 match s.push_index() { | ||||
|                     Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), | ||||
|                     None => { | ||||
|                         s.recv_waker.register(cx.waker()); | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         pub async fn send(&mut self) -> &mut T { | ||||
|             let i = poll_fn(|cx| { | ||||
|                 self.channel.state.lock(|s| { | ||||
|                     let s = &mut *s.borrow_mut(); | ||||
|                     match s.push_index() { | ||||
|                         Some(i) => Poll::Ready(i), | ||||
|                         None => { | ||||
|                             s.recv_waker.register(cx.waker()); | ||||
|                             Poll::Pending | ||||
|                         } | ||||
|                     } | ||||
|                 }) | ||||
|             }) | ||||
|             .await; | ||||
|             unsafe { &mut *self.channel.buf.add(i) } | ||||
|         } | ||||
|  | ||||
|         pub fn send_done(&mut self) { | ||||
|             self.channel.state.lock(|s| s.borrow_mut().push_done()) | ||||
|         } | ||||
|     } | ||||
|     pub struct Receiver<'a, M: RawMutex, T> { | ||||
|         channel: &'a Channel<'a, M, T>, | ||||
|     } | ||||
|  | ||||
|     impl<'a, M: RawMutex, T> Receiver<'a, M, T> { | ||||
|         pub fn borrow(&mut self) -> Receiver<'_, M, T> { | ||||
|             Receiver { channel: self.channel } | ||||
|         } | ||||
|  | ||||
|         pub fn try_recv(&mut self) -> Option<&mut T> { | ||||
|             self.channel.state.lock(|s| { | ||||
|                 let s = &mut *s.borrow_mut(); | ||||
|                 match s.pop_index() { | ||||
|                     Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), | ||||
|                     None => None, | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { | ||||
|             self.channel.state.lock(|s| { | ||||
|                 let s = &mut *s.borrow_mut(); | ||||
|                 match s.pop_index() { | ||||
|                     Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), | ||||
|                     None => { | ||||
|                         s.send_waker.register(cx.waker()); | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         pub async fn recv(&mut self) -> &mut T { | ||||
|             let i = poll_fn(|cx| { | ||||
|                 self.channel.state.lock(|s| { | ||||
|                     let s = &mut *s.borrow_mut(); | ||||
|                     match s.pop_index() { | ||||
|                         Some(i) => Poll::Ready(i), | ||||
|                         None => { | ||||
|                             s.send_waker.register(cx.waker()); | ||||
|                             Poll::Pending | ||||
|                         } | ||||
|                     } | ||||
|                 }) | ||||
|             }) | ||||
|             .await; | ||||
|             unsafe { &mut *self.channel.buf.add(i) } | ||||
|         } | ||||
|  | ||||
|         pub fn recv_done(&mut self) { | ||||
|             self.channel.state.lock(|s| s.borrow_mut().pop_done()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     struct State { | ||||
|         len: usize, | ||||
|  | ||||
|         /// Front index. Always 0..=(N-1) | ||||
|         front: usize, | ||||
|         /// Back index. Always 0..=(N-1). | ||||
|         back: usize, | ||||
|  | ||||
|         /// Used to distinguish "empty" and "full" cases when `front == back`. | ||||
|         /// May only be `true` if `front == back`, always `false` otherwise. | ||||
|         full: bool, | ||||
|  | ||||
|         send_waker: WakerRegistration, | ||||
|         recv_waker: WakerRegistration, | ||||
|     } | ||||
|  | ||||
|     impl State { | ||||
|         fn increment(&self, i: usize) -> usize { | ||||
|             if i + 1 == self.len { | ||||
|                 0 | ||||
|             } else { | ||||
|                 i + 1 | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn is_full(&self) -> bool { | ||||
|             self.full | ||||
|         } | ||||
|  | ||||
|         fn is_empty(&self) -> bool { | ||||
|             self.front == self.back && !self.full | ||||
|         } | ||||
|  | ||||
|         fn push_index(&mut self) -> Option<usize> { | ||||
|             match self.is_full() { | ||||
|                 true => None, | ||||
|                 false => Some(self.back), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn push_done(&mut self) { | ||||
|             assert!(!self.is_full()); | ||||
|             self.back = self.increment(self.back); | ||||
|             if self.back == self.front { | ||||
|                 self.full = true; | ||||
|             } | ||||
|             self.send_waker.wake(); | ||||
|         } | ||||
|  | ||||
|         fn pop_index(&mut self) -> Option<usize> { | ||||
|             match self.is_empty() { | ||||
|                 true => None, | ||||
|                 false => Some(self.front), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn pop_done(&mut self) { | ||||
|             assert!(!self.is_empty()); | ||||
|             self.front = self.increment(self.front); | ||||
|             self.full = false; | ||||
|             self.recv_waker.wake(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ edition = "2021" | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time" } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
|  | ||||
| defmt = { version = "0.3", optional = true } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ edition = "2021" | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|  | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time" } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time" } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync"} | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | ||||
|   | ||||
| @@ -19,6 +19,8 @@ pub struct Control<'a> { | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| enum WifiMode { | ||||
|     None = 0, | ||||
|     Sta = 1, | ||||
| @@ -26,6 +28,18 @@ enum WifiMode { | ||||
|     ApSta = 3, | ||||
| } | ||||
|  | ||||
| pub use proto::CtrlWifiSecProt as Security; | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub struct Status { | ||||
|     pub ssid: String<32>, | ||||
|     pub bssid: [u8; 6], | ||||
|     pub rssi: i32, | ||||
|     pub channel: u32, | ||||
|     pub security: Security, | ||||
| } | ||||
|  | ||||
| macro_rules! ioctl { | ||||
|     ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { | ||||
|         let mut msg = proto::CtrlMsg { | ||||
| @@ -34,7 +48,9 @@ macro_rules! ioctl { | ||||
|             payload: Some(proto::CtrlMsgPayload::$req_variant($req)), | ||||
|         }; | ||||
|         $self.ioctl(&mut msg).await?; | ||||
|         let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { | ||||
|         #[allow(unused_mut)] | ||||
|         let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload | ||||
|         else { | ||||
|             warn!("unexpected response variant"); | ||||
|             return Err(Error::Internal); | ||||
|         }; | ||||
| @@ -66,6 +82,19 @@ impl<'a> Control<'a> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn get_status(&mut self) -> Result<Status, Error> { | ||||
|         let req = proto::CtrlMsgReqGetApConfig {}; | ||||
|         ioctl!(self, ReqGetApConfig, RespGetApConfig, req, resp); | ||||
|         trim_nulls(&mut resp.ssid); | ||||
|         Ok(Status { | ||||
|             ssid: resp.ssid, | ||||
|             bssid: parse_mac(&resp.bssid)?, | ||||
|             rssi: resp.rssi as _, | ||||
|             channel: resp.chnl, | ||||
|             security: resp.sec_prot, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { | ||||
|         let req = proto::CtrlMsgReqConnectAp { | ||||
|             ssid: String::from(ssid), | ||||
| @@ -98,27 +127,7 @@ impl<'a> Control<'a> { | ||||
|             mode: WifiMode::Sta as _, | ||||
|         }; | ||||
|         ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); | ||||
|  | ||||
|         // WHY IS THIS A STRING? WHYYYY | ||||
|         fn nibble_from_hex(b: u8) -> u8 { | ||||
|             match b { | ||||
|                 b'0'..=b'9' => b - b'0', | ||||
|                 b'a'..=b'f' => b + 0xa - b'a', | ||||
|                 b'A'..=b'F' => b + 0xa - b'A', | ||||
|                 _ => panic!("invalid hex digit {}", b), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let mac = resp.mac.as_bytes(); | ||||
|         let mut res = [0; 6]; | ||||
|         if mac.len() != 17 { | ||||
|             warn!("unexpected MAC respnse length"); | ||||
|             return Err(Error::Internal); | ||||
|         } | ||||
|         for (i, b) in res.iter_mut().enumerate() { | ||||
|             *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) | ||||
|         } | ||||
|         Ok(res) | ||||
|         parse_mac(&resp.mac) | ||||
|     } | ||||
|  | ||||
|     async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { | ||||
| @@ -167,3 +176,35 @@ impl<'a> Control<'a> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // WHY IS THIS A STRING? WHYYYY | ||||
| fn parse_mac(mac: &str) -> Result<[u8; 6], Error> { | ||||
|     fn nibble_from_hex(b: u8) -> Result<u8, Error> { | ||||
|         match b { | ||||
|             b'0'..=b'9' => Ok(b - b'0'), | ||||
|             b'a'..=b'f' => Ok(b + 0xa - b'a'), | ||||
|             b'A'..=b'F' => Ok(b + 0xa - b'A'), | ||||
|             _ => { | ||||
|                 warn!("invalid hex digit {}", b); | ||||
|                 Err(Error::Internal) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mac = mac.as_bytes(); | ||||
|     let mut res = [0; 6]; | ||||
|     if mac.len() != 17 { | ||||
|         warn!("unexpected MAC length"); | ||||
|         return Err(Error::Internal); | ||||
|     } | ||||
|     for (i, b) in res.iter_mut().enumerate() { | ||||
|         *b = (nibble_from_hex(mac[i * 3])? << 4) | nibble_from_hex(mac[i * 3 + 1])? | ||||
|     } | ||||
|     Ok(res) | ||||
| } | ||||
|  | ||||
| fn trim_nulls<const N: usize>(s: &mut String<N>) { | ||||
|     while s.chars().rev().next() == Some(0 as char) { | ||||
|         s.pop(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -93,7 +93,7 @@ macro_rules! unreachable { | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*); | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -229,7 +229,8 @@ impl<T, E> Try for Result<T, E> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Bytes<'a>(pub &'a [u8]); | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|   | ||||
| @@ -18,7 +18,7 @@ log = { version = "0.4.14", optional = true } | ||||
| embedded-io-async = { version = "0.5.0" } | ||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| ppproto = { version = "0.1.1"} | ||||
| ppproto = { version = "0.1.2"} | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
|  | ||||
| [package.metadata.embassy_docs] | ||||
|   | ||||
| @@ -93,7 +93,7 @@ macro_rules! unreachable { | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*); | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -229,7 +229,8 @@ impl<T, E> Try for Result<T, E> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Bytes<'a>(pub &'a [u8]); | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|   | ||||
| @@ -53,6 +53,8 @@ pub enum RunError<E> { | ||||
|     WriteZero, | ||||
|     /// Writing to the serial got EOF. | ||||
|     Eof, | ||||
|     /// PPP protocol was terminated by the peer | ||||
|     Terminated, | ||||
| } | ||||
|  | ||||
| impl<E> From<WriteAllError<E>> for RunError<E> { | ||||
| @@ -128,6 +130,9 @@ impl<'d> Runner<'d> { | ||||
|  | ||||
|                     let status = ppp.status(); | ||||
|                     match status.phase { | ||||
|                         ppproto::Phase::Dead => { | ||||
|                             return Err(RunError::Terminated); | ||||
|                         } | ||||
|                         ppproto::Phase::Open => { | ||||
|                             if !was_up { | ||||
|                                 on_ipv4_up(status.ipv4.unwrap()); | ||||
|   | ||||
| @@ -11,7 +11,7 @@ edition = "2021" | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time" } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| defmt = { version = "0.3", optional = true } | ||||
|  | ||||
|   | ||||
| @@ -51,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ | ||||
| ] } | ||||
|  | ||||
| embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time" } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time" } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embedded-io-async = { version = "0.5.0", optional = true } | ||||
|  | ||||
|   | ||||
| @@ -22,13 +22,13 @@ where | ||||
|  | ||||
|     fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||||
|         self.inner | ||||
|             .receive(self.cx.as_deref_mut().unwrap()) | ||||
|             .receive(unwrap!(self.cx.as_deref_mut())) | ||||
|             .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) | ||||
|     } | ||||
|  | ||||
|     /// Construct a transmit token. | ||||
|     fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> { | ||||
|         self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) | ||||
|         self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter) | ||||
|     } | ||||
|  | ||||
|     /// Get a description of device capabilities. | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -172,6 +172,7 @@ impl Config { | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// ```rust | ||||
|     /// # use embassy_net::Config; | ||||
|     /// let _cfg = Config::dhcpv4(Default::default()); | ||||
|     /// ``` | ||||
|     #[cfg(feature = "dhcpv4")] | ||||
| @@ -226,6 +227,7 @@ struct Inner<D: Driver> { | ||||
|     static_v6: Option<StaticConfigV6>, | ||||
|     #[cfg(feature = "dhcpv4")] | ||||
|     dhcp_socket: Option<SocketHandle>, | ||||
|     config_waker: WakerRegistration, | ||||
|     #[cfg(feature = "dns")] | ||||
|     dns_socket: SocketHandle, | ||||
|     #[cfg(feature = "dns")] | ||||
| @@ -297,6 +299,7 @@ impl<D: Driver + 'static> Stack<D> { | ||||
|             static_v6: None, | ||||
|             #[cfg(feature = "dhcpv4")] | ||||
|             dhcp_socket: None, | ||||
|             config_waker: WakerRegistration::new(), | ||||
|             #[cfg(feature = "dns")] | ||||
|             dns_socket: socket.sockets.add(dns::Socket::new( | ||||
|                 &[], | ||||
| @@ -363,6 +366,55 @@ impl<D: Driver + 'static> Stack<D> { | ||||
|         v4_up || v6_up | ||||
|     } | ||||
|  | ||||
|     /// Wait for the network stack to obtain a valid IP configuration. | ||||
|     /// | ||||
|     /// ## Notes: | ||||
|     /// - Ensure [`Stack::run`] has been called before using this function. | ||||
|     /// | ||||
|     /// - This function may never return (e.g. if no configuration is obtained through DHCP). | ||||
|     /// The caller is supposed to handle a timeout for this case. | ||||
|     /// | ||||
|     /// ## Example | ||||
|     /// ```ignore | ||||
|     /// let config = embassy_net::Config::dhcpv4(Default::default()); | ||||
|     ///// Init network stack | ||||
|     /// let stack = &*make_static!(embassy_net::Stack::new( | ||||
|     ///    device, | ||||
|     ///    config, | ||||
|     ///    make_static!(embassy_net::StackResources::<2>::new()), | ||||
|     ///    seed | ||||
|     /// )); | ||||
|     /// // Launch network task that runs `stack.run().await` | ||||
|     /// spawner.spawn(net_task(stack)).unwrap(); | ||||
|     /// // Wait for DHCP config | ||||
|     /// stack.wait_config_up().await; | ||||
|     /// // use the network stack | ||||
|     /// // ... | ||||
|     /// ``` | ||||
|     pub async fn wait_config_up(&self) { | ||||
|         // If the config is up already, we can return immediately. | ||||
|         if self.is_config_up() { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         poll_fn(|cx| { | ||||
|             if self.is_config_up() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 // If the config is not up, we register a waker that is woken up | ||||
|                 // when a config is applied (static or DHCP). | ||||
|                 trace!("Waiting for config up"); | ||||
|  | ||||
|                 self.with_mut(|_, i| { | ||||
|                     i.config_waker.register(cx.waker()); | ||||
|                 }); | ||||
|  | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
|     } | ||||
|  | ||||
|     /// Get the current IPv4 configuration. | ||||
|     /// | ||||
|     /// If using DHCP, this will be None if DHCP hasn't been able to | ||||
| @@ -616,7 +668,7 @@ impl<D: Driver + 'static> Inner<D> { | ||||
|                 } | ||||
|  | ||||
|                 // Configure it | ||||
|                 let socket = _s.sockets.get_mut::<dhcpv4::Socket>(self.dhcp_socket.unwrap()); | ||||
|                 let socket = _s.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket)); | ||||
|                 socket.set_ignore_naks(c.ignore_naks); | ||||
|                 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); | ||||
|                 socket.set_ports(c.server_port, c.client_port); | ||||
| @@ -656,12 +708,12 @@ impl<D: Driver + 'static> Inner<D> { | ||||
|             debug!("   IP address:      {:?}", config.address); | ||||
|             debug!("   Default gateway: {:?}", config.gateway); | ||||
|  | ||||
|             addrs.push(IpCidr::Ipv4(config.address)).unwrap(); | ||||
|             unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok()); | ||||
|             gateway_v4 = config.gateway.into(); | ||||
|             #[cfg(feature = "dns")] | ||||
|             for s in &config.dns_servers { | ||||
|                 debug!("   DNS server:      {:?}", s); | ||||
|                 dns_servers.push(s.clone().into()).unwrap(); | ||||
|                 unwrap!(dns_servers.push(s.clone().into()).ok()); | ||||
|             } | ||||
|         } else { | ||||
|             info!("IPv4: DOWN"); | ||||
| @@ -673,12 +725,12 @@ impl<D: Driver + 'static> Inner<D> { | ||||
|             debug!("   IP address:      {:?}", config.address); | ||||
|             debug!("   Default gateway: {:?}", config.gateway); | ||||
|  | ||||
|             addrs.push(IpCidr::Ipv6(config.address)).unwrap(); | ||||
|             unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok()); | ||||
|             gateway_v6 = config.gateway.into(); | ||||
|             #[cfg(feature = "dns")] | ||||
|             for s in &config.dns_servers { | ||||
|                 debug!("   DNS server:      {:?}", s); | ||||
|                 dns_servers.push(s.clone().into()).unwrap(); | ||||
|                 unwrap!(dns_servers.push(s.clone().into()).ok()); | ||||
|             } | ||||
|         } else { | ||||
|             info!("IPv6: DOWN"); | ||||
| @@ -690,13 +742,13 @@ impl<D: Driver + 'static> Inner<D> { | ||||
|         // Apply gateways | ||||
|         #[cfg(feature = "proto-ipv4")] | ||||
|         if let Some(gateway) = gateway_v4 { | ||||
|             s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); | ||||
|             unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway)); | ||||
|         } else { | ||||
|             s.iface.routes_mut().remove_default_ipv4_route(); | ||||
|         } | ||||
|         #[cfg(feature = "proto-ipv6")] | ||||
|         if let Some(gateway) = gateway_v6 { | ||||
|             s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap(); | ||||
|             unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway)); | ||||
|         } else { | ||||
|             s.iface.routes_mut().remove_default_ipv6_route(); | ||||
|         } | ||||
| @@ -706,6 +758,8 @@ impl<D: Driver + 'static> Inner<D> { | ||||
|         s.sockets | ||||
|             .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket) | ||||
|             .update_servers(&dns_servers[..]); | ||||
|  | ||||
|         self.config_waker.wake(); | ||||
|     } | ||||
|  | ||||
|     fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { | ||||
|   | ||||
| @@ -440,7 +440,7 @@ impl<'d> TcpIo<'d> { | ||||
|                         Poll::Ready(Err(Error::ConnectionReset)) | ||||
|                     } | ||||
|                 } else { | ||||
|                     Poll::Ready(match s.send(f.take().unwrap()) { | ||||
|                     Poll::Ready(match s.send(unwrap!(f.take())) { | ||||
|                         // Connection reset. TODO: this can also be timeouts etc, investigate. | ||||
|                         Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), | ||||
|                         Ok(r) => Ok(r), | ||||
| @@ -468,7 +468,7 @@ impl<'d> TcpIo<'d> { | ||||
|                         Poll::Ready(Err(Error::ConnectionReset)) | ||||
|                     } | ||||
|                 } else { | ||||
|                     Poll::Ready(match s.recv(f.take().unwrap()) { | ||||
|                     Poll::Ready(match s.recv(unwrap!(f.take())) { | ||||
|                         // Connection reset. TODO: this can also be timeouts etc, investigate. | ||||
|                         Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { | ||||
|                             Err(Error::ConnectionReset) | ||||
|   | ||||
| @@ -29,6 +29,8 @@ pub enum BindError { | ||||
| pub enum Error { | ||||
|     /// No route to host. | ||||
|     NoRoute, | ||||
|     /// Socket not bound to an outgoing port. | ||||
|     SocketNotBound, | ||||
| } | ||||
|  | ||||
| /// An UDP socket. | ||||
| @@ -155,7 +157,14 @@ impl<'a> UdpSocket<'a> { | ||||
|                 s.register_send_waker(cx.waker()); | ||||
|                 Poll::Pending | ||||
|             } | ||||
|             Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), | ||||
|             Err(udp::SendError::Unaddressable) => { | ||||
|                 // If no sender/outgoing port is specified, there is not really "no route" | ||||
|                 if s.endpoint().port == 0 { | ||||
|                     Poll::Ready(Err(Error::SocketNotBound)) | ||||
|                 } else { | ||||
|                     Poll::Ready(Err(Error::NoRoute)) | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -91,7 +91,7 @@ _dppi = [] | ||||
| _gpio-p1 = [] | ||||
|  | ||||
| [dependencies] | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } | ||||
| embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -68,6 +68,28 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | ||||
|         let r = T::regs(); | ||||
|         let s = T::state(); | ||||
|  | ||||
|         #[cfg(feature = "nrf52832")] | ||||
|         // NRF32 Anomaly 109 workaround... NRF52832 | ||||
|         if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 { | ||||
|             // Handle the first "fake" transmission | ||||
|             r.events_started.reset(); | ||||
|             r.events_end.reset(); | ||||
|  | ||||
|             // Update DMA registers with correct rx/tx buffer sizes | ||||
|             r.rxd | ||||
|                 .maxcnt | ||||
|                 .write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) }); | ||||
|             r.txd | ||||
|                 .maxcnt | ||||
|                 .write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) }); | ||||
|  | ||||
|             // Disable interrupt for STARTED event... | ||||
|             r.intenclr.write(|w| w.started().clear()); | ||||
|             // ... and start actual, hopefully glitch-free transmission | ||||
|             r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if r.events_end.read().bits() != 0 { | ||||
|             s.end_waker.wake(); | ||||
|             r.intenclr.write(|w| w.end().clear()); | ||||
| @@ -167,42 +189,10 @@ impl<'d, T: Instance> Spim<'d, T> { | ||||
|         // Enable SPIM instance. | ||||
|         r.enable.write(|w| w.enable().enabled()); | ||||
|  | ||||
|         // Configure mode. | ||||
|         let mode = config.mode; | ||||
|         r.config.write(|w| { | ||||
|             match mode { | ||||
|                 MODE_0 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_high(); | ||||
|                     w.cpha().leading(); | ||||
|                 } | ||||
|                 MODE_1 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_high(); | ||||
|                     w.cpha().trailing(); | ||||
|                 } | ||||
|                 MODE_2 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_low(); | ||||
|                     w.cpha().leading(); | ||||
|                 } | ||||
|                 MODE_3 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_low(); | ||||
|                     w.cpha().trailing(); | ||||
|                 } | ||||
|             } | ||||
|         let mut spim = Self { _p: spim }; | ||||
|  | ||||
|             w | ||||
|         }); | ||||
|  | ||||
|         // Configure frequency. | ||||
|         let frequency = config.frequency; | ||||
|         r.frequency.write(|w| w.frequency().variant(frequency)); | ||||
|  | ||||
|         // Set over-read character | ||||
|         let orc = config.orc; | ||||
|         r.orc.write(|w| unsafe { w.orc().bits(orc) }); | ||||
|         // Apply runtime peripheral configuration | ||||
|         Self::set_config(&mut spim, &config); | ||||
|  | ||||
|         // Disable all events interrupts | ||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||
| @@ -210,7 +200,7 @@ impl<'d, T: Instance> Spim<'d, T> { | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
|  | ||||
|         Self { _p: spim } | ||||
|         spim | ||||
|     } | ||||
|  | ||||
|     fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||
| @@ -223,14 +213,33 @@ impl<'d, T: Instance> Spim<'d, T> { | ||||
|         let r = T::regs(); | ||||
|  | ||||
|         // Set up the DMA write. | ||||
|         let (ptr, len) = slice_ptr_parts(tx); | ||||
|         let (ptr, tx_len) = slice_ptr_parts(tx); | ||||
|         r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | ||||
|         r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | ||||
|         r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); | ||||
|  | ||||
|         // Set up the DMA read. | ||||
|         let (ptr, len) = slice_ptr_parts_mut(rx); | ||||
|         let (ptr, rx_len) = slice_ptr_parts_mut(rx); | ||||
|         r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | ||||
|         r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | ||||
|         r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); | ||||
|  | ||||
|         // ANOMALY 109 workaround | ||||
|         #[cfg(feature = "nrf52832")] | ||||
|         { | ||||
|             let s = T::state(); | ||||
|  | ||||
|             r.events_started.reset(); | ||||
|  | ||||
|             // Set rx/tx buffer lengths to 0... | ||||
|             r.txd.maxcnt.reset(); | ||||
|             r.rxd.maxcnt.reset(); | ||||
|  | ||||
|             // ...and keep track of original buffer lengths... | ||||
|             s.tx.store(tx_len as _, Ordering::Relaxed); | ||||
|             s.rx.store(rx_len as _, Ordering::Relaxed); | ||||
|  | ||||
|             // ...signalling the start of the fake transfer. | ||||
|             r.intenset.write(|w| w.started().bit(true)); | ||||
|         } | ||||
|  | ||||
|         // Reset and enable the event | ||||
|         r.events_end.reset(); | ||||
| @@ -386,18 +395,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { | ||||
| } | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|     #[cfg(feature = "nrf52832")] | ||||
|     use core::sync::atomic::AtomicU8; | ||||
|  | ||||
|     use embassy_sync::waitqueue::AtomicWaker; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     pub struct State { | ||||
|         pub end_waker: AtomicWaker, | ||||
|         #[cfg(feature = "nrf52832")] | ||||
|         pub rx: AtomicU8, | ||||
|         #[cfg(feature = "nrf52832")] | ||||
|         pub tx: AtomicU8, | ||||
|     } | ||||
|  | ||||
|     impl State { | ||||
|         pub const fn new() -> Self { | ||||
|             Self { | ||||
|                 end_waker: AtomicWaker::new(), | ||||
|                 #[cfg(feature = "nrf52832")] | ||||
|                 rx: AtomicU8::new(0), | ||||
|                 #[cfg(feature = "nrf52832")] | ||||
|                 tx: AtomicU8::new(0), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -169,47 +169,10 @@ impl<'d, T: Instance> Spis<'d, T> { | ||||
|         // Enable SPIS instance. | ||||
|         r.enable.write(|w| w.enable().enabled()); | ||||
|  | ||||
|         // Configure mode. | ||||
|         let mode = config.mode; | ||||
|         r.config.write(|w| { | ||||
|             match mode { | ||||
|                 MODE_0 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_high(); | ||||
|                     w.cpha().leading(); | ||||
|                 } | ||||
|                 MODE_1 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_high(); | ||||
|                     w.cpha().trailing(); | ||||
|                 } | ||||
|                 MODE_2 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_low(); | ||||
|                     w.cpha().leading(); | ||||
|                 } | ||||
|                 MODE_3 => { | ||||
|                     w.order().msb_first(); | ||||
|                     w.cpol().active_low(); | ||||
|                     w.cpha().trailing(); | ||||
|                 } | ||||
|             } | ||||
|         let mut spis = Self { _p: spis }; | ||||
|  | ||||
|             w | ||||
|         }); | ||||
|  | ||||
|         // Set over-read character. | ||||
|         let orc = config.orc; | ||||
|         r.orc.write(|w| unsafe { w.orc().bits(orc) }); | ||||
|  | ||||
|         // Set default character. | ||||
|         let def = config.def; | ||||
|         r.def.write(|w| unsafe { w.def().bits(def) }); | ||||
|  | ||||
|         // Configure auto-acquire on 'transfer end' event. | ||||
|         if config.auto_acquire { | ||||
|             r.shorts.write(|w| w.end_acquire().bit(true)); | ||||
|         } | ||||
|         // Apply runtime peripheral configuration | ||||
|         Self::set_config(&mut spis, &config); | ||||
|  | ||||
|         // Disable all events interrupts. | ||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||
| @@ -217,7 +180,7 @@ impl<'d, T: Instance> Spis<'d, T> { | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
|  | ||||
|         Self { _p: spis } | ||||
|         spis | ||||
|     } | ||||
|  | ||||
|     fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||
|   | ||||
| @@ -57,7 +57,6 @@ impl<'d> Temp<'d> { | ||||
|     /// ```no_run | ||||
|     /// use embassy_nrf::{bind_interrupts, temp}; | ||||
|     /// use embassy_nrf::temp::Temp; | ||||
|     /// use embassy_time::{Duration, Timer}; | ||||
|     /// | ||||
|     /// bind_interrupts!(struct Irqs { | ||||
|     ///     TEMP => temp::InterruptHandler; | ||||
|   | ||||
| @@ -167,9 +167,10 @@ impl<'d, T: Instance> Twim<'d, T> { | ||||
|         // Enable TWIM instance. | ||||
|         r.enable.write(|w| w.enable().enabled()); | ||||
|  | ||||
|         // Configure frequency. | ||||
|         r.frequency | ||||
|             .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); | ||||
|         let mut twim = Self { _p: twim }; | ||||
|  | ||||
|         // Apply runtime peripheral configuration | ||||
|         Self::set_config(&mut twim, &config); | ||||
|  | ||||
|         // Disable all events interrupts | ||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||
| @@ -177,7 +178,7 @@ impl<'d, T: Instance> Twim<'d, T> { | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
|  | ||||
|         Self { _p: twim } | ||||
|         twim | ||||
|     } | ||||
|  | ||||
|     /// Set TX buffer, checking that it is in RAM and has suitable length. | ||||
|   | ||||
| @@ -60,7 +60,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] | ||||
|  | ||||
| [dependencies] | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | ||||
| embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||
|   | ||||
| @@ -94,6 +94,7 @@ impl ClockConfig { | ||||
|                     post_div1: 6, | ||||
|                     post_div2: 5, | ||||
|                 }), | ||||
|                 delay_multiplier: 128, | ||||
|             }), | ||||
|             ref_clk: RefClkConfig { | ||||
|                 src: RefClkSrc::Xosc, | ||||
| @@ -203,6 +204,7 @@ pub struct XoscConfig { | ||||
|     pub hz: u32, | ||||
|     pub sys_pll: Option<PllConfig>, | ||||
|     pub usb_pll: Option<PllConfig>, | ||||
|     pub delay_multiplier: u32, | ||||
| } | ||||
|  | ||||
| pub struct PllConfig { | ||||
| @@ -363,7 +365,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { | ||||
|             // start XOSC | ||||
|             // datasheet mentions support for clock inputs into XIN, but doesn't go into | ||||
|             // how this is achieved. pico-sdk doesn't support this at all. | ||||
|             start_xosc(config.hz); | ||||
|             start_xosc(config.hz, config.delay_multiplier); | ||||
|  | ||||
|             let pll_sys_freq = match config.sys_pll { | ||||
|                 Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), | ||||
| @@ -624,12 +626,12 @@ pub fn clk_rtc_freq() -> u16 { | ||||
|     CLOCKS.rtc.load(Ordering::Relaxed) | ||||
| } | ||||
|  | ||||
| fn start_xosc(crystal_hz: u32) { | ||||
| fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { | ||||
|     pac::XOSC | ||||
|         .ctrl() | ||||
|         .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); | ||||
|  | ||||
|     let startup_delay = ((crystal_hz / 1000) + 128) / 256; | ||||
|     let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; | ||||
|     pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); | ||||
|     pac::XOSC.ctrl().write(|w| { | ||||
|         w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,8 @@ pub enum AbortReason { | ||||
|     NoAcknowledge, | ||||
|     /// The arbitration was lost, e.g. electrical problems with the clock signal | ||||
|     ArbitrationLoss, | ||||
|     /// Transmit ended with data still in fifo | ||||
|     TxNotEmpty(u16), | ||||
|     Other(u32), | ||||
| } | ||||
|  | ||||
| @@ -52,7 +54,7 @@ impl Default for Config { | ||||
|     } | ||||
| } | ||||
|  | ||||
| const FIFO_SIZE: u8 = 16; | ||||
| pub const FIFO_SIZE: u8 = 16; | ||||
|  | ||||
| pub struct I2c<'d, T: Instance, M: Mode> { | ||||
|     phantom: PhantomData<(&'d mut T, M)>, | ||||
| @@ -636,6 +638,7 @@ mod eh1 { | ||||
|                 Self::Abort(AbortReason::NoAcknowledge) => { | ||||
|                     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) | ||||
|                 } | ||||
|                 Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, | ||||
| @@ -738,8 +741,8 @@ mod nightly { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn i2c_reserved_addr(addr: u16) -> bool { | ||||
|     (addr & 0x78) == 0 || (addr & 0x78) == 0x78 | ||||
| pub fn i2c_reserved_addr(addr: u16) -> bool { | ||||
|     ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 | ||||
| } | ||||
|  | ||||
| mod sealed { | ||||
|   | ||||
							
								
								
									
										338
									
								
								embassy-rp/src/i2c_slave.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								embassy-rp/src/i2c_slave.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| use core::future; | ||||
| use core::marker::PhantomData; | ||||
| use core::task::Poll; | ||||
|  | ||||
| use embassy_hal_internal::into_ref; | ||||
| use pac::i2c; | ||||
|  | ||||
| use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; | ||||
| use crate::interrupt::typelevel::{Binding, Interrupt}; | ||||
| use crate::{pac, Peripheral}; | ||||
|  | ||||
| /// I2C error | ||||
| #[derive(Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| #[non_exhaustive] | ||||
| pub enum Error { | ||||
|     /// I2C abort with error | ||||
|     Abort(AbortReason), | ||||
|     /// User passed in a response buffer that was 0 length | ||||
|     InvalidResponseBufferLength, | ||||
| } | ||||
|  | ||||
| /// Received command | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum Command { | ||||
|     /// General Call | ||||
|     GeneralCall(usize), | ||||
|     /// Read | ||||
|     Read, | ||||
|     /// Write+read | ||||
|     WriteRead(usize), | ||||
|     /// Write | ||||
|     Write(usize), | ||||
| } | ||||
|  | ||||
| /// Possible responses to responding to a read | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum ReadStatus { | ||||
|     /// Transaction Complete, controller naked our last byte | ||||
|     Done, | ||||
|     /// Transaction Incomplete, controller trying to read more bytes than were provided | ||||
|     NeedMoreBytes, | ||||
|     /// Transaction Complere, but controller stopped reading bytes before we ran out | ||||
|     LeftoverBytes(u16), | ||||
| } | ||||
|  | ||||
| /// Slave Configuration | ||||
| #[non_exhaustive] | ||||
| #[derive(Copy, Clone)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub struct Config { | ||||
|     /// Target Address | ||||
|     pub addr: u16, | ||||
| } | ||||
|  | ||||
| impl Default for Config { | ||||
|     fn default() -> Self { | ||||
|         Self { addr: 0x55 } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct I2cSlave<'d, T: Instance> { | ||||
|     phantom: PhantomData<&'d mut T>, | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> I2cSlave<'d, T> { | ||||
|     pub fn new( | ||||
|         _peri: impl Peripheral<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||||
|         sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||||
|         _irq: impl Binding<T::Interrupt, InterruptHandler<T>>, | ||||
|         config: Config, | ||||
|     ) -> Self { | ||||
|         into_ref!(_peri, scl, sda); | ||||
|  | ||||
|         assert!(!i2c_reserved_addr(config.addr)); | ||||
|         assert!(config.addr != 0); | ||||
|  | ||||
|         let p = T::regs(); | ||||
|  | ||||
|         let reset = T::reset(); | ||||
|         crate::reset::reset(reset); | ||||
|         crate::reset::unreset_wait(reset); | ||||
|  | ||||
|         p.ic_enable().write(|w| w.set_enable(false)); | ||||
|  | ||||
|         p.ic_sar().write(|w| w.set_ic_sar(config.addr)); | ||||
|         p.ic_con().modify(|w| { | ||||
|             w.set_master_mode(false); | ||||
|             w.set_ic_slave_disable(false); | ||||
|             w.set_tx_empty_ctrl(true); | ||||
|         }); | ||||
|  | ||||
|         // Set FIFO watermarks to 1 to make things simpler. This is encoded | ||||
|         // by a register value of 0. Rx watermark should never change, but Tx watermark will be | ||||
|         // adjusted in operation. | ||||
|         p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | ||||
|         p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||||
|  | ||||
|         // Configure SCL & SDA pins | ||||
|         scl.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||||
|         sda.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||||
|  | ||||
|         scl.pad_ctrl().write(|w| { | ||||
|             w.set_schmitt(true); | ||||
|             w.set_ie(true); | ||||
|             w.set_od(false); | ||||
|             w.set_pue(true); | ||||
|             w.set_pde(false); | ||||
|         }); | ||||
|         sda.pad_ctrl().write(|w| { | ||||
|             w.set_schmitt(true); | ||||
|             w.set_ie(true); | ||||
|             w.set_od(false); | ||||
|             w.set_pue(true); | ||||
|             w.set_pde(false); | ||||
|         }); | ||||
|  | ||||
|         // Clear interrupts | ||||
|         p.ic_clr_intr().read(); | ||||
|  | ||||
|         // Enable I2C block | ||||
|         p.ic_enable().write(|w| w.set_enable(true)); | ||||
|  | ||||
|         // mask everything initially | ||||
|         p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
|  | ||||
|         Self { phantom: PhantomData } | ||||
|     } | ||||
|  | ||||
|     /// Calls `f` to check if we are ready or not. | ||||
|     /// If not, `g` is called once the waker is set (to eg enable the required interrupts). | ||||
|     #[inline(always)] | ||||
|     async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U | ||||
|     where | ||||
|         F: FnMut(&mut Self) -> Poll<U>, | ||||
|         G: FnMut(&mut Self), | ||||
|     { | ||||
|         future::poll_fn(|cx| { | ||||
|             let r = f(self); | ||||
|  | ||||
|             trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); | ||||
|  | ||||
|             if r.is_pending() { | ||||
|                 T::waker().register(cx.waker()); | ||||
|                 g(self); | ||||
|             } | ||||
|  | ||||
|             r | ||||
|         }) | ||||
|         .await | ||||
|     } | ||||
|  | ||||
|     #[inline(always)] | ||||
|     fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { | ||||
|         let p = T::regs(); | ||||
|         let len = p.ic_rxflr().read().rxflr() as usize; | ||||
|         let end = offset + len; | ||||
|         for i in offset..end { | ||||
|             buffer[i] = p.ic_data_cmd().read().dat(); | ||||
|         } | ||||
|         end | ||||
|     } | ||||
|  | ||||
|     #[inline(always)] | ||||
|     fn write_to_fifo(&mut self, buffer: &[u8]) { | ||||
|         let p = T::regs(); | ||||
|         for byte in buffer { | ||||
|             p.ic_data_cmd().write(|w| w.set_dat(*byte)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Wait asynchronously for commands from an I2C master. | ||||
|     /// `buffer` is provided in case master does a 'write' and is unused for 'read'. | ||||
|     pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { | ||||
|         let p = T::regs(); | ||||
|  | ||||
|         p.ic_clr_intr().read(); | ||||
|         // set rx fifo watermark to 1 byte | ||||
|         p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||||
|  | ||||
|         let mut len = 0; | ||||
|         let ret = self | ||||
|             .wait_on( | ||||
|                 |me| { | ||||
|                     let stat = p.ic_raw_intr_stat().read(); | ||||
|                     if p.ic_rxflr().read().rxflr() > 0 { | ||||
|                         len = me.drain_fifo(buffer, len); | ||||
|                         // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise | ||||
|                         p.ic_rx_tl().write(|w| w.set_rx_tl(11)); | ||||
|                     } | ||||
|  | ||||
|                     if stat.restart_det() && stat.rd_req() { | ||||
|                         Poll::Ready(Ok(Command::WriteRead(len))) | ||||
|                     } else if stat.gen_call() && stat.stop_det() && len > 0 { | ||||
|                         Poll::Ready(Ok(Command::GeneralCall(len))) | ||||
|                     } else if stat.stop_det() { | ||||
|                         Poll::Ready(Ok(Command::Write(len))) | ||||
|                     } else if stat.rd_req() { | ||||
|                         Poll::Ready(Ok(Command::Read)) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 }, | ||||
|                 |_me| { | ||||
|                     p.ic_intr_mask().modify(|w| { | ||||
|                         w.set_m_stop_det(true); | ||||
|                         w.set_m_restart_det(true); | ||||
|                         w.set_m_gen_call(true); | ||||
|                         w.set_m_rd_req(true); | ||||
|                         w.set_m_rx_full(true); | ||||
|                     }); | ||||
|                 }, | ||||
|             ) | ||||
|             .await; | ||||
|  | ||||
|         p.ic_clr_intr().read(); | ||||
|  | ||||
|         ret | ||||
|     } | ||||
|  | ||||
|     /// Respond to an I2C master READ command, asynchronously. | ||||
|     pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> { | ||||
|         let p = T::regs(); | ||||
|  | ||||
|         if buffer.len() == 0 { | ||||
|             return Err(Error::InvalidResponseBufferLength); | ||||
|         } | ||||
|  | ||||
|         let mut chunks = buffer.chunks(FIFO_SIZE as usize); | ||||
|  | ||||
|         let ret = self | ||||
|             .wait_on( | ||||
|                 |me| { | ||||
|                     if let Err(abort_reason) = me.read_and_clear_abort_reason() { | ||||
|                         if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { | ||||
|                             return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); | ||||
|                         } else { | ||||
|                             return Poll::Ready(Err(abort_reason)); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if let Some(chunk) = chunks.next() { | ||||
|                         me.write_to_fifo(chunk); | ||||
|  | ||||
|                         Poll::Pending | ||||
|                     } else { | ||||
|                         let stat = p.ic_raw_intr_stat().read(); | ||||
|  | ||||
|                         if stat.rx_done() && stat.stop_det() { | ||||
|                             Poll::Ready(Ok(ReadStatus::Done)) | ||||
|                         } else if stat.rd_req() { | ||||
|                             Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) | ||||
|                         } else { | ||||
|                             Poll::Pending | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 |_me| { | ||||
|                     p.ic_intr_mask().modify(|w| { | ||||
|                         w.set_m_stop_det(true); | ||||
|                         w.set_m_rx_done(true); | ||||
|                         w.set_m_tx_empty(true); | ||||
|                         w.set_m_tx_abrt(true); | ||||
|                     }) | ||||
|                 }, | ||||
|             ) | ||||
|             .await; | ||||
|  | ||||
|         p.ic_clr_intr().read(); | ||||
|  | ||||
|         ret | ||||
|     } | ||||
|  | ||||
|     /// Respond to reads with the fill byte until the controller stops asking | ||||
|     pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { | ||||
|         loop { | ||||
|             match self.respond_to_read(&[fill]).await { | ||||
|                 Ok(ReadStatus::NeedMoreBytes) => (), | ||||
|                 Ok(_) => break Ok(()), | ||||
|                 Err(e) => break Err(e), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Respond to a master read, then fill any remaining read bytes with `fill` | ||||
|     pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> { | ||||
|         let resp_stat = self.respond_to_read(buffer).await?; | ||||
|  | ||||
|         if resp_stat == ReadStatus::NeedMoreBytes { | ||||
|             self.respond_till_stop(fill).await?; | ||||
|             Ok(ReadStatus::Done) | ||||
|         } else { | ||||
|             Ok(resp_stat) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline(always)] | ||||
|     fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { | ||||
|         let p = T::regs(); | ||||
|         let mut abort_reason = p.ic_tx_abrt_source().read(); | ||||
|  | ||||
|         // Mask off fifo flush count | ||||
|         let tx_flush_cnt = abort_reason.tx_flush_cnt(); | ||||
|         abort_reason.set_tx_flush_cnt(0); | ||||
|  | ||||
|         // Mask off master_dis | ||||
|         abort_reason.set_abrt_master_dis(false); | ||||
|  | ||||
|         if abort_reason.0 != 0 { | ||||
|             // Note clearing the abort flag also clears the reason, and this | ||||
|             // instance of flag is clear-on-read! Note also the | ||||
|             // IC_CLR_TX_ABRT register always reads as 0. | ||||
|             p.ic_clr_tx_abrt().read(); | ||||
|  | ||||
|             let reason = if abort_reason.abrt_7b_addr_noack() | ||||
|                 | abort_reason.abrt_10addr1_noack() | ||||
|                 | abort_reason.abrt_10addr2_noack() | ||||
|             { | ||||
|                 AbortReason::NoAcknowledge | ||||
|             } else if abort_reason.arb_lost() { | ||||
|                 AbortReason::ArbitrationLoss | ||||
|             } else if abort_reason.abrt_slvflush_txfifo() { | ||||
|                 AbortReason::TxNotEmpty(tx_flush_cnt) | ||||
|             } else { | ||||
|                 AbortReason::Other(abort_reason.0) | ||||
|             }; | ||||
|  | ||||
|             Err(Error::Abort(reason)) | ||||
|         } else { | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -16,6 +16,7 @@ pub mod flash; | ||||
| mod float; | ||||
| pub mod gpio; | ||||
| pub mod i2c; | ||||
| pub mod i2c_slave; | ||||
| pub mod multicore; | ||||
| pub mod pwm; | ||||
| mod reset; | ||||
|   | ||||
| @@ -13,7 +13,7 @@ features = ["stm32wb55rg"] | ||||
| [dependencies] | ||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } | ||||
| embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,13 +8,13 @@ license = "MIT OR Apache-2.0" | ||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" | ||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" | ||||
|  | ||||
| features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"] | ||||
| features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] | ||||
| flavors = [ | ||||
|     { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, | ||||
|     { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, | ||||
|     { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, | ||||
|     { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, | ||||
|     { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, | ||||
|     { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, | ||||
|     { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, | ||||
|     { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, | ||||
|     { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, | ||||
| @@ -32,7 +32,7 @@ flavors = [ | ||||
|  | ||||
| [dependencies] | ||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||
| embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } | ||||
| embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | ||||
| embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||
| @@ -58,7 +58,7 @@ sdio-host = "0.5.0" | ||||
| embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | ||||
| critical-section = "1.1" | ||||
| atomic-polyfill = "1.0.1" | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" } | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e667107cf81934383ec5744f49b2cda0599ec749" } | ||||
| vcell = "0.1.3" | ||||
| bxcan = "0.7.0" | ||||
| nb = "1.0.0" | ||||
| @@ -77,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] } | ||||
| [build-dependencies] | ||||
| proc-macro2 = "1.0.36" | ||||
| quote = "1.0.15" | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]} | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e667107cf81934383ec5744f49b2cda0599ec749", default-features = false, features = ["metadata"]} | ||||
|  | ||||
| [features] | ||||
| default = ["rt"] | ||||
|   | ||||
| @@ -308,13 +308,11 @@ fn main() { | ||||
|     // ======== | ||||
|     // Generate RccPeripheral impls | ||||
|  | ||||
|     let refcounted_peripherals = HashSet::from(["usart", "adc"]); | ||||
|     let mut refcount_statics = HashSet::new(); | ||||
|  | ||||
|     for p in METADATA.peripherals { | ||||
|         // generating RccPeripheral impl for H7 ADC3 would result in bad frequency | ||||
|         if !singletons.contains(&p.name.to_string()) | ||||
|             || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) | ||||
|             || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) | ||||
|             || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) | ||||
|         { | ||||
|         if !singletons.contains(&p.name.to_string()) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
| @@ -344,11 +342,36 @@ fn main() { | ||||
|                 TokenStream::new() | ||||
|             }; | ||||
|  | ||||
|             let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; | ||||
|             let pname = format_ident!("{}", p.name); | ||||
|             let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); | ||||
|             let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); | ||||
|             let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); | ||||
|  | ||||
|             let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) { | ||||
|                 let refcount_static = | ||||
|                     format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase()); | ||||
|  | ||||
|                 refcount_statics.insert(refcount_static.clone()); | ||||
|  | ||||
|                 ( | ||||
|                     quote! { | ||||
|                         unsafe { refcount_statics::#refcount_static += 1 }; | ||||
|                         if unsafe { refcount_statics::#refcount_static } > 1 { | ||||
|                             return; | ||||
|                         } | ||||
|                     }, | ||||
|                     quote! { | ||||
|                         unsafe { refcount_statics::#refcount_static -= 1 }; | ||||
|                         if unsafe { refcount_statics::#refcount_static } > 0  { | ||||
|                             return; | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|             } else { | ||||
|                 (TokenStream::new(), TokenStream::new()) | ||||
|             }; | ||||
|  | ||||
|             g.extend(quote! { | ||||
|                 impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { | ||||
|                     fn frequency() -> crate::time::Hertz { | ||||
| @@ -356,6 +379,7 @@ fn main() { | ||||
|                     } | ||||
|                     fn enable() { | ||||
|                         critical_section::with(|_| { | ||||
|                             #before_enable | ||||
|                             #[cfg(feature = "low-power")] | ||||
|                             crate::rcc::clock_refcount_add(); | ||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); | ||||
| @@ -364,6 +388,7 @@ fn main() { | ||||
|                     } | ||||
|                     fn disable() { | ||||
|                         critical_section::with(|_| { | ||||
|                             #before_disable | ||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); | ||||
|                             #[cfg(feature = "low-power")] | ||||
|                             crate::rcc::clock_refcount_sub(); | ||||
| @@ -379,6 +404,19 @@ fn main() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut refcount_mod = TokenStream::new(); | ||||
|     for refcount_static in refcount_statics { | ||||
|         refcount_mod.extend(quote! { | ||||
|             pub(crate) static mut #refcount_static: u8 = 0; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     g.extend(quote! { | ||||
|         mod refcount_statics { | ||||
|             #refcount_mod | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     // ======== | ||||
|     // Generate fns to enable GPIO, DMA in RCC | ||||
|  | ||||
| @@ -664,6 +702,10 @@ fn main() { | ||||
|  | ||||
|                 // ADC is special | ||||
|                 if regs.kind == "adc" { | ||||
|                     if p.rcc.is_none() { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     let peri = format_ident!("{}", p.name); | ||||
|                     let pin_name = format_ident!("{}", pin.pin); | ||||
|  | ||||
|   | ||||
| @@ -60,7 +60,7 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|     } | ||||
|  | ||||
|     fn freq() -> Hertz { | ||||
|         unsafe { get_freqs() }.adc | ||||
|         unsafe { get_freqs() }.adc.unwrap() | ||||
|     } | ||||
|  | ||||
|     pub fn sample_time_for_us(&self, us: u32) -> SampleTime { | ||||
|   | ||||
							
								
								
									
										123
									
								
								embassy-stm32/src/adc/f3.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								embassy-stm32/src/adc/f3.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| use embassy_hal_internal::into_ref; | ||||
| use embedded_hal_02::blocking::delay::DelayUs; | ||||
|  | ||||
| use crate::adc::{Adc, AdcPin, Instance, SampleTime}; | ||||
| use crate::time::Hertz; | ||||
| use crate::Peripheral; | ||||
|  | ||||
| pub const VDDA_CALIB_MV: u32 = 3300; | ||||
| pub const ADC_MAX: u32 = (1 << 12) - 1; | ||||
| pub const VREF_INT: u32 = 1230; | ||||
|  | ||||
| pub struct Vref; | ||||
| impl<T: Instance> AdcPin<T> for Vref {} | ||||
| impl<T: Instance> super::sealed::AdcPin<T> for Vref { | ||||
|     fn channel(&self) -> u8 { | ||||
|         18 | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Temperature; | ||||
| impl<T: Instance> AdcPin<T> for Temperature {} | ||||
| impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | ||||
|     fn channel(&self) -> u8 { | ||||
|         16 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> Adc<'d, T> { | ||||
|     pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { | ||||
|         use crate::pac::adc::vals; | ||||
|  | ||||
|         into_ref!(adc); | ||||
|  | ||||
|         T::enable(); | ||||
|         T::reset(); | ||||
|  | ||||
|         // Enable the adc regulator | ||||
|         T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE)); | ||||
|         T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED)); | ||||
|  | ||||
|         // Wait for the regulator to stabilize | ||||
|         delay.delay_us(10); | ||||
|  | ||||
|         assert!(!T::regs().cr().read().aden()); | ||||
|  | ||||
|         // Begin calibration | ||||
|         T::regs().cr().modify(|w| w.set_adcaldif(false)); | ||||
|         T::regs().cr().modify(|w| w.set_adcal(true)); | ||||
|  | ||||
|         while T::regs().cr().read().adcal() {} | ||||
|  | ||||
|         // Enable the adc | ||||
|         T::regs().cr().modify(|w| w.set_aden(true)); | ||||
|  | ||||
|         // Wait until the adc is ready | ||||
|         while !T::regs().isr().read().adrdy() {} | ||||
|  | ||||
|         Self { | ||||
|             adc, | ||||
|             sample_time: Default::default(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn freq() -> Hertz { | ||||
|         <T as crate::adc::sealed::Instance>::frequency() | ||||
|     } | ||||
|  | ||||
|     pub fn sample_time_for_us(&self, us: u32) -> SampleTime { | ||||
|         match us * Self::freq().0 / 1_000_000 { | ||||
|             0..=1 => SampleTime::Cycles1_5, | ||||
|             2..=4 => SampleTime::Cycles4_5, | ||||
|             5..=7 => SampleTime::Cycles7_5, | ||||
|             8..=19 => SampleTime::Cycles19_5, | ||||
|             20..=61 => SampleTime::Cycles61_5, | ||||
|             62..=181 => SampleTime::Cycles181_5, | ||||
|             _ => SampleTime::Cycles601_5, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn enable_vref(&self, _delay: &mut impl DelayUs<u32>) -> Vref { | ||||
|         T::common_regs().ccr().modify(|w| w.set_vrefen(true)); | ||||
|  | ||||
|         Vref {} | ||||
|     } | ||||
|  | ||||
|     pub fn enable_temperature(&self) -> Temperature { | ||||
|         T::common_regs().ccr().modify(|w| w.set_tsen(true)); | ||||
|  | ||||
|         Temperature {} | ||||
|     } | ||||
|  | ||||
|     pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||||
|         self.sample_time = sample_time; | ||||
|     } | ||||
|  | ||||
|     /// Perform a single conversion. | ||||
|     fn convert(&mut self) -> u16 { | ||||
|         T::regs().isr().write(|_| {}); | ||||
|         T::regs().cr().modify(|w| w.set_adstart(true)); | ||||
|  | ||||
|         while !T::regs().isr().read().eoc() && !T::regs().isr().read().eos() {} | ||||
|         T::regs().isr().write(|_| {}); | ||||
|  | ||||
|         T::regs().dr().read().rdata() | ||||
|     } | ||||
|  | ||||
|     pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 { | ||||
|         Self::set_channel_sample_time(pin.channel(), self.sample_time); | ||||
|  | ||||
|         // Configure the channel to sample | ||||
|         T::regs().sqr1().write(|w| w.set_sq(0, pin.channel())); | ||||
|         self.convert() | ||||
|     } | ||||
|  | ||||
|     fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||||
|         let sample_time = sample_time.into(); | ||||
|         if ch <= 9 { | ||||
|             T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||||
|         } else { | ||||
|             T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,23 +1,24 @@ | ||||
| #![macro_use] | ||||
|  | ||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] | ||||
| #[cfg(not(adc_f3_v2))] | ||||
| #[cfg_attr(adc_f1, path = "f1.rs")] | ||||
| #[cfg_attr(adc_f3, path = "f3.rs")] | ||||
| #[cfg_attr(adc_v1, path = "v1.rs")] | ||||
| #[cfg_attr(adc_v2, path = "v2.rs")] | ||||
| #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] | ||||
| #[cfg_attr(adc_v4, path = "v4.rs")] | ||||
| mod _version; | ||||
|  | ||||
| #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] | ||||
| #[cfg(not(any(adc_f1, adc_f3_v2)))] | ||||
| mod resolution; | ||||
| mod sample_time; | ||||
|  | ||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] | ||||
| #[allow(unused)] | ||||
| #[cfg(not(adc_f3_v2))] | ||||
| pub use _version::*; | ||||
| #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] | ||||
| pub use resolution::Resolution; | ||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] | ||||
| #[cfg(not(adc_f3_v2))] | ||||
| pub use sample_time::SampleTime; | ||||
|  | ||||
| use crate::peripherals; | ||||
| @@ -25,15 +26,17 @@ use crate::peripherals; | ||||
| pub struct Adc<'d, T: Instance> { | ||||
|     #[allow(unused)] | ||||
|     adc: crate::PeripheralRef<'d, T>, | ||||
|     #[cfg(not(any(adc_f3, adc_f3_v2)))] | ||||
|     #[cfg(not(adc_f3_v2))] | ||||
|     sample_time: SampleTime, | ||||
| } | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|     pub trait Instance { | ||||
|         fn regs() -> crate::pac::adc::Adc; | ||||
|         #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] | ||||
|         #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] | ||||
|         fn common_regs() -> crate::pac::adccommon::AdcCommon; | ||||
|         #[cfg(adc_f3)] | ||||
|         fn frequency() -> crate::time::Hertz; | ||||
|     } | ||||
|  | ||||
|     pub trait AdcPin<T: Instance> { | ||||
| @@ -45,22 +48,22 @@ pub(crate) mod sealed { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] | ||||
| #[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3)))] | ||||
| pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} | ||||
| #[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] | ||||
| #[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3))] | ||||
| pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} | ||||
|  | ||||
| pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | ||||
| pub trait InternalChannel<T>: sealed::InternalChannel<T> {} | ||||
|  | ||||
| #[cfg(not(stm32h7))] | ||||
| #[cfg(not(any(stm32h7, adc_f3, adc_v4)))] | ||||
| foreach_peripheral!( | ||||
|     (adc, $inst:ident) => { | ||||
|         impl crate::adc::sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::adc::Adc { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|             #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] | ||||
|             #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] | ||||
|             fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||||
|                 foreach_peripheral!{ | ||||
|                     (adccommon, $common_inst:ident) => { | ||||
| @@ -74,9 +77,10 @@ foreach_peripheral!( | ||||
|     }; | ||||
| ); | ||||
|  | ||||
| #[cfg(stm32h7)] | ||||
| #[cfg(any(stm32h7, adc_f3, adc_v4))] | ||||
| foreach_peripheral!( | ||||
|     (adc, ADC3) => { | ||||
|         #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] | ||||
|         impl crate::adc::sealed::Instance for peripherals::ADC3 { | ||||
|             fn regs() -> crate::pac::adc::Adc { | ||||
|                 crate::pac::ADC3 | ||||
| @@ -89,16 +93,49 @@ foreach_peripheral!( | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             #[cfg(adc_f3)] | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 unsafe { crate::rcc::get_freqs() }.adc34.unwrap() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] | ||||
|         impl crate::adc::Instance for peripherals::ADC3 {} | ||||
|     }; | ||||
|     (adc, ADC4) => { | ||||
|         #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] | ||||
|         impl crate::adc::sealed::Instance for peripherals::ADC4 { | ||||
|             fn regs() -> crate::pac::adc::Adc { | ||||
|                 crate::pac::ADC4 | ||||
|             } | ||||
|             #[cfg(not(any(adc_f1, adc_v1)))] | ||||
|             fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||||
|                 foreach_peripheral!{ | ||||
|                     (adccommon, ADC3_COMMON) => { | ||||
|                         return crate::pac::ADC3_COMMON | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             #[cfg(adc_f3)] | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 unsafe { crate::rcc::get_freqs() }.adc34.unwrap() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] | ||||
|         impl crate::adc::Instance for peripherals::ADC4 {} | ||||
|     }; | ||||
|     (adc, ADC5) => { | ||||
|  | ||||
|     }; | ||||
|     (adc, $inst:ident) => { | ||||
|         impl crate::adc::sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::adc::Adc { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|             #[cfg(all(not(adc_f1), not(adc_v1)))] | ||||
|             #[cfg(not(any(adc_f1, adc_v1)))] | ||||
|             fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||||
|                 foreach_peripheral!{ | ||||
|                     (adccommon, ADC_COMMON) => { | ||||
| @@ -106,6 +143,11 @@ foreach_peripheral!( | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             #[cfg(adc_f3)] | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 unsafe { crate::rcc::get_freqs() }.adc.unwrap() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl crate::adc::Instance for peripherals::$inst {} | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] | ||||
| #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] | ||||
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||||
| pub enum Resolution { | ||||
|     TwelveBit, | ||||
| @@ -19,7 +19,7 @@ pub enum Resolution { | ||||
|  | ||||
| impl Default for Resolution { | ||||
|     fn default() -> Self { | ||||
|         #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] | ||||
|         #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] | ||||
|         { | ||||
|             Self::TwelveBit | ||||
|         } | ||||
| @@ -40,7 +40,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res { | ||||
|             Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, | ||||
|             Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, | ||||
|             Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, | ||||
|             #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] | ||||
|             #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] | ||||
|             Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, | ||||
|         } | ||||
|     } | ||||
| @@ -56,7 +56,7 @@ impl Resolution { | ||||
|             Resolution::TwelveBit => (1 << 12) - 1, | ||||
|             Resolution::TenBit => (1 << 10) - 1, | ||||
|             Resolution::EightBit => (1 << 8) - 1, | ||||
|             #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] | ||||
|             #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] | ||||
|             Resolution::SixBit => (1 << 6) - 1, | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] | ||||
| #[cfg(not(adc_f3_v2))] | ||||
| macro_rules! impl_sample_time { | ||||
|     ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { | ||||
|         #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] | ||||
| @@ -105,3 +105,19 @@ impl_sample_time!( | ||||
|         ("810.5", Cycles810_5, CYCLES810_5) | ||||
|     ) | ||||
| ); | ||||
|  | ||||
| #[cfg(adc_f3)] | ||||
| impl_sample_time!( | ||||
|     "1.5", | ||||
|     Cycles1_5, | ||||
|     ( | ||||
|         ("1.5", Cycles1_5, CYCLES1_5), | ||||
|         ("2.5", Cycles2_5, CYCLES2_5), | ||||
|         ("4.5", Cycles4_5, CYCLES4_5), | ||||
|         ("7.5", Cycles7_5, CYCLES7_5), | ||||
|         ("19.5", Cycles19_5, CYCLES19_5), | ||||
|         ("61.5", Cycles61_5, CYCLES61_5), | ||||
|         ("181.5", Cycles181_5, CYCLES181_5), | ||||
|         ("601.5", Cycles601_5, CYCLES601_5) | ||||
|     ) | ||||
| ); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ pub const VREF_CALIB_MV: u32 = 3000; | ||||
| /// configuration. | ||||
| fn enable() { | ||||
|     critical_section::with(|_| { | ||||
|         #[cfg(stm32h7)] | ||||
|         #[cfg(any(stm32h7, stm32wl))] | ||||
|         crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); | ||||
|         #[cfg(stm32g0)] | ||||
|         crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true)); | ||||
| @@ -26,9 +26,9 @@ pub struct VrefInt; | ||||
| impl<T: Instance> AdcPin<T> for VrefInt {} | ||||
| impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { | ||||
|     fn channel(&self) -> u8 { | ||||
|         #[cfg(not(stm32g0))] | ||||
|         #[cfg(not(adc_g0))] | ||||
|         let val = 0; | ||||
|         #[cfg(stm32g0)] | ||||
|         #[cfg(adc_g0)] | ||||
|         let val = 13; | ||||
|         val | ||||
|     } | ||||
| @@ -38,9 +38,9 @@ pub struct Temperature; | ||||
| impl<T: Instance> AdcPin<T> for Temperature {} | ||||
| impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | ||||
|     fn channel(&self) -> u8 { | ||||
|         #[cfg(not(stm32g0))] | ||||
|         #[cfg(not(adc_g0))] | ||||
|         let val = 17; | ||||
|         #[cfg(stm32g0)] | ||||
|         #[cfg(adc_g0)] | ||||
|         let val = 12; | ||||
|         val | ||||
|     } | ||||
| @@ -50,9 +50,9 @@ pub struct Vbat; | ||||
| impl<T: Instance> AdcPin<T> for Vbat {} | ||||
| impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | ||||
|     fn channel(&self) -> u8 { | ||||
|         #[cfg(not(stm32g0))] | ||||
|         #[cfg(not(adc_g0))] | ||||
|         let val = 18; | ||||
|         #[cfg(stm32g0)] | ||||
|         #[cfg(adc_g0)] | ||||
|         let val = 14; | ||||
|         val | ||||
|     } | ||||
| @@ -92,9 +92,14 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|     } | ||||
|  | ||||
|     pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt { | ||||
|         #[cfg(not(adc_g0))] | ||||
|         T::common_regs().ccr().modify(|reg| { | ||||
|             reg.set_vrefen(true); | ||||
|         }); | ||||
|         #[cfg(adc_g0)] | ||||
|         T::regs().ccr().modify(|reg| { | ||||
|             reg.set_vrefen(true); | ||||
|         }); | ||||
|  | ||||
|         // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||||
|         // to stabilize the internal voltage reference, we wait a little more. | ||||
| @@ -106,17 +111,27 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|     } | ||||
|  | ||||
|     pub fn enable_temperature(&self) -> Temperature { | ||||
|         #[cfg(not(adc_g0))] | ||||
|         T::common_regs().ccr().modify(|reg| { | ||||
|             reg.set_ch17sel(true); | ||||
|         }); | ||||
|         #[cfg(adc_g0)] | ||||
|         T::regs().ccr().modify(|reg| { | ||||
|             reg.set_tsen(true); | ||||
|         }); | ||||
|  | ||||
|         Temperature {} | ||||
|     } | ||||
|  | ||||
|     pub fn enable_vbat(&self) -> Vbat { | ||||
|         #[cfg(not(adc_g0))] | ||||
|         T::common_regs().ccr().modify(|reg| { | ||||
|             reg.set_ch18sel(true); | ||||
|         }); | ||||
|         #[cfg(adc_g0)] | ||||
|         T::regs().ccr().modify(|reg| { | ||||
|             reg.set_vbaten(true); | ||||
|         }); | ||||
|  | ||||
|         Vbat {} | ||||
|     } | ||||
| @@ -126,9 +141,9 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|     } | ||||
|  | ||||
|     pub fn set_resolution(&mut self, resolution: Resolution) { | ||||
|         #[cfg(not(stm32g0))] | ||||
|         #[cfg(not(adc_g0))] | ||||
|         T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||||
|         #[cfg(stm32g0)] | ||||
|         #[cfg(adc_g0)] | ||||
|         T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||||
|     } | ||||
|  | ||||
| @@ -182,9 +197,9 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|         Self::set_channel_sample_time(pin.channel(), self.sample_time); | ||||
|  | ||||
|         // Select channel | ||||
|         #[cfg(not(stm32g0))] | ||||
|         #[cfg(not(adc_g0))] | ||||
|         T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); | ||||
|         #[cfg(stm32g0)] | ||||
|         #[cfg(adc_g0)] | ||||
|         T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); | ||||
|  | ||||
|         // Some models are affected by an erratum: | ||||
| @@ -203,12 +218,12 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|         val | ||||
|     } | ||||
|  | ||||
|     #[cfg(stm32g0)] | ||||
|     #[cfg(adc_g0)] | ||||
|     fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||||
|         T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(stm32g0))] | ||||
|     #[cfg(not(adc_g0))] | ||||
|     fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||||
|         let sample_time = sample_time.into(); | ||||
|         T::regs() | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| use core::sync::atomic::{AtomicU8, Ordering}; | ||||
|  | ||||
| use embedded_hal_02::blocking::delay::DelayUs; | ||||
| #[allow(unused)] | ||||
| use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | ||||
| use pac::adccommon::vals::Presc; | ||||
|  | ||||
| @@ -13,12 +12,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | ||||
| /// VREF voltage used for factory calibration of VREFINTCAL register. | ||||
| pub const VREF_CALIB_MV: u32 = 3300; | ||||
|  | ||||
| // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||||
| /// Max single ADC operation clock frequency | ||||
| #[cfg(stm32g4)] | ||||
| const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | ||||
| #[cfg(stm32h7)] | ||||
| const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | ||||
|  | ||||
| #[cfg(stm32g4)] | ||||
| const VREF_CHANNEL: u8 = 18; | ||||
| #[cfg(stm32g4)] | ||||
| const TEMP_CHANNEL: u8 = 16; | ||||
|  | ||||
| #[cfg(stm32h7)] | ||||
| const VREF_CHANNEL: u8 = 19; | ||||
| #[cfg(stm32h7)] | ||||
| const TEMP_CHANNEL: u8 = 18; | ||||
|  | ||||
| // TODO this should be 14 for H7a/b/35 | ||||
| const VBAT_CHANNEL: u8 = 17; | ||||
|  | ||||
| // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||||
| pub struct VrefInt; | ||||
| impl<T: Instance> InternalChannel<T> for VrefInt {} | ||||
| impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { | ||||
|     fn channel(&self) -> u8 { | ||||
|         19 | ||||
|         VREF_CHANNEL | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -26,7 +44,7 @@ pub struct Temperature; | ||||
| impl<T: Instance> InternalChannel<T> for Temperature {} | ||||
| impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { | ||||
|     fn channel(&self) -> u8 { | ||||
|         18 | ||||
|         TEMP_CHANNEL | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -34,128 +52,10 @@ pub struct Vbat; | ||||
| impl<T: Instance> InternalChannel<T> for Vbat {} | ||||
| impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { | ||||
|     fn channel(&self) -> u8 { | ||||
|         // TODO this should be 14 for H7a/b/35 | ||||
|         17 | ||||
|         VBAT_CHANNEL | ||||
|     } | ||||
| } | ||||
|  | ||||
| static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); | ||||
|  | ||||
| #[cfg(stm32h7)] | ||||
| foreach_peripheral!( | ||||
|     (adc, ADC1) => { | ||||
|         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 { | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 critical_section::with(|_| { | ||||
|                     match unsafe { crate::rcc::get_freqs() }.adc { | ||||
|                         Some(ck) => ck, | ||||
|                         None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             fn enable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) | ||||
|                 }); | ||||
|                 ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); | ||||
|             } | ||||
|  | ||||
|             fn disable() { | ||||
|                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||||
|                     critical_section::with(|_| { | ||||
|                         crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); | ||||
|                     }) | ||||
|                 } | ||||
|                 ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); | ||||
|             } | ||||
|  | ||||
|             fn reset() { | ||||
|                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||||
|                     critical_section::with(|_| { | ||||
|                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); | ||||
|                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {} | ||||
|     }; | ||||
|     (adc, ADC2) => { | ||||
|         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 { | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 critical_section::with(|_| { | ||||
|                     match unsafe { crate::rcc::get_freqs() }.adc { | ||||
|                         Some(ck) => ck, | ||||
|                         None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             fn enable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) | ||||
|                 }); | ||||
|                 ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); | ||||
|             } | ||||
|  | ||||
|             fn disable() { | ||||
|                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||||
|                     critical_section::with(|_| { | ||||
|                         crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); | ||||
|                     }) | ||||
|                 } | ||||
|                 ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); | ||||
|             } | ||||
|  | ||||
|             fn reset() { | ||||
|                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { | ||||
|                     critical_section::with(|_| { | ||||
|                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); | ||||
|                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {} | ||||
|     }; | ||||
|     (adc, ADC3) => { | ||||
|         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 { | ||||
|             fn frequency() -> crate::time::Hertz { | ||||
|                 critical_section::with(|_| { | ||||
|                     match unsafe { crate::rcc::get_freqs() }.adc { | ||||
|                         Some(ck) => ck, | ||||
|                         None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             fn enable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             fn disable() { | ||||
|                 critical_section::with(|_| { | ||||
|                     crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             fn reset() { | ||||
|                 critical_section::with(|_| { | ||||
|                     crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); | ||||
|                     crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {} | ||||
|     }; | ||||
| ); | ||||
|  | ||||
| // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | ||||
| // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | ||||
| #[allow(unused)] | ||||
| @@ -176,7 +76,7 @@ enum Prescaler { | ||||
|  | ||||
| impl Prescaler { | ||||
|     fn from_ker_ck(frequency: Hertz) -> Self { | ||||
|         let raw_prescaler = frequency.0 / 50_000_000; | ||||
|         let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||||
|         match raw_prescaler { | ||||
|             0 => Self::NotDivided, | ||||
|             1 => Self::DividedBy2, | ||||
| @@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|         let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||||
|         info!("ADC frequency set to {} Hz", frequency.0); | ||||
|  | ||||
|         if frequency > Hertz::mhz(50) { | ||||
|             panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); | ||||
|         if frequency > MAX_ADC_CLK_FREQ { | ||||
|             panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 /  1_000_000 ); | ||||
|         } | ||||
|         let boost = if frequency < Hertz::khz(6_250) { | ||||
|             Boost::LT6_25 | ||||
|         } else if frequency < Hertz::khz(12_500) { | ||||
|             Boost::LT12_5 | ||||
|         } else if frequency < Hertz::mhz(25) { | ||||
|             Boost::LT25 | ||||
|         } else { | ||||
|             Boost::LT50 | ||||
|         }; | ||||
|         T::regs().cr().modify(|w| w.set_boost(boost)); | ||||
|  | ||||
|         #[cfg(stm32h7)] | ||||
|         { | ||||
|             let boost = if frequency < Hertz::khz(6_250) { | ||||
|                 Boost::LT6_25 | ||||
|             } else if frequency < Hertz::khz(12_500) { | ||||
|                 Boost::LT12_5 | ||||
|             } else if frequency < Hertz::mhz(25) { | ||||
|                 Boost::LT25 | ||||
|             } else { | ||||
|                 Boost::LT50 | ||||
|             }; | ||||
|             T::regs().cr().modify(|w| w.set_boost(boost)); | ||||
|         } | ||||
|         let mut s = Self { | ||||
|             adc, | ||||
|             sample_time: Default::default(), | ||||
| @@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> { | ||||
|         // Configure channel | ||||
|         Self::set_channel_sample_time(channel, self.sample_time); | ||||
|  | ||||
|         T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||||
|         T::regs() | ||||
|             .pcsel() | ||||
|             .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||||
|         #[cfg(stm32h7)] | ||||
|         { | ||||
|             T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||||
|             T::regs() | ||||
|                 .pcsel() | ||||
|                 .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||||
|         } | ||||
|  | ||||
|         T::regs().sqr1().write(|reg| { | ||||
|             reg.set_sq(0, channel); | ||||
|             reg.set_l(0); | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| @@ -81,14 +83,17 @@ macro_rules! todo { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::unreachable!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::unreachable!($($x)*); | ||||
|         } | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,8 @@ pub use traits::Instance; | ||||
| #[allow(unused_imports)] | ||||
| use crate::gpio::sealed::{AFType, Pin}; | ||||
| use crate::gpio::AnyPin; | ||||
| #[cfg(stm32f334)] | ||||
| use crate::rcc::get_freqs; | ||||
| use crate::time::Hertz; | ||||
| use crate::Peripheral; | ||||
|  | ||||
| @@ -158,17 +160,29 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { | ||||
|         T::enable(); | ||||
|         <T as crate::rcc::sealed::RccPeripheral>::reset(); | ||||
|  | ||||
|         //        // Enable and and stabilize the DLL | ||||
|         //        T::regs().dllcr().modify(|w| { | ||||
|         //            // w.set_calen(true); | ||||
|         //            // w.set_calrte(11); | ||||
|         //            w.set_cal(true); | ||||
|         //        }); | ||||
|         // | ||||
|         //        debug!("wait for dll calibration"); | ||||
|         //        while !T::regs().isr().read().dllrdy() {} | ||||
|         // | ||||
|         //        debug!("dll calibration complete"); | ||||
|         #[cfg(stm32f334)] | ||||
|         if unsafe { get_freqs() }.hrtim.is_some() { | ||||
|             // Enable and and stabilize the DLL | ||||
|             T::regs().dllcr().modify(|w| { | ||||
|                 w.set_cal(true); | ||||
|             }); | ||||
|  | ||||
|             trace!("hrtim: wait for dll calibration"); | ||||
|             while !T::regs().isr().read().dllrdy() {} | ||||
|  | ||||
|             trace!("hrtim: dll calibration complete"); | ||||
|  | ||||
|             // Enable periodic calibration | ||||
|             // Cal must be disabled before we can enable it | ||||
|             T::regs().dllcr().modify(|w| { | ||||
|                 w.set_cal(false); | ||||
|             }); | ||||
|  | ||||
|             T::regs().dllcr().modify(|w| { | ||||
|                 w.set_calen(true); | ||||
|                 w.set_calrte(11); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         Self { | ||||
|             _inner: tim, | ||||
|   | ||||
| @@ -1,31 +1,17 @@ | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
| use crate::time::Hertz; | ||||
|  | ||||
| #[repr(u8)] | ||||
| #[derive(Clone, Copy)] | ||||
| pub(crate) enum Prescaler { | ||||
|     Div1, | ||||
|     Div2, | ||||
|     Div4, | ||||
|     Div8, | ||||
|     Div16, | ||||
|     Div32, | ||||
|     Div64, | ||||
|     Div128, | ||||
| } | ||||
|  | ||||
| impl From<Prescaler> for u32 { | ||||
|     fn from(val: Prescaler) -> Self { | ||||
|         match val { | ||||
|             Prescaler::Div1 => 1, | ||||
|             Prescaler::Div2 => 2, | ||||
|             Prescaler::Div4 => 4, | ||||
|             Prescaler::Div8 => 8, | ||||
|             Prescaler::Div16 => 16, | ||||
|             Prescaler::Div32 => 32, | ||||
|             Prescaler::Div64 => 64, | ||||
|             Prescaler::Div128 => 128, | ||||
|         } | ||||
|     } | ||||
|     Div1 = 1, | ||||
|     Div2 = 2, | ||||
|     Div4 = 4, | ||||
|     Div8 = 8, | ||||
|     Div16 = 16, | ||||
|     Div32 = 32, | ||||
|     Div64 = 64, | ||||
|     Div128 = 128, | ||||
| } | ||||
|  | ||||
| impl From<Prescaler> for u8 { | ||||
| @@ -72,7 +58,7 @@ impl Prescaler { | ||||
|             Prescaler::Div128, | ||||
|         ] | ||||
|         .iter() | ||||
|         .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val) | ||||
|         .skip_while(|psc| **psc as u32 <= val) | ||||
|         .next() | ||||
|         .unwrap() | ||||
|     } | ||||
| @@ -80,7 +66,7 @@ impl Prescaler { | ||||
|     pub fn compute_min_low_res(val: u32) -> Self { | ||||
|         *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] | ||||
|             .iter() | ||||
|             .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val) | ||||
|             .skip_while(|psc| **psc as u32 <= val) | ||||
|             .next() | ||||
|             .unwrap() | ||||
|     } | ||||
| @@ -118,7 +104,13 @@ foreach_interrupt! { | ||||
|                 use crate::rcc::sealed::RccPeripheral; | ||||
|  | ||||
|                 let f = frequency.0; | ||||
|                 #[cfg(not(stm32f334))] | ||||
|                 let timer_f = Self::frequency().0; | ||||
|                 #[cfg(stm32f334)] | ||||
|                 let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or( | ||||
|                     Self::frequency() | ||||
|                 ).0; | ||||
|  | ||||
|                 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); | ||||
|                 let psc = if Self::regs().isr().read().dllrdy() { | ||||
|                     Prescaler::compute_min_high_res(psc_min) | ||||
| @@ -126,8 +118,7 @@ foreach_interrupt! { | ||||
|                     Prescaler::compute_min_low_res(psc_min) | ||||
|                 }; | ||||
|  | ||||
|                 let psc_val: u32 = psc.into(); | ||||
|                 let timer_f = 32 * (timer_f / psc_val); | ||||
|                 let timer_f = 32 * (timer_f / psc as u32); | ||||
|                 let per: u16 = (timer_f / f) as u16; | ||||
|  | ||||
|                 let regs = Self::regs(); | ||||
| @@ -140,7 +131,13 @@ foreach_interrupt! { | ||||
|                 use crate::rcc::sealed::RccPeripheral; | ||||
|  | ||||
|                 let f = frequency.0; | ||||
|                 #[cfg(not(stm32f334))] | ||||
|                 let timer_f = Self::frequency().0; | ||||
|                 #[cfg(stm32f334)] | ||||
|                 let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or( | ||||
|                     Self::frequency() | ||||
|                 ).0; | ||||
|  | ||||
|                 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); | ||||
|                 let psc = if Self::regs().isr().read().dllrdy() { | ||||
|                     Prescaler::compute_min_high_res(psc_min) | ||||
| @@ -148,8 +145,7 @@ foreach_interrupt! { | ||||
|                     Prescaler::compute_min_low_res(psc_min) | ||||
|                 }; | ||||
|  | ||||
|                 let psc_val: u32 = psc.into(); | ||||
|                 let timer_f = 32 * (timer_f / psc_val); | ||||
|                 let timer_f = 32 * (timer_f / psc as u32); | ||||
|                 let per: u16 = (timer_f / f) as u16; | ||||
|  | ||||
|                 let regs = Self::regs(); | ||||
| @@ -163,20 +159,17 @@ foreach_interrupt! { | ||||
|                 let regs = Self::regs(); | ||||
|  | ||||
|                 let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); | ||||
|                 let psc_val: u32 = channel_psc.into(); | ||||
|  | ||||
|  | ||||
|                 // The dead-time base clock runs 4 times slower than the hrtim base clock | ||||
|                 // u9::MAX = 511 | ||||
|                 let psc_min = (psc_val * dead_time as u32) / (4 * 511); | ||||
|                 let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511); | ||||
|                 let psc = if Self::regs().isr().read().dllrdy() { | ||||
|                     Prescaler::compute_min_high_res(psc_min) | ||||
|                 } else { | ||||
|                     Prescaler::compute_min_low_res(psc_min) | ||||
|                 }; | ||||
|  | ||||
|                 let dt_psc_val: u32 = psc.into(); | ||||
|                 let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); | ||||
|                 let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32); | ||||
|  | ||||
|                 regs.tim(channel).dt().modify(|w| { | ||||
|                     w.set_dtprsc(psc.into()); | ||||
|   | ||||
| @@ -339,6 +339,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { | ||||
|     fn drop(&mut self) { | ||||
|         T::disable(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | ||||
|     type Error = Error; | ||||
|  | ||||
|   | ||||
| @@ -838,6 +838,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { | ||||
|     fn drop(&mut self) { | ||||
|         T::disable(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod eh02 { | ||||
|     use super::*; | ||||
|  | ||||
|   | ||||
| @@ -197,6 +197,11 @@ pub fn init(config: Config) -> Peripherals { | ||||
|         // must be after rcc init | ||||
|         #[cfg(feature = "_time-driver")] | ||||
|         time_driver::init(); | ||||
|  | ||||
|         #[cfg(feature = "low-power")] | ||||
|         while !crate::rcc::low_power_ready() { | ||||
|             crate::rcc::clock_refcount_sub(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     p | ||||
|   | ||||
| @@ -3,46 +3,51 @@ use core::marker::PhantomData; | ||||
|  | ||||
| use cortex_m::peripheral::SCB; | ||||
| use embassy_executor::*; | ||||
| use embassy_time::Duration; | ||||
|  | ||||
| use crate::interrupt; | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::pac::EXTI; | ||||
| use crate::rcc::low_power_ready; | ||||
| use crate::time_driver::{get_driver, RtcDriver}; | ||||
|  | ||||
| const THREAD_PENDER: usize = usize::MAX; | ||||
| const THRESHOLD: Duration = Duration::from_millis(500); | ||||
|  | ||||
| use crate::rtc::{Rtc, RtcInstant}; | ||||
| use crate::rtc::Rtc; | ||||
|  | ||||
| static mut RTC: Option<&'static Rtc> = None; | ||||
| static mut EXECUTOR: Option<Executor> = None; | ||||
|  | ||||
| foreach_interrupt! { | ||||
|     (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | ||||
|         #[interrupt] | ||||
|         unsafe fn $irq() { | ||||
|             Executor::on_wakeup_irq(); | ||||
|             unsafe { EXECUTOR.as_mut().unwrap() }.on_wakeup_irq(); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| // pub fn timer_driver_pause_time() { | ||||
| //     pause_time(); | ||||
| // } | ||||
|  | ||||
| pub fn stop_with_rtc(rtc: &'static Rtc) { | ||||
|     crate::interrupt::typelevel::RTC_WKUP::unpend(); | ||||
|     unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; | ||||
|  | ||||
|     EXTI.rtsr(0).modify(|w| w.set_line(22, true)); | ||||
|     EXTI.imr(0).modify(|w| w.set_line(22, true)); | ||||
|  | ||||
|     unsafe { RTC = Some(rtc) }; | ||||
|     unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) | ||||
| } | ||||
|  | ||||
| pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { | ||||
|     unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) | ||||
| } | ||||
|  | ||||
| pub fn stop_wakeup_alarm() -> RtcInstant { | ||||
|     unsafe { RTC }.unwrap().stop_wakeup_alarm() | ||||
| } | ||||
| // pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) { | ||||
| //     let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() } | ||||
| //         .rtc | ||||
| //         .unwrap() | ||||
| //         .start_wakeup_alarm(requested_duration); | ||||
| // | ||||
| //     unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant); | ||||
| // } | ||||
| // | ||||
| // pub fn set_sleepdeep() { | ||||
| //     unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep(); | ||||
| // } | ||||
| // | ||||
| // pub fn stop_wakeup_alarm() -> RtcInstant { | ||||
| //     unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm() | ||||
| // } | ||||
|  | ||||
| /// Thread mode executor, using WFE/SEV. | ||||
| /// | ||||
| @@ -57,54 +62,61 @@ pub fn stop_wakeup_alarm() -> RtcInstant { | ||||
| pub struct Executor { | ||||
|     inner: raw::Executor, | ||||
|     not_send: PhantomData<*mut ()>, | ||||
|     scb: SCB, | ||||
|     time_driver: &'static RtcDriver, | ||||
| } | ||||
|  | ||||
| impl Executor { | ||||
|     /// Create a new Executor. | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: raw::Executor::new(THREAD_PENDER as *mut ()), | ||||
|             not_send: PhantomData, | ||||
|     pub fn take() -> &'static mut Self { | ||||
|         unsafe { | ||||
|             assert!(EXECUTOR.is_none()); | ||||
|  | ||||
|             EXECUTOR = Some(Self { | ||||
|                 inner: raw::Executor::new(THREAD_PENDER as *mut ()), | ||||
|                 not_send: PhantomData, | ||||
|                 scb: cortex_m::Peripherals::steal().SCB, | ||||
|                 time_driver: get_driver(), | ||||
|             }); | ||||
|  | ||||
|             EXECUTOR.as_mut().unwrap() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     unsafe fn on_wakeup_irq() { | ||||
|         info!("on wakeup irq"); | ||||
|     unsafe fn on_wakeup_irq(&mut self) { | ||||
|         trace!("low power: on wakeup irq"); | ||||
|  | ||||
|         cortex_m::asm::bkpt(); | ||||
|         self.time_driver.resume_time(); | ||||
|         trace!("low power: resume time"); | ||||
|     } | ||||
|  | ||||
|     fn time_until_next_alarm(&self) -> Duration { | ||||
|         Duration::from_secs(3) | ||||
|     pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | ||||
|         trace!("low power: stop with rtc configured"); | ||||
|  | ||||
|         self.time_driver.set_rtc(rtc); | ||||
|  | ||||
|         crate::interrupt::typelevel::RTC_WKUP::unpend(); | ||||
|         unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; | ||||
|  | ||||
|         rtc.enable_wakeup_line(); | ||||
|     } | ||||
|  | ||||
|     fn get_scb() -> SCB { | ||||
|         unsafe { cortex_m::Peripherals::steal() }.SCB | ||||
|     } | ||||
|  | ||||
|     fn configure_pwr(&self) { | ||||
|         trace!("configure_pwr"); | ||||
|     fn configure_pwr(&mut self) { | ||||
|         trace!("low power: configure_pwr"); | ||||
|  | ||||
|         self.scb.clear_sleepdeep(); | ||||
|         if !low_power_ready() { | ||||
|             trace!("low power: configure_pwr: low power not ready"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let time_until_next_alarm = self.time_until_next_alarm(); | ||||
|         if time_until_next_alarm < THRESHOLD { | ||||
|         if self.time_driver.pause_time().is_err() { | ||||
|             trace!("low power: configure_pwr: time driver failed to pause"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         trace!("low power stop required"); | ||||
|  | ||||
|         critical_section::with(|_| { | ||||
|             trace!("executor: set wakeup alarm..."); | ||||
|  | ||||
|             start_wakeup_alarm(time_until_next_alarm); | ||||
|  | ||||
|             trace!("low power wait for rtc ready..."); | ||||
|  | ||||
|             Self::get_scb().set_sleepdeep(); | ||||
|         }); | ||||
|         trace!("low power: enter stop..."); | ||||
|         self.scb.set_sleepdeep(); | ||||
|     } | ||||
|  | ||||
|     /// Run the executor. | ||||
| @@ -126,11 +138,11 @@ impl Executor { | ||||
|     /// | ||||
|     /// This function never returns. | ||||
|     pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||||
|         init(self.inner.spawner()); | ||||
|         init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); | ||||
|  | ||||
|         loop { | ||||
|             unsafe { | ||||
|                 self.inner.poll(); | ||||
|                 EXECUTOR.as_mut().unwrap().inner.poll(); | ||||
|                 self.configure_pwr(); | ||||
|                 asm!("wfe"); | ||||
|             }; | ||||
|   | ||||
							
								
								
									
										199
									
								
								embassy-stm32/src/rcc/bd.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								embassy-stm32/src/rcc/bd.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| #[allow(dead_code)] | ||||
| #[derive(Default)] | ||||
| pub enum LseDrive { | ||||
|     #[cfg(any(rtc_v2f7, rtc_v2l4))] | ||||
|     Low = 0, | ||||
|     MediumLow = 0x01, | ||||
|     #[default] | ||||
|     MediumHigh = 0x02, | ||||
|     #[cfg(any(rtc_v2f7, rtc_v2l4))] | ||||
|     High = 0x03, | ||||
| } | ||||
|  | ||||
| #[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))] | ||||
| impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | ||||
|     fn from(value: LseDrive) -> Self { | ||||
|         use crate::pac::rcc::vals::Lsedrv; | ||||
|  | ||||
|         match value { | ||||
|             #[cfg(any(rtc_v2f7, rtc_v2l4))] | ||||
|             LseDrive::Low => Lsedrv::LOW, | ||||
|             LseDrive::MediumLow => Lsedrv::MEDIUMLOW, | ||||
|             LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH, | ||||
|             #[cfg(any(rtc_v2f7, rtc_v2l4))] | ||||
|             LseDrive::High => Lsedrv::HIGH, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Copy, Clone, Debug, PartialEq)] | ||||
| #[repr(u8)] | ||||
| pub enum RtcClockSource { | ||||
|     /// 00: No clock | ||||
|     NoClock = 0b00, | ||||
|     /// 01: LSE oscillator clock used as RTC clock | ||||
|     LSE = 0b01, | ||||
|     /// 10: LSI oscillator clock used as RTC clock | ||||
|     LSI = 0b10, | ||||
|     /// 11: HSE oscillator clock divided by 32 used as RTC clock | ||||
|     HSE = 0b11, | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] | ||||
| #[allow(dead_code)] | ||||
| type Bdcr = crate::pac::rcc::regs::Bdcr; | ||||
|  | ||||
| #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
| #[allow(dead_code)] | ||||
| type Bdcr = crate::pac::rcc::regs::Csr; | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| pub struct BackupDomain {} | ||||
|  | ||||
| impl BackupDomain { | ||||
|     #[cfg(any( | ||||
|         rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3, | ||||
|         rtc_v3u5 | ||||
|     ))] | ||||
|     #[allow(dead_code, unused_variables)] | ||||
|     fn modify<R>(f: impl FnOnce(&mut Bdcr) -> R) -> R { | ||||
|         #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] | ||||
|         let cr = crate::pac::PWR.cr(); | ||||
|         #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | ||||
|         let cr = crate::pac::PWR.cr1(); | ||||
|  | ||||
|         // TODO: Missing from PAC for l0 and f0? | ||||
|         #[cfg(not(any(rtc_v2f0, rtc_v2l0, rtc_v3u5)))] | ||||
|         { | ||||
|             cr.modify(|w| w.set_dbp(true)); | ||||
|             while !cr.read().dbp() {} | ||||
|         } | ||||
|  | ||||
|         #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
|         let cr = crate::pac::RCC.csr(); | ||||
|  | ||||
|         #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|         let cr = crate::pac::RCC.bdcr(); | ||||
|  | ||||
|         cr.modify(|w| f(w)) | ||||
|     } | ||||
|  | ||||
|     #[cfg(any( | ||||
|         rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3, | ||||
|         rtc_v3u5 | ||||
|     ))] | ||||
|     #[allow(dead_code)] | ||||
|     fn read() -> Bdcr { | ||||
|         #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
|         let r = crate::pac::RCC.csr().read(); | ||||
|  | ||||
|         #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|         let r = crate::pac::RCC.bdcr().read(); | ||||
|  | ||||
|         r | ||||
|     } | ||||
|  | ||||
|     #[cfg(any( | ||||
|         rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3, | ||||
|         rtc_v3u5 | ||||
|     ))] | ||||
|     #[allow(dead_code, unused_variables)] | ||||
|     pub fn configure_ls(clock_source: RtcClockSource, lse_drive: Option<LseDrive>) { | ||||
|         match clock_source { | ||||
|             RtcClockSource::LSI => { | ||||
|                 #[cfg(rtc_v3u5)] | ||||
|                 let csr = crate::pac::RCC.bdcr(); | ||||
|  | ||||
|                 #[cfg(not(rtc_v3u5))] | ||||
|                 let csr = crate::pac::RCC.csr(); | ||||
|  | ||||
|                 Self::modify(|_| { | ||||
|                     #[cfg(not(rtc_v2wb))] | ||||
|                     csr.modify(|w| w.set_lsion(true)); | ||||
|  | ||||
|                     #[cfg(rtc_v2wb)] | ||||
|                     csr.modify(|w| w.set_lsi1on(true)); | ||||
|                 }); | ||||
|  | ||||
|                 #[cfg(not(rtc_v2wb))] | ||||
|                 while !csr.read().lsirdy() {} | ||||
|  | ||||
|                 #[cfg(rtc_v2wb)] | ||||
|                 while !csr.read().lsi1rdy() {} | ||||
|             } | ||||
|             RtcClockSource::LSE => { | ||||
|                 let lse_drive = lse_drive.unwrap_or_default(); | ||||
|  | ||||
|                 Self::modify(|w| { | ||||
|                     #[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))] | ||||
|                     w.set_lsedrv(lse_drive.into()); | ||||
|                     w.set_lseon(true); | ||||
|                 }); | ||||
|  | ||||
|                 while !Self::read().lserdy() {} | ||||
|             } | ||||
|             _ => {} | ||||
|         }; | ||||
|  | ||||
|         Self::configure_rtc(clock_source); | ||||
|     } | ||||
|  | ||||
|     #[cfg(any( | ||||
|         rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3, | ||||
|         rtc_v3u5 | ||||
|     ))] | ||||
|     #[allow(dead_code, unused_variables)] | ||||
|     pub fn configure_rtc(clock_source: RtcClockSource) { | ||||
|         let clock_source = clock_source as u8; | ||||
|         #[cfg(any( | ||||
|             not(any(rtc_v3, rtc_v3u5, rtc_v2wb)), | ||||
|             all(any(rtc_v3, rtc_v3u5), not(any(rcc_wl5, rcc_wle))) | ||||
|         ))] | ||||
|         let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); | ||||
|  | ||||
|         #[cfg(not(rtc_v2wb))] | ||||
|         Self::modify(|w| { | ||||
|             // Select RTC source | ||||
|             w.set_rtcsel(clock_source); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     #[cfg(any( | ||||
|         rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3, | ||||
|         rtc_v3u5 | ||||
|     ))] | ||||
|     #[allow(dead_code)] | ||||
|     pub fn enable_rtc() { | ||||
|         let reg = Self::read(); | ||||
|  | ||||
|         #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | ||||
|         assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | ||||
|  | ||||
|         if !reg.rtcen() { | ||||
|             #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] | ||||
|             Self::modify(|w| w.set_bdrst(true)); | ||||
|  | ||||
|             Self::modify(|w| { | ||||
|                 // Reset | ||||
|                 #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] | ||||
|                 w.set_bdrst(false); | ||||
|  | ||||
|                 w.set_rtcen(true); | ||||
|                 w.set_rtcsel(reg.rtcsel()); | ||||
|  | ||||
|                 // Restore bcdr | ||||
|                 #[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | ||||
|                 w.set_lscosel(reg.lscosel()); | ||||
|                 #[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | ||||
|                 w.set_lscoen(reg.lscoen()); | ||||
|  | ||||
|                 w.set_lseon(reg.lseon()); | ||||
|  | ||||
|                 #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | ||||
|                 w.set_lsedrv(reg.lsedrv()); | ||||
|                 w.set_lsebyp(reg.lsebyp()); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::flash::vals::Latency; | ||||
| use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
|   | ||||
| @@ -184,6 +184,6 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         apb1_tim: Hertz(pclk1 * timer_mul1), | ||||
|         apb2_tim: Hertz(pclk2 * timer_mul2), | ||||
|         ahb1: Hertz(hclk), | ||||
|         adc: Hertz(adcclk), | ||||
|         adc: Some(Hertz(adcclk)), | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| use core::convert::TryFrom; | ||||
| use core::ops::{Div, Mul}; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::flash::vals::Latency; | ||||
| use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
| use crate::rcc::bd::BackupDomain; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::rtc::RtcClockSource; | ||||
| use crate::time::Hertz; | ||||
|  | ||||
| /// HSI speed | ||||
| @@ -201,7 +203,7 @@ pub struct PLLClocks { | ||||
|     pub pll48_freq: Hertz, | ||||
| } | ||||
|  | ||||
| pub use super::common::VoltageScale; | ||||
| pub use super::bus::VoltageScale; | ||||
|  | ||||
| impl VoltageScale { | ||||
|     const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> { | ||||
| @@ -288,6 +290,7 @@ pub struct Config { | ||||
|     pub pll_mux: PLLSrc, | ||||
|     pub pll: PLLConfig, | ||||
|     pub mux: ClockSrc, | ||||
|     pub rtc: Option<RtcClockSource>, | ||||
|     pub voltage: VoltageScale, | ||||
|     pub ahb_pre: AHBPrescaler, | ||||
|     pub apb1_pre: APBPrescaler, | ||||
| @@ -304,6 +307,7 @@ impl Default for Config { | ||||
|             pll: PLLConfig::default(), | ||||
|             voltage: VoltageScale::Scale3, | ||||
|             mux: ClockSrc::HSI, | ||||
|             rtc: None, | ||||
|             ahb_pre: AHBPrescaler::NotDivided, | ||||
|             apb1_pre: APBPrescaler::NotDivided, | ||||
|             apb2_pre: APBPrescaler::NotDivided, | ||||
| @@ -414,6 +418,13 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         RCC.cr().modify(|w| w.set_hsion(false)); | ||||
|     } | ||||
|  | ||||
|     RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||||
|     PWR.cr().read(); | ||||
|  | ||||
|     config | ||||
|         .rtc | ||||
|         .map(|clock_source| BackupDomain::configure_ls(clock_source, None)); | ||||
|  | ||||
|     set_freqs(Clocks { | ||||
|         sys: sys_clk, | ||||
|         ahb1: ahb_freq, | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| #[cfg(rcc_f3)] | ||||
| use crate::pac::adccommon::vals::Ckmode; | ||||
| use crate::pac::flash::vals::Latency; | ||||
| use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; | ||||
| use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::time::Hertz; | ||||
| @@ -10,6 +12,82 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000); | ||||
| /// LSI speed | ||||
| pub const LSI_FREQ: Hertz = Hertz(40_000); | ||||
|  | ||||
| impl From<AdcClockSource> for Adcpres { | ||||
|     fn from(value: AdcClockSource) -> Self { | ||||
|         match value { | ||||
|             AdcClockSource::PllDiv1 => Adcpres::DIV1, | ||||
|             AdcClockSource::PllDiv2 => Adcpres::DIV2, | ||||
|             AdcClockSource::PllDiv4 => Adcpres::DIV4, | ||||
|             AdcClockSource::PllDiv6 => Adcpres::DIV6, | ||||
|             AdcClockSource::PllDiv8 => Adcpres::DIV8, | ||||
|             AdcClockSource::PllDiv12 => Adcpres::DIV12, | ||||
|             AdcClockSource::PllDiv16 => Adcpres::DIV16, | ||||
|             AdcClockSource::PllDiv32 => Adcpres::DIV32, | ||||
|             AdcClockSource::PllDiv64 => Adcpres::DIV64, | ||||
|             AdcClockSource::PllDiv128 => Adcpres::DIV128, | ||||
|             AdcClockSource::PllDiv256 => Adcpres::DIV256, | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(rcc_f3)] | ||||
| impl From<AdcClockSource> for Ckmode { | ||||
|     fn from(value: AdcClockSource) -> Self { | ||||
|         match value { | ||||
|             AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1, | ||||
|             AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2, | ||||
|             AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4, | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum AdcClockSource { | ||||
|     PllDiv1 = 1, | ||||
|     PllDiv2 = 2, | ||||
|     PllDiv4 = 4, | ||||
|     PllDiv6 = 6, | ||||
|     PllDiv8 = 8, | ||||
|     PllDiv12 = 12, | ||||
|     PllDiv16 = 16, | ||||
|     PllDiv32 = 32, | ||||
|     PllDiv64 = 64, | ||||
|     PllDiv128 = 128, | ||||
|     PllDiv256 = 256, | ||||
|     BusDiv1, | ||||
|     BusDiv2, | ||||
|     BusDiv4, | ||||
| } | ||||
|  | ||||
| impl AdcClockSource { | ||||
|     pub fn is_bus(&self) -> bool { | ||||
|         match self { | ||||
|             Self::BusDiv1 => true, | ||||
|             Self::BusDiv2 => true, | ||||
|             Self::BusDiv4 => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn bus_div(&self) -> u32 { | ||||
|         match self { | ||||
|             Self::BusDiv1 => 1, | ||||
|             Self::BusDiv2 => 2, | ||||
|             Self::BusDiv4 => 4, | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default)] | ||||
| pub enum HrtimClockSource { | ||||
|     #[default] | ||||
|     BusClk, | ||||
|     PllClk, | ||||
| } | ||||
|  | ||||
| /// Clocks configutation | ||||
| #[non_exhaustive] | ||||
| #[derive(Default)] | ||||
| @@ -36,9 +114,20 @@ pub struct Config { | ||||
|     /// - The System clock frequency is either 48MHz or 72MHz | ||||
|     /// - APB1 clock has a minimum frequency of 10MHz | ||||
|     pub pll48: bool, | ||||
|     #[cfg(rcc_f3)] | ||||
|     /// ADC clock setup | ||||
|     /// - For AHB, a psc of 4 or less must be used | ||||
|     pub adc: Option<AdcClockSource>, | ||||
|     #[cfg(rcc_f3)] | ||||
|     /// ADC clock setup | ||||
|     /// - For AHB, a psc of 4 or less must be used | ||||
|     pub adc34: Option<AdcClockSource>, | ||||
|     #[cfg(stm32f334)] | ||||
|     pub hrtim: HrtimClockSource, | ||||
| } | ||||
|  | ||||
| // Information required to setup the PLL clock | ||||
| #[derive(Clone, Copy)] | ||||
| struct PllConfig { | ||||
|     pll_src: Pllsrc, | ||||
|     pll_mul: Pllmul, | ||||
| @@ -170,6 +259,61 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         }) | ||||
|     }); | ||||
|  | ||||
|     #[cfg(rcc_f3)] | ||||
|     let adc = config.adc.map(|adc| { | ||||
|         if !adc.is_bus() { | ||||
|             RCC.cfgr2().modify(|w| { | ||||
|                 // Make sure that we're using the PLL | ||||
|                 pll_config.unwrap(); | ||||
|                 w.set_adc12pres(adc.into()); | ||||
|  | ||||
|                 Hertz(sysclk / adc as u32) | ||||
|             }) | ||||
|         } else { | ||||
|             crate::pac::ADC_COMMON.ccr().modify(|w| { | ||||
|                 assert!(!(adc.bus_div() == 1 && hpre_bits != Hpre::DIV1)); | ||||
|  | ||||
|                 w.set_ckmode(adc.into()); | ||||
|  | ||||
|                 Hertz(sysclk / adc.bus_div() as u32) | ||||
|             }) | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     #[cfg(rcc_f3)] | ||||
|     let adc34 = config.adc.map(|adc| { | ||||
|         if !adc.is_bus() { | ||||
|             RCC.cfgr2().modify(|w| { | ||||
|                 // Make sure that we're using the PLL | ||||
|                 pll_config.unwrap(); | ||||
|                 w.set_adc12pres(adc.into()); | ||||
|  | ||||
|                 Hertz(sysclk / adc as u32) | ||||
|             }) | ||||
|         } else { | ||||
|             // TODO: need to use only if adc32_common is present | ||||
|  | ||||
|             todo!() | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     #[cfg(stm32f334)] | ||||
|     let hrtim = match config.hrtim { | ||||
|         // Must be configured after the bus is ready, otherwise it won't work | ||||
|         HrtimClockSource::BusClk => None, | ||||
|         HrtimClockSource::PllClk => { | ||||
|             use crate::pac::rcc::vals::Timsw; | ||||
|  | ||||
|             // Make sure that we're using the PLL | ||||
|             pll_config.unwrap(); | ||||
|             assert!((pclk2 == sysclk) || (pclk2 * 2 == sysclk)); | ||||
|  | ||||
|             RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL)); | ||||
|  | ||||
|             Some(Hertz(sysclk * 2)) | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     set_freqs(Clocks { | ||||
|         sys: Hertz(sysclk), | ||||
|         apb1: Hertz(pclk1), | ||||
| @@ -177,6 +321,12 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         apb1_tim: Hertz(pclk1 * timer_mul1), | ||||
|         apb2_tim: Hertz(pclk2 * timer_mul2), | ||||
|         ahb1: Hertz(hclk), | ||||
|         #[cfg(rcc_f3)] | ||||
|         adc: adc, | ||||
|         #[cfg(rcc_f3)] | ||||
|         adc34: adc34, | ||||
|         #[cfg(stm32f334)] | ||||
|         hrtim: hrtim, | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -201,9 +351,9 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { | ||||
|     // Calculates the Multiplier and the Divisor to arrive at | ||||
|     // the required System clock from PLL source frequency | ||||
|     let get_mul_div = |sysclk, pllsrcclk| { | ||||
|         let common_div = gcd(sysclk, pllsrcclk); | ||||
|         let mut multiplier = sysclk / common_div; | ||||
|         let mut divisor = pllsrcclk / common_div; | ||||
|         let bus_div = gcd(sysclk, pllsrcclk); | ||||
|         let mut multiplier = sysclk / bus_div; | ||||
|         let mut divisor = pllsrcclk / bus_div; | ||||
|         // Minimum PLL multiplier is two | ||||
|         if multiplier == 1 { | ||||
|             multiplier *= 2; | ||||
|   | ||||
| @@ -8,8 +8,8 @@ use crate::gpio::sealed::AFType; | ||||
| use crate::gpio::Speed; | ||||
| use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
| use crate::rcc::bd::{BackupDomain, RtcClockSource}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::rtc::{Rtc, RtcClockSource}; | ||||
| use crate::time::Hertz; | ||||
| use crate::{peripherals, Peripheral}; | ||||
|  | ||||
| @@ -461,17 +461,9 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         }) | ||||
|     }); | ||||
|  | ||||
|     match config.rtc { | ||||
|         Some(RtcClockSource::LSI) => { | ||||
|             RCC.csr().modify(|w| w.set_lsion(true)); | ||||
|             while !RCC.csr().read().lsirdy() {} | ||||
|         } | ||||
|         _ => {} | ||||
|     } | ||||
|  | ||||
|     config.rtc.map(|clock_source| { | ||||
|         Rtc::set_clock_source(clock_source); | ||||
|     }); | ||||
|     config | ||||
|         .rtc | ||||
|         .map(|clock_source| BackupDomain::configure_ls(clock_source, None)); | ||||
|  | ||||
|     let rtc = match config.rtc { | ||||
|         Some(RtcClockSource::LSI) => Some(LSI_FREQ), | ||||
| @@ -499,6 +491,7 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         pllsai: None, | ||||
|  | ||||
|         rtc: rtc, | ||||
|         rtc_hse: None, | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::flash::vals::Latency; | ||||
| use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use stm32_metapac::flash::vals::Latency; | ||||
| use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; | ||||
| use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw}; | ||||
| use stm32_metapac::FLASH; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::{PWR, RCC}; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| @@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||||
| /// LSI speed | ||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum AdcClockSource { | ||||
|     NoClk, | ||||
|     SysClk, | ||||
|     PllP, | ||||
| } | ||||
|  | ||||
| impl AdcClockSource { | ||||
|     pub fn adcsel(&self) -> Adcsel { | ||||
|         match self { | ||||
|             AdcClockSource::NoClk => Adcsel::NOCLK, | ||||
|             AdcClockSource::SysClk => Adcsel::SYSCLK, | ||||
|             AdcClockSource::PllP => Adcsel::PLLP, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for AdcClockSource { | ||||
|     fn default() -> Self { | ||||
|         Self::NoClk | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// System clock mux source | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum ClockSrc { | ||||
| @@ -327,6 +350,8 @@ pub struct Config { | ||||
|     pub pll: Option<Pll>, | ||||
|     /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. | ||||
|     pub clock_48mhz_src: Option<Clock48MhzSrc>, | ||||
|     pub adc12_clock_source: AdcClockSource, | ||||
|     pub adc345_clock_source: AdcClockSource, | ||||
| } | ||||
|  | ||||
| /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. | ||||
| @@ -346,6 +371,8 @@ impl Default for Config { | ||||
|             low_power_run: false, | ||||
|             pll: None, | ||||
|             clock_48mhz_src: None, | ||||
|             adc12_clock_source: Default::default(), | ||||
|             adc345_clock_source: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -549,6 +576,29 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         RCC.ccipr().modify(|w| w.set_clk48sel(source)); | ||||
|     } | ||||
|  | ||||
|     RCC.ccipr() | ||||
|         .modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel())); | ||||
|     RCC.ccipr() | ||||
|         .modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel())); | ||||
|  | ||||
|     let adc12_ck = match config.adc12_clock_source { | ||||
|         AdcClockSource::NoClk => None, | ||||
|         AdcClockSource::PllP => match &pll_freq { | ||||
|             Some(pll) => pll.pll_p, | ||||
|             None => None, | ||||
|         }, | ||||
|         AdcClockSource::SysClk => Some(Hertz(sys_clk)), | ||||
|     }; | ||||
|  | ||||
|     let adc345_ck = match config.adc345_clock_source { | ||||
|         AdcClockSource::NoClk => None, | ||||
|         AdcClockSource::PllP => match &pll_freq { | ||||
|             Some(pll) => pll.pll_p, | ||||
|             None => None, | ||||
|         }, | ||||
|         AdcClockSource::SysClk => Some(Hertz(sys_clk)), | ||||
|     }; | ||||
|  | ||||
|     if config.low_power_run { | ||||
|         assert!(sys_clk <= 2_000_000); | ||||
|         PWR.cr1().modify(|w| w.set_lpr(true)); | ||||
| @@ -562,5 +612,7 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         apb1_tim: Hertz(apb1_tim_freq), | ||||
|         apb2: Hertz(apb2_freq), | ||||
|         apb2_tim: Hertz(apb2_tim_freq), | ||||
|         adc: adc12_ck, | ||||
|         adc34: adc345_ck, | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ const VCO_MAX: u32 = 420_000_000; | ||||
| const VCO_WIDE_MIN: u32 = 128_000_000; | ||||
| const VCO_WIDE_MAX: u32 = 560_000_000; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale}; | ||||
|  | ||||
| pub enum HseMode { | ||||
|     /// crystal/ceramic oscillator (HSEBYP=0) | ||||
|   | ||||
| @@ -24,7 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||||
| /// LSI speed | ||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | ||||
|  | ||||
| pub use super::common::VoltageScale; | ||||
| pub use super::bus::VoltageScale; | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum AdcClockSource { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||||
| use crate::pac::RCC; | ||||
| #[cfg(crs)] | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
|   | ||||
| @@ -2,15 +2,15 @@ use core::marker::PhantomData; | ||||
|  | ||||
| use embassy_hal_internal::into_ref; | ||||
| use stm32_metapac::rcc::regs::Cfgr; | ||||
| use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; | ||||
| use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::gpio::sealed::AFType; | ||||
| use crate::gpio::Speed; | ||||
| use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::bd::{BackupDomain, RtcClockSource}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::rtc::{Rtc, RtcClockSource as RCS}; | ||||
| use crate::time::Hertz; | ||||
| use crate::{peripherals, Peripheral}; | ||||
|  | ||||
| @@ -254,16 +254,11 @@ impl Default for Config { | ||||
|             pllsai1: None, | ||||
|             #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] | ||||
|             hsi48: false, | ||||
|             rtc_mux: RtcClockSource::LSI32, | ||||
|             rtc_mux: RtcClockSource::LSI, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum RtcClockSource { | ||||
|     LSE32, | ||||
|     LSI32, | ||||
| } | ||||
|  | ||||
| pub enum McoClock { | ||||
|     DIV1, | ||||
|     DIV2, | ||||
| @@ -412,35 +407,7 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|  | ||||
|     RCC.apb1enr1().modify(|w| w.set_pwren(true)); | ||||
|  | ||||
|     match config.rtc_mux { | ||||
|         RtcClockSource::LSE32 => { | ||||
|             // 1. Unlock the backup domain | ||||
|             PWR.cr1().modify(|w| w.set_dbp(true)); | ||||
|  | ||||
|             // 2. Setup the LSE | ||||
|             RCC.bdcr().modify(|w| { | ||||
|                 // Enable LSE | ||||
|                 w.set_lseon(true); | ||||
|                 // Max drive strength | ||||
|                 // TODO: should probably be settable | ||||
|                 w.set_lsedrv(Lsedrv::HIGH); | ||||
|             }); | ||||
|  | ||||
|             // Wait until LSE is running | ||||
|             while !RCC.bdcr().read().lserdy() {} | ||||
|  | ||||
|             Rtc::set_clock_source(RCS::LSE); | ||||
|         } | ||||
|         RtcClockSource::LSI32 => { | ||||
|             // Turn on the internal 32 kHz LSI oscillator | ||||
|             RCC.csr().modify(|w| w.set_lsion(true)); | ||||
|  | ||||
|             // Wait until LSI is running | ||||
|             while !RCC.csr().read().lsirdy() {} | ||||
|  | ||||
|             Rtc::set_clock_source(RCS::LSI); | ||||
|         } | ||||
|     } | ||||
|     BackupDomain::configure_ls(config.rtc_mux, None); | ||||
|  | ||||
|     let (sys_clk, sw) = match config.mux { | ||||
|         ClockSrc::MSI(range) => { | ||||
| @@ -451,7 +418,7 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|                 w.set_msirgsel(true); | ||||
|                 w.set_msion(true); | ||||
|  | ||||
|                 if let RtcClockSource::LSE32 = config.rtc_mux { | ||||
|                 if let RtcClockSource::LSE = config.rtc_mux { | ||||
|                     // If LSE is enabled, enable calibration of MSI | ||||
|                     w.set_msipllen(true); | ||||
|                 } else { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use stm32_metapac::PWR; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| #![macro_use] | ||||
|  | ||||
| pub mod common; | ||||
|  | ||||
| pub(crate) mod bd; | ||||
| pub mod bus; | ||||
| use core::mem::MaybeUninit; | ||||
|  | ||||
| pub use crate::rcc::bd::RtcClockSource; | ||||
| use crate::time::Hertz; | ||||
|  | ||||
| #[cfg_attr(rcc_f0, path = "f0.rs")] | ||||
| @@ -70,15 +71,22 @@ pub struct Clocks { | ||||
|     #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||||
|     pub pllsai: Option<Hertz>, | ||||
|  | ||||
|     #[cfg(stm32f1)] | ||||
|     pub adc: Hertz, | ||||
|  | ||||
|     #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] | ||||
|     #[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))] | ||||
|     pub adc: Option<Hertz>, | ||||
|  | ||||
|     #[cfg(any(rcc_f3, rcc_g4))] | ||||
|     pub adc34: Option<Hertz>, | ||||
|  | ||||
|     #[cfg(stm32f334)] | ||||
|     pub hrtim: Option<Hertz>, | ||||
|  | ||||
|     #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||||
|     /// Set only if the lsi or lse is configured | ||||
|     /// Set only if the lsi or lse is configured, indicates stop is supported | ||||
|     pub rtc: Option<Hertz>, | ||||
|  | ||||
|     #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||||
|     /// Set if the hse is configured, indicates stop is not supported | ||||
|     pub rtc_hse: Option<Hertz>, | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "low-power")] | ||||
| @@ -86,6 +94,8 @@ static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); | ||||
|  | ||||
| #[cfg(feature = "low-power")] | ||||
| pub fn low_power_ready() -> bool { | ||||
|     trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst)); | ||||
|  | ||||
|     CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; | ||||
|  | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::time::Hertz; | ||||
| @@ -11,7 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||||
| /// LSI speed | ||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | ||||
|  | ||||
| pub use super::common::VoltageScale; | ||||
| pub use super::bus::VoltageScale; | ||||
|  | ||||
| #[derive(Copy, Clone)] | ||||
| pub enum ClockSrc { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||
| use crate::rcc::bd::{BackupDomain, RtcClockSource}; | ||||
| use crate::rcc::Clocks; | ||||
| use crate::rtc::{Rtc, RtcClockSource}; | ||||
| use crate::time::{khz, mhz, Hertz}; | ||||
|  | ||||
| /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||||
| @@ -271,11 +271,11 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { | ||||
|         apb1_tim: apb1_tim_clk, | ||||
|         apb2_tim: apb2_tim_clk, | ||||
|         rtc: rtc_clk, | ||||
|         rtc_hse: None, | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) fn configure_clocks(config: &Config) { | ||||
|     let pwr = crate::pac::PWR; | ||||
|     let rcc = crate::pac::RCC; | ||||
|  | ||||
|     let needs_hsi = if let Some(pll_mux) = &config.mux { | ||||
| @@ -292,29 +292,11 @@ pub(crate) fn configure_clocks(config: &Config) { | ||||
|         while !rcc.cr().read().hsirdy() {} | ||||
|     } | ||||
|  | ||||
|     let needs_lsi = if let Some(rtc_mux) = &config.rtc { | ||||
|         *rtc_mux == RtcClockSource::LSI | ||||
|     } else { | ||||
|         false | ||||
|     }; | ||||
|     rcc.cfgr().modify(|w| w.set_stopwuck(true)); | ||||
|  | ||||
|     if needs_lsi { | ||||
|         rcc.csr().modify(|w| w.set_lsi1on(true)); | ||||
|  | ||||
|         while !rcc.csr().read().lsi1rdy() {} | ||||
|     } | ||||
|  | ||||
|     match &config.lse { | ||||
|         Some(_) => { | ||||
|             rcc.cfgr().modify(|w| w.set_stopwuck(true)); | ||||
|  | ||||
|             pwr.cr1().modify(|w| w.set_dbp(true)); | ||||
|             pwr.cr1().modify(|w| w.set_dbp(true)); | ||||
|  | ||||
|             rcc.bdcr().modify(|w| w.set_lseon(true)); | ||||
|         } | ||||
|         _ => {} | ||||
|     } | ||||
|     config | ||||
|         .rtc | ||||
|         .map(|clock_source| BackupDomain::configure_ls(clock_source, None)); | ||||
|  | ||||
|     match &config.hse { | ||||
|         Some(hse) => { | ||||
| @@ -374,6 +356,4 @@ pub(crate) fn configure_clocks(config: &Config) { | ||||
|         w.set_c2hpre(config.ahb2_pre.into()); | ||||
|         w.set_shdhpre(config.ahb3_pre.into()); | ||||
|     }); | ||||
|  | ||||
|     config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source)); | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; | ||||
| use crate::pac::pwr::vals::Dbp; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
| pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale}; | ||||
| use crate::pac::rcc::vals::Adcsel; | ||||
| use crate::pac::{FLASH, RCC}; | ||||
| use crate::rcc::bd::{BackupDomain, RtcClockSource}; | ||||
| use crate::rcc::{set_freqs, Clocks}; | ||||
| use crate::rtc::{Rtc, RtcClockSource as RCS}; | ||||
| use crate::time::Hertz; | ||||
|  | ||||
| /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||||
| @@ -107,6 +107,29 @@ impl Into<u8> for MSIRange { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum AdcClockSource { | ||||
|     HSI16, | ||||
|     PLLPCLK, | ||||
|     SYSCLK, | ||||
| } | ||||
|  | ||||
| impl AdcClockSource { | ||||
|     pub fn adcsel(&self) -> Adcsel { | ||||
|         match self { | ||||
|             AdcClockSource::HSI16 => Adcsel::HSI16, | ||||
|             AdcClockSource::PLLPCLK => Adcsel::PLLPCLK, | ||||
|             AdcClockSource::SYSCLK => Adcsel::SYSCLK, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for AdcClockSource { | ||||
|     fn default() -> Self { | ||||
|         Self::HSI16 | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Clocks configutation | ||||
| pub struct Config { | ||||
|     pub mux: ClockSrc, | ||||
| @@ -114,9 +137,8 @@ pub struct Config { | ||||
|     pub shd_ahb_pre: AHBPrescaler, | ||||
|     pub apb1_pre: APBPrescaler, | ||||
|     pub apb2_pre: APBPrescaler, | ||||
|     pub enable_lsi: bool, | ||||
|     pub enable_rtc_apb: bool, | ||||
|     pub rtc_mux: RtcClockSource, | ||||
|     pub adc_clock_source: AdcClockSource, | ||||
| } | ||||
|  | ||||
| impl Default for Config { | ||||
| @@ -128,18 +150,12 @@ impl Default for Config { | ||||
|             shd_ahb_pre: AHBPrescaler::NotDivided, | ||||
|             apb1_pre: APBPrescaler::NotDivided, | ||||
|             apb2_pre: APBPrescaler::NotDivided, | ||||
|             enable_lsi: false, | ||||
|             enable_rtc_apb: false, | ||||
|             rtc_mux: RtcClockSource::LSI32, | ||||
|             rtc_mux: RtcClockSource::LSI, | ||||
|             adc_clock_source: AdcClockSource::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum RtcClockSource { | ||||
|     LSE32, | ||||
|     LSI32, | ||||
| } | ||||
|  | ||||
| #[repr(u8)] | ||||
| pub enum Lsedrv { | ||||
|     Low = 0, | ||||
| @@ -214,35 +230,8 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|  | ||||
|     while FLASH.acr().read().latency() != ws {} | ||||
|  | ||||
|     match config.rtc_mux { | ||||
|         RtcClockSource::LSE32 => { | ||||
|             // 1. Unlock the backup domain | ||||
|             PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); | ||||
|  | ||||
|             // 2. Setup the LSE | ||||
|             RCC.bdcr().modify(|w| { | ||||
|                 // Enable LSE | ||||
|                 w.set_lseon(true); | ||||
|                 // Max drive strength | ||||
|                 // TODO: should probably be settable | ||||
|                 w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented | ||||
|             }); | ||||
|  | ||||
|             // Wait until LSE is running | ||||
|             while !RCC.bdcr().read().lserdy() {} | ||||
|  | ||||
|             Rtc::set_clock_source(RCS::LSE); | ||||
|         } | ||||
|         RtcClockSource::LSI32 => { | ||||
|             // Turn on the internal 32 kHz LSI oscillator | ||||
|             RCC.csr().modify(|w| w.set_lsion(true)); | ||||
|  | ||||
|             // Wait until LSI is running | ||||
|             while !RCC.csr().read().lsirdy() {} | ||||
|  | ||||
|             Rtc::set_clock_source(RCS::LSI); | ||||
|         } | ||||
|     } | ||||
|     // Enables the LSI if configured | ||||
|     BackupDomain::configure_ls(config.rtc_mux, None); | ||||
|  | ||||
|     match config.mux { | ||||
|         ClockSrc::HSI16 => { | ||||
| @@ -266,7 +255,7 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|                 w.set_msirange(range.into()); | ||||
|                 w.set_msion(true); | ||||
|  | ||||
|                 if let RtcClockSource::LSE32 = config.rtc_mux { | ||||
|                 if let RtcClockSource::LSE = config.rtc_mux { | ||||
|                     // If LSE is enabled, enable calibration of MSI | ||||
|                     w.set_msipllen(true); | ||||
|                 } else { | ||||
| @@ -277,14 +266,6 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if config.enable_rtc_apb { | ||||
|         // enable peripheral clock for communication | ||||
|         crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); | ||||
|  | ||||
|         // read to allow the pwr clock to enable | ||||
|         crate::pac::PWR.cr1().read(); | ||||
|     } | ||||
|  | ||||
|     RCC.extcfgr().modify(|w| { | ||||
|         if config.shd_ahb_pre == AHBPrescaler::NotDivided { | ||||
|             w.set_shdhpre(0); | ||||
| @@ -304,15 +285,10 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         w.set_ppre2(config.apb2_pre.into()); | ||||
|     }); | ||||
|  | ||||
|     // TODO: switch voltage range | ||||
|     // ADC clock MUX | ||||
|     RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel())); | ||||
|  | ||||
|     if config.enable_lsi { | ||||
|         let csr = RCC.csr().read(); | ||||
|         if !csr.lsion() { | ||||
|             RCC.csr().modify(|w| w.set_lsion(true)); | ||||
|             while !RCC.csr().read().lsirdy() {} | ||||
|         } | ||||
|     } | ||||
|     // TODO: switch voltage range | ||||
|  | ||||
|     set_freqs(Clocks { | ||||
|         sys: Hertz(sys_clk), | ||||
|   | ||||
| @@ -119,7 +119,31 @@ impl<'d, T: Instance> Rng<'d, T> { | ||||
|  | ||||
|     pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||||
|         for chunk in dest.chunks_mut(4) { | ||||
|             let bits = T::regs().sr().read(); | ||||
|             let mut bits = T::regs().sr().read(); | ||||
|             if !bits.seis() && !bits.ceis() && !bits.drdy() { | ||||
|                 // wait for interrupt | ||||
|                 poll_fn(|cx| { | ||||
|                     // quick check to avoid registration if already done. | ||||
|                     let bits = T::regs().sr().read(); | ||||
|                     if bits.drdy() || bits.seis() || bits.ceis() { | ||||
|                         return Poll::Ready(()); | ||||
|                     } | ||||
|                     RNG_WAKER.register(cx.waker()); | ||||
|                     T::regs().cr().modify(|reg| reg.set_ie(true)); | ||||
|                     // Need to check condition **after** `register` to avoid a race | ||||
|                     // condition that would result in lost notifications. | ||||
|                     let bits = T::regs().sr().read(); | ||||
|                     if bits.drdy() || bits.seis() || bits.ceis() { | ||||
|                         Poll::Ready(()) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 }) | ||||
|                 .await; | ||||
|  | ||||
|                 // Re-read the status register after wait. | ||||
|                 bits = T::regs().sr().read() | ||||
|             } | ||||
|             if bits.seis() { | ||||
|                 // in case of noise-source or seed error we try to recover here | ||||
|                 // but we must not use the data in DR and we return an error | ||||
| @@ -143,26 +167,6 @@ impl<'d, T: Instance> Rng<'d, T> { | ||||
|                 for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) { | ||||
|                     *dest = *src | ||||
|                 } | ||||
|             } else { | ||||
|                 // wait for interrupt | ||||
|                 poll_fn(|cx| { | ||||
|                     // quick check to avoid registration if already done. | ||||
|                     let bits = T::regs().sr().read(); | ||||
|                     if bits.drdy() || bits.seis() || bits.ceis() { | ||||
|                         return Poll::Ready(()); | ||||
|                     } | ||||
|                     RNG_WAKER.register(cx.waker()); | ||||
|                     T::regs().cr().modify(|reg| reg.set_ie(true)); | ||||
|                     // Need to check condition **after** `register` to avoid a race | ||||
|                     // condition that would result in lost notifications. | ||||
|                     let bits = T::regs().sr().read(); | ||||
|                     if bits.drdy() || bits.seis() || bits.ceis() { | ||||
|                         Poll::Ready(()) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 }) | ||||
|                 .await; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -89,7 +89,7 @@ pub enum DayOfWeek { | ||||
| #[cfg(feature = "chrono")] | ||||
| impl From<chrono::Weekday> for DayOfWeek { | ||||
|     fn from(weekday: Weekday) -> Self { | ||||
|         day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() | ||||
|         day_of_week_from_u8(weekday.num_days_from_monday() as u8).unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,18 @@ | ||||
| //! RTC peripheral abstraction | ||||
| mod datetime; | ||||
|  | ||||
| #[cfg(feature = "low-power")] | ||||
| use core::cell::Cell; | ||||
|  | ||||
| #[cfg(feature = "low-power")] | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| #[cfg(feature = "low-power")] | ||||
| use embassy_sync::blocking_mutex::Mutex; | ||||
|  | ||||
| pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | ||||
| use crate::rcc::bd::BackupDomain; | ||||
| pub use crate::rcc::RtcClockSource; | ||||
| use crate::time::Hertz; | ||||
|  | ||||
| /// refer to AN4759 to compare features of RTC2 and RTC3 | ||||
| #[cfg_attr(any(rtc_v1), path = "v1.rs")] | ||||
| @@ -30,60 +41,67 @@ pub enum RtcError { | ||||
|     NotRunning, | ||||
| } | ||||
|  | ||||
| /// RTC Abstraction | ||||
| pub struct Rtc { | ||||
|     rtc_config: RtcConfig, | ||||
| #[cfg(feature = "low-power")] | ||||
| /// Represents an instant in time that can be substracted to compute a duration | ||||
| struct RtcInstant { | ||||
|     second: u8, | ||||
|     subsecond: u16, | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, Debug, PartialEq)] | ||||
| #[repr(u8)] | ||||
| pub enum RtcClockSource { | ||||
|     /// 00: No clock | ||||
|     NoClock = 0b00, | ||||
|     /// 01: LSE oscillator clock used as RTC clock | ||||
|     LSE = 0b01, | ||||
|     /// 10: LSI oscillator clock used as RTC clock | ||||
|     LSI = 0b10, | ||||
|     /// 11: HSE oscillator clock divided by 32 used as RTC clock | ||||
|     HSE = 0b11, | ||||
| #[cfg(all(feature = "low-power", feature = "defmt"))] | ||||
| impl defmt::Format for RtcInstant { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!( | ||||
|             fmt, | ||||
|             "{}:{}", | ||||
|             self.second, | ||||
|             RTC::regs().prer().read().prediv_s() - self.subsecond, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "low-power")] | ||||
| impl core::ops::Sub for RtcInstant { | ||||
|     type Output = embassy_time::Duration; | ||||
|  | ||||
|     fn sub(self, rhs: Self) -> Self::Output { | ||||
|         use embassy_time::{Duration, TICK_HZ}; | ||||
|  | ||||
|         let second = if self.second < rhs.second { | ||||
|             self.second + 60 | ||||
|         } else { | ||||
|             self.second | ||||
|         }; | ||||
|  | ||||
|         let psc = RTC::regs().prer().read().prediv_s() as u32; | ||||
|  | ||||
|         let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); | ||||
|         let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); | ||||
|         let rtc_ticks = self_ticks - other_ticks; | ||||
|  | ||||
|         Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// RTC Abstraction | ||||
| pub struct Rtc { | ||||
|     #[cfg(feature = "low-power")] | ||||
|     stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>, | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, PartialEq)] | ||||
| pub struct RtcConfig { | ||||
|     /// Asynchronous prescaler factor | ||||
|     /// This is the asynchronous division factor: | ||||
|     /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) | ||||
|     /// ck_apre drives the subsecond register | ||||
|     async_prescaler: u8, | ||||
|     /// Synchronous prescaler factor | ||||
|     /// This is the synchronous division factor: | ||||
|     /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1) | ||||
|     /// ck_spre must be 1Hz | ||||
|     sync_prescaler: u16, | ||||
|     /// The subsecond counter frequency; default is 256 | ||||
|     /// | ||||
|     /// A high counter frequency may impact stop power consumption | ||||
|     pub frequency: Hertz, | ||||
| } | ||||
|  | ||||
| impl Default for RtcConfig { | ||||
|     /// LSI with prescalers assuming 32.768 kHz. | ||||
|     /// Raw sub-seconds in 1/256. | ||||
|     fn default() -> Self { | ||||
|         RtcConfig { | ||||
|             async_prescaler: 127, | ||||
|             sync_prescaler: 255, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl RtcConfig { | ||||
|     /// Set the asynchronous prescaler of RTC config | ||||
|     pub fn async_prescaler(mut self, prescaler: u8) -> Self { | ||||
|         self.async_prescaler = prescaler; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the synchronous prescaler of RTC config | ||||
|     pub fn sync_prescaler(mut self, prescaler: u16) -> Self { | ||||
|         self.sync_prescaler = prescaler; | ||||
|         self | ||||
|         RtcConfig { frequency: Hertz(256) } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -106,16 +124,37 @@ impl Default for RtcCalibrationCyclePeriod { | ||||
|  | ||||
| impl Rtc { | ||||
|     pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { | ||||
|         #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||||
|         use crate::rcc::get_freqs; | ||||
|  | ||||
|         RTC::enable_peripheral_clk(); | ||||
|         BackupDomain::enable_rtc(); | ||||
|  | ||||
|         let mut rtc_struct = Self { rtc_config }; | ||||
|         let mut this = Self { | ||||
|             #[cfg(feature = "low-power")] | ||||
|             stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | ||||
|         }; | ||||
|  | ||||
|         Self::enable(); | ||||
|         #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||||
|         let freqs = unsafe { get_freqs() }; | ||||
|  | ||||
|         rtc_struct.configure(rtc_config); | ||||
|         rtc_struct.rtc_config = rtc_config; | ||||
|         // Load the clock frequency from the rcc mod, if supported | ||||
|         #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||||
|         let frequency = match freqs.rtc { | ||||
|             Some(hertz) => hertz, | ||||
|             None => freqs.rtc_hse.unwrap(), | ||||
|         }; | ||||
|  | ||||
|         rtc_struct | ||||
|         // Assume the  default value, if not supported | ||||
|         #[cfg(not(any(rcc_wb, rcc_f4, rcc_f410)))] | ||||
|         let frequency = Hertz(32_768); | ||||
|  | ||||
|         let async_psc = ((frequency.0 / rtc_config.frequency.0) - 1) as u8; | ||||
|         let sync_psc = (rtc_config.frequency.0 - 1) as u16; | ||||
|  | ||||
|         this.configure(async_psc, sync_psc); | ||||
|  | ||||
|         this | ||||
|     } | ||||
|  | ||||
|     /// Set the datetime to a new value. | ||||
| @@ -130,6 +169,20 @@ impl Rtc { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "low-power")] | ||||
|     /// Return the current instant. | ||||
|     fn instant(&self) -> RtcInstant { | ||||
|         let r = RTC::regs(); | ||||
|         let tr = r.tr().read(); | ||||
|         let subsecond = r.ssr().read().ss(); | ||||
|         let second = bcd2_to_byte((tr.st(), tr.su())); | ||||
|  | ||||
|         // Unlock the registers | ||||
|         r.dr().read(); | ||||
|  | ||||
|         RtcInstant { second, subsecond } | ||||
|     } | ||||
|  | ||||
|     /// Return the current datetime. | ||||
|     /// | ||||
|     /// # Errors | ||||
| @@ -166,10 +219,6 @@ impl Rtc { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn get_config(&self) -> RtcConfig { | ||||
|         self.rtc_config | ||||
|     } | ||||
|  | ||||
|     pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; | ||||
|  | ||||
|     /// Read content of the backup register. | ||||
| @@ -219,7 +268,7 @@ pub(crate) mod sealed { | ||||
|             crate::pac::RTC | ||||
|         } | ||||
|  | ||||
|         fn enable_peripheral_clk() {} | ||||
|         fn enable_peripheral_clk(); | ||||
|  | ||||
|         /// Read content of the backup register. | ||||
|         /// | ||||
|   | ||||
| @@ -1,74 +1,18 @@ | ||||
| use stm32_metapac::rtc::vals::{Init, Osel, Pol}; | ||||
|  | ||||
| use super::{sealed, RtcClockSource, RtcConfig}; | ||||
| use super::sealed; | ||||
| use crate::pac::rtc::Rtc; | ||||
| use crate::peripherals::RTC; | ||||
| use crate::rtc::sealed::Instance; | ||||
|  | ||||
| #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||||
| pub struct RtcInstant { | ||||
|     ssr: u16, | ||||
|     st: u8, | ||||
| } | ||||
|  | ||||
| #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||||
| impl RtcInstant { | ||||
|     pub fn now() -> Self { | ||||
|         // TODO: read value twice | ||||
|         use crate::rtc::bcd2_to_byte; | ||||
|  | ||||
|         let tr = RTC::regs().tr().read(); | ||||
|         let tr2 = RTC::regs().tr().read(); | ||||
|         let ssr = RTC::regs().ssr().read().ss(); | ||||
|         let ssr2 = RTC::regs().ssr().read().ss(); | ||||
|  | ||||
|         let st = bcd2_to_byte((tr.st(), tr.su())); | ||||
|         let st2 = bcd2_to_byte((tr2.st(), tr2.su())); | ||||
|  | ||||
|         assert!(st == st2); | ||||
|         assert!(ssr == ssr2); | ||||
|  | ||||
|         let _ = RTC::regs().dr().read(); | ||||
|  | ||||
|         trace!("ssr: {}", ssr); | ||||
|         trace!("st: {}", st); | ||||
|  | ||||
|         Self { ssr, st } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||||
| impl core::ops::Sub for RtcInstant { | ||||
|     type Output = embassy_time::Duration; | ||||
|  | ||||
|     fn sub(self, rhs: Self) -> Self::Output { | ||||
|         use embassy_time::{Duration, TICK_HZ}; | ||||
|  | ||||
|         let st = if self.st < rhs.st { self.st + 60 } else { self.st }; | ||||
|  | ||||
|         // TODO: read prescaler | ||||
|  | ||||
|         let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); | ||||
|         let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); | ||||
|         let rtc_ticks = self_ticks - other_ticks; | ||||
|  | ||||
|         trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); | ||||
|  | ||||
|         Duration::from_ticks( | ||||
|             ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) | ||||
|                 * TICK_HZ as u32) as u32 | ||||
|                 / 256u32) as u64, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[repr(u8)] | ||||
| #[derive(Clone, Copy, Debug)] | ||||
| pub(crate) enum WakeupPrescaler { | ||||
|     Div2, | ||||
|     Div4, | ||||
|     Div8, | ||||
|     Div16, | ||||
|     Div2 = 2, | ||||
|     Div4 = 4, | ||||
|     Div8 = 8, | ||||
|     Div16 = 16, | ||||
| } | ||||
|  | ||||
| #[cfg(any(stm32wb, stm32f4))] | ||||
| @@ -100,17 +44,6 @@ impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<WakeupPrescaler> for u32 { | ||||
|     fn from(val: WakeupPrescaler) -> Self { | ||||
|         match val { | ||||
|             WakeupPrescaler::Div2 => 2, | ||||
|             WakeupPrescaler::Div4 => 4, | ||||
|             WakeupPrescaler::Div8 => 8, | ||||
|             WakeupPrescaler::Div16 => 16, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| impl WakeupPrescaler { | ||||
|     pub fn compute_min(val: u32) -> Self { | ||||
| @@ -121,38 +54,17 @@ impl WakeupPrescaler { | ||||
|             WakeupPrescaler::Div16, | ||||
|         ] | ||||
|         .iter() | ||||
|         .skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val) | ||||
|         .skip_while(|psc| **psc as u32 <= val) | ||||
|         .next() | ||||
|         .unwrap_or(&WakeupPrescaler::Div16) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl super::Rtc { | ||||
|     fn unlock_registers() { | ||||
|         #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] | ||||
|         let cr = crate::pac::PWR.cr(); | ||||
|         #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] | ||||
|         let cr = crate::pac::PWR.cr1(); | ||||
|  | ||||
|         // TODO: Missing from PAC for l0 and f0? | ||||
|         #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] | ||||
|         { | ||||
|             if !cr.read().dbp() { | ||||
|                 cr.modify(|w| w.set_dbp(true)); | ||||
|                 while !cr.read().dbp() {} | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||||
|     /// start the wakeup alarm and return the actual duration of the alarm | ||||
|     /// the actual duration will be the closest value possible that is less | ||||
|     /// than the requested duration. | ||||
|     /// | ||||
|     /// note: this api is exposed for testing purposes until low power is implemented. | ||||
|     /// it is not intended to be public | ||||
|     pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { | ||||
|     #[cfg(feature = "low-power")] | ||||
|     /// start the wakeup alarm and wtih a duration that is as close to but less than | ||||
|     /// the requested duration, and record the instant the wakeup alarm was started | ||||
|     pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) { | ||||
|         use embassy_time::{Duration, TICK_HZ}; | ||||
|  | ||||
|         use crate::rcc::get_freqs; | ||||
| @@ -162,117 +74,74 @@ impl super::Rtc { | ||||
|         let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; | ||||
|         let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | ||||
|  | ||||
|         // adjust the rtc ticks to the prescaler | ||||
|         let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64); | ||||
|         // adjust the rtc ticks to the prescaler and subtract one rtc tick | ||||
|         let rtc_ticks = rtc_ticks / prescaler as u64; | ||||
|         let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { | ||||
|             u16::MAX - 1 | ||||
|         } else { | ||||
|             rtc_ticks as u16 | ||||
|         }; | ||||
|  | ||||
|         let duration = Duration::from_ticks( | ||||
|             rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, | ||||
|         ); | ||||
|  | ||||
|         trace!("set wakeup timer for {} ms", duration.as_millis()); | ||||
|         } | ||||
|         .saturating_sub(1); | ||||
|  | ||||
|         self.write(false, |regs| { | ||||
|             regs.cr().modify(|w| w.set_wutie(true)); | ||||
|  | ||||
|             regs.cr().modify(|w| w.set_wute(false)); | ||||
|             regs.isr().modify(|w| w.set_wutf(false)); | ||||
|             while !regs.isr().read().wutwf() {} | ||||
|  | ||||
|             regs.cr().modify(|w| w.set_wucksel(prescaler.into())); | ||||
|             regs.wutr().write(|w| w.set_wut(rtc_ticks)); | ||||
|             regs.cr().modify(|w| w.set_wute(true)); | ||||
|             regs.cr().modify(|w| w.set_wutie(true)); | ||||
|         }); | ||||
|  | ||||
|         RtcInstant::now() | ||||
|         trace!( | ||||
|             "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", | ||||
|             Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), | ||||
|             prescaler as u32, | ||||
|             rtc_ticks, | ||||
|             self.instant(), | ||||
|         ); | ||||
|  | ||||
|         critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none())) | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||||
|     /// stop the wakeup alarm and return the time remaining | ||||
|     /// | ||||
|     /// note: this api is exposed for testing purposes until low power is implemented. | ||||
|     /// it is not intended to be public | ||||
|     pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { | ||||
|         trace!("disable wakeup timer..."); | ||||
|     #[cfg(feature = "low-power")] | ||||
|     pub(crate) fn enable_wakeup_line(&self) { | ||||
|         use crate::pac::EXTI; | ||||
|  | ||||
|         EXTI.rtsr(0).modify(|w| w.set_line(22, true)); | ||||
|         EXTI.imr(0).modify(|w| w.set_line(22, true)); | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "low-power")] | ||||
|     /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | ||||
|     /// was called, otherwise none | ||||
|     pub(crate) fn stop_wakeup_alarm(&self) -> Option<embassy_time::Duration> { | ||||
|         use crate::interrupt::typelevel::Interrupt; | ||||
|  | ||||
|         trace!("rtc: stop wakeup alarm at {}", self.instant()); | ||||
|  | ||||
|         self.write(false, |regs| { | ||||
|             regs.cr().modify(|w| w.set_wutie(false)); | ||||
|             regs.cr().modify(|w| w.set_wute(false)); | ||||
|             regs.isr().modify(|w| w.set_wutf(false)); | ||||
|  | ||||
|             crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true)); | ||||
|             crate::interrupt::typelevel::RTC_WKUP::unpend(); | ||||
|         }); | ||||
|  | ||||
|         RtcInstant::now() | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     pub(crate) fn set_clock_source(clock_source: RtcClockSource) { | ||||
|         #[cfg(not(rtc_v2wb))] | ||||
|         use stm32_metapac::rcc::vals::Rtcsel; | ||||
|  | ||||
|         #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|         let cr = crate::pac::RCC.bdcr(); | ||||
|         #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
|         let cr = crate::pac::RCC.csr(); | ||||
|  | ||||
|         Self::unlock_registers(); | ||||
|  | ||||
|         cr.modify(|w| { | ||||
|             // Select RTC source | ||||
|             #[cfg(not(rtc_v2wb))] | ||||
|             w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); | ||||
|             #[cfg(rtc_v2wb)] | ||||
|             w.set_rtcsel(clock_source as u8); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     pub(super) fn enable() { | ||||
|         #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|         let reg = crate::pac::RCC.bdcr().read(); | ||||
|         #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
|         let reg = crate::pac::RCC.csr().read(); | ||||
|  | ||||
|         #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] | ||||
|         assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | ||||
|  | ||||
|         if !reg.rtcen() { | ||||
|             Self::unlock_registers(); | ||||
|  | ||||
|             #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] | ||||
|             crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); | ||||
|             #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|             let cr = crate::pac::RCC.bdcr(); | ||||
|             #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||||
|             let cr = crate::pac::RCC.csr(); | ||||
|  | ||||
|             cr.modify(|w| { | ||||
|                 // Reset | ||||
|                 #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||||
|                 w.set_bdrst(false); | ||||
|  | ||||
|                 w.set_rtcen(true); | ||||
|                 w.set_rtcsel(reg.rtcsel()); | ||||
|  | ||||
|                 // Restore bcdr | ||||
|                 #[cfg(any(rtc_v2l4, rtc_v2wb))] | ||||
|                 w.set_lscosel(reg.lscosel()); | ||||
|                 #[cfg(any(rtc_v2l4, rtc_v2wb))] | ||||
|                 w.set_lscoen(reg.lscoen()); | ||||
|  | ||||
|                 w.set_lseon(reg.lseon()); | ||||
|  | ||||
|                 #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] | ||||
|                 w.set_lsedrv(reg.lsedrv()); | ||||
|                 w.set_lsebyp(reg.lsebyp()); | ||||
|             }); | ||||
|         } | ||||
|         critical_section::with(|cs| { | ||||
|             if let Some(stop_time) = self.stop_time.borrow(cs).take() { | ||||
|                 Some(self.instant() - stop_time) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Applies the RTC config | ||||
|     /// It this changes the RTC clock source the time will be reset | ||||
|     pub(super) fn configure(&mut self, rtc_config: RtcConfig) { | ||||
|     pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { | ||||
|         self.write(true, |rtc| { | ||||
|             rtc.cr().modify(|w| { | ||||
|                 #[cfg(rtc_v2f2)] | ||||
| @@ -284,8 +153,8 @@ impl super::Rtc { | ||||
|             }); | ||||
|  | ||||
|             rtc.prer().modify(|w| { | ||||
|                 w.set_prediv_s(rtc_config.sync_prescaler); | ||||
|                 w.set_prediv_a(rtc_config.async_prescaler); | ||||
|                 w.set_prediv_s(sync_psc); | ||||
|                 w.set_prediv_a(async_psc); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| @@ -401,9 +270,18 @@ impl sealed::Instance for crate::peripherals::RTC { | ||||
|         } | ||||
|         #[cfg(any(rtc_v2f2))] | ||||
|         { | ||||
|             // enable peripheral clock for communication | ||||
|             crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||||
|  | ||||
|             // read to allow the pwr clock to enable | ||||
|             crate::pac::PWR.cr().read(); | ||||
|         } | ||||
|  | ||||
|         #[cfg(any(rtc_v2f0))] | ||||
|         { | ||||
|             // enable peripheral clock for communication | ||||
|             crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> { | ||||
|   | ||||
| @@ -1,77 +1,14 @@ | ||||
| use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; | ||||
|  | ||||
| use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; | ||||
| use super::{sealed, RtcCalibrationCyclePeriod}; | ||||
| use crate::pac::rtc::Rtc; | ||||
| use crate::peripherals::RTC; | ||||
| use crate::rtc::sealed::Instance; | ||||
|  | ||||
| impl super::Rtc { | ||||
|     fn unlock_registers() { | ||||
|         // Unlock the backup domain | ||||
|         #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] | ||||
|         { | ||||
|             if !crate::pac::PWR.cr1().read().dbp() { | ||||
|                 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); | ||||
|                 while !crate::pac::PWR.cr1().read().dbp() {} | ||||
|             } | ||||
|         } | ||||
|         #[cfg(any(rcc_wl5, rcc_wle))] | ||||
|         { | ||||
|             use crate::pac::pwr::vals::Dbp; | ||||
|  | ||||
|             if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED { | ||||
|                 crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); | ||||
|                 while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     pub(crate) fn set_clock_source(clock_source: RtcClockSource) { | ||||
|         let clock_source = clock_source as u8; | ||||
|         #[cfg(not(any(rcc_wl5, rcc_wle)))] | ||||
|         let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); | ||||
|  | ||||
|         Self::unlock_registers(); | ||||
|  | ||||
|         crate::pac::RCC.bdcr().modify(|w| { | ||||
|             // Select RTC source | ||||
|             w.set_rtcsel(clock_source); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     pub(super) fn enable() { | ||||
|         let bdcr = crate::pac::RCC.bdcr(); | ||||
|  | ||||
|         let reg = bdcr.read(); | ||||
|         assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | ||||
|  | ||||
|         if !reg.rtcen() { | ||||
|             Self::unlock_registers(); | ||||
|  | ||||
|             bdcr.modify(|w| w.set_bdrst(true)); | ||||
|  | ||||
|             bdcr.modify(|w| { | ||||
|                 // Reset | ||||
|                 w.set_bdrst(false); | ||||
|  | ||||
|                 w.set_rtcen(true); | ||||
|                 w.set_rtcsel(reg.rtcsel()); | ||||
|  | ||||
|                 // Restore bcdr | ||||
|                 w.set_lscosel(reg.lscosel()); | ||||
|                 w.set_lscoen(reg.lscoen()); | ||||
|  | ||||
|                 w.set_lseon(reg.lseon()); | ||||
|                 w.set_lsedrv(reg.lsedrv()); | ||||
|                 w.set_lsebyp(reg.lsebyp()); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Applies the RTC config | ||||
|     /// It this changes the RTC clock source the time will be reset | ||||
|     pub(super) fn configure(&mut self, rtc_config: RtcConfig) { | ||||
|     pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { | ||||
|         self.write(true, |rtc| { | ||||
|             rtc.cr().modify(|w| { | ||||
|                 w.set_fmt(Fmt::TWENTYFOURHOUR); | ||||
| @@ -80,8 +17,8 @@ impl super::Rtc { | ||||
|             }); | ||||
|  | ||||
|             rtc.prer().modify(|w| { | ||||
|                 w.set_prediv_s(rtc_config.sync_prescaler); | ||||
|                 w.set_prediv_a(rtc_config.async_prescaler); | ||||
|                 w.set_prediv_s(sync_psc); | ||||
|                 w.set_prediv_a(async_psc); | ||||
|             }); | ||||
|  | ||||
|             // TODO: configuration for output pins | ||||
| @@ -191,6 +128,23 @@ impl super::Rtc { | ||||
| impl sealed::Instance for crate::peripherals::RTC { | ||||
|     const BACKUP_REGISTER_COUNT: usize = 32; | ||||
|  | ||||
|     fn enable_peripheral_clk() { | ||||
|         #[cfg(any(rcc_wle, rcc_wl5, rcc_g4))] | ||||
|         { | ||||
|             // enable peripheral clock for communication | ||||
|             crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); | ||||
|         } | ||||
|  | ||||
|         #[cfg(rcc_g0)] | ||||
|         { | ||||
|             // enable peripheral clock for communication | ||||
|             crate::pac::RCC.apbenr1().modify(|w| w.set_rtcapben(true)); | ||||
|         } | ||||
|  | ||||
|         // read to allow the pwr clock to enable | ||||
|         crate::pac::PWR.cr1().read(); | ||||
|     } | ||||
|  | ||||
|     fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> { | ||||
|         #[allow(clippy::if_same_then_else)] | ||||
|         if register < Self::BACKUP_REGISTER_COUNT { | ||||
|   | ||||
| @@ -33,6 +33,8 @@ impl<T: Instance> InterruptHandler<T> { | ||||
|             w.set_dtimeoutie(enable); | ||||
|             w.set_dataendie(enable); | ||||
|  | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             w.set_stbiterre(enable); | ||||
|             #[cfg(sdmmc_v2)] | ||||
|             w.set_dabortie(enable); | ||||
|         }); | ||||
| @@ -102,6 +104,8 @@ pub enum Error { | ||||
|     BadClock, | ||||
|     SignalingSwitchFailed, | ||||
|     PeripheralBusy, | ||||
|     #[cfg(sdmmc_v1)] | ||||
|     StBitErr, | ||||
| } | ||||
|  | ||||
| /// A SD command | ||||
| @@ -707,9 +711,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|  | ||||
|             if status.dcrcfail() { | ||||
|                 return Poll::Ready(Err(Error::Crc)); | ||||
|             } else if status.dtimeout() { | ||||
|             } | ||||
|             if status.dtimeout() { | ||||
|                 return Poll::Ready(Err(Error::Timeout)); | ||||
|             } else if status.dataend() { | ||||
|             } | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             if status.stbiterr() { | ||||
|                 return Poll::Ready(Err(Error::StBitErr)); | ||||
|             } | ||||
|             if status.dataend() { | ||||
|                 return Poll::Ready(Ok(())); | ||||
|             } | ||||
|             Poll::Pending | ||||
| @@ -782,9 +792,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|  | ||||
|             if status.dcrcfail() { | ||||
|                 return Poll::Ready(Err(Error::Crc)); | ||||
|             } else if status.dtimeout() { | ||||
|             } | ||||
|             if status.dtimeout() { | ||||
|                 return Poll::Ready(Err(Error::Timeout)); | ||||
|             } else if status.dataend() { | ||||
|             } | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             if status.stbiterr() { | ||||
|                 return Poll::Ready(Err(Error::StBitErr)); | ||||
|             } | ||||
|             if status.dataend() { | ||||
|                 return Poll::Ready(Ok(())); | ||||
|             } | ||||
|             Poll::Pending | ||||
| @@ -836,6 +852,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|             w.set_dataendc(true); | ||||
|             w.set_dbckendc(true); | ||||
|             w.set_sdioitc(true); | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             w.set_stbiterrc(true); | ||||
|  | ||||
|             #[cfg(sdmmc_v2)] | ||||
|             { | ||||
| @@ -873,9 +891,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|  | ||||
|             if status.dcrcfail() { | ||||
|                 return Poll::Ready(Err(Error::Crc)); | ||||
|             } else if status.dtimeout() { | ||||
|             } | ||||
|             if status.dtimeout() { | ||||
|                 return Poll::Ready(Err(Error::Timeout)); | ||||
|             } else if status.dataend() { | ||||
|             } | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             if status.stbiterr() { | ||||
|                 return Poll::Ready(Err(Error::StBitErr)); | ||||
|             } | ||||
|             if status.dataend() { | ||||
|                 return Poll::Ready(Ok(())); | ||||
|             } | ||||
|             Poll::Pending | ||||
| @@ -1156,9 +1180,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|  | ||||
|             if status.dcrcfail() { | ||||
|                 return Poll::Ready(Err(Error::Crc)); | ||||
|             } else if status.dtimeout() { | ||||
|             } | ||||
|             if status.dtimeout() { | ||||
|                 return Poll::Ready(Err(Error::Timeout)); | ||||
|             } else if status.dataend() { | ||||
|             } | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             if status.stbiterr() { | ||||
|                 return Poll::Ready(Err(Error::StBitErr)); | ||||
|             } | ||||
|             if status.dataend() { | ||||
|                 return Poll::Ready(Ok(())); | ||||
|             } | ||||
|             Poll::Pending | ||||
| @@ -1207,9 +1237,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||||
|  | ||||
|             if status.dcrcfail() { | ||||
|                 return Poll::Ready(Err(Error::Crc)); | ||||
|             } else if status.dtimeout() { | ||||
|             } | ||||
|             if status.dtimeout() { | ||||
|                 return Poll::Ready(Err(Error::Timeout)); | ||||
|             } else if status.dataend() { | ||||
|             } | ||||
|             #[cfg(sdmmc_v1)] | ||||
|             if status.stbiterr() { | ||||
|                 return Poll::Ready(Err(Error::StBitErr)); | ||||
|             } | ||||
|             if status.dataend() { | ||||
|                 return Poll::Ready(Ok(())); | ||||
|             } | ||||
|             Poll::Pending | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user