Compare commits
	
		
			171 Commits
		
	
	
		
			embassy-ma
			...
			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! |     echo Got teleprobe token! | ||||||
|     export TELEPROBE_HOST=https://teleprobe.embassy.dev |     export TELEPROBE_HOST=https://teleprobe.embassy.dev | ||||||
|     export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) |     export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) | ||||||
|  |     export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json | ||||||
| fi | fi | ||||||
|  |  | ||||||
| hashtime restore /ci/cache/filetime.json || true | 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,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 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 \ | ||||||
|  |     --- 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-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-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 \ |     --- 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/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/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/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/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/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 \ |     --- 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/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/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/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 --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ | ||||||
|     $BUILD_EXTRA |     $BUILD_EXTRA | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ log = ["dep:log"] | |||||||
| firmware-logs = [] | firmware-logs = [] | ||||||
|  |  | ||||||
| [dependencies] | [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-sync = { version = "0.2.0", path = "../embassy-sync"} | ||||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | ||||||
| @@ -31,4 +31,4 @@ num_enum = { version = "0.5.7", default-features = false } | |||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/" | src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/" | ||||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/" | ||||||
| target = "thumbv6m-none-eabi" | target = "thumbv6m-none-eabi" | ||||||
| features = ["defmt", "firmware-logs"] | features = ["defmt", "firmware-logs"] | ||||||
|   | |||||||
| @@ -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_DOWN: u32 = 3; | ||||||
| pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; | pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; | ||||||
| pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30; | 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_ANTDIV: u32 = 64; | ||||||
| pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; | pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; | ||||||
| pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; | pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; | ||||||
|   | |||||||
| @@ -124,7 +124,7 @@ impl<'a> Control<'a> { | |||||||
|         Timer::after(Duration::from_millis(100)).await; |         Timer::after(Duration::from_millis(100)).await; | ||||||
|  |  | ||||||
|         // set wifi up |         // set wifi up | ||||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; |         self.up().await; | ||||||
|  |  | ||||||
|         Timer::after(Duration::from_millis(100)).await; |         Timer::after(Duration::from_millis(100)).await; | ||||||
|  |  | ||||||
| @@ -138,6 +138,16 @@ impl<'a> Control<'a> { | |||||||
|         debug!("INIT DONE"); |         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) { |     pub async fn set_power_management(&mut self, mode: PowerManagementMode) { | ||||||
|         // power save mode |         // power save mode | ||||||
|         let mode_num = mode.mode(); |         let mode_num = mode.mode(); | ||||||
| @@ -256,13 +266,13 @@ impl<'a> Control<'a> { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Temporarily set wifi down |         // Temporarily set wifi down | ||||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; |         self.down().await; | ||||||
|  |  | ||||||
|         // Turn off APSTA mode |         // Turn off APSTA mode | ||||||
|         self.set_iovar_u32("apsta", 0).await; |         self.set_iovar_u32("apsta", 0).await; | ||||||
|  |  | ||||||
|         // Set wifi up again |         // Set wifi up again | ||||||
|         self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; |         self.up().await; | ||||||
|  |  | ||||||
|         // Turn on AP mode |         // Turn on AP mode | ||||||
|         self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; |         self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; | ||||||
| @@ -423,6 +433,11 @@ impl<'a> Control<'a> { | |||||||
|             events: &self.events, |             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> { | pub struct Scanner<'a> { | ||||||
|   | |||||||
| @@ -83,14 +83,17 @@ macro_rules! todo { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(not(feature = "defmt"))] | ||||||
| macro_rules! unreachable { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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> { | impl<'a> Debug for Bytes<'a> { | ||||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ use ioctl::IoctlState; | |||||||
|  |  | ||||||
| use crate::bus::Bus; | use crate::bus::Bus; | ||||||
| pub use crate::bus::SpiBusCyw43; | 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::runner::Runner; | ||||||
| pub use crate::structs::BssInfo; | pub use crate::structs::BssInfo; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | [dependencies] | ||||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } | embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } | ||||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | 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 = [ | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ | ||||||
|     "unproven", |     "unproven", | ||||||
| ] } | ] } | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ rtos-trace = { version = "0.1.2", optional = true } | |||||||
|  |  | ||||||
| futures-util = { version = "0.3.17", default-features = false } | futures-util = { version = "0.3.17", default-features = false } | ||||||
| embassy-macros = { version = "0.2.1", path = "../embassy-macros" } | 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" | atomic-polyfill = "1.0.1" | ||||||
| critical-section = "1.1" | critical-section = "1.1" | ||||||
| static_cell = "1.1" | static_cell = "1.1" | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| const THREAD_PENDER: usize = usize::MAX; |  | ||||||
|  |  | ||||||
| #[export_name = "__pender"] | #[export_name = "__pender"] | ||||||
| #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] | #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] | ||||||
| fn __pender(context: *mut ()) { | fn __pender(context: *mut ()) { | ||||||
| @@ -48,13 +46,14 @@ fn __pender(context: *mut ()) { | |||||||
| pub use thread::*; | pub use thread::*; | ||||||
| #[cfg(feature = "executor-thread")] | #[cfg(feature = "executor-thread")] | ||||||
| mod thread { | mod thread { | ||||||
|  |     pub(super) const THREAD_PENDER: usize = usize::MAX; | ||||||
|  |  | ||||||
|     use core::arch::asm; |     use core::arch::asm; | ||||||
|     use core::marker::PhantomData; |     use core::marker::PhantomData; | ||||||
|  |  | ||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|     pub use embassy_macros::main_cortex_m as main; |     pub use embassy_macros::main_cortex_m as main; | ||||||
|  |  | ||||||
|     use crate::arch::THREAD_PENDER; |  | ||||||
|     use crate::{raw, Spawner}; |     use crate::{raw, Spawner}; | ||||||
|  |  | ||||||
|     /// Thread mode executor, using WFE/SEV. |     /// Thread mode executor, using WFE/SEV. | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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 } | defmt = { version = "0.3", optional = true } | ||||||
| log = { version = "0.4.14", 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-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | ||||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||||
| @@ -31,4 +31,4 @@ lora-phy = { version = "1" } | |||||||
| lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } | lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } | ||||||
|  |  | ||||||
| [patch.crates-io] | [patch.crates-io] | ||||||
| lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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] | [package] | ||||||
| name = "embassy-net-adin1110" | name = "embassy-net-adin1110" | ||||||
| version = "0.1.0" | version = "0.2.0" | ||||||
| description = "embassy-net driver for the ADIN1110 ethernet chip" | description = "embassy-net driver for the ADIN1110 ethernet chip" | ||||||
| keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"] | keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"] | ||||||
| categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] | categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] | ||||||
| @@ -12,30 +12,31 @@ edition = "2021" | |||||||
| [dependencies] | [dependencies] | ||||||
| heapless = "0.7.16" | heapless = "0.7.16" | ||||||
| defmt = { version = "0.3", optional = true } | 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-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||||
| embedded-hal-async = { 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"] } | 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-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" } | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||||
| bitfield = "0.14.0" | bitfield = "0.14.0" | ||||||
|  |  | ||||||
|  |  | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| # reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged. | # 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/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"] } | embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] } | ||||||
| crc = "3.0.1" | crc = "3.0.1" | ||||||
| env_logger = "0.10" | env_logger = "0.10" | ||||||
| critical-section = { version = "1.1.1", features = ["std"] } | critical-section = { version = "1.1.2", features = ["std"] } | ||||||
| futures-test = "0.3.17" | futures-test = "0.3.28" | ||||||
|  |  | ||||||
| [features] | [features] | ||||||
| default = [ ] | default = [ ] | ||||||
| defmt = [ "dep:defmt" ] | defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ] | ||||||
|  | log = ["dep:log"] | ||||||
|  |  | ||||||
| [package.metadata.embassy_docs] | [package.metadata.embassy_docs] | ||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/" | 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/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/" | ||||||
| target = "thumbv7em-none-eabi" | target = "thumbv7em-none-eabi" | ||||||
|  | features = ["defmt"] | ||||||
|   | |||||||
| @@ -30,8 +30,8 @@ Currently only `Generic` SPI with or without CRC is supported. | |||||||
|  |  | ||||||
| ## Hardware | ## 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. | - 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`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** | - [`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 | ## 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` | $ `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 | ## License | ||||||
|  |  | ||||||
| This work is licensed under either of | This work is licensed under either of | ||||||
|   | |||||||
| @@ -257,29 +257,30 @@ pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [ | |||||||
|     0x2D02_EF8D, |     0x2D02_EF8D, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | /// Generate Ethernet Frame Check Sequence | ||||||
| #[allow(non_camel_case_types)] | #[allow(non_camel_case_types)] | ||||||
| #[derive(Debug)] | #[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; |     pub const CRC32_OK: u32 = 0x2144_df1c; | ||||||
|  |  | ||||||
|     #[must_use] |     #[must_use] | ||||||
|     pub fn new(data: &[u8]) -> Self { |     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; |             let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; | ||||||
|             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) |             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) | ||||||
|         }) ^ u32::MAX; |         }) ^ u32::MAX; | ||||||
|         Self(fsc) |         Self(fcs) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[must_use] |     #[must_use] | ||||||
|     pub fn update(self, data: &[u8]) -> Self { |     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; |             let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; | ||||||
|             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) |             CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) | ||||||
|         }) ^ u32::MAX; |         }) ^ u32::MAX; | ||||||
|         Self(fsc) |         Self(fcs) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[must_use] |     #[must_use] | ||||||
| @@ -319,24 +320,24 @@ mod tests { | |||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         // Packet A |         // 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(); |         let crc_bytes = own_crc.hton_bytes(); | ||||||
|         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); |         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); | ||||||
|         assert_eq!(&crc_bytes, &packet_a[60..64]); |         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); |         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 |         // 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(); |         let crc_bytes = own_crc.hton_bytes(); | ||||||
|         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); |         println!("{:08x} {:02x?}", own_crc.0, crc_bytes); | ||||||
|         assert_eq!(&crc_bytes, &packet_b[60..64]); |         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); |         println!("{:08x}", own_crc.0); | ||||||
|         assert_eq!(own_crc.0, ETH_FSC::CRC32_OK); |         assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -349,9 +350,9 @@ mod tests { | |||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         let (part_a, part_b) = full_data.split_at(16); |         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); |         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, |     PostReadIncAddr = 0b10 << 14, | ||||||
|     Read = 0b11 << 14, |     Read = 0b11 << 14, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// `MdioBus` trait | /// `MdioBus` trait | ||||||
| /// Driver needs to implement the Clause 22 | /// Driver needs to implement the Clause 22 | ||||||
| /// Optional Clause 45 is the device supports this. | /// 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 { | pub trait MdioBus { | ||||||
|     type Error; |     type Error; | ||||||
|  |  | ||||||
| @@ -87,89 +88,89 @@ pub trait MdioBus { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // #[cfg(test)] | #[cfg(test)] | ||||||
| // mod tests { | mod tests { | ||||||
| //     use core::convert::Infallible; |     use core::convert::Infallible; | ||||||
|  |  | ||||||
| //     use super::{MdioBus, PhyAddr, RegC22, RegVal}; |     use super::{MdioBus, PhyAddr, RegC22, RegVal}; | ||||||
|  |  | ||||||
| //     #[derive(Debug, PartialEq, Eq)] |     #[derive(Debug, PartialEq, Eq)] | ||||||
| //     enum A { |     enum A { | ||||||
| //         Read(PhyAddr, RegC22), |         Read(PhyAddr, RegC22), | ||||||
| //         Write(PhyAddr, RegC22, RegVal), |         Write(PhyAddr, RegC22, RegVal), | ||||||
| //     } |     } | ||||||
|  |  | ||||||
| //     struct MockMdioBus(Vec<A>); |     struct MockMdioBus(Vec<A>); | ||||||
|  |  | ||||||
| //     impl MockMdioBus { |     impl MockMdioBus { | ||||||
| //         pub fn clear(&mut self) { |         pub fn clear(&mut self) { | ||||||
| //             self.0.clear(); |             self.0.clear(); | ||||||
| //         } |         } | ||||||
| //     } |     } | ||||||
|  |  | ||||||
| //     impl MdioBus for MockMdioBus { |     impl MdioBus for MockMdioBus { | ||||||
| //         type Error = Infallible; |         type Error = Infallible; | ||||||
|  |  | ||||||
| //         fn write_cl22( |         async fn write_cl22( | ||||||
| //             &mut self, |             &mut self, | ||||||
| //             phy_id: super::PhyAddr, |             phy_id: super::PhyAddr, | ||||||
| //             reg: super::RegC22, |             reg: super::RegC22, | ||||||
| //             reg_val: super::RegVal, |             reg_val: super::RegVal, | ||||||
| //         ) -> Result<(), Self::Error> { |         ) -> Result<(), Self::Error> { | ||||||
| //             self.0.push(A::Write(phy_id, reg, reg_val)); |             self.0.push(A::Write(phy_id, reg, reg_val)); | ||||||
| //             Ok(()) |             Ok(()) | ||||||
| //         } |         } | ||||||
|  |  | ||||||
| //         fn read_cl22( |         async fn read_cl22( | ||||||
| //             &mut self, |             &mut self, | ||||||
| //             phy_id: super::PhyAddr, |             phy_id: super::PhyAddr, | ||||||
| //             reg: super::RegC22, |             reg: super::RegC22, | ||||||
| //         ) -> Result<super::RegVal, Self::Error> { |         ) -> Result<super::RegVal, Self::Error> { | ||||||
| //             self.0.push(A::Read(phy_id, reg)); |             self.0.push(A::Read(phy_id, reg)); | ||||||
| //             Ok(0) |             Ok(0) | ||||||
| //         } |         } | ||||||
| //     } |     } | ||||||
|  |  | ||||||
| //     #[test] |     #[futures_test::test] | ||||||
| //     fn read_test() { |     async fn read_test() { | ||||||
| //         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); |         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||||
|  |  | ||||||
| //         mdiobus.clear(); |         mdiobus.clear(); | ||||||
| //         mdiobus.read_cl22(0x01, 0x00).unwrap(); |         mdiobus.read_cl22(0x01, 0x00).await.unwrap(); | ||||||
| //         assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]); |         assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]); | ||||||
|  |  | ||||||
| //         mdiobus.clear(); |         mdiobus.clear(); | ||||||
| //         mdiobus.read_cl45(0x01, (0xBB, 0x1234)).unwrap(); |         mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.unwrap(); | ||||||
| //         assert_eq!( |         assert_eq!( | ||||||
| //             mdiobus.0, |             mdiobus.0, | ||||||
| //             vec![ |             vec![ | ||||||
| //                 #[allow(clippy::identity_op)] |                 #[allow(clippy::identity_op)] | ||||||
| //                 A::Write(0x01, 13, (0b00 << 14) | 27), |                 A::Write(0x01, 13, (0b00 << 14) | 27), | ||||||
| //                 A::Write(0x01, 14, 0x1234), |                 A::Write(0x01, 14, 0x1234), | ||||||
| //                 A::Write(0x01, 13, (0b11 << 14) | 27), |                 A::Write(0x01, 13, (0b11 << 14) | 27), | ||||||
| //                 A::Read(0x01, 14) |                 A::Read(0x01, 14) | ||||||
| //             ] |             ] | ||||||
| //         ); |         ); | ||||||
| //     } |     } | ||||||
|  |  | ||||||
| //     #[test] |     #[futures_test::test] | ||||||
| //     fn write_test() { |     async fn write_test() { | ||||||
| //         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); |         let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); | ||||||
|  |  | ||||||
| //         mdiobus.clear(); |         mdiobus.clear(); | ||||||
| //         mdiobus.write_cl22(0x01, 0x00, 0xABCD).unwrap(); |         mdiobus.write_cl22(0x01, 0x00, 0xABCD).await.unwrap(); | ||||||
| //         assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]); |         assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]); | ||||||
|  |  | ||||||
| //         mdiobus.clear(); |         mdiobus.clear(); | ||||||
| //         mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).unwrap(); |         mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.unwrap(); | ||||||
| //         assert_eq!( |         assert_eq!( | ||||||
| //             mdiobus.0, |             mdiobus.0, | ||||||
| //             vec![ |             vec![ | ||||||
| //                 A::Write(0x01, 13, 27), |                 A::Write(0x01, 13, 27), | ||||||
| //                 A::Write(0x01, 14, 0x1234), |                 A::Write(0x01, 14, 0x1234), | ||||||
| //                 A::Write(0x01, 13, (0b01 << 14) | 27), |                 A::Write(0x01, 13, (0b01 << 14) | 27), | ||||||
| //                 A::Write(0x01, 14, 0xABCD) |                 A::Write(0x01, 14, 0xABCD) | ||||||
| //             ] |             ] | ||||||
| //         ); |         ); | ||||||
| //     } |     } | ||||||
| // } | } | ||||||
|   | |||||||
| @@ -111,6 +111,7 @@ pub mod RegsC45 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// 10-BASE-T1x PHY functions. | ||||||
| pub struct Phy10BaseT1x(u8); | pub struct Phy10BaseT1x(u8); | ||||||
|  |  | ||||||
| impl Default for Phy10BaseT1x { | impl Default for Phy10BaseT1x { | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | use core::fmt::{Debug, Display}; | ||||||
|  |  | ||||||
| use bitfield::{bitfield, bitfield_bitrange, bitfield_fields}; | use bitfield::{bitfield, bitfield_bitrange, bitfield_fields}; | ||||||
|  |  | ||||||
| #[allow(non_camel_case_types)] | #[allow(non_camel_case_types)] | ||||||
| @@ -34,6 +36,12 @@ pub enum SpiRegisters { | |||||||
|     RX = 0x91, |     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 { | impl From<SpiRegisters> for u16 { | ||||||
|     fn from(val: SpiRegisters) -> Self { |     fn from(val: SpiRegisters) -> Self { | ||||||
|         val as u16 |         val as u16 | ||||||
| @@ -68,7 +76,7 @@ impl From<u16> for SpiRegisters { | |||||||
|             0x73 => Self::ADDR_MSK_UPR1, |             0x73 => Self::ADDR_MSK_UPR1, | ||||||
|             0x90 => Self::RX_FSIZE, |             0x90 => Self::RX_FSIZE, | ||||||
|             0x91 => Self::RX, |             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; |     pub sdf_detect_src, set_sdf_detect_src : 7; | ||||||
|     /// Statistics Clear on Reading |     /// Statistics Clear on Reading | ||||||
|     pub stats_clr_on_rd, set_stats_clr_on_rd : 6; |     pub stats_clr_on_rd, set_stats_clr_on_rd : 6; | ||||||
|     /// Enable CRC Append |     /// Enable SPI CRC | ||||||
|     pub crc_append, set_crc_append : 5; |     pub crc_append, set_crc_append : 5; | ||||||
|     /// Admit Frames with IFG Errors on Port 1 (P1) |     /// Admit Frames with IFG Errors on Port 1 (P1) | ||||||
|     pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4; |     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, |             26 => LedFunc::Clk25Ref, | ||||||
|             27 => LedFunc::TxTCLK, |             27 => LedFunc::TxTCLK, | ||||||
|             28 => LedFunc::Clk120MHz, |             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, |             0 => LedPol::AutoSense, | ||||||
|             1 => LedPol::ActiveHigh, |             1 => LedPol::ActiveHigh, | ||||||
|             2 => LedPol::ActiveLow, |             2 => LedPol::ActiveLow, | ||||||
|             e => panic!("Invalid value {e}"), |             e => panic!("Invalid value {}", e), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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::raw::NoopRawMutex; | ||||||
| use embassy_sync::blocking_mutex::Mutex; | use embassy_sync::blocking_mutex::Mutex; | ||||||
| use embassy_sync::waitqueue::WakerRegistration; | use embassy_sync::waitqueue::WakerRegistration; | ||||||
|  | use embassy_sync::zerocopy_channel; | ||||||
|  |  | ||||||
| pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||||||
|     rx: [PacketBuf<MTU>; N_RX], |     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] { |     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] |         &mut p.buf[..p.len] | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { |     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]) |         Some(&mut p.buf[..p.len]) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { |     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::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), | ||||||
|             Poll::Pending => Poll::Pending, |             Poll::Pending => Poll::Pending, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn tx_done(&mut self) { |     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> { | impl<'d, const MTU: usize> TxRunner<'d, MTU> { | ||||||
|     pub async fn tx_buf(&mut self) -> &mut [u8] { |     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] |         &mut p.buf[..p.len] | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { |     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]) |         Some(&mut p.buf[..p.len]) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { |     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::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), | ||||||
|             Poll::Pending => Poll::Pending, |             Poll::Pending => Poll::Pending, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn tx_done(&mut self) { |     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 ; |     type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; | ||||||
|  |  | ||||||
|     fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |     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() })) |             Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) | ||||||
|         } else { |         } else { | ||||||
|             None |             None | ||||||
| @@ -337,9 +338,9 @@ impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { | |||||||
|         F: FnOnce(&mut [u8]) -> R, |         F: FnOnce(&mut [u8]) -> R, | ||||||
|     { |     { | ||||||
|         // NOTE(unwrap): we checked the queue wasn't full when creating the token. |         // 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]); |         let r = f(&mut pkt.buf[..pkt.len]); | ||||||
|         self.rx.recv_done(); |         self.rx.receive_done(); | ||||||
|         r |         r | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -361,215 +362,3 @@ impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { | |||||||
|         r |         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 = { version = "1.0.0-rc.1" } | ||||||
| embedded-hal-async = { 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-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" } | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||||
|  |  | ||||||
| defmt = { version = "0.3", optional = true } | defmt = { version = "0.3", optional = true } | ||||||
| @@ -20,4 +20,4 @@ log = { version = "0.4.14", optional = true } | |||||||
| [package.metadata.embassy_docs] | [package.metadata.embassy_docs] | ||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" | ||||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" | ||||||
| target = "thumbv7em-none-eabi" | target = "thumbv7em-none-eabi" | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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 } | defmt = { version = "0.3", optional = true } | ||||||
| log = { version = "0.4.14", 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-sync = { version = "0.2.0", path = "../embassy-sync"} | ||||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | ||||||
| @@ -23,4 +23,4 @@ heapless = "0.7.16" | |||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" | ||||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" | ||||||
| target = "thumbv7em-none-eabi" | target = "thumbv7em-none-eabi" | ||||||
| features = ["defmt"] | features = ["defmt"] | ||||||
|   | |||||||
| @@ -19,6 +19,8 @@ pub struct Control<'a> { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[allow(unused)] | #[allow(unused)] | ||||||
|  | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
| enum WifiMode { | enum WifiMode { | ||||||
|     None = 0, |     None = 0, | ||||||
|     Sta = 1, |     Sta = 1, | ||||||
| @@ -26,6 +28,18 @@ enum WifiMode { | |||||||
|     ApSta = 3, |     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 { | macro_rules! ioctl { | ||||||
|     ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { |     ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { | ||||||
|         let mut msg = proto::CtrlMsg { |         let mut msg = proto::CtrlMsg { | ||||||
| @@ -34,7 +48,9 @@ macro_rules! ioctl { | |||||||
|             payload: Some(proto::CtrlMsgPayload::$req_variant($req)), |             payload: Some(proto::CtrlMsgPayload::$req_variant($req)), | ||||||
|         }; |         }; | ||||||
|         $self.ioctl(&mut msg).await?; |         $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"); |             warn!("unexpected response variant"); | ||||||
|             return Err(Error::Internal); |             return Err(Error::Internal); | ||||||
|         }; |         }; | ||||||
| @@ -66,6 +82,19 @@ impl<'a> Control<'a> { | |||||||
|         Ok(()) |         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> { |     pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { | ||||||
|         let req = proto::CtrlMsgReqConnectAp { |         let req = proto::CtrlMsgReqConnectAp { | ||||||
|             ssid: String::from(ssid), |             ssid: String::from(ssid), | ||||||
| @@ -98,27 +127,7 @@ impl<'a> Control<'a> { | |||||||
|             mode: WifiMode::Sta as _, |             mode: WifiMode::Sta as _, | ||||||
|         }; |         }; | ||||||
|         ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); |         ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); | ||||||
|  |         parse_mac(&resp.mac) | ||||||
|         // 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) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { |     async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { | ||||||
| @@ -167,3 +176,35 @@ impl<'a> Control<'a> { | |||||||
|         Ok(()) |         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")] | #[cfg(feature = "defmt")] | ||||||
| macro_rules! unreachable { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($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> { | impl<'a> Debug for Bytes<'a> { | ||||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |     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" } | embedded-io-async = { version = "0.5.0" } | ||||||
| embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } | embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } | ||||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 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" } | embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||||
|  |  | ||||||
| [package.metadata.embassy_docs] | [package.metadata.embassy_docs] | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ macro_rules! unreachable { | |||||||
| #[cfg(feature = "defmt")] | #[cfg(feature = "defmt")] | ||||||
| macro_rules! unreachable { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($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> { | impl<'a> Debug for Bytes<'a> { | ||||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||||
|   | |||||||
| @@ -53,6 +53,8 @@ pub enum RunError<E> { | |||||||
|     WriteZero, |     WriteZero, | ||||||
|     /// Writing to the serial got EOF. |     /// Writing to the serial got EOF. | ||||||
|     Eof, |     Eof, | ||||||
|  |     /// PPP protocol was terminated by the peer | ||||||
|  |     Terminated, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<E> From<WriteAllError<E>> for RunError<E> { | impl<E> From<WriteAllError<E>> for RunError<E> { | ||||||
| @@ -128,6 +130,9 @@ impl<'d> Runner<'d> { | |||||||
|  |  | ||||||
|                     let status = ppp.status(); |                     let status = ppp.status(); | ||||||
|                     match status.phase { |                     match status.phase { | ||||||
|  |                         ppproto::Phase::Dead => { | ||||||
|  |                             return Err(RunError::Terminated); | ||||||
|  |                         } | ||||||
|                         ppproto::Phase::Open => { |                         ppproto::Phase::Open => { | ||||||
|                             if !was_up { |                             if !was_up { | ||||||
|                                 on_ipv4_up(status.ipv4.unwrap()); |                                 on_ipv4_up(status.ipv4.unwrap()); | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ edition = "2021" | |||||||
| embedded-hal = { version = "1.0.0-rc.1" } | embedded-hal = { version = "1.0.0-rc.1" } | ||||||
| embedded-hal-async = { 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-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" } | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||||
| defmt = { version = "0.3", optional = true } | defmt = { version = "0.3", optional = true } | ||||||
|  |  | ||||||
| @@ -19,4 +19,4 @@ defmt = { version = "0.3", optional = true } | |||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" | ||||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" | ||||||
| target = "thumbv7em-none-eabi" | target = "thumbv7em-none-eabi" | ||||||
| features = ["defmt"] | features = ["defmt"] | ||||||
|   | |||||||
| @@ -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-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" } | embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | ||||||
| embedded-io-async = { version = "0.5.0", optional = true } | 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<'_>)> { |     fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||||||
|         self.inner |         self.inner | ||||||
|             .receive(self.cx.as_deref_mut().unwrap()) |             .receive(unwrap!(self.cx.as_deref_mut())) | ||||||
|             .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) |             .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Construct a transmit token. |     /// Construct a transmit token. | ||||||
|     fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> { |     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. |     /// Get a description of device capabilities. | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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 |     /// # Example | ||||||
|     /// ```rust |     /// ```rust | ||||||
|  |     /// # use embassy_net::Config; | ||||||
|     /// let _cfg = Config::dhcpv4(Default::default()); |     /// let _cfg = Config::dhcpv4(Default::default()); | ||||||
|     /// ``` |     /// ``` | ||||||
|     #[cfg(feature = "dhcpv4")] |     #[cfg(feature = "dhcpv4")] | ||||||
| @@ -226,6 +227,7 @@ struct Inner<D: Driver> { | |||||||
|     static_v6: Option<StaticConfigV6>, |     static_v6: Option<StaticConfigV6>, | ||||||
|     #[cfg(feature = "dhcpv4")] |     #[cfg(feature = "dhcpv4")] | ||||||
|     dhcp_socket: Option<SocketHandle>, |     dhcp_socket: Option<SocketHandle>, | ||||||
|  |     config_waker: WakerRegistration, | ||||||
|     #[cfg(feature = "dns")] |     #[cfg(feature = "dns")] | ||||||
|     dns_socket: SocketHandle, |     dns_socket: SocketHandle, | ||||||
|     #[cfg(feature = "dns")] |     #[cfg(feature = "dns")] | ||||||
| @@ -297,6 +299,7 @@ impl<D: Driver + 'static> Stack<D> { | |||||||
|             static_v6: None, |             static_v6: None, | ||||||
|             #[cfg(feature = "dhcpv4")] |             #[cfg(feature = "dhcpv4")] | ||||||
|             dhcp_socket: None, |             dhcp_socket: None, | ||||||
|  |             config_waker: WakerRegistration::new(), | ||||||
|             #[cfg(feature = "dns")] |             #[cfg(feature = "dns")] | ||||||
|             dns_socket: socket.sockets.add(dns::Socket::new( |             dns_socket: socket.sockets.add(dns::Socket::new( | ||||||
|                 &[], |                 &[], | ||||||
| @@ -363,6 +366,55 @@ impl<D: Driver + 'static> Stack<D> { | |||||||
|         v4_up || v6_up |         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. |     /// Get the current IPv4 configuration. | ||||||
|     /// |     /// | ||||||
|     /// If using DHCP, this will be None if DHCP hasn't been able to |     /// 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 |                 // 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_ignore_naks(c.ignore_naks); | ||||||
|                 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); |                 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); | ||||||
|                 socket.set_ports(c.server_port, c.client_port); |                 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!("   IP address:      {:?}", config.address); | ||||||
|             debug!("   Default gateway: {:?}", config.gateway); |             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(); |             gateway_v4 = config.gateway.into(); | ||||||
|             #[cfg(feature = "dns")] |             #[cfg(feature = "dns")] | ||||||
|             for s in &config.dns_servers { |             for s in &config.dns_servers { | ||||||
|                 debug!("   DNS server:      {:?}", s); |                 debug!("   DNS server:      {:?}", s); | ||||||
|                 dns_servers.push(s.clone().into()).unwrap(); |                 unwrap!(dns_servers.push(s.clone().into()).ok()); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             info!("IPv4: DOWN"); |             info!("IPv4: DOWN"); | ||||||
| @@ -673,12 +725,12 @@ impl<D: Driver + 'static> Inner<D> { | |||||||
|             debug!("   IP address:      {:?}", config.address); |             debug!("   IP address:      {:?}", config.address); | ||||||
|             debug!("   Default gateway: {:?}", config.gateway); |             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(); |             gateway_v6 = config.gateway.into(); | ||||||
|             #[cfg(feature = "dns")] |             #[cfg(feature = "dns")] | ||||||
|             for s in &config.dns_servers { |             for s in &config.dns_servers { | ||||||
|                 debug!("   DNS server:      {:?}", s); |                 debug!("   DNS server:      {:?}", s); | ||||||
|                 dns_servers.push(s.clone().into()).unwrap(); |                 unwrap!(dns_servers.push(s.clone().into()).ok()); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             info!("IPv6: DOWN"); |             info!("IPv6: DOWN"); | ||||||
| @@ -690,13 +742,13 @@ impl<D: Driver + 'static> Inner<D> { | |||||||
|         // Apply gateways |         // Apply gateways | ||||||
|         #[cfg(feature = "proto-ipv4")] |         #[cfg(feature = "proto-ipv4")] | ||||||
|         if let Some(gateway) = gateway_v4 { |         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 { |         } else { | ||||||
|             s.iface.routes_mut().remove_default_ipv4_route(); |             s.iface.routes_mut().remove_default_ipv4_route(); | ||||||
|         } |         } | ||||||
|         #[cfg(feature = "proto-ipv6")] |         #[cfg(feature = "proto-ipv6")] | ||||||
|         if let Some(gateway) = gateway_v6 { |         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 { |         } else { | ||||||
|             s.iface.routes_mut().remove_default_ipv6_route(); |             s.iface.routes_mut().remove_default_ipv6_route(); | ||||||
|         } |         } | ||||||
| @@ -706,6 +758,8 @@ impl<D: Driver + 'static> Inner<D> { | |||||||
|         s.sockets |         s.sockets | ||||||
|             .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket) |             .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket) | ||||||
|             .update_servers(&dns_servers[..]); |             .update_servers(&dns_servers[..]); | ||||||
|  |  | ||||||
|  |         self.config_waker.wake(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { |     fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { | ||||||
|   | |||||||
| @@ -440,7 +440,7 @@ impl<'d> TcpIo<'d> { | |||||||
|                         Poll::Ready(Err(Error::ConnectionReset)) |                         Poll::Ready(Err(Error::ConnectionReset)) | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } 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. |                         // Connection reset. TODO: this can also be timeouts etc, investigate. | ||||||
|                         Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), |                         Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), | ||||||
|                         Ok(r) => Ok(r), |                         Ok(r) => Ok(r), | ||||||
| @@ -468,7 +468,7 @@ impl<'d> TcpIo<'d> { | |||||||
|                         Poll::Ready(Err(Error::ConnectionReset)) |                         Poll::Ready(Err(Error::ConnectionReset)) | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } 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. |                         // Connection reset. TODO: this can also be timeouts etc, investigate. | ||||||
|                         Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { |                         Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { | ||||||
|                             Err(Error::ConnectionReset) |                             Err(Error::ConnectionReset) | ||||||
|   | |||||||
| @@ -29,6 +29,8 @@ pub enum BindError { | |||||||
| pub enum Error { | pub enum Error { | ||||||
|     /// No route to host. |     /// No route to host. | ||||||
|     NoRoute, |     NoRoute, | ||||||
|  |     /// Socket not bound to an outgoing port. | ||||||
|  |     SocketNotBound, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// An UDP socket. | /// An UDP socket. | ||||||
| @@ -155,7 +157,14 @@ impl<'a> UdpSocket<'a> { | |||||||
|                 s.register_send_waker(cx.waker()); |                 s.register_send_waker(cx.waker()); | ||||||
|                 Poll::Pending |                 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 = [] | _gpio-p1 = [] | ||||||
|  |  | ||||||
| [dependencies] | [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-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-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" } | embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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 r = T::regs(); | ||||||
|         let s = T::state(); |         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 { |         if r.events_end.read().bits() != 0 { | ||||||
|             s.end_waker.wake(); |             s.end_waker.wake(); | ||||||
|             r.intenclr.write(|w| w.end().clear()); |             r.intenclr.write(|w| w.end().clear()); | ||||||
| @@ -167,42 +189,10 @@ impl<'d, T: Instance> Spim<'d, T> { | |||||||
|         // Enable SPIM instance. |         // Enable SPIM instance. | ||||||
|         r.enable.write(|w| w.enable().enabled()); |         r.enable.write(|w| w.enable().enabled()); | ||||||
|  |  | ||||||
|         // Configure mode. |         let mut spim = Self { _p: spim }; | ||||||
|         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(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             w |         // Apply runtime peripheral configuration | ||||||
|         }); |         Self::set_config(&mut spim, &config); | ||||||
|  |  | ||||||
|         // 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) }); |  | ||||||
|  |  | ||||||
|         // Disable all events interrupts |         // Disable all events interrupts | ||||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); |         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||||
| @@ -210,7 +200,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||||||
|         T::Interrupt::unpend(); |         T::Interrupt::unpend(); | ||||||
|         unsafe { T::Interrupt::enable() }; |         unsafe { T::Interrupt::enable() }; | ||||||
|  |  | ||||||
|         Self { _p: spim } |         spim | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { |     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(); |         let r = T::regs(); | ||||||
|  |  | ||||||
|         // Set up the DMA write. |         // 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.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. |         // 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.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 |         // Reset and enable the event | ||||||
|         r.events_end.reset(); |         r.events_end.reset(); | ||||||
| @@ -386,18 +395,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) mod sealed { | pub(crate) mod sealed { | ||||||
|  |     #[cfg(feature = "nrf52832")] | ||||||
|  |     use core::sync::atomic::AtomicU8; | ||||||
|  |  | ||||||
|     use embassy_sync::waitqueue::AtomicWaker; |     use embassy_sync::waitqueue::AtomicWaker; | ||||||
|  |  | ||||||
|     use super::*; |     use super::*; | ||||||
|  |  | ||||||
|     pub struct State { |     pub struct State { | ||||||
|         pub end_waker: AtomicWaker, |         pub end_waker: AtomicWaker, | ||||||
|  |         #[cfg(feature = "nrf52832")] | ||||||
|  |         pub rx: AtomicU8, | ||||||
|  |         #[cfg(feature = "nrf52832")] | ||||||
|  |         pub tx: AtomicU8, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     impl State { |     impl State { | ||||||
|         pub const fn new() -> Self { |         pub const fn new() -> Self { | ||||||
|             Self { |             Self { | ||||||
|                 end_waker: AtomicWaker::new(), |                 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. |         // Enable SPIS instance. | ||||||
|         r.enable.write(|w| w.enable().enabled()); |         r.enable.write(|w| w.enable().enabled()); | ||||||
|  |  | ||||||
|         // Configure mode. |         let mut spis = Self { _p: spis }; | ||||||
|         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(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             w |         // Apply runtime peripheral configuration | ||||||
|         }); |         Self::set_config(&mut spis, &config); | ||||||
|  |  | ||||||
|         // 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)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Disable all events interrupts. |         // Disable all events interrupts. | ||||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); |         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||||
| @@ -217,7 +180,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||||||
|         T::Interrupt::unpend(); |         T::Interrupt::unpend(); | ||||||
|         unsafe { T::Interrupt::enable() }; |         unsafe { T::Interrupt::enable() }; | ||||||
|  |  | ||||||
|         Self { _p: spis } |         spis | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { |     fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | ||||||
|   | |||||||
| @@ -57,7 +57,6 @@ impl<'d> Temp<'d> { | |||||||
|     /// ```no_run |     /// ```no_run | ||||||
|     /// use embassy_nrf::{bind_interrupts, temp}; |     /// use embassy_nrf::{bind_interrupts, temp}; | ||||||
|     /// use embassy_nrf::temp::Temp; |     /// use embassy_nrf::temp::Temp; | ||||||
|     /// use embassy_time::{Duration, Timer}; |  | ||||||
|     /// |     /// | ||||||
|     /// bind_interrupts!(struct Irqs { |     /// bind_interrupts!(struct Irqs { | ||||||
|     ///     TEMP => temp::InterruptHandler; |     ///     TEMP => temp::InterruptHandler; | ||||||
|   | |||||||
| @@ -167,9 +167,10 @@ impl<'d, T: Instance> Twim<'d, T> { | |||||||
|         // Enable TWIM instance. |         // Enable TWIM instance. | ||||||
|         r.enable.write(|w| w.enable().enabled()); |         r.enable.write(|w| w.enable().enabled()); | ||||||
|  |  | ||||||
|         // Configure frequency. |         let mut twim = Self { _p: twim }; | ||||||
|         r.frequency |  | ||||||
|             .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); |         // Apply runtime peripheral configuration | ||||||
|  |         Self::set_config(&mut twim, &config); | ||||||
|  |  | ||||||
|         // Disable all events interrupts |         // Disable all events interrupts | ||||||
|         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); |         r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); | ||||||
| @@ -177,7 +178,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||||||
|         T::Interrupt::unpend(); |         T::Interrupt::unpend(); | ||||||
|         unsafe { T::Interrupt::enable() }; |         unsafe { T::Interrupt::enable() }; | ||||||
|  |  | ||||||
|         Self { _p: twim } |         twim | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Set TX buffer, checking that it is in RAM and has suitable length. |     /// 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] | [dependencies] | ||||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | 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-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-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" } | embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||||
|   | |||||||
| @@ -94,6 +94,7 @@ impl ClockConfig { | |||||||
|                     post_div1: 6, |                     post_div1: 6, | ||||||
|                     post_div2: 5, |                     post_div2: 5, | ||||||
|                 }), |                 }), | ||||||
|  |                 delay_multiplier: 128, | ||||||
|             }), |             }), | ||||||
|             ref_clk: RefClkConfig { |             ref_clk: RefClkConfig { | ||||||
|                 src: RefClkSrc::Xosc, |                 src: RefClkSrc::Xosc, | ||||||
| @@ -203,6 +204,7 @@ pub struct XoscConfig { | |||||||
|     pub hz: u32, |     pub hz: u32, | ||||||
|     pub sys_pll: Option<PllConfig>, |     pub sys_pll: Option<PllConfig>, | ||||||
|     pub usb_pll: Option<PllConfig>, |     pub usb_pll: Option<PllConfig>, | ||||||
|  |     pub delay_multiplier: u32, | ||||||
| } | } | ||||||
|  |  | ||||||
| pub struct PllConfig { | pub struct PllConfig { | ||||||
| @@ -363,7 +365,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||||||
|             // start XOSC |             // start XOSC | ||||||
|             // datasheet mentions support for clock inputs into XIN, but doesn't go into |             // 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. |             // 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 { |             let pll_sys_freq = match config.sys_pll { | ||||||
|                 Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), |                 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) |     CLOCKS.rtc.load(Ordering::Relaxed) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn start_xosc(crystal_hz: u32) { | fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { | ||||||
|     pac::XOSC |     pac::XOSC | ||||||
|         .ctrl() |         .ctrl() | ||||||
|         .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); |         .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.startup().write(|w| w.set_delay(startup_delay as u16)); | ||||||
|     pac::XOSC.ctrl().write(|w| { |     pac::XOSC.ctrl().write(|w| { | ||||||
|         w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); |         w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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, |     NoAcknowledge, | ||||||
|     /// The arbitration was lost, e.g. electrical problems with the clock signal |     /// The arbitration was lost, e.g. electrical problems with the clock signal | ||||||
|     ArbitrationLoss, |     ArbitrationLoss, | ||||||
|  |     /// Transmit ended with data still in fifo | ||||||
|  |     TxNotEmpty(u16), | ||||||
|     Other(u32), |     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> { | pub struct I2c<'d, T: Instance, M: Mode> { | ||||||
|     phantom: PhantomData<(&'d mut T, M)>, |     phantom: PhantomData<(&'d mut T, M)>, | ||||||
| @@ -636,6 +638,7 @@ mod eh1 { | |||||||
|                 Self::Abort(AbortReason::NoAcknowledge) => { |                 Self::Abort(AbortReason::NoAcknowledge) => { | ||||||
|                     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) |                     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::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, | ||||||
|                 Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, |                 Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, | ||||||
|                 Self::InvalidWriteBufferLength => 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 { | pub fn i2c_reserved_addr(addr: u16) -> bool { | ||||||
|     (addr & 0x78) == 0 || (addr & 0x78) == 0x78 |     ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 | ||||||
| } | } | ||||||
|  |  | ||||||
| mod sealed { | 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; | mod float; | ||||||
| pub mod gpio; | pub mod gpio; | ||||||
| pub mod i2c; | pub mod i2c; | ||||||
|  | pub mod i2c_slave; | ||||||
| pub mod multicore; | pub mod multicore; | ||||||
| pub mod pwm; | pub mod pwm; | ||||||
| mod reset; | mod reset; | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ features = ["stm32wb55rg"] | |||||||
| [dependencies] | [dependencies] | ||||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } | ||||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | 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-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||||
| embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } | embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } | ||||||
| embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } | embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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 = "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/" | 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 = [ | flavors = [ | ||||||
|     { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, |     { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, | ||||||
|     { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, |     { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, | ||||||
|     { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, |     { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, | ||||||
|     { regex_feature = "stm32f3.*", target = "thumbv7em-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 = "stm32f7.*", target = "thumbv7em-none-eabi" }, | ||||||
|     { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, |     { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, | ||||||
|     { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, |     { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, | ||||||
| @@ -32,7 +32,7 @@ flavors = [ | |||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
| embassy-sync = { version = "0.2.0", path = "../embassy-sync" } | 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-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-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" } | 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 } | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | ||||||
| critical-section = "1.1" | critical-section = "1.1" | ||||||
| atomic-polyfill = "1.0.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" | vcell = "0.1.3" | ||||||
| bxcan = "0.7.0" | bxcan = "0.7.0" | ||||||
| nb = "1.0.0" | nb = "1.0.0" | ||||||
| @@ -77,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||||||
| [build-dependencies] | [build-dependencies] | ||||||
| proc-macro2 = "1.0.36" | proc-macro2 = "1.0.36" | ||||||
| quote = "1.0.15" | 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] | [features] | ||||||
| default = ["rt"] | default = ["rt"] | ||||||
|   | |||||||
| @@ -308,13 +308,11 @@ fn main() { | |||||||
|     // ======== |     // ======== | ||||||
|     // Generate RccPeripheral impls |     // Generate RccPeripheral impls | ||||||
|  |  | ||||||
|  |     let refcounted_peripherals = HashSet::from(["usart", "adc"]); | ||||||
|  |     let mut refcount_statics = HashSet::new(); | ||||||
|  |  | ||||||
|     for p in METADATA.peripherals { |     for p in METADATA.peripherals { | ||||||
|         // generating RccPeripheral impl for H7 ADC3 would result in bad frequency |         if !singletons.contains(&p.name.to_string()) { | ||||||
|         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")) |  | ||||||
|         { |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -344,11 +342,36 @@ fn main() { | |||||||
|                 TokenStream::new() |                 TokenStream::new() | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|  |             let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; | ||||||
|             let pname = format_ident!("{}", p.name); |             let pname = format_ident!("{}", p.name); | ||||||
|             let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); |             let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); | ||||||
|             let en_reg = format_ident!("{}", en.register.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 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! { |             g.extend(quote! { | ||||||
|                 impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { |                 impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { | ||||||
|                     fn frequency() -> crate::time::Hertz { |                     fn frequency() -> crate::time::Hertz { | ||||||
| @@ -356,6 +379,7 @@ fn main() { | |||||||
|                     } |                     } | ||||||
|                     fn enable() { |                     fn enable() { | ||||||
|                         critical_section::with(|_| { |                         critical_section::with(|_| { | ||||||
|  |                             #before_enable | ||||||
|                             #[cfg(feature = "low-power")] |                             #[cfg(feature = "low-power")] | ||||||
|                             crate::rcc::clock_refcount_add(); |                             crate::rcc::clock_refcount_add(); | ||||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); |                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); | ||||||
| @@ -364,6 +388,7 @@ fn main() { | |||||||
|                     } |                     } | ||||||
|                     fn disable() { |                     fn disable() { | ||||||
|                         critical_section::with(|_| { |                         critical_section::with(|_| { | ||||||
|  |                             #before_disable | ||||||
|                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); |                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); | ||||||
|                             #[cfg(feature = "low-power")] |                             #[cfg(feature = "low-power")] | ||||||
|                             crate::rcc::clock_refcount_sub(); |                             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 |     // Generate fns to enable GPIO, DMA in RCC | ||||||
|  |  | ||||||
| @@ -664,6 +702,10 @@ fn main() { | |||||||
|  |  | ||||||
|                 // ADC is special |                 // ADC is special | ||||||
|                 if regs.kind == "adc" { |                 if regs.kind == "adc" { | ||||||
|  |                     if p.rcc.is_none() { | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|                     let peri = format_ident!("{}", p.name); |                     let peri = format_ident!("{}", p.name); | ||||||
|                     let pin_name = format_ident!("{}", pin.pin); |                     let pin_name = format_ident!("{}", pin.pin); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn freq() -> Hertz { |     fn freq() -> Hertz { | ||||||
|         unsafe { get_freqs() }.adc |         unsafe { get_freqs() }.adc.unwrap() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn sample_time_for_us(&self, us: u32) -> SampleTime { |     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] | #![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_f1, path = "f1.rs")] | ||||||
|  | #[cfg_attr(adc_f3, path = "f3.rs")] | ||||||
| #[cfg_attr(adc_v1, path = "v1.rs")] | #[cfg_attr(adc_v1, path = "v1.rs")] | ||||||
| #[cfg_attr(adc_v2, path = "v2.rs")] | #[cfg_attr(adc_v2, path = "v2.rs")] | ||||||
| #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] | #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] | ||||||
| #[cfg_attr(adc_v4, path = "v4.rs")] | #[cfg_attr(adc_v4, path = "v4.rs")] | ||||||
| mod _version; | mod _version; | ||||||
|  |  | ||||||
| #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] | #[cfg(not(any(adc_f1, adc_f3_v2)))] | ||||||
| mod resolution; | mod resolution; | ||||||
| mod sample_time; | mod sample_time; | ||||||
|  |  | ||||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] |  | ||||||
| #[allow(unused)] | #[allow(unused)] | ||||||
|  | #[cfg(not(adc_f3_v2))] | ||||||
| pub use _version::*; | pub use _version::*; | ||||||
| #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] | #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] | ||||||
| pub use resolution::Resolution; | pub use resolution::Resolution; | ||||||
| #[cfg(not(any(adc_f3, adc_f3_v2)))] | #[cfg(not(adc_f3_v2))] | ||||||
| pub use sample_time::SampleTime; | pub use sample_time::SampleTime; | ||||||
|  |  | ||||||
| use crate::peripherals; | use crate::peripherals; | ||||||
| @@ -25,15 +26,17 @@ use crate::peripherals; | |||||||
| pub struct Adc<'d, T: Instance> { | pub struct Adc<'d, T: Instance> { | ||||||
|     #[allow(unused)] |     #[allow(unused)] | ||||||
|     adc: crate::PeripheralRef<'d, T>, |     adc: crate::PeripheralRef<'d, T>, | ||||||
|     #[cfg(not(any(adc_f3, adc_f3_v2)))] |     #[cfg(not(adc_f3_v2))] | ||||||
|     sample_time: SampleTime, |     sample_time: SampleTime, | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) mod sealed { | pub(crate) mod sealed { | ||||||
|     pub trait Instance { |     pub trait Instance { | ||||||
|         fn regs() -> crate::pac::adc::Adc; |         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; |         fn common_regs() -> crate::pac::adccommon::AdcCommon; | ||||||
|  |         #[cfg(adc_f3)] | ||||||
|  |         fn frequency() -> crate::time::Hertz; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub trait AdcPin<T: Instance> { |     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> {} | 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 Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} | ||||||
|  |  | ||||||
| pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | ||||||
| pub trait InternalChannel<T>: sealed::InternalChannel<T> {} | pub trait InternalChannel<T>: sealed::InternalChannel<T> {} | ||||||
|  |  | ||||||
| #[cfg(not(stm32h7))] | #[cfg(not(any(stm32h7, adc_f3, adc_v4)))] | ||||||
| foreach_peripheral!( | foreach_peripheral!( | ||||||
|     (adc, $inst:ident) => { |     (adc, $inst:ident) => { | ||||||
|         impl crate::adc::sealed::Instance for peripherals::$inst { |         impl crate::adc::sealed::Instance for peripherals::$inst { | ||||||
|             fn regs() -> crate::pac::adc::Adc { |             fn regs() -> crate::pac::adc::Adc { | ||||||
|                 crate::pac::$inst |                 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 { |             fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||||||
|                 foreach_peripheral!{ |                 foreach_peripheral!{ | ||||||
|                     (adccommon, $common_inst:ident) => { |                     (adccommon, $common_inst:ident) => { | ||||||
| @@ -74,9 +77,10 @@ foreach_peripheral!( | |||||||
|     }; |     }; | ||||||
| ); | ); | ||||||
|  |  | ||||||
| #[cfg(stm32h7)] | #[cfg(any(stm32h7, adc_f3, adc_v4))] | ||||||
| foreach_peripheral!( | foreach_peripheral!( | ||||||
|     (adc, ADC3) => { |     (adc, ADC3) => { | ||||||
|  |         #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] | ||||||
|         impl crate::adc::sealed::Instance for peripherals::ADC3 { |         impl crate::adc::sealed::Instance for peripherals::ADC3 { | ||||||
|             fn regs() -> crate::pac::adc::Adc { |             fn regs() -> crate::pac::adc::Adc { | ||||||
|                 crate::pac::ADC3 |                 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 {} |         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) => { |     (adc, $inst:ident) => { | ||||||
|         impl crate::adc::sealed::Instance for peripherals::$inst { |         impl crate::adc::sealed::Instance for peripherals::$inst { | ||||||
|             fn regs() -> crate::pac::adc::Adc { |             fn regs() -> crate::pac::adc::Adc { | ||||||
|                 crate::pac::$inst |                 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 { |             fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||||||
|                 foreach_peripheral!{ |                 foreach_peripheral!{ | ||||||
|                     (adccommon, ADC_COMMON) => { |                     (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 {} |         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)] | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||||||
| pub enum Resolution { | pub enum Resolution { | ||||||
|     TwelveBit, |     TwelveBit, | ||||||
| @@ -19,7 +19,7 @@ pub enum Resolution { | |||||||
|  |  | ||||||
| impl Default for Resolution { | impl Default for Resolution { | ||||||
|     fn default() -> Self { |     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 |             Self::TwelveBit | ||||||
|         } |         } | ||||||
| @@ -40,7 +40,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res { | |||||||
|             Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, |             Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, | ||||||
|             Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, |             Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, | ||||||
|             Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, |             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, |             Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -56,7 +56,7 @@ impl Resolution { | |||||||
|             Resolution::TwelveBit => (1 << 12) - 1, |             Resolution::TwelveBit => (1 << 12) - 1, | ||||||
|             Resolution::TenBit => (1 << 10) - 1, |             Resolution::TenBit => (1 << 10) - 1, | ||||||
|             Resolution::EightBit => (1 << 8) - 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, |             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 { | macro_rules! impl_sample_time { | ||||||
|     ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { |     ($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.")] |         #[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) |         ("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. | /// configuration. | ||||||
| fn enable() { | fn enable() { | ||||||
|     critical_section::with(|_| { |     critical_section::with(|_| { | ||||||
|         #[cfg(stm32h7)] |         #[cfg(any(stm32h7, stm32wl))] | ||||||
|         crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); |         crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); | ||||||
|         #[cfg(stm32g0)] |         #[cfg(stm32g0)] | ||||||
|         crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true)); |         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> AdcPin<T> for VrefInt {} | ||||||
| impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { | impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         #[cfg(not(stm32g0))] |         #[cfg(not(adc_g0))] | ||||||
|         let val = 0; |         let val = 0; | ||||||
|         #[cfg(stm32g0)] |         #[cfg(adc_g0)] | ||||||
|         let val = 13; |         let val = 13; | ||||||
|         val |         val | ||||||
|     } |     } | ||||||
| @@ -38,9 +38,9 @@ pub struct Temperature; | |||||||
| impl<T: Instance> AdcPin<T> for Temperature {} | impl<T: Instance> AdcPin<T> for Temperature {} | ||||||
| impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         #[cfg(not(stm32g0))] |         #[cfg(not(adc_g0))] | ||||||
|         let val = 17; |         let val = 17; | ||||||
|         #[cfg(stm32g0)] |         #[cfg(adc_g0)] | ||||||
|         let val = 12; |         let val = 12; | ||||||
|         val |         val | ||||||
|     } |     } | ||||||
| @@ -50,9 +50,9 @@ pub struct Vbat; | |||||||
| impl<T: Instance> AdcPin<T> for Vbat {} | impl<T: Instance> AdcPin<T> for Vbat {} | ||||||
| impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         #[cfg(not(stm32g0))] |         #[cfg(not(adc_g0))] | ||||||
|         let val = 18; |         let val = 18; | ||||||
|         #[cfg(stm32g0)] |         #[cfg(adc_g0)] | ||||||
|         let val = 14; |         let val = 14; | ||||||
|         val |         val | ||||||
|     } |     } | ||||||
| @@ -92,9 +92,14 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt { |     pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt { | ||||||
|  |         #[cfg(not(adc_g0))] | ||||||
|         T::common_regs().ccr().modify(|reg| { |         T::common_regs().ccr().modify(|reg| { | ||||||
|             reg.set_vrefen(true); |             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 |         // "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. |         // 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 { |     pub fn enable_temperature(&self) -> Temperature { | ||||||
|  |         #[cfg(not(adc_g0))] | ||||||
|         T::common_regs().ccr().modify(|reg| { |         T::common_regs().ccr().modify(|reg| { | ||||||
|             reg.set_ch17sel(true); |             reg.set_ch17sel(true); | ||||||
|         }); |         }); | ||||||
|  |         #[cfg(adc_g0)] | ||||||
|  |         T::regs().ccr().modify(|reg| { | ||||||
|  |             reg.set_tsen(true); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         Temperature {} |         Temperature {} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn enable_vbat(&self) -> Vbat { |     pub fn enable_vbat(&self) -> Vbat { | ||||||
|  |         #[cfg(not(adc_g0))] | ||||||
|         T::common_regs().ccr().modify(|reg| { |         T::common_regs().ccr().modify(|reg| { | ||||||
|             reg.set_ch18sel(true); |             reg.set_ch18sel(true); | ||||||
|         }); |         }); | ||||||
|  |         #[cfg(adc_g0)] | ||||||
|  |         T::regs().ccr().modify(|reg| { | ||||||
|  |             reg.set_vbaten(true); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         Vbat {} |         Vbat {} | ||||||
|     } |     } | ||||||
| @@ -126,9 +141,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn set_resolution(&mut self, resolution: Resolution) { |     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())); |         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())); |         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); |         Self::set_channel_sample_time(pin.channel(), self.sample_time); | ||||||
|  |  | ||||||
|         // Select channel |         // Select channel | ||||||
|         #[cfg(not(stm32g0))] |         #[cfg(not(adc_g0))] | ||||||
|         T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); |         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())); |         T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); | ||||||
|  |  | ||||||
|         // Some models are affected by an erratum: |         // Some models are affected by an erratum: | ||||||
| @@ -203,12 +218,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|         val |         val | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[cfg(stm32g0)] |     #[cfg(adc_g0)] | ||||||
|     fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { |     fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||||||
|         T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); |         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) { |     fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||||||
|         let sample_time = sample_time.into(); |         let sample_time = sample_time.into(); | ||||||
|         T::regs() |         T::regs() | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| use core::sync::atomic::{AtomicU8, Ordering}; |  | ||||||
|  |  | ||||||
| use embedded_hal_02::blocking::delay::DelayUs; | use embedded_hal_02::blocking::delay::DelayUs; | ||||||
|  | #[allow(unused)] | ||||||
| use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | ||||||
| use pac::adccommon::vals::Presc; | 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. | /// VREF voltage used for factory calibration of VREFINTCAL register. | ||||||
| pub const VREF_CALIB_MV: u32 = 3300; | 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; | pub struct VrefInt; | ||||||
| impl<T: Instance> InternalChannel<T> for VrefInt {} | impl<T: Instance> InternalChannel<T> for VrefInt {} | ||||||
| impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { | impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         19 |         VREF_CHANNEL | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -26,7 +44,7 @@ pub struct Temperature; | |||||||
| impl<T: Instance> InternalChannel<T> for Temperature {} | impl<T: Instance> InternalChannel<T> for Temperature {} | ||||||
| impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { | impl<T: Instance> super::sealed::InternalChannel<T> for Temperature { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         18 |         TEMP_CHANNEL | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -34,128 +52,10 @@ pub struct Vbat; | |||||||
| impl<T: Instance> InternalChannel<T> for Vbat {} | impl<T: Instance> InternalChannel<T> for Vbat {} | ||||||
| impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { | impl<T: Instance> super::sealed::InternalChannel<T> for Vbat { | ||||||
|     fn channel(&self) -> u8 { |     fn channel(&self) -> u8 { | ||||||
|         // TODO this should be 14 for H7a/b/35 |         VBAT_CHANNEL | ||||||
|         17 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| 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, | // 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. | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | ||||||
| #[allow(unused)] | #[allow(unused)] | ||||||
| @@ -176,7 +76,7 @@ enum Prescaler { | |||||||
|  |  | ||||||
| impl Prescaler { | impl Prescaler { | ||||||
|     fn from_ker_ck(frequency: Hertz) -> Self { |     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 { |         match raw_prescaler { | ||||||
|             0 => Self::NotDivided, |             0 => Self::NotDivided, | ||||||
|             1 => Self::DividedBy2, |             1 => Self::DividedBy2, | ||||||
| @@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|         let frequency = Hertz(T::frequency().0 / prescaler.divisor()); |         let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||||||
|         info!("ADC frequency set to {} Hz", frequency.0); |         info!("ADC frequency set to {} Hz", frequency.0); | ||||||
|  |  | ||||||
|         if frequency > Hertz::mhz(50) { |         if frequency > MAX_ADC_CLK_FREQ { | ||||||
|             panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); |             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 { |         let mut s = Self { | ||||||
|             adc, |             adc, | ||||||
|             sample_time: Default::default(), |             sample_time: Default::default(), | ||||||
| @@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> { | |||||||
|         // Configure channel |         // Configure channel | ||||||
|         Self::set_channel_sample_time(channel, self.sample_time); |         Self::set_channel_sample_time(channel, self.sample_time); | ||||||
|  |  | ||||||
|         T::regs().cfgr2().modify(|w| w.set_lshift(0)); |         #[cfg(stm32h7)] | ||||||
|         T::regs() |         { | ||||||
|             .pcsel() |             T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||||||
|             .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); |             T::regs() | ||||||
|  |                 .pcsel() | ||||||
|  |                 .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         T::regs().sqr1().write(|reg| { |         T::regs().sqr1().write(|reg| { | ||||||
|             reg.set_sq(0, channel); |             reg.set_sq(0, channel); | ||||||
|             reg.set_l(0); |             reg.set_l(0); | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
| #![allow(unused_macros)] | #![allow(unused_macros)] | ||||||
|  |  | ||||||
|  | use core::fmt::{Debug, Display, LowerHex}; | ||||||
|  |  | ||||||
| #[cfg(all(feature = "defmt", feature = "log"))] | #[cfg(all(feature = "defmt", feature = "log"))] | ||||||
| compile_error!("You may not enable both `defmt` and `log` features."); | 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 { | macro_rules! unreachable { | ||||||
|     ($($x:tt)*) => { |     ($($x:tt)*) => { | ||||||
|         { |         ::core::unreachable!($($x)*) | ||||||
|             #[cfg(not(feature = "defmt"))] |     }; | ||||||
|             ::core::unreachable!($($x)*); | } | ||||||
|             #[cfg(feature = "defmt")] |  | ||||||
|             ::defmt::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 |         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)] | #[allow(unused_imports)] | ||||||
| use crate::gpio::sealed::{AFType, Pin}; | use crate::gpio::sealed::{AFType, Pin}; | ||||||
| use crate::gpio::AnyPin; | use crate::gpio::AnyPin; | ||||||
|  | #[cfg(stm32f334)] | ||||||
|  | use crate::rcc::get_freqs; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
| use crate::Peripheral; | use crate::Peripheral; | ||||||
|  |  | ||||||
| @@ -158,17 +160,29 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { | |||||||
|         T::enable(); |         T::enable(); | ||||||
|         <T as crate::rcc::sealed::RccPeripheral>::reset(); |         <T as crate::rcc::sealed::RccPeripheral>::reset(); | ||||||
|  |  | ||||||
|         //        // Enable and and stabilize the DLL |         #[cfg(stm32f334)] | ||||||
|         //        T::regs().dllcr().modify(|w| { |         if unsafe { get_freqs() }.hrtim.is_some() { | ||||||
|         //            // w.set_calen(true); |             // Enable and and stabilize the DLL | ||||||
|         //            // w.set_calrte(11); |             T::regs().dllcr().modify(|w| { | ||||||
|         //            w.set_cal(true); |                 w.set_cal(true); | ||||||
|         //        }); |             }); | ||||||
|         // |  | ||||||
|         //        debug!("wait for dll calibration"); |             trace!("hrtim: wait for dll calibration"); | ||||||
|         //        while !T::regs().isr().read().dllrdy() {} |             while !T::regs().isr().read().dllrdy() {} | ||||||
|         // |  | ||||||
|         //        debug!("dll calibration complete"); |             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 { |         Self { | ||||||
|             _inner: tim, |             _inner: tim, | ||||||
|   | |||||||
| @@ -1,31 +1,17 @@ | |||||||
| use crate::rcc::sealed::RccPeripheral; | use crate::rcc::sealed::RccPeripheral; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
|  |  | ||||||
|  | #[repr(u8)] | ||||||
| #[derive(Clone, Copy)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum Prescaler { | pub(crate) enum Prescaler { | ||||||
|     Div1, |     Div1 = 1, | ||||||
|     Div2, |     Div2 = 2, | ||||||
|     Div4, |     Div4 = 4, | ||||||
|     Div8, |     Div8 = 8, | ||||||
|     Div16, |     Div16 = 16, | ||||||
|     Div32, |     Div32 = 32, | ||||||
|     Div64, |     Div64 = 64, | ||||||
|     Div128, |     Div128 = 128, | ||||||
| } |  | ||||||
|  |  | ||||||
| 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, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<Prescaler> for u8 { | impl From<Prescaler> for u8 { | ||||||
| @@ -72,7 +58,7 @@ impl Prescaler { | |||||||
|             Prescaler::Div128, |             Prescaler::Div128, | ||||||
|         ] |         ] | ||||||
|         .iter() |         .iter() | ||||||
|         .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val) |         .skip_while(|psc| **psc as u32 <= val) | ||||||
|         .next() |         .next() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|     } |     } | ||||||
| @@ -80,7 +66,7 @@ impl Prescaler { | |||||||
|     pub fn compute_min_low_res(val: u32) -> Self { |     pub fn compute_min_low_res(val: u32) -> Self { | ||||||
|         *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] |         *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] | ||||||
|             .iter() |             .iter() | ||||||
|             .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val) |             .skip_while(|psc| **psc as u32 <= val) | ||||||
|             .next() |             .next() | ||||||
|             .unwrap() |             .unwrap() | ||||||
|     } |     } | ||||||
| @@ -118,7 +104,13 @@ foreach_interrupt! { | |||||||
|                 use crate::rcc::sealed::RccPeripheral; |                 use crate::rcc::sealed::RccPeripheral; | ||||||
|  |  | ||||||
|                 let f = frequency.0; |                 let f = frequency.0; | ||||||
|  |                 #[cfg(not(stm32f334))] | ||||||
|                 let timer_f = Self::frequency().0; |                 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_min = (timer_f / f) / (u16::MAX as u32 / 32); | ||||||
|                 let psc = if Self::regs().isr().read().dllrdy() { |                 let psc = if Self::regs().isr().read().dllrdy() { | ||||||
|                     Prescaler::compute_min_high_res(psc_min) |                     Prescaler::compute_min_high_res(psc_min) | ||||||
| @@ -126,8 +118,7 @@ foreach_interrupt! { | |||||||
|                     Prescaler::compute_min_low_res(psc_min) |                     Prescaler::compute_min_low_res(psc_min) | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let psc_val: u32 = psc.into(); |                 let timer_f = 32 * (timer_f / psc as u32); | ||||||
|                 let timer_f = 32 * (timer_f / psc_val); |  | ||||||
|                 let per: u16 = (timer_f / f) as u16; |                 let per: u16 = (timer_f / f) as u16; | ||||||
|  |  | ||||||
|                 let regs = Self::regs(); |                 let regs = Self::regs(); | ||||||
| @@ -140,7 +131,13 @@ foreach_interrupt! { | |||||||
|                 use crate::rcc::sealed::RccPeripheral; |                 use crate::rcc::sealed::RccPeripheral; | ||||||
|  |  | ||||||
|                 let f = frequency.0; |                 let f = frequency.0; | ||||||
|  |                 #[cfg(not(stm32f334))] | ||||||
|                 let timer_f = Self::frequency().0; |                 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_min = (timer_f / f) / (u16::MAX as u32 / 32); | ||||||
|                 let psc = if Self::regs().isr().read().dllrdy() { |                 let psc = if Self::regs().isr().read().dllrdy() { | ||||||
|                     Prescaler::compute_min_high_res(psc_min) |                     Prescaler::compute_min_high_res(psc_min) | ||||||
| @@ -148,8 +145,7 @@ foreach_interrupt! { | |||||||
|                     Prescaler::compute_min_low_res(psc_min) |                     Prescaler::compute_min_low_res(psc_min) | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let psc_val: u32 = psc.into(); |                 let timer_f = 32 * (timer_f / psc as u32); | ||||||
|                 let timer_f = 32 * (timer_f / psc_val); |  | ||||||
|                 let per: u16 = (timer_f / f) as u16; |                 let per: u16 = (timer_f / f) as u16; | ||||||
|  |  | ||||||
|                 let regs = Self::regs(); |                 let regs = Self::regs(); | ||||||
| @@ -163,20 +159,17 @@ foreach_interrupt! { | |||||||
|                 let regs = Self::regs(); |                 let regs = Self::regs(); | ||||||
|  |  | ||||||
|                 let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); |                 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 |                 // The dead-time base clock runs 4 times slower than the hrtim base clock | ||||||
|                 // u9::MAX = 511 |                 // 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() { |                 let psc = if Self::regs().isr().read().dllrdy() { | ||||||
|                     Prescaler::compute_min_high_res(psc_min) |                     Prescaler::compute_min_high_res(psc_min) | ||||||
|                 } else { |                 } else { | ||||||
|                     Prescaler::compute_min_low_res(psc_min) |                     Prescaler::compute_min_low_res(psc_min) | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let dt_psc_val: u32 = psc.into(); |                 let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32); | ||||||
|                 let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); |  | ||||||
|  |  | ||||||
|                 regs.tim(channel).dt().modify(|w| { |                 regs.tim(channel).dt().modify(|w| { | ||||||
|                     w.set_dtprsc(psc.into()); |                     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> { | impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | ||||||
|     type Error = Error; |     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 { | mod eh02 { | ||||||
|     use super::*; |     use super::*; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -197,6 +197,11 @@ pub fn init(config: Config) -> Peripherals { | |||||||
|         // must be after rcc init |         // must be after rcc init | ||||||
|         #[cfg(feature = "_time-driver")] |         #[cfg(feature = "_time-driver")] | ||||||
|         time_driver::init(); |         time_driver::init(); | ||||||
|  |  | ||||||
|  |         #[cfg(feature = "low-power")] | ||||||
|  |         while !crate::rcc::low_power_ready() { | ||||||
|  |             crate::rcc::clock_refcount_sub(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     p |     p | ||||||
|   | |||||||
| @@ -3,46 +3,51 @@ use core::marker::PhantomData; | |||||||
|  |  | ||||||
| use cortex_m::peripheral::SCB; | use cortex_m::peripheral::SCB; | ||||||
| use embassy_executor::*; | use embassy_executor::*; | ||||||
| use embassy_time::Duration; |  | ||||||
|  |  | ||||||
| use crate::interrupt; | use crate::interrupt; | ||||||
| use crate::interrupt::typelevel::Interrupt; | use crate::interrupt::typelevel::Interrupt; | ||||||
| use crate::pac::EXTI; |  | ||||||
| use crate::rcc::low_power_ready; | use crate::rcc::low_power_ready; | ||||||
|  | use crate::time_driver::{get_driver, RtcDriver}; | ||||||
|  |  | ||||||
| const THREAD_PENDER: usize = usize::MAX; | 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! { | foreach_interrupt! { | ||||||
|     (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |     (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | ||||||
|         #[interrupt] |         #[interrupt] | ||||||
|         unsafe fn $irq() { |         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) { | pub fn stop_with_rtc(rtc: &'static Rtc) { | ||||||
|     crate::interrupt::typelevel::RTC_WKUP::unpend(); |     unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) | ||||||
|     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) }; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { | // pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) { | ||||||
|     unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) | //     let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() } | ||||||
| } | //         .rtc | ||||||
|  | //         .unwrap() | ||||||
| pub fn stop_wakeup_alarm() -> RtcInstant { | //         .start_wakeup_alarm(requested_duration); | ||||||
|     unsafe { RTC }.unwrap().stop_wakeup_alarm() | // | ||||||
| } | //     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. | /// Thread mode executor, using WFE/SEV. | ||||||
| /// | /// | ||||||
| @@ -57,54 +62,61 @@ pub fn stop_wakeup_alarm() -> RtcInstant { | |||||||
| pub struct Executor { | pub struct Executor { | ||||||
|     inner: raw::Executor, |     inner: raw::Executor, | ||||||
|     not_send: PhantomData<*mut ()>, |     not_send: PhantomData<*mut ()>, | ||||||
|  |     scb: SCB, | ||||||
|  |     time_driver: &'static RtcDriver, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Executor { | impl Executor { | ||||||
|     /// Create a new Executor. |     /// Create a new Executor. | ||||||
|     pub fn new() -> Self { |     pub fn take() -> &'static mut Self { | ||||||
|         Self { |         unsafe { | ||||||
|             inner: raw::Executor::new(THREAD_PENDER as *mut ()), |             assert!(EXECUTOR.is_none()); | ||||||
|             not_send: PhantomData, |  | ||||||
|  |             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() { |     unsafe fn on_wakeup_irq(&mut self) { | ||||||
|         info!("on wakeup irq"); |         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 { |     pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | ||||||
|         Duration::from_secs(3) |         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 { |     fn configure_pwr(&mut self) { | ||||||
|         unsafe { cortex_m::Peripherals::steal() }.SCB |         trace!("low power: configure_pwr"); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn configure_pwr(&self) { |  | ||||||
|         trace!("configure_pwr"); |  | ||||||
|  |  | ||||||
|  |         self.scb.clear_sleepdeep(); | ||||||
|         if !low_power_ready() { |         if !low_power_ready() { | ||||||
|  |             trace!("low power: configure_pwr: low power not ready"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let time_until_next_alarm = self.time_until_next_alarm(); |         if self.time_driver.pause_time().is_err() { | ||||||
|         if time_until_next_alarm < THRESHOLD { |             trace!("low power: configure_pwr: time driver failed to pause"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         trace!("low power stop required"); |         trace!("low power: enter stop..."); | ||||||
|  |         self.scb.set_sleepdeep(); | ||||||
|         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(); |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Run the executor. |     /// Run the executor. | ||||||
| @@ -126,11 +138,11 @@ impl Executor { | |||||||
|     /// |     /// | ||||||
|     /// This function never returns. |     /// This function never returns. | ||||||
|     pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |     pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||||||
|         init(self.inner.spawner()); |         init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); | ||||||
|  |  | ||||||
|         loop { |         loop { | ||||||
|             unsafe { |             unsafe { | ||||||
|                 self.inner.poll(); |                 EXECUTOR.as_mut().unwrap().inner.poll(); | ||||||
|                 self.configure_pwr(); |                 self.configure_pwr(); | ||||||
|                 asm!("wfe"); |                 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::flash::vals::Latency; | ||||||
| use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; | use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; | ||||||
| use crate::pac::{FLASH, RCC}; | use crate::pac::{FLASH, RCC}; | ||||||
|   | |||||||
| @@ -184,6 +184,6 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|         apb1_tim: Hertz(pclk1 * timer_mul1), |         apb1_tim: Hertz(pclk1 * timer_mul1), | ||||||
|         apb2_tim: Hertz(pclk2 * timer_mul2), |         apb2_tim: Hertz(pclk2 * timer_mul2), | ||||||
|         ahb1: Hertz(hclk), |         ahb1: Hertz(hclk), | ||||||
|         adc: Hertz(adcclk), |         adc: Some(Hertz(adcclk)), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,13 @@ | |||||||
| use core::convert::TryFrom; | use core::convert::TryFrom; | ||||||
| use core::ops::{Div, Mul}; | 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::flash::vals::Latency; | ||||||
| use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; | 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::rcc::{set_freqs, Clocks}; | ||||||
|  | use crate::rtc::RtcClockSource; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
|  |  | ||||||
| /// HSI speed | /// HSI speed | ||||||
| @@ -201,7 +203,7 @@ pub struct PLLClocks { | |||||||
|     pub pll48_freq: Hertz, |     pub pll48_freq: Hertz, | ||||||
| } | } | ||||||
|  |  | ||||||
| pub use super::common::VoltageScale; | pub use super::bus::VoltageScale; | ||||||
|  |  | ||||||
| impl VoltageScale { | impl VoltageScale { | ||||||
|     const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> { |     const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> { | ||||||
| @@ -288,6 +290,7 @@ pub struct Config { | |||||||
|     pub pll_mux: PLLSrc, |     pub pll_mux: PLLSrc, | ||||||
|     pub pll: PLLConfig, |     pub pll: PLLConfig, | ||||||
|     pub mux: ClockSrc, |     pub mux: ClockSrc, | ||||||
|  |     pub rtc: Option<RtcClockSource>, | ||||||
|     pub voltage: VoltageScale, |     pub voltage: VoltageScale, | ||||||
|     pub ahb_pre: AHBPrescaler, |     pub ahb_pre: AHBPrescaler, | ||||||
|     pub apb1_pre: APBPrescaler, |     pub apb1_pre: APBPrescaler, | ||||||
| @@ -304,6 +307,7 @@ impl Default for Config { | |||||||
|             pll: PLLConfig::default(), |             pll: PLLConfig::default(), | ||||||
|             voltage: VoltageScale::Scale3, |             voltage: VoltageScale::Scale3, | ||||||
|             mux: ClockSrc::HSI, |             mux: ClockSrc::HSI, | ||||||
|  |             rtc: None, | ||||||
|             ahb_pre: AHBPrescaler::NotDivided, |             ahb_pre: AHBPrescaler::NotDivided, | ||||||
|             apb1_pre: APBPrescaler::NotDivided, |             apb1_pre: APBPrescaler::NotDivided, | ||||||
|             apb2_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.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 { |     set_freqs(Clocks { | ||||||
|         sys: sys_clk, |         sys: sys_clk, | ||||||
|         ahb1: ahb_freq, |         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::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::pac::{FLASH, RCC}; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
| @@ -10,6 +12,82 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000); | |||||||
| /// LSI speed | /// LSI speed | ||||||
| pub const LSI_FREQ: Hertz = Hertz(40_000); | 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 | /// Clocks configutation | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
| #[derive(Default)] | #[derive(Default)] | ||||||
| @@ -36,9 +114,20 @@ pub struct Config { | |||||||
|     /// - The System clock frequency is either 48MHz or 72MHz |     /// - The System clock frequency is either 48MHz or 72MHz | ||||||
|     /// - APB1 clock has a minimum frequency of 10MHz |     /// - APB1 clock has a minimum frequency of 10MHz | ||||||
|     pub pll48: bool, |     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 | // Information required to setup the PLL clock | ||||||
|  | #[derive(Clone, Copy)] | ||||||
| struct PllConfig { | struct PllConfig { | ||||||
|     pll_src: Pllsrc, |     pll_src: Pllsrc, | ||||||
|     pll_mul: Pllmul, |     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 { |     set_freqs(Clocks { | ||||||
|         sys: Hertz(sysclk), |         sys: Hertz(sysclk), | ||||||
|         apb1: Hertz(pclk1), |         apb1: Hertz(pclk1), | ||||||
| @@ -177,6 +321,12 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|         apb1_tim: Hertz(pclk1 * timer_mul1), |         apb1_tim: Hertz(pclk1 * timer_mul1), | ||||||
|         apb2_tim: Hertz(pclk2 * timer_mul2), |         apb2_tim: Hertz(pclk2 * timer_mul2), | ||||||
|         ahb1: Hertz(hclk), |         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 |     // Calculates the Multiplier and the Divisor to arrive at | ||||||
|     // the required System clock from PLL source frequency |     // the required System clock from PLL source frequency | ||||||
|     let get_mul_div = |sysclk, pllsrcclk| { |     let get_mul_div = |sysclk, pllsrcclk| { | ||||||
|         let common_div = gcd(sysclk, pllsrcclk); |         let bus_div = gcd(sysclk, pllsrcclk); | ||||||
|         let mut multiplier = sysclk / common_div; |         let mut multiplier = sysclk / bus_div; | ||||||
|         let mut divisor = pllsrcclk / common_div; |         let mut divisor = pllsrcclk / bus_div; | ||||||
|         // Minimum PLL multiplier is two |         // Minimum PLL multiplier is two | ||||||
|         if multiplier == 1 { |         if multiplier == 1 { | ||||||
|             multiplier *= 2; |             multiplier *= 2; | ||||||
|   | |||||||
| @@ -8,8 +8,8 @@ use crate::gpio::sealed::AFType; | |||||||
| use crate::gpio::Speed; | use crate::gpio::Speed; | ||||||
| use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; | use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; | ||||||
| use crate::pac::{FLASH, PWR, RCC}; | use crate::pac::{FLASH, PWR, RCC}; | ||||||
|  | use crate::rcc::bd::{BackupDomain, RtcClockSource}; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
| use crate::rtc::{Rtc, RtcClockSource}; |  | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
| use crate::{peripherals, Peripheral}; | use crate::{peripherals, Peripheral}; | ||||||
|  |  | ||||||
| @@ -461,17 +461,9 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|         }) |         }) | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     match config.rtc { |     config | ||||||
|         Some(RtcClockSource::LSI) => { |         .rtc | ||||||
|             RCC.csr().modify(|w| w.set_lsion(true)); |         .map(|clock_source| BackupDomain::configure_ls(clock_source, None)); | ||||||
|             while !RCC.csr().read().lsirdy() {} |  | ||||||
|         } |  | ||||||
|         _ => {} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     config.rtc.map(|clock_source| { |  | ||||||
|         Rtc::set_clock_source(clock_source); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     let rtc = match config.rtc { |     let rtc = match config.rtc { | ||||||
|         Some(RtcClockSource::LSI) => Some(LSI_FREQ), |         Some(RtcClockSource::LSI) => Some(LSI_FREQ), | ||||||
| @@ -499,6 +491,7 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|         pllsai: None, |         pllsai: None, | ||||||
|  |  | ||||||
|         rtc: rtc, |         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::flash::vals::Latency; | ||||||
| use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; | use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; | ||||||
| use crate::pac::{FLASH, PWR, RCC}; | use crate::pac::{FLASH, PWR, RCC}; | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| use stm32_metapac::flash::vals::Latency; | 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; | use stm32_metapac::FLASH; | ||||||
|  |  | ||||||
| pub use super::common::{AHBPrescaler, APBPrescaler}; | pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||||||
| use crate::pac::{PWR, RCC}; | use crate::pac::{PWR, RCC}; | ||||||
| use crate::rcc::sealed::RccPeripheral; | use crate::rcc::sealed::RccPeripheral; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
| @@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); | |||||||
| /// LSI speed | /// LSI speed | ||||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | 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 | /// System clock mux source | ||||||
| #[derive(Clone, Copy)] | #[derive(Clone, Copy)] | ||||||
| pub enum ClockSrc { | pub enum ClockSrc { | ||||||
| @@ -327,6 +350,8 @@ pub struct Config { | |||||||
|     pub pll: Option<Pll>, |     pub pll: Option<Pll>, | ||||||
|     /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. |     /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. | ||||||
|     pub clock_48mhz_src: Option<Clock48MhzSrc>, |     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. | /// 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, |             low_power_run: false, | ||||||
|             pll: None, |             pll: None, | ||||||
|             clock_48mhz_src: 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_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 { |     if config.low_power_run { | ||||||
|         assert!(sys_clk <= 2_000_000); |         assert!(sys_clk <= 2_000_000); | ||||||
|         PWR.cr1().modify(|w| w.set_lpr(true)); |         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), |         apb1_tim: Hertz(apb1_tim_freq), | ||||||
|         apb2: Hertz(apb2_freq), |         apb2: Hertz(apb2_freq), | ||||||
|         apb2_tim: Hertz(apb2_tim_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_MIN: u32 = 128_000_000; | ||||||
| const VCO_WIDE_MAX: u32 = 560_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 { | pub enum HseMode { | ||||||
|     /// crystal/ceramic oscillator (HSEBYP=0) |     /// crystal/ceramic oscillator (HSEBYP=0) | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | |||||||
| /// LSI speed | /// LSI speed | ||||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||||||
|  |  | ||||||
| pub use super::common::VoltageScale; | pub use super::bus::VoltageScale; | ||||||
|  |  | ||||||
| #[derive(Clone, Copy)] | #[derive(Clone, Copy)] | ||||||
| pub enum AdcClockSource { | 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::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||||||
| use crate::pac::RCC; | use crate::pac::RCC; | ||||||
| #[cfg(crs)] | #[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::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||||||
| use crate::pac::{FLASH, RCC}; | use crate::pac::{FLASH, RCC}; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
|   | |||||||
| @@ -2,15 +2,15 @@ use core::marker::PhantomData; | |||||||
|  |  | ||||||
| use embassy_hal_internal::into_ref; | use embassy_hal_internal::into_ref; | ||||||
| use stm32_metapac::rcc::regs::Cfgr; | 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::sealed::AFType; | ||||||
| use crate::gpio::Speed; | use crate::gpio::Speed; | ||||||
| use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | 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::rcc::{set_freqs, Clocks}; | ||||||
| use crate::rtc::{Rtc, RtcClockSource as RCS}; |  | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
| use crate::{peripherals, Peripheral}; | use crate::{peripherals, Peripheral}; | ||||||
|  |  | ||||||
| @@ -254,16 +254,11 @@ impl Default for Config { | |||||||
|             pllsai1: None, |             pllsai1: None, | ||||||
|             #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] |             #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] | ||||||
|             hsi48: false, |             hsi48: false, | ||||||
|             rtc_mux: RtcClockSource::LSI32, |             rtc_mux: RtcClockSource::LSI, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub enum RtcClockSource { |  | ||||||
|     LSE32, |  | ||||||
|     LSI32, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub enum McoClock { | pub enum McoClock { | ||||||
|     DIV1, |     DIV1, | ||||||
|     DIV2, |     DIV2, | ||||||
| @@ -412,35 +407,7 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|  |  | ||||||
|     RCC.apb1enr1().modify(|w| w.set_pwren(true)); |     RCC.apb1enr1().modify(|w| w.set_pwren(true)); | ||||||
|  |  | ||||||
|     match config.rtc_mux { |     BackupDomain::configure_ls(config.rtc_mux, None); | ||||||
|         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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let (sys_clk, sw) = match config.mux { |     let (sys_clk, sw) = match config.mux { | ||||||
|         ClockSrc::MSI(range) => { |         ClockSrc::MSI(range) => { | ||||||
| @@ -451,7 +418,7 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|                 w.set_msirgsel(true); |                 w.set_msirgsel(true); | ||||||
|                 w.set_msion(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 |                     // If LSE is enabled, enable calibration of MSI | ||||||
|                     w.set_msipllen(true); |                     w.set_msipllen(true); | ||||||
|                 } else { |                 } else { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use stm32_metapac::PWR; | 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::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | ||||||
| use crate::pac::{FLASH, RCC}; | use crate::pac::{FLASH, RCC}; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| #![macro_use] | #![macro_use] | ||||||
|  |  | ||||||
| pub mod common; | pub(crate) mod bd; | ||||||
|  | pub mod bus; | ||||||
| use core::mem::MaybeUninit; | use core::mem::MaybeUninit; | ||||||
|  |  | ||||||
|  | pub use crate::rcc::bd::RtcClockSource; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
|  |  | ||||||
| #[cfg_attr(rcc_f0, path = "f0.rs")] | #[cfg_attr(rcc_f0, path = "f0.rs")] | ||||||
| @@ -70,15 +71,22 @@ pub struct Clocks { | |||||||
|     #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] |     #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||||||
|     pub pllsai: Option<Hertz>, |     pub pllsai: Option<Hertz>, | ||||||
|  |  | ||||||
|     #[cfg(stm32f1)] |     #[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))] | ||||||
|     pub adc: Hertz, |  | ||||||
|  |  | ||||||
|     #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] |  | ||||||
|     pub adc: Option<Hertz>, |     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))] |     #[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>, |     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")] | #[cfg(feature = "low-power")] | ||||||
| @@ -86,6 +94,8 @@ static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); | |||||||
|  |  | ||||||
| #[cfg(feature = "low-power")] | #[cfg(feature = "low-power")] | ||||||
| pub fn low_power_ready() -> bool { | pub fn low_power_ready() -> bool { | ||||||
|  |     trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst)); | ||||||
|  |  | ||||||
|     CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 |     CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; | 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::pac::{FLASH, RCC}; | ||||||
| use crate::rcc::{set_freqs, Clocks}; | use crate::rcc::{set_freqs, Clocks}; | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
| @@ -11,7 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); | |||||||
| /// LSI speed | /// LSI speed | ||||||
| pub const LSI_FREQ: Hertz = Hertz(32_000); | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||||||
|  |  | ||||||
| pub use super::common::VoltageScale; | pub use super::bus::VoltageScale; | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Copy, Clone)] | ||||||
| pub enum ClockSrc { | 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::rcc::Clocks; | ||||||
| use crate::rtc::{Rtc, RtcClockSource}; |  | ||||||
| use crate::time::{khz, mhz, Hertz}; | use crate::time::{khz, mhz, Hertz}; | ||||||
|  |  | ||||||
| /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | /// 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, |         apb1_tim: apb1_tim_clk, | ||||||
|         apb2_tim: apb2_tim_clk, |         apb2_tim: apb2_tim_clk, | ||||||
|         rtc: rtc_clk, |         rtc: rtc_clk, | ||||||
|  |         rtc_hse: None, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn configure_clocks(config: &Config) { | pub(crate) fn configure_clocks(config: &Config) { | ||||||
|     let pwr = crate::pac::PWR; |  | ||||||
|     let rcc = crate::pac::RCC; |     let rcc = crate::pac::RCC; | ||||||
|  |  | ||||||
|     let needs_hsi = if let Some(pll_mux) = &config.mux { |     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() {} |         while !rcc.cr().read().hsirdy() {} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let needs_lsi = if let Some(rtc_mux) = &config.rtc { |     rcc.cfgr().modify(|w| w.set_stopwuck(true)); | ||||||
|         *rtc_mux == RtcClockSource::LSI |  | ||||||
|     } else { |  | ||||||
|         false |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if needs_lsi { |     config | ||||||
|         rcc.csr().modify(|w| w.set_lsi1on(true)); |         .rtc | ||||||
|  |         .map(|clock_source| BackupDomain::configure_ls(clock_source, None)); | ||||||
|         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)); |  | ||||||
|         } |  | ||||||
|         _ => {} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     match &config.hse { |     match &config.hse { | ||||||
|         Some(hse) => { |         Some(hse) => { | ||||||
| @@ -374,6 +356,4 @@ pub(crate) fn configure_clocks(config: &Config) { | |||||||
|         w.set_c2hpre(config.ahb2_pre.into()); |         w.set_c2hpre(config.ahb2_pre.into()); | ||||||
|         w.set_shdhpre(config.ahb3_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}; | pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale}; | ||||||
| use crate::pac::pwr::vals::Dbp; | use crate::pac::rcc::vals::Adcsel; | ||||||
| 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::rcc::{set_freqs, Clocks}; | ||||||
| use crate::rtc::{Rtc, RtcClockSource as RCS}; |  | ||||||
| use crate::time::Hertz; | use crate::time::Hertz; | ||||||
|  |  | ||||||
| /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | /// 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 | /// Clocks configutation | ||||||
| pub struct Config { | pub struct Config { | ||||||
|     pub mux: ClockSrc, |     pub mux: ClockSrc, | ||||||
| @@ -114,9 +137,8 @@ pub struct Config { | |||||||
|     pub shd_ahb_pre: AHBPrescaler, |     pub shd_ahb_pre: AHBPrescaler, | ||||||
|     pub apb1_pre: APBPrescaler, |     pub apb1_pre: APBPrescaler, | ||||||
|     pub apb2_pre: APBPrescaler, |     pub apb2_pre: APBPrescaler, | ||||||
|     pub enable_lsi: bool, |  | ||||||
|     pub enable_rtc_apb: bool, |  | ||||||
|     pub rtc_mux: RtcClockSource, |     pub rtc_mux: RtcClockSource, | ||||||
|  |     pub adc_clock_source: AdcClockSource, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Default for Config { | impl Default for Config { | ||||||
| @@ -128,18 +150,12 @@ impl Default for Config { | |||||||
|             shd_ahb_pre: AHBPrescaler::NotDivided, |             shd_ahb_pre: AHBPrescaler::NotDivided, | ||||||
|             apb1_pre: APBPrescaler::NotDivided, |             apb1_pre: APBPrescaler::NotDivided, | ||||||
|             apb2_pre: APBPrescaler::NotDivided, |             apb2_pre: APBPrescaler::NotDivided, | ||||||
|             enable_lsi: false, |             rtc_mux: RtcClockSource::LSI, | ||||||
|             enable_rtc_apb: false, |             adc_clock_source: AdcClockSource::default(), | ||||||
|             rtc_mux: RtcClockSource::LSI32, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub enum RtcClockSource { |  | ||||||
|     LSE32, |  | ||||||
|     LSI32, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[repr(u8)] | #[repr(u8)] | ||||||
| pub enum Lsedrv { | pub enum Lsedrv { | ||||||
|     Low = 0, |     Low = 0, | ||||||
| @@ -214,35 +230,8 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|  |  | ||||||
|     while FLASH.acr().read().latency() != ws {} |     while FLASH.acr().read().latency() != ws {} | ||||||
|  |  | ||||||
|     match config.rtc_mux { |     // Enables the LSI if configured | ||||||
|         RtcClockSource::LSE32 => { |     BackupDomain::configure_ls(config.rtc_mux, None); | ||||||
|             // 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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     match config.mux { |     match config.mux { | ||||||
|         ClockSrc::HSI16 => { |         ClockSrc::HSI16 => { | ||||||
| @@ -266,7 +255,7 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|                 w.set_msirange(range.into()); |                 w.set_msirange(range.into()); | ||||||
|                 w.set_msion(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 |                     // If LSE is enabled, enable calibration of MSI | ||||||
|                     w.set_msipllen(true); |                     w.set_msipllen(true); | ||||||
|                 } else { |                 } 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| { |     RCC.extcfgr().modify(|w| { | ||||||
|         if config.shd_ahb_pre == AHBPrescaler::NotDivided { |         if config.shd_ahb_pre == AHBPrescaler::NotDivided { | ||||||
|             w.set_shdhpre(0); |             w.set_shdhpre(0); | ||||||
| @@ -304,15 +285,10 @@ pub(crate) unsafe fn init(config: Config) { | |||||||
|         w.set_ppre2(config.apb2_pre.into()); |         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 { |     // TODO: switch voltage range | ||||||
|         let csr = RCC.csr().read(); |  | ||||||
|         if !csr.lsion() { |  | ||||||
|             RCC.csr().modify(|w| w.set_lsion(true)); |  | ||||||
|             while !RCC.csr().read().lsirdy() {} |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     set_freqs(Clocks { |     set_freqs(Clocks { | ||||||
|         sys: Hertz(sys_clk), |         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> { |     pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||||||
|         for chunk in dest.chunks_mut(4) { |         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() { |             if bits.seis() { | ||||||
|                 // in case of noise-source or seed error we try to recover here |                 // 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 |                 // 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()) { |                 for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) { | ||||||
|                     *dest = *src |                     *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")] | #[cfg(feature = "chrono")] | ||||||
| impl From<chrono::Weekday> for DayOfWeek { | impl From<chrono::Weekday> for DayOfWeek { | ||||||
|     fn from(weekday: Weekday) -> Self { |     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 | //! RTC peripheral abstraction | ||||||
| mod datetime; | 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}; | 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 | /// refer to AN4759 to compare features of RTC2 and RTC3 | ||||||
| #[cfg_attr(any(rtc_v1), path = "v1.rs")] | #[cfg_attr(any(rtc_v1), path = "v1.rs")] | ||||||
| @@ -30,60 +41,67 @@ pub enum RtcError { | |||||||
|     NotRunning, |     NotRunning, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// RTC Abstraction | #[cfg(feature = "low-power")] | ||||||
| pub struct Rtc { | /// Represents an instant in time that can be substracted to compute a duration | ||||||
|     rtc_config: RtcConfig, | struct RtcInstant { | ||||||
|  |     second: u8, | ||||||
|  |     subsecond: u16, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone, Debug, PartialEq)] | #[cfg(all(feature = "low-power", feature = "defmt"))] | ||||||
| #[repr(u8)] | impl defmt::Format for RtcInstant { | ||||||
| pub enum RtcClockSource { |     fn format(&self, fmt: defmt::Formatter) { | ||||||
|     /// 00: No clock |         defmt::write!( | ||||||
|     NoClock = 0b00, |             fmt, | ||||||
|     /// 01: LSE oscillator clock used as RTC clock |             "{}:{}", | ||||||
|     LSE = 0b01, |             self.second, | ||||||
|     /// 10: LSI oscillator clock used as RTC clock |             RTC::regs().prer().read().prediv_s() - self.subsecond, | ||||||
|     LSI = 0b10, |         ) | ||||||
|     /// 11: HSE oscillator clock divided by 32 used as RTC clock |     } | ||||||
|     HSE = 0b11, | } | ||||||
|  |  | ||||||
|  | #[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)] | #[derive(Copy, Clone, PartialEq)] | ||||||
| pub struct RtcConfig { | pub struct RtcConfig { | ||||||
|     /// Asynchronous prescaler factor |     /// The subsecond counter frequency; default is 256 | ||||||
|     /// This is the asynchronous division factor: |     /// | ||||||
|     /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) |     /// A high counter frequency may impact stop power consumption | ||||||
|     /// ck_apre drives the subsecond register |     pub frequency: Hertz, | ||||||
|     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, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Default for RtcConfig { | impl Default for RtcConfig { | ||||||
|     /// LSI with prescalers assuming 32.768 kHz. |     /// LSI with prescalers assuming 32.768 kHz. | ||||||
|     /// Raw sub-seconds in 1/256. |     /// Raw sub-seconds in 1/256. | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         RtcConfig { |         RtcConfig { frequency: Hertz(256) } | ||||||
|             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 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -106,16 +124,37 @@ impl Default for RtcCalibrationCyclePeriod { | |||||||
|  |  | ||||||
| impl Rtc { | impl Rtc { | ||||||
|     pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { |     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(); |         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); |         // Load the clock frequency from the rcc mod, if supported | ||||||
|         rtc_struct.rtc_config = rtc_config; |         #[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. |     /// Set the datetime to a new value. | ||||||
| @@ -130,6 +169,20 @@ impl Rtc { | |||||||
|         Ok(()) |         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. |     /// Return the current datetime. | ||||||
|     /// |     /// | ||||||
|     /// # Errors |     /// # 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; |     pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; | ||||||
|  |  | ||||||
|     /// Read content of the backup register. |     /// Read content of the backup register. | ||||||
| @@ -219,7 +268,7 @@ pub(crate) mod sealed { | |||||||
|             crate::pac::RTC |             crate::pac::RTC | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         fn enable_peripheral_clk() {} |         fn enable_peripheral_clk(); | ||||||
|  |  | ||||||
|         /// Read content of the backup register. |         /// Read content of the backup register. | ||||||
|         /// |         /// | ||||||
|   | |||||||
| @@ -1,74 +1,18 @@ | |||||||
| use stm32_metapac::rtc::vals::{Init, Osel, Pol}; | use stm32_metapac::rtc::vals::{Init, Osel, Pol}; | ||||||
|  |  | ||||||
| use super::{sealed, RtcClockSource, RtcConfig}; | use super::sealed; | ||||||
| use crate::pac::rtc::Rtc; | use crate::pac::rtc::Rtc; | ||||||
| use crate::peripherals::RTC; | use crate::peripherals::RTC; | ||||||
| use crate::rtc::sealed::Instance; | 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)] | #[allow(dead_code)] | ||||||
|  | #[repr(u8)] | ||||||
| #[derive(Clone, Copy, Debug)] | #[derive(Clone, Copy, Debug)] | ||||||
| pub(crate) enum WakeupPrescaler { | pub(crate) enum WakeupPrescaler { | ||||||
|     Div2, |     Div2 = 2, | ||||||
|     Div4, |     Div4 = 4, | ||||||
|     Div8, |     Div8 = 8, | ||||||
|     Div16, |     Div16 = 16, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(any(stm32wb, stm32f4))] | #[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)] | #[allow(dead_code)] | ||||||
| impl WakeupPrescaler { | impl WakeupPrescaler { | ||||||
|     pub fn compute_min(val: u32) -> Self { |     pub fn compute_min(val: u32) -> Self { | ||||||
| @@ -121,38 +54,17 @@ impl WakeupPrescaler { | |||||||
|             WakeupPrescaler::Div16, |             WakeupPrescaler::Div16, | ||||||
|         ] |         ] | ||||||
|         .iter() |         .iter() | ||||||
|         .skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val) |         .skip_while(|psc| **psc as u32 <= val) | ||||||
|         .next() |         .next() | ||||||
|         .unwrap_or(&WakeupPrescaler::Div16) |         .unwrap_or(&WakeupPrescaler::Div16) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl super::Rtc { | impl super::Rtc { | ||||||
|     fn unlock_registers() { |     #[cfg(feature = "low-power")] | ||||||
|         #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] |     /// start the wakeup alarm and wtih a duration that is as close to but less than | ||||||
|         let cr = crate::pac::PWR.cr(); |     /// the requested duration, and record the instant the wakeup alarm was started | ||||||
|         #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] |     pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) { | ||||||
|         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 { |  | ||||||
|         use embassy_time::{Duration, TICK_HZ}; |         use embassy_time::{Duration, TICK_HZ}; | ||||||
|  |  | ||||||
|         use crate::rcc::get_freqs; |         use crate::rcc::get_freqs; | ||||||
| @@ -162,117 +74,74 @@ impl super::Rtc { | |||||||
|         let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; |         let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; | ||||||
|         let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); |         let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | ||||||
|  |  | ||||||
|         // adjust the rtc ticks to the prescaler |         // adjust the rtc ticks to the prescaler and subtract one rtc tick | ||||||
|         let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64); |         let rtc_ticks = rtc_ticks / prescaler as u64; | ||||||
|         let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { |         let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { | ||||||
|             u16::MAX - 1 |             u16::MAX - 1 | ||||||
|         } else { |         } else { | ||||||
|             rtc_ticks as u16 |             rtc_ticks as u16 | ||||||
|         }; |         } | ||||||
|  |         .saturating_sub(1); | ||||||
|         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()); |  | ||||||
|  |  | ||||||
|         self.write(false, |regs| { |         self.write(false, |regs| { | ||||||
|             regs.cr().modify(|w| w.set_wutie(true)); |  | ||||||
|  |  | ||||||
|             regs.cr().modify(|w| w.set_wute(false)); |             regs.cr().modify(|w| w.set_wute(false)); | ||||||
|             regs.isr().modify(|w| w.set_wutf(false)); |             regs.isr().modify(|w| w.set_wutf(false)); | ||||||
|             while !regs.isr().read().wutwf() {} |             while !regs.isr().read().wutwf() {} | ||||||
|  |  | ||||||
|             regs.cr().modify(|w| w.set_wucksel(prescaler.into())); |             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_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(feature = "low-power")] | ||||||
|     #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] |     pub(crate) fn enable_wakeup_line(&self) { | ||||||
|     /// stop the wakeup alarm and return the time remaining |         use crate::pac::EXTI; | ||||||
|     /// |  | ||||||
|     /// note: this api is exposed for testing purposes until low power is implemented. |         EXTI.rtsr(0).modify(|w| w.set_line(22, true)); | ||||||
|     /// it is not intended to be public |         EXTI.imr(0).modify(|w| w.set_line(22, true)); | ||||||
|     pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { |     } | ||||||
|         trace!("disable wakeup timer..."); |  | ||||||
|  |     #[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| { |         self.write(false, |regs| { | ||||||
|  |             regs.cr().modify(|w| w.set_wutie(false)); | ||||||
|             regs.cr().modify(|w| w.set_wute(false)); |             regs.cr().modify(|w| w.set_wute(false)); | ||||||
|             regs.isr().modify(|w| w.set_wutf(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() |         critical_section::with(|cs| { | ||||||
|     } |             if let Some(stop_time) = self.stop_time.borrow(cs).take() { | ||||||
|  |                 Some(self.instant() - stop_time) | ||||||
|     #[allow(dead_code)] |             } else { | ||||||
|     pub(crate) fn set_clock_source(clock_source: RtcClockSource) { |                 None | ||||||
|         #[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()); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Applies the RTC config |     /// Applies the RTC config | ||||||
|     /// It this changes the RTC clock source the time will be reset |     /// 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| { |         self.write(true, |rtc| { | ||||||
|             rtc.cr().modify(|w| { |             rtc.cr().modify(|w| { | ||||||
|                 #[cfg(rtc_v2f2)] |                 #[cfg(rtc_v2f2)] | ||||||
| @@ -284,8 +153,8 @@ impl super::Rtc { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             rtc.prer().modify(|w| { |             rtc.prer().modify(|w| { | ||||||
|                 w.set_prediv_s(rtc_config.sync_prescaler); |                 w.set_prediv_s(sync_psc); | ||||||
|                 w.set_prediv_a(rtc_config.async_prescaler); |                 w.set_prediv_a(async_psc); | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -401,9 +270,18 @@ impl sealed::Instance for crate::peripherals::RTC { | |||||||
|         } |         } | ||||||
|         #[cfg(any(rtc_v2f2))] |         #[cfg(any(rtc_v2f2))] | ||||||
|         { |         { | ||||||
|  |             // enable peripheral clock for communication | ||||||
|             crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); |             crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||||||
|  |  | ||||||
|  |             // read to allow the pwr clock to enable | ||||||
|             crate::pac::PWR.cr().read(); |             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> { |     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 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::pac::rtc::Rtc; | ||||||
| use crate::peripherals::RTC; | use crate::peripherals::RTC; | ||||||
| use crate::rtc::sealed::Instance; | use crate::rtc::sealed::Instance; | ||||||
|  |  | ||||||
| impl super::Rtc { | 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 |     /// Applies the RTC config | ||||||
|     /// It this changes the RTC clock source the time will be reset |     /// 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| { |         self.write(true, |rtc| { | ||||||
|             rtc.cr().modify(|w| { |             rtc.cr().modify(|w| { | ||||||
|                 w.set_fmt(Fmt::TWENTYFOURHOUR); |                 w.set_fmt(Fmt::TWENTYFOURHOUR); | ||||||
| @@ -80,8 +17,8 @@ impl super::Rtc { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             rtc.prer().modify(|w| { |             rtc.prer().modify(|w| { | ||||||
|                 w.set_prediv_s(rtc_config.sync_prescaler); |                 w.set_prediv_s(sync_psc); | ||||||
|                 w.set_prediv_a(rtc_config.async_prescaler); |                 w.set_prediv_a(async_psc); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // TODO: configuration for output pins |             // TODO: configuration for output pins | ||||||
| @@ -191,6 +128,23 @@ impl super::Rtc { | |||||||
| impl sealed::Instance for crate::peripherals::RTC { | impl sealed::Instance for crate::peripherals::RTC { | ||||||
|     const BACKUP_REGISTER_COUNT: usize = 32; |     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> { |     fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> { | ||||||
|         #[allow(clippy::if_same_then_else)] |         #[allow(clippy::if_same_then_else)] | ||||||
|         if register < Self::BACKUP_REGISTER_COUNT { |         if register < Self::BACKUP_REGISTER_COUNT { | ||||||
|   | |||||||
| @@ -33,6 +33,8 @@ impl<T: Instance> InterruptHandler<T> { | |||||||
|             w.set_dtimeoutie(enable); |             w.set_dtimeoutie(enable); | ||||||
|             w.set_dataendie(enable); |             w.set_dataendie(enable); | ||||||
|  |  | ||||||
|  |             #[cfg(sdmmc_v1)] | ||||||
|  |             w.set_stbiterre(enable); | ||||||
|             #[cfg(sdmmc_v2)] |             #[cfg(sdmmc_v2)] | ||||||
|             w.set_dabortie(enable); |             w.set_dabortie(enable); | ||||||
|         }); |         }); | ||||||
| @@ -102,6 +104,8 @@ pub enum Error { | |||||||
|     BadClock, |     BadClock, | ||||||
|     SignalingSwitchFailed, |     SignalingSwitchFailed, | ||||||
|     PeripheralBusy, |     PeripheralBusy, | ||||||
|  |     #[cfg(sdmmc_v1)] | ||||||
|  |     StBitErr, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A SD command | /// A SD command | ||||||
| @@ -707,9 +711,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|  |  | ||||||
|             if status.dcrcfail() { |             if status.dcrcfail() { | ||||||
|                 return Poll::Ready(Err(Error::Crc)); |                 return Poll::Ready(Err(Error::Crc)); | ||||||
|             } else if status.dtimeout() { |             } | ||||||
|  |             if status.dtimeout() { | ||||||
|                 return Poll::Ready(Err(Error::Timeout)); |                 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(())); |                 return Poll::Ready(Ok(())); | ||||||
|             } |             } | ||||||
|             Poll::Pending |             Poll::Pending | ||||||
| @@ -782,9 +792,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|  |  | ||||||
|             if status.dcrcfail() { |             if status.dcrcfail() { | ||||||
|                 return Poll::Ready(Err(Error::Crc)); |                 return Poll::Ready(Err(Error::Crc)); | ||||||
|             } else if status.dtimeout() { |             } | ||||||
|  |             if status.dtimeout() { | ||||||
|                 return Poll::Ready(Err(Error::Timeout)); |                 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(())); |                 return Poll::Ready(Ok(())); | ||||||
|             } |             } | ||||||
|             Poll::Pending |             Poll::Pending | ||||||
| @@ -836,6 +852,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|             w.set_dataendc(true); |             w.set_dataendc(true); | ||||||
|             w.set_dbckendc(true); |             w.set_dbckendc(true); | ||||||
|             w.set_sdioitc(true); |             w.set_sdioitc(true); | ||||||
|  |             #[cfg(sdmmc_v1)] | ||||||
|  |             w.set_stbiterrc(true); | ||||||
|  |  | ||||||
|             #[cfg(sdmmc_v2)] |             #[cfg(sdmmc_v2)] | ||||||
|             { |             { | ||||||
| @@ -873,9 +891,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|  |  | ||||||
|             if status.dcrcfail() { |             if status.dcrcfail() { | ||||||
|                 return Poll::Ready(Err(Error::Crc)); |                 return Poll::Ready(Err(Error::Crc)); | ||||||
|             } else if status.dtimeout() { |             } | ||||||
|  |             if status.dtimeout() { | ||||||
|                 return Poll::Ready(Err(Error::Timeout)); |                 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(())); |                 return Poll::Ready(Ok(())); | ||||||
|             } |             } | ||||||
|             Poll::Pending |             Poll::Pending | ||||||
| @@ -1156,9 +1180,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|  |  | ||||||
|             if status.dcrcfail() { |             if status.dcrcfail() { | ||||||
|                 return Poll::Ready(Err(Error::Crc)); |                 return Poll::Ready(Err(Error::Crc)); | ||||||
|             } else if status.dtimeout() { |             } | ||||||
|  |             if status.dtimeout() { | ||||||
|                 return Poll::Ready(Err(Error::Timeout)); |                 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(())); |                 return Poll::Ready(Ok(())); | ||||||
|             } |             } | ||||||
|             Poll::Pending |             Poll::Pending | ||||||
| @@ -1207,9 +1237,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||||||
|  |  | ||||||
|             if status.dcrcfail() { |             if status.dcrcfail() { | ||||||
|                 return Poll::Ready(Err(Error::Crc)); |                 return Poll::Ready(Err(Error::Crc)); | ||||||
|             } else if status.dtimeout() { |             } | ||||||
|  |             if status.dtimeout() { | ||||||
|                 return Poll::Ready(Err(Error::Timeout)); |                 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(())); |                 return Poll::Ready(Ok(())); | ||||||
|             } |             } | ||||||
|             Poll::Pending |             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